From c8dc13785a7f0c718d9052bca9843b199f1217e0 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 2 Jan 2020 10:36:43 +0100 Subject: [PATCH 001/528] start implementation of bc 3d --- examples/builddual.jl | 38 ++++---- examples/ex_dual2forms.jl | 67 ++++++++++++++ src/BEAST.jl | 1 + src/bases/dual3d.jl | 168 ++++++++++++++++++++++++++++++++++ src/bases/lagrange.jl | 20 ++++ src/bases/local/ndlcdlocal.jl | 36 ++++++++ src/bases/ndlcdspace.jl | 2 + src/identityop.jl | 11 +++ test/runtests.jl | 1 + test/test_ndlcd_restrict.jl | 11 +++ 10 files changed, 336 insertions(+), 19 deletions(-) create mode 100644 examples/ex_dual2forms.jl create mode 100644 src/bases/dual3d.jl create mode 100644 test/test_ndlcd_restrict.jl diff --git a/examples/builddual.jl b/examples/builddual.jl index 4d965626..c7f44294 100644 --- a/examples/builddual.jl +++ b/examples/builddual.jl @@ -21,30 +21,30 @@ for (i,face) in enumerate(cells(faces)) push!(patch_idcs, i) end end -patch = Mesh(vertices(faces), cells(faces)[patch_idcs]) +ptch = Mesh(vertices(faces), cells(faces)[patch_idcs]) -port = Mesh(vertices(edges), filter(c -> port_idx in c, cells(boundary(patch)))) +port = Mesh(vertices(edges), filter(c -> port_idx in c, cells(boundary(ptch)))) -@show numcells(patch) +@show numcells(ptch) @show numcells(port) # D, C, d, c, d0, d1, -RT_int, RT_prt, x_int, x_prt = BEAST.buildhalfbc2(patch, port, nothing) - -BF = BEAST.Shape{Float64}[] -for (m,bf) in enumerate(RT_int.fns) - for sh in bf - cellid = patch_idcs[sh.cellid] - BEAST.add!(BF,cellid, sh.refid, sh.coeff * x_int[m]) - end -end - -for (m,bf) in enumerate(RT_prt.fns) - for sh in bf - cellid = patch_idcs[sh.cellid] - BEAST.add!(BF,cellid, sh.refid, sh.coeff * x_prt[m]) - end -end +# RT_int, RT_prt, x_int, x_prt = BEAST.buildhalfbc2(ptch, port, nothing) +# +# BF = BEAST.Shape{Float64}[] +# for (m,bf) in enumerate(RT_int.fns) +# for sh in bf +# cellid = patch_idcs[sh.cellid] +# BEAST.add!(BF,cellid, sh.refid, sh.coeff * x_int[m]) +# end +# end +# +# for (m,bf) in enumerate(RT_prt.fns) +# for sh in bf +# cellid = patch_idcs[sh.cellid] +# BEAST.add!(BF,cellid, sh.refid, sh.coeff * x_prt[m]) +# end +# end # RT_prt = raviartthomas(patch, cellpairs(patch, port)) diff --git a/examples/ex_dual2forms.jl b/examples/ex_dual2forms.jl new file mode 100644 index 00000000..670aad7b --- /dev/null +++ b/examples/ex_dual2forms.jl @@ -0,0 +1,67 @@ +using CompScienceMeshes +using BEAST + +Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.35) + +cells_Bndry = [sort(c) for c in cells(boundary(Tetrs))] +Edges = submesh(skeleton(Tetrs,1)) do Edge + sort(Edge) in cells_Bndry ? false : true +end +Edges = Mesh(vertices(Edges), cells(Edges)) +@show numcells(Edges) + +# pred = CompScienceMeshes.interior_tpredicate(Tetrs) +# AllFaces = skeleton(Tetrs,2) +# sm = submesh(pred, AllFaces) + +Y = BEAST.dual2forms(Tetrs, Edges) + + +import PlotlyJS +function showfn(space,i) + geo = geometry(space) + T = coordtype(geo) + X = Dict{Int,T}() + Y = Dict{Int,T}() + Z = Dict{Int,T}() + U = Dict{Int,T}() + V = Dict{Int,T}() + W = Dict{Int,T}() + for sh in space.fns[i] + chrt = chart(geo, cells(geo)[sh.cellid]) + nbd = center(chrt) + vals = refspace(space)(nbd) + x,y,z = cartesian(nbd) + # @show vals[sh.refid].value + u,v,w = vals[sh.refid].value + # @show x, y, z + # @show u, v, w + X[sh.cellid] = x + Y[sh.cellid] = y + Z[sh.cellid] = z + U[sh.cellid] = get(U,sh.cellid,zero(T)) + sh.coeff * u + V[sh.cellid] = get(V,sh.cellid,zero(T)) + sh.coeff * v + W[sh.cellid] = get(W,sh.cellid,zero(T)) + sh.coeff * w + end + X = collect(values(X)) + Y = collect(values(Y)) + Z = collect(values(Z)) + U = collect(values(U)) + V = collect(values(V)) + W = collect(values(W)) + PlotlyJS.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) +end + + +function compress!(space) + T = scalartype(space) + for (i,fn) in pairs(space.fns) + shapes = Dict{Tuple{Int,Int},T}() + for shape in fn + v = get(shapes, (shape.cellid, shape.refid), zero(T)) + shapes[(shape.cellid, shape.refid)] = v + shape.coeff + # set!(shapes, (shape.cellid, shape.refid), v + shape.coeff) + end + space.fns[i] = [BEAST.Shape(k[1], k[2], v) for (k,v) in shapes] + end +end diff --git a/src/BEAST.jl b/src/BEAST.jl index 928b81b8..b63c4fd6 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -132,6 +132,7 @@ include("bases/ndspace.jl") include("bases/bdmdiv.jl") include("bases/ndlccspace.jl") include("bases/ndlcdspace.jl") +include("bases/dual3d.jl") include("bases/subdbasis.jl") diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl new file mode 100644 index 00000000..e1319a37 --- /dev/null +++ b/src/bases/dual3d.jl @@ -0,0 +1,168 @@ +using LinearAlgebra + +function builddual2form(support, port, dirichlet, prt_fluxes) + + println() + + faces = skeleton(support,2) + edges = skeleton(support,1) + bndry = boundary(support) + @show numcells(faces) + + cells_bndry = [sort(c) for c in cells(bndry)] + dirbnd = submesh(dirichlet) do face + sort(face) in cells(bndry) ? true : false + end + @show numcells(support) + @show numcells(dirbnd) + # @assert numcells(dirbnd) ≤ 4 + + bnd_dirbnd = boundary(dirbnd) + edges_dirbnd = skeleton(dirbnd,0) + cells_bnd_dirbnd = [sort(c) for c in cells(bnd_dirbnd)] + int_edges_dirbnd = submesh(edges_dirbnd) do edge + sort(edge) in cells(bnd_dirbnd) ? false : true + end + @show numcells(int_edges_dirbnd) + # @assert numcells(int_edges_dirbnd) ≤ 2 + + int_pred = interior_tpredicate(support) + num_faces_on_port = 0 + num_faces_on_dirc = 0 + + + # for edge in cells(faces) + # println(edge) + # end + # println() + # for edge in cells(dirbnd) + # println(edge) + # end + # println() + # for edge in cells(port) + # println(edge) + # end + + cells_port = [sort(c) for c in cells(port)] + cells_dirbnd = [sort(c) for c in cells(dirbnd)] + int_faces = submesh(faces) do face + sort(face) in cells_port && return false + sort(face) in cells_dirbnd && return true + int_pred(face) ? true : false + end + println() + for edge in cells(int_faces) + println(edge) + end + @show numcells(int_faces) + @show num_faces_on_port + @show num_faces_on_dirc + + bnd_edges = skeleton(bndry,1) + prt_edges = skeleton(port,1) + + cells_int_edges_dirbnd = [sort(c) for c in cells(int_edges_dirbnd)] + cells_bnd_edges = [sort(c) for c in cells(bnd_edges)] + cells_prt_edges = [sort(c) for c in cells(prt_edges)] + int_edges = submesh(edges) do edge + sort(edge) in cells_int_edges_dirbnd && return true + sort(edge) in cells_bnd_edges && return false + sort(edge) in cells_prt_edges && return false + return true + end + @show numcells(int_edges) + + RT_int = nedelecd3d(support, int_faces) + RT_prt = nedelecd3d(support, port) + # vertex_list = [c[1] for c in cells(int_edges)] + # L0_int = lagrangec0d1(suppport, vertex_list, Val{4}) + L0_int = nedelecc3d(support, int_edges) + + + Id = BEAST.Identity() + D = assemble(Id, divergence(RT_int), divergence(RT_int)) + Q = assemble(Id, divergence(RT_int), divergence(RT_prt)) + d = -Q * prt_fluxes + + @show numfunctions(L0_int) + # @assert numfunctions(L0_int) in [1,2] + C = assemble(Id, curl(L0_int), RT_int) + curl_L0_int = curl(L0_int) + c = real(assemble(Id, curl_L0_int, RT_prt)) * prt_fluxes + + x1 = pinv(D) * d + N = nullspace(D) + @show size(N) + @show size(C) + p = (C*N) \ (c - C*x1) + x = x1 + N*p + + @assert D*x ≈ d atol=1e-8 + if !isapprox(C*x, c, atol=1e-8) + @show norm(C*x-c) + error("error") + end + + return RT_int, RT_prt, x, prt_fluxes + +end + + +function dual2forms(Tetrs, Edges) + + tetrs = barycentric_refinement(Tetrs) + # Bndry = boundary(Tetrs) + + T = coordtype(Tetrs) + bfs = Vector{Vector{Shape{T}}}(undef, numcells(Edges)) + pos = Vector{vertextype(Edges)}(undef, numcells(Edges)) + dirichlet = boundary(tetrs) + for (F,Edge) in enumerate(cells(Edges)) + bfs[F] = Vector{Shape{T}}() + + pos[F] = cartesian(center(chart(Edges,Edge))) + port_vertex_idx = argmin(norm.(vertices(tetrs) .- Ref(pos[F]))) + + # Build the dual support + ptch_idcs1 = [i for (i,tetr) in enumerate(cells(tetrs)) if Edge[1] in tetr] + ptch_idcs2 = [i for (i,tetr) in enumerate(cells(tetrs)) if Edge[2] in tetr] + patch1 = Mesh(vertices(tetrs), cells(tetrs)[ptch_idcs1]) + patch2 = Mesh(vertices(tetrs), cells(tetrs)[ptch_idcs2]) + patch_bnd = boundary(patch1) + # @assert CompScienceMeshes.isoriented(patch_bnd) + port = Mesh(vertices(tetrs), filter(c->port_vertex_idx in c, cells(patch_bnd))) + # @assert CompScienceMeshes.isoriented(port) + patch = CompScienceMeshes.union(patch1, patch2) + @show numcells(patch_bnd) + @show numcells(patch) + @show numcells(port) + # @assert numcells(skeleton(patch,1))+2 == numcells(skeleton(patch1,1)) + numcells(skeleton(patch2,1)) + + # @assert numcells(patch) >= 6 + # @assert numcells(port) == 2 + prt_fluxes = ones(T, numcells(port)) / numcells(port) + tgt = vertices(Edges)[Edge[1]] - vertices(Edges)[Edge[2]] + for (i,face) in enumerate(cells(port)) + chrt = chart(port, face) + prt_fluxes[i] *= sign(dot(normal(chrt), tgt)) + end + RT_int, RT_prt, x_int, x_prt = builddual2form(patch, port, dirichlet, prt_fluxes) + + ptch_idcs = vcat(ptch_idcs1, ptch_idcs2) + for (m,bf) in enumerate(RT_int.fns) + for sh in bf + cellid = ptch_idcs[sh.cellid] + BEAST.add!(bfs[F], cellid, sh.refid, sh.coeff * x_int[m]) + end + end + + for (m,bf) in enumerate(RT_prt.fns) + for sh in bf + cellid = ptch_idcs[sh.cellid] + BEAST.add!(bfs[F],cellid, sh.refid, sh.coeff * x_prt[m]) + end + end + end + + NDLCDBasis(tetrs, bfs, pos) +end diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 5bdd4e8a..bbf61add 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -259,7 +259,27 @@ end +function lagrangec0d1(mesh::Mesh{3,4}, nodes::Mesh{3,2}) + T = coordtype(mesh) + P = vertextype(mesh) + S = Shape{T} + fns = [Vector{S}() for i in 1:numcells(nodes) ] + pos = Vector{P}(undef, numcells(nodes)) + + D = connectivity(nodes, mesh) + rows = rowvals(D) + vals = nonzeros(D) + for (j,node) in enumerate(cells(nodes)) + for k in nzrange(D,j) + i = rows[k] + locid = abs(vals[k]) + push!(fns[j], S(i, locid, 1.0)) + end + pos[j] = cartesian(center(chart(nodes, node))) + end + LagrangeBasis{1,0,4}(mesh, fns, pos) +end duallagrangec0d1(mesh) = duallagrangec0d1(mesh, barycentric_refinement(mesh), x->false, Val{dimension(mesh)+1}) diff --git a/src/bases/local/ndlcdlocal.jl b/src/bases/local/ndlcdlocal.jl index 72773d50..81e979db 100644 --- a/src/bases/local/ndlcdlocal.jl +++ b/src/bases/local/ndlcdlocal.jl @@ -68,3 +68,39 @@ function ttrace(x::NDLCDRefSpace, el, q, fc) end return t end + +divergence(ref::NDLCDRefSpace, sh, el) = Shape(sh.cellid, 1, sh.coeff/volume(el)) + + +function restrict(ϕ::NDLCDRefSpace{T}, dom1, dom2) where {T} + # dom2 is the smaller of the domains + + K = numfunctions(ϕ) + D = dimension(dom1) + + @assert K == 4 + @assert D == 3 + @assert D == dimension(dom2) + + Q = zeros(T,K,K) + for (i,face) in enumerate(faces(dom2)) + + p = center(face) + c = cartesian(p) + A = volume(face) + + m = normal(p) + u = carttobary(dom1,p) + + u = carttobary(dom1, c) + x = neighborhood(dom1, u) + + y = ϕ(x) + + for j in 1:K + Q[j,i] = -dot(y[j].value, m) * A + end + end + + return Q +end diff --git a/src/bases/ndlcdspace.jl b/src/bases/ndlcdspace.jl index 45c11985..2622c11a 100644 --- a/src/bases/ndlcdspace.jl +++ b/src/bases/ndlcdspace.jl @@ -42,3 +42,5 @@ end ntrace(X::NDLCDBasis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(X.pos)) ttrace(X::NDLCDBasis, geo, fns) = NDBasis{}(geo, fns, deepcopy(X.pos)) + +divergence(space::NDLCDBasis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, space.pos) diff --git a/src/identityop.jl b/src/identityop.jl index 273a0c0d..e1af7d11 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -50,6 +50,8 @@ function quaddata(op::LocalOperator, g::NDLCDRefSpace, f::NDLCDRefSpace, tels, b [(w, parametric(p)) for (p,w) in qps] end + + quaddata(op::LocalOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, tels::Vector, bels::Vector) = quaddata(op, g, f, tels, bels, Val{dimension(tels[1])}) @@ -68,6 +70,15 @@ function quaddata(op::LocalOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, [(w[i], SVector(u[1,i], u[2,i])) for i in 1:length(w)] end +function quaddata(op::LocalOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, + tels, bels, dim::Type{Val{3}}) + + o, x, y, z = CompScienceMeshes.euclidianbasis(3) + reftet = simplex(x,y,z,o) + qps = quadpoints(reftet, 6) + [(w, parametric(p)) for (p,w) in qps] +end + function quadrule(op::LocalOperator, ψ::RefSpace, ϕ::RefSpace, τ, qd) q = qd[1] diff --git a/test/runtests.jl b/test/runtests.jl index 9331a843..a0783370 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -32,6 +32,7 @@ include("test_rtports.jl") include("test_ndjunction.jl") include("test_ndspace.jl") include("test_restrict.jl") +include("test_ndlcd_restrict.jl") include("test_gram.jl") include("test_vector_gram.jl") diff --git a/test/test_ndlcd_restrict.jl b/test/test_ndlcd_restrict.jl new file mode 100644 index 00000000..37bec04b --- /dev/null +++ b/test/test_ndlcd_restrict.jl @@ -0,0 +1,11 @@ +using CompScienceMeshes +using BEAST +using Test +using LinearAlgebra + +o, x, y, z = CompScienceMeshes.euclidianbasis(3) +tet = simplex(x,y,z,o) + +rs = BEAST.NDLCDRefSpace{Float64}() +Q = BEAST.restrict(rs, tet, tet) +@test Q ≈ Matrix(1.0LinearAlgebra.I, 4, 4) From d499c0af2cce121491dee41e7a418937d25cd5a9 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 9 Jan 2020 16:51:32 +0100 Subject: [PATCH 002/528] private part of the dual whitney work --- examples/ex_dual2forms.jl | 146 ++++++++++++++++++++++++++++++++++---- src/bases/dual3d.jl | 46 +++++++++--- 2 files changed, 166 insertions(+), 26 deletions(-) diff --git a/examples/ex_dual2forms.jl b/examples/ex_dual2forms.jl index 670aad7b..cf151620 100644 --- a/examples/ex_dual2forms.jl +++ b/examples/ex_dual2forms.jl @@ -1,9 +1,35 @@ using CompScienceMeshes using BEAST +using LinearAlgebra + +const Id = BEAST.Identity() + +function add!(fn, x, space) + for (m,bf) in enumerate(space.fns) + for sh in bf + BEAST.add!(fn, sh.cellid, sh.refid, sh.coeff * x[m]) + end + end +end + + +function compress!(space) + T = scalartype(space) + for (i,fn) in pairs(space.fns) + shapes = Dict{Tuple{Int,Int},T}() + for shape in fn + v = get(shapes, (shape.cellid, shape.refid), zero(T)) + shapes[(shape.cellid, shape.refid)] = v + shape.coeff + # set!(shapes, (shape.cellid, shape.refid), v + shape.coeff) + end + space.fns[i] = [BEAST.Shape(k[1], k[2], v) for (k,v) in shapes] + end +end Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.35) +tetrs = barycentric_refinement(Tetrs) -cells_Bndry = [sort(c) for c in cells(boundary(Tetrs))] +cells_Bndry = [sort(c) for c in cells(skeleton(boundary(Tetrs),1))] Edges = submesh(skeleton(Tetrs,1)) do Edge sort(Edge) in cells_Bndry ? false : true end @@ -14,6 +40,110 @@ Edges = Mesh(vertices(Edges), cells(Edges)) # AllFaces = skeleton(Tetrs,2) # sm = submesh(pred, AllFaces) +E = 102 + +Edge = cells(Edges)[E] +pos = cartesian(center(chart(Edges, Edge))) +v = argmin(norm.(vertices(tetrs) .- Ref(pos))) +support1 = submesh(tetr -> Edge[1] in tetr, tetrs.mesh) +support2 = submesh(tetr -> Edge[2] in tetr, tetrs.mesh) +port = submesh(face -> v in face, boundary(support1)) +port2 = submesh(face -> v in face, boundary(support2)) +for p in port + @assert sort(p) in sort.(port2) +end +@show length(port) + +support = CompScienceMeshes.union(support1, support2) +@assert CompScienceMeshes.isoriented(support) +@show length(support) +@assert length(skeleton(support1,2)) + length(skeleton(support2,2)) == + length(skeleton(support,2)) + length(port) +edges = skeleton(support,1) + +cells_bnd_edges = [sort(c) for c in skeleton(boundary(support),1)] +cells_prt_edges = [sort(c) for c in skeleton(port,1)] +cells_dir_edges = [sort(c) for c in skeleton(boundary(tetrs),1)] + +bnd_support = boundary(support) +cells_bnd_tetrs = sort.(boundary(tetrs)) +dir_cap_bnd = submesh(face -> sort(face) in cells_bnd_tetrs, bnd_support) +cells_bnd_dir_cap_bnd = sort.(boundary(dir_cap_bnd)) + +int_edges = submesh(edges) do edge + sort(edge) in cells_bnd_dir_cap_bnd && return false + sort(edge) in cells_dir_edges && return true + sort(edge) in cells_bnd_edges && return false + sort(edge) in cells_prt_edges && return false + return true +end +@show length(int_edges) + +Nd_int = BEAST.nedelecc3d(support, int_edges) +Q = assemble(Id, Nd_int, Nd_int) + +Q2, store = BEAST.allocatestorage(Id, Nd_int, Nd_int, + Val{:bandedstorage}, BEAST.LongDelays{:ignore}) +BEAST.assemble_local_mixed!(Id, Nd_int, Nd_int, store) + +@assert Q ≈ Q2 + +Q3 = assemble(Id, curl(Nd_int), curl(Nd_int)) +rank(Q3) + +cells_bnd_faces = [sort(c) for c in boundary(support)] +cells_prt_faces = [sort(c) for c in port] +int_faces = submesh(skeleton(support,2)) do face + sort(face) in cells_bnd_tetrs && return true + sort(face) in cells_bnd_faces && return false + sort(face) in cells_prt_faces && return false + return true +end +@show length(int_faces) +@assert length(int_faces) + length(boundary(support)) + length(port) == + length(skeleton(support,2)) + length(dir_cap_bnd) + +RT_int = BEAST.nedelecd3d(support, int_faces) +for (m,fn) in enumerate(RT_int.fns) + ct = count(sh -> sh.cellid <= length(support1), fn) + # @show m, ct + @assert ct == 2 || ct == 0 +end +Q4 = assemble(Id, divergence(RT_int), divergence(RT_int)) +numfunctions(RT_int) - rank(Q4) + +RT_prt = BEAST.nedelecd3d(support, port) +for fn in RT_prt.fns + @assert count(sh -> sh.cellid <= length(support1), fn) == 1 + @assert count(sh -> sh.cellid > length(support1), fn) == 1 +end +Q5 = assemble(Id, divergence(RT_int), divergence(RT_int)) +x0 = ones(length(port)) / length(port) +tgt = vertices(Edges)[Edge[1]] - vertices(Edges)[Edge[2]] +for (i,face) in enumerate(port) + x0[i] *= sign(dot(normal(chart(port,face)), tgt)) + # if RT_prt.fns[i][1].coeff < 0 + # x0[i] *= -1 + # end +end +Q6 = assemble(Id, divergence(RT_int), divergence(RT_prt)) +d = -Q6 * x0 +x1 = pinv(Q5) * d + +# L0 = lagrangecxd0(support) +# Q7 = assemble(Id, L0, divergence(RT_int)) +# Q8 = assemble(Id, L0, L0) +# ch1 = [] +# ch2 = [] +# ch = [ch1; ch2] +# x1 = pinv(Q7) * () + +fn = BEAST.Shape{Float64}[] +add!(fn, x0, RT_prt) +add!(fn, x1, RT_int) +Y1 = BEAST.NDLCDBasis(support, [fn], [pos]) +divY1 = divergence(Y1); compress!(divY1) + Y = BEAST.dual2forms(Tetrs, Edges) @@ -51,17 +181,3 @@ function showfn(space,i) W = collect(values(W)) PlotlyJS.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) end - - -function compress!(space) - T = scalartype(space) - for (i,fn) in pairs(space.fns) - shapes = Dict{Tuple{Int,Int},T}() - for shape in fn - v = get(shapes, (shape.cellid, shape.refid), zero(T)) - shapes[(shape.cellid, shape.refid)] = v + shape.coeff - # set!(shapes, (shape.cellid, shape.refid), v + shape.coeff) - end - space.fns[i] = [BEAST.Shape(k[1], k[2], v) for (k,v) in shapes] - end -end diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index e1319a37..672d16d3 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -6,6 +6,8 @@ function builddual2form(support, port, dirichlet, prt_fluxes) faces = skeleton(support,2) edges = skeleton(support,1) + verts = skeleton(support,0) + @assert length(verts) - length(edges) + length(faces) - length(support) == 1 bndry = boundary(support) @show numcells(faces) @@ -18,7 +20,7 @@ function builddual2form(support, port, dirichlet, prt_fluxes) # @assert numcells(dirbnd) ≤ 4 bnd_dirbnd = boundary(dirbnd) - edges_dirbnd = skeleton(dirbnd,0) + edges_dirbnd = skeleton(dirbnd,1) cells_bnd_dirbnd = [sort(c) for c in cells(bnd_dirbnd)] int_edges_dirbnd = submesh(edges_dirbnd) do edge sort(edge) in cells(bnd_dirbnd) ? false : true @@ -30,6 +32,8 @@ function builddual2form(support, port, dirichlet, prt_fluxes) num_faces_on_port = 0 num_faces_on_dirc = 0 + @assert numcells(submesh(!int_pred, faces)) == numcells(boundary(support)) + # for edge in cells(faces) # println(edge) @@ -50,13 +54,13 @@ function builddual2form(support, port, dirichlet, prt_fluxes) sort(face) in cells_dirbnd && return true int_pred(face) ? true : false end - println() - for edge in cells(int_faces) - println(edge) - end + # println() + # for edge in cells(int_faces) + # println(edge) + # end @show numcells(int_faces) - @show num_faces_on_port - @show num_faces_on_dirc + # @show num_faces_on_port + # @show num_faces_on_dirc bnd_edges = skeleton(bndry,1) prt_edges = skeleton(port,1) @@ -64,6 +68,10 @@ function builddual2form(support, port, dirichlet, prt_fluxes) cells_int_edges_dirbnd = [sort(c) for c in cells(int_edges_dirbnd)] cells_bnd_edges = [sort(c) for c in cells(bnd_edges)] cells_prt_edges = [sort(c) for c in cells(prt_edges)] + + @show length(cells_int_edges_dirbnd) + @show length(cells_bnd_edges) + @show length(cells_prt_edges) int_edges = submesh(edges) do edge sort(edge) in cells_int_edges_dirbnd && return true sort(edge) in cells_bnd_edges && return false @@ -78,7 +86,6 @@ function builddual2form(support, port, dirichlet, prt_fluxes) # L0_int = lagrangec0d1(suppport, vertex_list, Val{4}) L0_int = nedelecc3d(support, int_edges) - Id = BEAST.Identity() D = assemble(Id, divergence(RT_int), divergence(RT_int)) Q = assemble(Id, divergence(RT_int), divergence(RT_prt)) @@ -86,23 +93,37 @@ function builddual2form(support, port, dirichlet, prt_fluxes) @show numfunctions(L0_int) # @assert numfunctions(L0_int) in [1,2] - C = assemble(Id, curl(L0_int), RT_int) curl_L0_int = curl(L0_int) + div_curl_L0_int = divergence(curl_L0_int) + ZZ = real(assemble(Id, div_curl_L0_int, div_curl_L0_int)) + @assert isapprox(norm(ZZ), 0.0, atol=1e-8) + C = assemble(Id, curl_L0_int, RT_int) c = real(assemble(Id, curl_L0_int, RT_prt)) * prt_fluxes x1 = pinv(D) * d N = nullspace(D) @show size(N) + @show rank(C) + @assert size(N,2) == rank(C) @show size(C) + # @assert rank(C) == size(C,1) p = (C*N) \ (c - C*x1) x = x1 + N*p - @assert D*x ≈ d atol=1e-8 - if !isapprox(C*x, c, atol=1e-8) + # D*x ≈ d atol=1e-8 + if !isapprox(C*x, c, atol=1e-8) || !isapprox(D*x, d, atol=1e-6) + @show norm(D*x-d) @show norm(C*x-c) + @show rank(C) + @show size(C,1) error("error") end + if rank(C) != size(C,1) + @show rank(C) + @show size(C) + end + return RT_int, RT_prt, x, prt_fluxes end @@ -118,6 +139,7 @@ function dual2forms(Tetrs, Edges) pos = Vector{vertextype(Edges)}(undef, numcells(Edges)) dirichlet = boundary(tetrs) for (F,Edge) in enumerate(cells(Edges)) + @show F bfs[F] = Vector{Shape{T}}() pos[F] = cartesian(center(chart(Edges,Edge))) @@ -162,6 +184,8 @@ function dual2forms(Tetrs, Edges) BEAST.add!(bfs[F],cellid, sh.refid, sh.coeff * x_prt[m]) end end + + # F == 25 && error("stop") end NDLCDBasis(tetrs, bfs, pos) From 38ad2606c3efc9562e5db3774da9f726049a35d2 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 9 Jan 2020 17:52:25 +0100 Subject: [PATCH 003/528] compute port as intersection of the dual domains --- examples/ex_dual2forms.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/ex_dual2forms.jl b/examples/ex_dual2forms.jl index 38d481cf..241fbcd8 100644 --- a/examples/ex_dual2forms.jl +++ b/examples/ex_dual2forms.jl @@ -44,11 +44,12 @@ E = 102 Edge = cells(Edges)[E] pos = cartesian(center(chart(Edges, Edge))) -v = argmin(norm.(vertices(tetrs) .- Ref(pos))) +# v = argmin(norm.(vertices(tetrs) .- Ref(pos))) support1 = submesh(tetr -> Edge[1] in tetr, tetrs.mesh) support2 = submesh(tetr -> Edge[2] in tetr, tetrs.mesh) -port = submesh(face -> v in face, boundary(support1)) -port2 = submesh(face -> v in face, boundary(support2)) +# port = submesh(face -> v in face, boundary(support1)) +# port2 = submesh(face -> v in face, boundary(support2)) +port = submesh(face -> sort(face) in sort.(boundary(support2)), boundary(support1)) for p in port @assert sort(p) in sort.(port2) end From 5d6501032ddc2cfd4ecefa010f229da6d8615dc6 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 10 Jan 2020 18:34:56 +0100 Subject: [PATCH 004/528] private part of the commit --- examples/ex_dual1forms.jl | 171 ++++++++++++++++++++++++++++++++++++++ src/bases/dual3d.jl | 116 ++++++++++++++++++++++++++ src/bases/lagrange.jl | 2 +- 3 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 examples/ex_dual1forms.jl diff --git a/examples/ex_dual1forms.jl b/examples/ex_dual1forms.jl new file mode 100644 index 00000000..efc5617f --- /dev/null +++ b/examples/ex_dual1forms.jl @@ -0,0 +1,171 @@ +using CompScienceMeshes +using BEAST +using LinearAlgebra + +const Id = BEAST.Identity() + +function add!(fn, x, space) + for (m,bf) in enumerate(space.fns) + for sh in bf + BEAST.add!(fn, sh.cellid, sh.refid, sh.coeff * x[m]) + end + end +end + + +function compress!(space) + T = scalartype(space) + for (i,fn) in pairs(space.fns) + shapes = Dict{Tuple{Int,Int},T}() + for shape in fn + v = get(shapes, (shape.cellid, shape.refid), zero(T)) + shapes[(shape.cellid, shape.refid)] = v + shape.coeff + # set!(shapes, (shape.cellid, shape.refid), v + shape.coeff) + end + space.fns[i] = [BEAST.Shape(k[1], k[2], v) for (k,v) in shapes] + end +end + +import PlotlyJS +function showfn(space,i) + geo = geometry(space) + T = coordtype(geo) + X = Dict{Int,T}() + Y = Dict{Int,T}() + Z = Dict{Int,T}() + U = Dict{Int,T}() + V = Dict{Int,T}() + W = Dict{Int,T}() + for sh in space.fns[i] + chrt = chart(geo, cells(geo)[sh.cellid]) + nbd = center(chrt) + vals = refspace(space)(nbd) + x,y,z = cartesian(nbd) + # @show vals[sh.refid].value + u,v,w = vals[sh.refid].value + # @show x, y, z + # @show u, v, w + X[sh.cellid] = x + Y[sh.cellid] = y + Z[sh.cellid] = z + U[sh.cellid] = get(U,sh.cellid,zero(T)) + sh.coeff * u + V[sh.cellid] = get(V,sh.cellid,zero(T)) + sh.coeff * v + W[sh.cellid] = get(W,sh.cellid,zero(T)) + sh.coeff * w + end + X = collect(values(X)) + Y = collect(values(Y)) + Z = collect(values(Z)) + U = collect(values(U)) + V = collect(values(V)) + W = collect(values(W)) + PlotlyJS.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) +end + +Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.35) +tetrs = barycentric_refinement(Tetrs) + +bnd_Tetrs = boundary(Tetrs) +bnd_tetrs = boundary(tetrs) +srt_bnd_Tetrs = sort.(bnd_Tetrs) +srt_bnd_tetrs = sort.(bnd_tetrs) + +Faces = submesh(Face -> !(sort(Face) in srt_bnd_Tetrs), skeleton(Tetrs,2)) +@show length(Faces) + +F = 603 +Face = cells(Faces)[F] +pos = cartesian(center(chart(Faces, Face))) + +supp1 = submesh(tet -> Face[1] in tet, tetrs.mesh) +supp2 = submesh(tet -> Face[2] in tet, tetrs.mesh) +supp3 = submesh(tet -> Face[3] in tet, tetrs.mesh) + +supp = CompScienceMeshes.union(supp1, supp2) +supp = CompScienceMeshes.union(supp, supp3) +# PlotlyJS.plot(patch(boundary(supp))) + +port = skeleton(supp1, 1) +port = submesh(edge -> sort(edge) in sort.(skeleton(supp2,1)), port) +port = submesh(edge -> sort(edge) in sort.(skeleton(supp3,1)), port) +@show length(port) +@assert 1 ≤ length(port) ≤ 2 + +dir = submesh(face -> sort(face) in srt_bnd_tetrs, boundary(supp)) +rim = boundary(dir) +@show length(dir) + +srt_port = sort.(port) +srt_dir_edges = sort.(skeleton(dir,1)) +srt_bnd_edges = sort.(skeleton(boundary(supp),1)) +srt_rim = sort.(rim) +int_edges = submesh(skeleton(supp,1)) do edge + sort(edge) in srt_port && return false + sort(edge) in srt_rim && return false + sort(edge) in srt_dir_edges && return true + sort(edge) in srt_bnd_edges && return false + return true +end +@show length(int_edges) +@assert length(skeleton(supp,0)) - length(skeleton(supp,1)) + + length(skeleton(supp,2)) - length(supp) == 1 + +Nd_prt = BEAST.nedelecc3d(supp, port) +Nd_int = BEAST.nedelecc3d(supp, int_edges) +@show numfunctions(Nd_int) + +x0 = ones(length(port)) / length(port) +for (i,edge) in enumerate(port) + tgt = tangents(center(chart(port,edge)),1) + if dot(normal(chart(Faces,Face)), tgt) < 0 + x0[i] *= -1 + end +end + +A = assemble(Id, curl(Nd_int), curl(Nd_int)) +N = nullspace(A) +@show size(N,2) +a = -assemble(Id, curl(Nd_int), curl(Nd_prt)) * x0 +x1 = pinv(A) * a +@show norm(N'*a) +@show norm(A*x1-a) + +srt_bnd_verts = sort.(skeleton(boundary(supp),0)) +srt_prt_verts = sort.(skeleton(port,0)) +int_verts = submesh(skeleton(supp,0)) do vert + sort(vert) in srt_bnd_verts && return false + sort(vert) in srt_prt_verts && return false + return true +end +@show length(int_verts) + +L0_int = lagrangec0d1(supp, int_verts) +@show numfunctions(L0_int) +grad_L0_int = BEAST.gradient(L0_int) +@assert numfunctions(grad_L0_int) == numfunctions(L0_int) +B = assemble(Id, grad_L0_int, Nd_int) + +B2, store = BEAST.allocatestorage(Id, grad_L0_int, Nd_int, + Val{:bandedstorage}, BEAST.LongDelays{:ignore}) +BEAST.assemble_local_mixed!(Id, grad_L0_int, Nd_int, store) +@assert isapprox(B, B2, atol=1e-8) +@show rank(B2) + +b = -assemble(Id, grad_L0_int, Nd_prt) * x0 +p = (B*N) \ (b-B*x1) +x1 = x1 + N*p + +@show norm(A*x1-a) +@show norm(B*x1-b) + +fn = BEAST.Shape{Float64}[] +add!(fn, x0, Nd_prt) +add!(fn, x1, Nd_int) + +Y1 = BEAST.NDLCCBasis(supp, [fn],[pos]) +curlY1 = curl(Y1); compress!(curl(Y1)); + +include(joinpath(@__DIR__, "utils/edge_values.jl")) +EV = edge_values(Y1,1) +check_edge_values(EV) + +Y = BEAST.dual1forms(Tetrs, Faces) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 672d16d3..44451770 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -190,3 +190,119 @@ function dual2forms(Tetrs, Edges) NDLCDBasis(tetrs, bfs, pos) end + + +function add!(fn::Vector{<:Shape}, x, space) + for (m,bf) in enumerate(space.fns) + for sh in bf + BEAST.add!(fn, sh.cellid, sh.refid, sh.coeff * x[m]) + end + end +end + +function builddual1form(supp, port, dir, x0) + + Id = BEAST.Identity() + + rim = boundary(dir) + bnd = boundary(supp) + supp_edges = skeleton(supp,1) + supp_nodes = skeleton(supp,0) + dir_edges = skeleton(dir,1) + bnd_edges = skeleton(bnd,1) + bnd_nodes = skeleton(bnd,0) + prt_nodes = skeleton(port,0) + + srt_rim = sort.(rim) + srt_port = sort.(port) + srt_dir_edges = sort.(dir_edges) + srt_bnd_edges = sort.(bnd_edges) + srt_bnd_verts = sort.(bnd_nodes) + srt_prt_verts = sort.(prt_nodes) + + int_edges = submesh(supp_edges) do edge + sort(edge) in srt_port && return false + sort(edge) in srt_rim && return false + sort(edge) in srt_dir_edges && return true + sort(edge) in srt_bnd_edges && return false + return true + end + @assert length(supp_nodes) - length(supp_edges) + + length(skeleton(supp,2)) - length(supp) == 1 + + Nd_prt = BEAST.nedelecc3d(supp, port) + Nd_int = BEAST.nedelecc3d(supp, int_edges) + + A = assemble(Id, curl(Nd_int), curl(Nd_int)) + N = nullspace(A) + a = -assemble(Id, curl(Nd_int), curl(Nd_prt)) * x0 + x1 = pinv(A) * a + + int_verts = submesh(supp_nodes) do vert + sort(vert) in srt_bnd_verts && return false + sort(vert) in srt_prt_verts && return false + return true + end + + L0_int = lagrangec0d1(supp, int_verts) + grad_L0_int = BEAST.gradient(L0_int) + @assert numfunctions(grad_L0_int) == numfunctions(L0_int) + + B = assemble(Id, grad_L0_int, Nd_int) + b = -assemble(Id, grad_L0_int, Nd_prt) * x0 + p = (B*N) \ (b-B*x1) + x1 = x1 + N*p + + @assert isapprox(A*x1, a, atol=1e-8) + @assert isapprox(B*x1, b, atol=1e-8) + + return Nd_int, Nd_prt, x1, x0 +end + +function dual1forms(Tetrs, Faces) + + T = coordtype(Tetrs) + P = vertextype(Tetrs) + S = Shape{T} + + tetrs = CompScienceMeshes.barycentric_refinement(Tetrs) + bnd_tetrs = boundary(tetrs) + srt_bnd_tetrs = sort.(bnd_tetrs) + + fns = Vector{Vector{S}}() + pos = Vector{P}() + for Face in Faces + + po = cartesian(center(chart(Faces, Face))) + supp1 = submesh(tet -> Face[1] in tet, tetrs.mesh) + supp2 = submesh(tet -> Face[2] in tet, tetrs.mesh) + supp3 = submesh(tet -> Face[3] in tet, tetrs.mesh) + + supp = CompScienceMeshes.union(supp1, supp2) + supp = CompScienceMeshes.union(supp, supp3) + + dir = submesh(face -> sort(face) in srt_bnd_tetrs, boundary(supp)) + + port = skeleton(supp1, 1) + port = submesh(edge -> sort(edge) in sort.(skeleton(supp2,1)), port) + port = submesh(edge -> sort(edge) in sort.(skeleton(supp3,1)), port) + @assert 1 ≤ length(port) ≤ 2 + + x0 = ones(length(port)) / length(port) + for (i,edge) in enumerate(port) + tgt = tangents(center(chart(port,edge)),1) + dot(normal(chart(Faces,Face)), tgt) < 0 && (x0[i] *= -1) + end + + Nd_int, Nd_prt, x_int, x_prt = builddual1form(supp, port, dir, x0) + + fn = Vector{S}() + add!(fn, x_prt, Nd_prt) + add!(fn, x_int, Nd_int) + + push!(fns, fn) + push!(pos, po) + end + + NDLCCBasis(tetrs, fns, pos) +end diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index bbf61add..bbf710bd 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -259,7 +259,7 @@ end -function lagrangec0d1(mesh::Mesh{3,4}, nodes::Mesh{3,2}) +function lagrangec0d1(mesh::Mesh{3,4}, nodes::Mesh{3,1}) T = coordtype(mesh) P = vertextype(mesh) From 66d8f492e27fe1e2b05583a43d27d563ea078d39 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sat, 11 Jan 2020 14:16:13 +0100 Subject: [PATCH 005/528] Fixed missing index translation --- examples/ex_dual1forms.jl | 6 ++++-- src/bases/dual3d.jl | 25 ++++++++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/examples/ex_dual1forms.jl b/examples/ex_dual1forms.jl index efc5617f..39375e67 100644 --- a/examples/ex_dual1forms.jl +++ b/examples/ex_dual1forms.jl @@ -26,7 +26,7 @@ function compress!(space) end end -import PlotlyJS +import Plotly function showfn(space,i) geo = geometry(space) T = coordtype(geo) @@ -58,7 +58,7 @@ function showfn(space,i) U = collect(values(U)) V = collect(values(V)) W = collect(values(W)) - PlotlyJS.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) + Plotly.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) end Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.35) @@ -168,4 +168,6 @@ include(joinpath(@__DIR__, "utils/edge_values.jl")) EV = edge_values(Y1,1) check_edge_values(EV) +error("stop") + Y = BEAST.dual1forms(Tetrs, Faces) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 44451770..b849a4ab 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -192,10 +192,11 @@ function dual2forms(Tetrs, Edges) end -function add!(fn::Vector{<:Shape}, x, space) +function addf!(fn::Vector{<:Shape}, x::Vector, space::Space, idcs::Vector{Int}) for (m,bf) in enumerate(space.fns) for sh in bf - BEAST.add!(fn, sh.cellid, sh.refid, sh.coeff * x[m]) + cellid = idcs[sh.cellid] + BEAST.add!(fn, cellid, sh.refid, sh.coeff * x[m]) end end end @@ -271,9 +272,21 @@ function dual1forms(Tetrs, Faces) fns = Vector{Vector{S}}() pos = Vector{P}() - for Face in Faces + for (F,Face) in enumerate(Faces) + @show F po = cartesian(center(chart(Faces, Face))) + + idcs1 = [i for (i,tet) in enumerate(tetrs) if Face[1] in tet] + idcs2 = [i for (i,tet) in enumerate(tetrs) if Face[2] in tet] + idcs3 = [i for (i,tet) in enumerate(tetrs) if Face[3] in tet] + idcs = vcat(idcs1, idcs2, idcs3) + # @show idcs + + supp1 = Mesh(vertices(tetrs), cells(tetrs)[idcs1]) + supp2 = Mesh(vertices(tetrs), cells(tetrs)[idcs1]) + supp3 = Mesh(vertices(tetrs), cells(tetrs)[idcs1]) + supp1 = submesh(tet -> Face[1] in tet, tetrs.mesh) supp2 = submesh(tet -> Face[2] in tet, tetrs.mesh) supp3 = submesh(tet -> Face[3] in tet, tetrs.mesh) @@ -295,10 +308,12 @@ function dual1forms(Tetrs, Faces) end Nd_int, Nd_prt, x_int, x_prt = builddual1form(supp, port, dir, x0) + # @show norm(x_int) + # @show norm(x_prt) fn = Vector{S}() - add!(fn, x_prt, Nd_prt) - add!(fn, x_int, Nd_int) + addf!(fn, x_prt, Nd_prt, idcs) + addf!(fn, x_int, Nd_int, idcs) push!(fns, fn) push!(pos, po) From 21b7c1e8faad96287511865117ea916e44eb02ed Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sat, 11 Jan 2020 15:49:47 +0100 Subject: [PATCH 006/528] optimised computation dual edge elements --- examples/ex_dual1forms.jl | 1 + src/bases/dual3d.jl | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/ex_dual1forms.jl b/examples/ex_dual1forms.jl index 39375e67..07bd5a3e 100644 --- a/examples/ex_dual1forms.jl +++ b/examples/ex_dual1forms.jl @@ -171,3 +171,4 @@ check_edge_values(EV) error("stop") Y = BEAST.dual1forms(Tetrs, Faces) +curlY = curl(Y) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index b849a4ab..a44a2112 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -207,10 +207,10 @@ function builddual1form(supp, port, dir, x0) rim = boundary(dir) bnd = boundary(supp) - supp_edges = skeleton(supp,1) - supp_nodes = skeleton(supp,0) + supp_edges = CompScienceMeshes.skeleton_fast(supp,1) + supp_nodes = CompScienceMeshes.skeleton_fast(supp,0) dir_edges = skeleton(dir,1) - bnd_edges = skeleton(bnd,1) + bnd_edges = CompScienceMeshes.skeleton_fast(bnd,1) bnd_nodes = skeleton(bnd,0) prt_nodes = skeleton(port,0) @@ -228,8 +228,7 @@ function builddual1form(supp, port, dir, x0) sort(edge) in srt_bnd_edges && return false return true end - @assert length(supp_nodes) - length(supp_edges) + - length(skeleton(supp,2)) - length(supp) == 1 + # @assert length(supp_nodes) - length(supp_edges) + length(skeleton(supp,2)) - length(supp) == 1 Nd_prt = BEAST.nedelecc3d(supp, port) Nd_int = BEAST.nedelecc3d(supp, int_edges) @@ -308,8 +307,8 @@ function dual1forms(Tetrs, Faces) end Nd_int, Nd_prt, x_int, x_prt = builddual1form(supp, port, dir, x0) - # @show norm(x_int) - # @show norm(x_prt) + @show norm(x_int) + @show norm(x_prt) fn = Vector{S}() addf!(fn, x_prt, Nd_prt, idcs) From 655b20ab880e9434ddde1b72e7365d102620005d Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sat, 11 Jan 2020 17:08:43 +0100 Subject: [PATCH 007/528] use smaller example --- examples/ex_dual1forms.jl | 13 +++++++++---- examples/ex_dual2forms.jl | 6 +++--- src/bases/dual3d.jl | 10 ++++++---- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/examples/ex_dual1forms.jl b/examples/ex_dual1forms.jl index 07bd5a3e..5a2e7f6b 100644 --- a/examples/ex_dual1forms.jl +++ b/examples/ex_dual1forms.jl @@ -61,7 +61,7 @@ function showfn(space,i) Plotly.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) end -Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.35) +Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.45) tetrs = barycentric_refinement(Tetrs) bnd_Tetrs = boundary(Tetrs) @@ -72,7 +72,7 @@ srt_bnd_tetrs = sort.(bnd_tetrs) Faces = submesh(Face -> !(sort(Face) in srt_bnd_Tetrs), skeleton(Tetrs,2)) @show length(Faces) -F = 603 +F = 396 Face = cells(Faces)[F] pos = cartesian(center(chart(Faces, Face))) @@ -121,11 +121,13 @@ for (i,edge) in enumerate(port) end end -A = assemble(Id, curl(Nd_int), curl(Nd_int)) +curl_Nd_int = curl(Nd_int) +A = assemble(Id, curl_Nd_int, curl_Nd_int) N = nullspace(A) @show size(N,2) -a = -assemble(Id, curl(Nd_int), curl(Nd_prt)) * x0 +a = -assemble(Id, curl_Nd_int, curl(Nd_prt)) * x0 x1 = pinv(A) * a +# x1 = A \ a @show norm(N'*a) @show norm(A*x1-a) @@ -172,3 +174,6 @@ error("stop") Y = BEAST.dual1forms(Tetrs, Faces) curlY = curl(Y) +CC = assemble(Id, curlY, curlY) + +Plotly.plot(svdvals(CC)) diff --git a/examples/ex_dual2forms.jl b/examples/ex_dual2forms.jl index 241fbcd8..998536bd 100644 --- a/examples/ex_dual2forms.jl +++ b/examples/ex_dual2forms.jl @@ -50,9 +50,9 @@ support2 = submesh(tetr -> Edge[2] in tetr, tetrs.mesh) # port = submesh(face -> v in face, boundary(support1)) # port2 = submesh(face -> v in face, boundary(support2)) port = submesh(face -> sort(face) in sort.(boundary(support2)), boundary(support1)) -for p in port - @assert sort(p) in sort.(port2) -end +# for p in port +# @assert sort(p) in sort.(port2) +# end @show length(port) support = CompScienceMeshes.union(support1, support2) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index a44a2112..d0203467 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -209,10 +209,10 @@ function builddual1form(supp, port, dir, x0) bnd = boundary(supp) supp_edges = CompScienceMeshes.skeleton_fast(supp,1) supp_nodes = CompScienceMeshes.skeleton_fast(supp,0) - dir_edges = skeleton(dir,1) + dir_edges = CompScienceMeshes.skeleton_fast(dir,1) bnd_edges = CompScienceMeshes.skeleton_fast(bnd,1) - bnd_nodes = skeleton(bnd,0) - prt_nodes = skeleton(port,0) + bnd_nodes = CompScienceMeshes.skeleton_fast(bnd,0) + prt_nodes = CompScienceMeshes.skeleton_fast(port,0) srt_rim = sort.(rim) srt_port = sort.(port) @@ -233,10 +233,12 @@ function builddual1form(supp, port, dir, x0) Nd_prt = BEAST.nedelecc3d(supp, port) Nd_int = BEAST.nedelecc3d(supp, int_edges) - A = assemble(Id, curl(Nd_int), curl(Nd_int)) + curl_Nd_int = curl(Nd_int) + A = assemble(Id, curl_Nd_int, curl_Nd_int) N = nullspace(A) a = -assemble(Id, curl(Nd_int), curl(Nd_prt)) * x0 x1 = pinv(A) * a + # x1 = A \ a int_verts = submesh(supp_nodes) do vert sort(vert) in srt_bnd_verts && return false From a4147cdc95a18d8f9f50f64567dfa708baaa436c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sat, 11 Jan 2020 17:39:54 +0100 Subject: [PATCH 008/528] compute loop connectivity matrix in ex_dual2forms --- examples/ex_dual2forms.jl | 8 +++++++- test/test_mult.jl | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 test/test_mult.jl diff --git a/examples/ex_dual2forms.jl b/examples/ex_dual2forms.jl index 998536bd..1ade225b 100644 --- a/examples/ex_dual2forms.jl +++ b/examples/ex_dual2forms.jl @@ -33,8 +33,14 @@ cells_Bndry = [sort(c) for c in cells(skeleton(boundary(Tetrs),1))] Edges = submesh(skeleton(Tetrs,1)) do Edge sort(Edge) in cells_Bndry ? false : true end -Edges = Mesh(vertices(Edges), cells(Edges)) +# Edges = Mesh(vertices(Edges), cells(Edges)) +srt_bnd_Faces = sort.(boundary(Tetrs)) +Faces = submesh(skeleton(Tetrs,2)) do Face + !(sort(Face) in srt_bnd_Faces) +end + @show numcells(Edges) +@show length(Faces) # pred = CompScienceMeshes.interior_tpredicate(Tetrs) # AllFaces = skeleton(Tetrs,2) diff --git a/test/test_mult.jl b/test/test_mult.jl new file mode 100644 index 00000000..d8b2081d --- /dev/null +++ b/test/test_mult.jl @@ -0,0 +1,32 @@ +using CompScienceMeshes +using BEAST + +using Test +using LinearAlgebra + +faces = meshrectangle(1.0, 1.0, 0.5, 3) +srt_bnd_faces = sort.(boundary(faces)) +edges = submesh(skeleton(faces,1)) do edge + !(sort(edge) in srt_bnd_faces) +end + +srt_bnd_nodes = sort.(skeleton(boundary(faces),0)) +@test length(srt_bnd_nodes) == 8 +nodes = submesh(skeleton(faces,0)) do node + !(sort(node) in srt_bnd_nodes) +end +@test length(nodes) == 1 + +Conn = connectivity(nodes, edges, sign) + +X = raviartthomas(faces, cellpairs(faces,edges)) +@test numfunctions(X) == 8 + +divX = divergence(X) +Id = BEAST.Identity() +DD = assemble(Id, divX, divX) +@test rank(DD) == 7 +L = divX * Conn +for sh in L.fns[1] + @test isapprox(sh.coeff, 0, atol=1e-8) +end From 7adf068346f9208d5b7afaa5980145aebb1083df Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sun, 12 Jan 2020 11:34:37 +0100 Subject: [PATCH 009/528] less verbose construction --- examples/ex_dual2forms.jl | 7 ++++++- src/bases/dual3d.jl | 26 +++++++++++++------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/examples/ex_dual2forms.jl b/examples/ex_dual2forms.jl index 1ade225b..87ba8c95 100644 --- a/examples/ex_dual2forms.jl +++ b/examples/ex_dual2forms.jl @@ -39,6 +39,11 @@ Faces = submesh(skeleton(Tetrs,2)) do Face !(sort(Face) in srt_bnd_Faces) end +srt_bnd_Nodes = sort.(skeleton(boundary(Tetrs),0)) +Nodes = submesh(skeleton(Tetrs,0)) do node + !(sort(node) in srt_bnd_Nodes) +end + @show numcells(Edges) @show length(Faces) @@ -46,7 +51,7 @@ end # AllFaces = skeleton(Tetrs,2) # sm = submesh(pred, AllFaces) -E = 102 +E = 1 Edge = cells(Edges)[E] pos = cartesian(center(chart(Edges, Edge))) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index d0203467..fc025d2a 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -9,14 +9,14 @@ function builddual2form(support, port, dirichlet, prt_fluxes) verts = skeleton(support,0) @assert length(verts) - length(edges) + length(faces) - length(support) == 1 bndry = boundary(support) - @show numcells(faces) + # @show numcells(faces) cells_bndry = [sort(c) for c in cells(bndry)] dirbnd = submesh(dirichlet) do face sort(face) in cells(bndry) ? true : false end - @show numcells(support) - @show numcells(dirbnd) + # @show numcells(support) + # @show numcells(dirbnd) # @assert numcells(dirbnd) ≤ 4 bnd_dirbnd = boundary(dirbnd) @@ -25,7 +25,7 @@ function builddual2form(support, port, dirichlet, prt_fluxes) int_edges_dirbnd = submesh(edges_dirbnd) do edge sort(edge) in cells(bnd_dirbnd) ? false : true end - @show numcells(int_edges_dirbnd) + # @show numcells(int_edges_dirbnd) # @assert numcells(int_edges_dirbnd) ≤ 2 int_pred = interior_tpredicate(support) @@ -58,7 +58,7 @@ function builddual2form(support, port, dirichlet, prt_fluxes) # for edge in cells(int_faces) # println(edge) # end - @show numcells(int_faces) + # @show numcells(int_faces) # @show num_faces_on_port # @show num_faces_on_dirc @@ -69,16 +69,16 @@ function builddual2form(support, port, dirichlet, prt_fluxes) cells_bnd_edges = [sort(c) for c in cells(bnd_edges)] cells_prt_edges = [sort(c) for c in cells(prt_edges)] - @show length(cells_int_edges_dirbnd) - @show length(cells_bnd_edges) - @show length(cells_prt_edges) + # @show length(cells_int_edges_dirbnd) + # @show length(cells_bnd_edges) + # @show length(cells_prt_edges) int_edges = submesh(edges) do edge sort(edge) in cells_int_edges_dirbnd && return true sort(edge) in cells_bnd_edges && return false sort(edge) in cells_prt_edges && return false return true end - @show numcells(int_edges) + # @show numcells(int_edges) RT_int = nedelecd3d(support, int_faces) RT_prt = nedelecd3d(support, port) @@ -91,7 +91,7 @@ function builddual2form(support, port, dirichlet, prt_fluxes) Q = assemble(Id, divergence(RT_int), divergence(RT_prt)) d = -Q * prt_fluxes - @show numfunctions(L0_int) + # @show numfunctions(L0_int) # @assert numfunctions(L0_int) in [1,2] curl_L0_int = curl(L0_int) div_curl_L0_int = divergence(curl_L0_int) @@ -102,10 +102,10 @@ function builddual2form(support, port, dirichlet, prt_fluxes) x1 = pinv(D) * d N = nullspace(D) - @show size(N) - @show rank(C) + # @show size(N) + # @show rank(C) @assert size(N,2) == rank(C) - @show size(C) + # @show size(C) # @assert rank(C) == size(C,1) p = (C*N) \ (c - C*x1) x = x1 + N*p From c3b1e80c66c91de4e059ad526f106d03f235d87d Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sun, 12 Jan 2020 13:18:25 +0100 Subject: [PATCH 010/528] Apapt example dual2forms to deal with Neumann boundaries --- examples/ex_dual2forms.jl | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/examples/ex_dual2forms.jl b/examples/ex_dual2forms.jl index 87ba8c95..2b9a4956 100644 --- a/examples/ex_dual2forms.jl +++ b/examples/ex_dual2forms.jl @@ -30,11 +30,23 @@ Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.35) tetrs = barycentric_refinement(Tetrs) cells_Bndry = [sort(c) for c in cells(skeleton(boundary(Tetrs),1))] + +Bnd = boundary(Tetrs) +Neu = Bnd +Dir = Mesh(vertices(Bnd), CompScienceMeshes.celltype(Bnd)[]) + +bnd = boundary(tetrs) +neu = bnd +dir = Mesh(vertices(bnd), CompScienceMeshes.celltype(bnd)[]) + +srt_Dir = sort.(Dir) Edges = submesh(skeleton(Tetrs,1)) do Edge - sort(Edge) in cells_Bndry ? false : true + sort(Edge) in srt_Dir && return false + return true end -# Edges = Mesh(vertices(Edges), cells(Edges)) -srt_bnd_Faces = sort.(boundary(Tetrs)) +# Edges = skeleton(Tetrs,1) +# srt_bnd_Faces = sort.(boundary(Tetrs)) +# srt_neu = sort.(neu) Faces = submesh(skeleton(Tetrs,2)) do Face !(sort(Face) in srt_bnd_Faces) end @@ -78,8 +90,10 @@ cells_prt_edges = [sort(c) for c in skeleton(port,1)] cells_dir_edges = [sort(c) for c in skeleton(boundary(tetrs),1)] bnd_support = boundary(support) -cells_bnd_tetrs = sort.(boundary(tetrs)) -dir_cap_bnd = submesh(face -> sort(face) in cells_bnd_tetrs, bnd_support) +# cells_bnd_tetrs = sort.(boundary(tetrs)) + +srt_dir = sort.(dir) +dir_cap_bnd = submesh(face -> sort(face) in sort.(dir), bnd_support) cells_bnd_dir_cap_bnd = sort.(boundary(dir_cap_bnd)) int_edges = submesh(edges) do edge @@ -105,8 +119,10 @@ rank(Q3) cells_bnd_faces = [sort(c) for c in boundary(support)] cells_prt_faces = [sort(c) for c in port] +srt_dir_cap_bnd = sort.(dir_cap_bnd) int_faces = submesh(skeleton(support,2)) do face - sort(face) in cells_bnd_tetrs && return true + # sort(face) in cells_bnd_tetrs && return true + sort(face) in srt_dir_cap_bnd && return true sort(face) in cells_bnd_faces && return false sort(face) in cells_prt_faces && return false return true @@ -119,7 +135,7 @@ RT_int = BEAST.nedelecd3d(support, int_faces) for (m,fn) in enumerate(RT_int.fns) ct = count(sh -> sh.cellid <= length(support1), fn) # @show m, ct - @assert ct == 2 || ct == 0 + @assert 0 ≤ ct ≤ 2 end Q4 = assemble(Id, divergence(RT_int), divergence(RT_int)) numfunctions(RT_int) - rank(Q4) From b6840ed685d4f261781be01c2d69f7a0899db317 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 15 Jan 2020 14:38:36 +0100 Subject: [PATCH 011/528] Private part of the commit --- examples/ex_dual2forms.jl | 24 +++++++----- examples/ex_fem.jl | 82 ++++++++++++++++++++++++++++++++++++--- src/bases/dual3d.jl | 69 +++++++++++++++++++++++++------- 3 files changed, 146 insertions(+), 29 deletions(-) diff --git a/examples/ex_dual2forms.jl b/examples/ex_dual2forms.jl index 2b9a4956..451d8b8f 100644 --- a/examples/ex_dual2forms.jl +++ b/examples/ex_dual2forms.jl @@ -47,9 +47,9 @@ end # Edges = skeleton(Tetrs,1) # srt_bnd_Faces = sort.(boundary(Tetrs)) # srt_neu = sort.(neu) -Faces = submesh(skeleton(Tetrs,2)) do Face - !(sort(Face) in srt_bnd_Faces) -end +# Faces = submesh(skeleton(Tetrs,2)) do Face +# !(sort(Face) in srt_bnd_Faces) +# end srt_bnd_Nodes = sort.(skeleton(boundary(Tetrs),0)) Nodes = submesh(skeleton(Tetrs,0)) do node @@ -57,7 +57,7 @@ Nodes = submesh(skeleton(Tetrs,0)) do node end @show numcells(Edges) -@show length(Faces) +# @show length(Faces) # pred = CompScienceMeshes.interior_tpredicate(Tetrs) # AllFaces = skeleton(Tetrs,2) @@ -66,7 +66,7 @@ end E = 1 Edge = cells(Edges)[E] -pos = cartesian(center(chart(Edges, Edge))) +pos = cartesian(CompScienceMeshes.center(chart(Edges, Edge))) # v = argmin(norm.(vertices(tetrs) .- Ref(pos))) support1 = submesh(tetr -> Edge[1] in tetr, tetrs.mesh) support2 = submesh(tetr -> Edge[2] in tetr, tetrs.mesh) @@ -173,7 +173,7 @@ Y1 = BEAST.NDLCDBasis(support, [fn], [pos]) divY1 = divergence(Y1); compress!(divY1) -import PlotlyJS +import Plotly function showfn(space,i) geo = geometry(space) T = coordtype(geo) @@ -185,7 +185,7 @@ function showfn(space,i) W = Dict{Int,T}() for sh in space.fns[i] chrt = chart(geo, cells(geo)[sh.cellid]) - nbd = center(chrt) + nbd = CompScienceMeshes.center(chrt) vals = refspace(space)(nbd) x,y,z = cartesian(nbd) # @show vals[sh.refid].value @@ -205,8 +205,14 @@ function showfn(space,i) U = collect(values(U)) V = collect(values(V)) W = collect(values(W)) - PlotlyJS.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) + Plotly.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) end error("stop") -Y = BEAST.dual2forms(Tetrs, Edges) +Dir = Mesh(vertices(Tetrs), CompScienceMeshes.celltype(int_faces)[]) +error() + +tetrs, bnd, v2t, v2n = BEAST.dual2forms_init(Tetrs) +Y = BEAST.dual2forms_body(Tetrs, Edges[collect(1:10)], Dir, tetrs, bnd, v2t, v2n) + +Y = BEAST.dual2forms(Tetrs, Edges, Dir) diff --git a/examples/ex_fem.jl b/examples/ex_fem.jl index 4f4f0e3f..fd51ef57 100644 --- a/examples/ex_fem.jl +++ b/examples/ex_fem.jl @@ -1,7 +1,47 @@ using CompScienceMeshes using BEAST -tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.10) +using SparseArrays +function isdivconforming(space) + + geo = geometry(space) + mesh = geo + + edges = skeleton(mesh,1) + D = connectivity(edges, mesh, abs) + rows = rowvals(D) + vals = nonzeros(D) + + Flux = zeros(Float64, numcells(mesh),3) + TotalFlux = zeros(Float64, numcells(edges), numfunctions(space)) + + for i in 1 : numfunctions(space) + + fill!(Flux, 0) + bfs = BEAST.basisfunction(space,i) + for bf in bfs + c = bf.cellid + e = bf.refid + x = bf.coeff + Flux[c,e] += x + end + + for E in 1 : numcells(edges) + for j in nzrange(D,E) + F = rows[j] + e = vals[j] + @assert 1 <= e <= 3 + TotalFlux[E,i] += Flux[F,e] + end + end + end + + I = findall(abs.(TotalFlux) .> 1e-6) + @show length(I) + return TotalFlux, I +end + +tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.15) @show numcells(tetrs) bndry = boundary(tetrs) @@ -25,7 +65,8 @@ A2 = assemble(Id, X, X) A = A1 - A2 using LinearAlgebra -f = BEAST.ScalarTrace(x -> point(1,0,0) * exp(-norm(x)^2/4)) +# f = BEAST.ScalarTrace(x -> point(1,0,0) * exp(-norm(x)^2/4)) +f = BEAST.ScalarTrace(x -> point(1,0,0)) b = assemble(f, X) u = A \ b @@ -68,6 +109,37 @@ o, x, y, z = euclidianbasis(3) G = boundary(tetrs) Z = BEAST.ttrace(curl(X), G) -fcr, geo = facecurrents(u, Z) -import PlotlyJS -PlotlyJS.plot(patch(geo, norm.(fcr))) +# fcr, geo = facecurrents(u, Z) +import Plotly +# Plotly.plot(patch(geo, norm.(fcr))) + +Dir = Mesh(vertices(tetrs), CompScienceMeshes.celltype(G)[]) +# error("stop") +Xplus = BEAST.nedelecc3d(tetrs, skeleton(tetrs,1)) + +bnd_tetrs = boundary(tetrs) +ttXplus = BEAST.ttrace(Xplus, bnd_tetrs) +TF, Idcs = isdivconforming(ttXplus) +@show length(Idcs) + +# error("stop") +Q = BEAST.dual2forms(tetrs, skeleton(tetrs,1), Dir) + + + +QXplus = assemble(Id, Q, Xplus) +curlX = curl(X) +QcurlX = assemble(Id, Q, curlX) + +v = QXplus \ (QcurlX * u) +fcr1, geo1 = facecurrents(v, BEAST.ttrace(Xplus, bnd_tetrs)); +fcr2, geo2 = facecurrents(u, BEAST.ttrace(curl(X), bnd_tetrs)); + +# tetrs1 = skeleton(tetrs,1) +# tetrs2 = skeleton(tetrs,2) +# ttXplus = BEAST.ttrace(Xplus, bnd_tetrs) +# +# @assert dimension(geometry(ttXplus)) == 2 +# length(geometry(ttXplus)) == length(tetrs2) +# +# Conn = connectivity(tetrs1, tetrs2) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index fc025d2a..2cfe2c25 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -4,23 +4,23 @@ function builddual2form(support, port, dirichlet, prt_fluxes) println() - faces = skeleton(support,2) - edges = skeleton(support,1) - verts = skeleton(support,0) + faces = CompScienceMeshes.skeleton_fast(support,2) + edges = CompScienceMeshes.skeleton_fast(support,1) + verts = CompScienceMeshes.skeleton_fast(support,0) @assert length(verts) - length(edges) + length(faces) - length(support) == 1 bndry = boundary(support) # @show numcells(faces) cells_bndry = [sort(c) for c in cells(bndry)] dirbnd = submesh(dirichlet) do face - sort(face) in cells(bndry) ? true : false + sort(face) in cells_bndry ? true : false end # @show numcells(support) # @show numcells(dirbnd) # @assert numcells(dirbnd) ≤ 4 bnd_dirbnd = boundary(dirbnd) - edges_dirbnd = skeleton(dirbnd,1) + edges_dirbnd = CompScienceMeshes.skeleton_fast(dirbnd,1) cells_bnd_dirbnd = [sort(c) for c in cells(bnd_dirbnd)] int_edges_dirbnd = submesh(edges_dirbnd) do edge sort(edge) in cells(bnd_dirbnd) ? false : true @@ -32,7 +32,7 @@ function builddual2form(support, port, dirichlet, prt_fluxes) num_faces_on_port = 0 num_faces_on_dirc = 0 - @assert numcells(submesh(!int_pred, faces)) == numcells(boundary(support)) + # @assert numcells(submesh(!int_pred, faces)) == numcells(boundary(support)) # for edge in cells(faces) @@ -62,8 +62,8 @@ function builddual2form(support, port, dirichlet, prt_fluxes) # @show num_faces_on_port # @show num_faces_on_dirc - bnd_edges = skeleton(bndry,1) - prt_edges = skeleton(port,1) + bnd_edges = CompScienceMeshes.skeleton_fast(bndry,1) + prt_edges = CompScienceMeshes.skeleton_fast(port,1) cells_int_edges_dirbnd = [sort(c) for c in cells(int_edges_dirbnd)] cells_bnd_edges = [sort(c) for c in cells(bnd_edges)] @@ -87,8 +87,10 @@ function builddual2form(support, port, dirichlet, prt_fluxes) L0_int = nedelecc3d(support, int_edges) Id = BEAST.Identity() - D = assemble(Id, divergence(RT_int), divergence(RT_int)) - Q = assemble(Id, divergence(RT_int), divergence(RT_prt)) + div_RT_int = divergence(RT_int) + div_RT_prt = divergence(RT_prt) + D = assemble(Id, div_RT_int, div_RT_int) + Q = assemble(Id, div_RT_int, div_RT_prt) d = -Q * prt_fluxes # @show numfunctions(L0_int) @@ -129,15 +131,36 @@ function builddual2form(support, port, dirichlet, prt_fluxes) end -function dual2forms(Tetrs, Edges) +function dual2forms_init(Tetrs) tetrs = barycentric_refinement(Tetrs) + v2t, v2n = CompScienceMeshes.vertextocellmap(tetrs) + bnd = boundary(tetrs) + + return tetrs, bnd, v2t, v2n +end + + +function dual2forms(Tetrs, Edges, Dir) + tetrs, bnd, v2t, v2n = dual2forms_init(Tetrs) + dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) +end + +function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) + + # Bndry = boundary(Tetrs) + # tetrs, bnd, v2t, v2n = dual2forms_init(Tetrs) T = coordtype(Tetrs) bfs = Vector{Vector{Shape{T}}}(undef, numcells(Edges)) pos = Vector{vertextype(Edges)}(undef, numcells(Edges)) - dirichlet = boundary(tetrs) + # dirichlet = boundary(tetrs) + gpred = CompScienceMeshes.overlap_gpredicate(Dir) + dirichlet = submesh(bnd) do face + gpred(chart(bnd,face)) + end + @show numcells(dirichlet) for (F,Edge) in enumerate(cells(Edges)) @show F bfs[F] = Vector{Shape{T}}() @@ -146,13 +169,26 @@ function dual2forms(Tetrs, Edges) port_vertex_idx = argmin(norm.(vertices(tetrs) .- Ref(pos[F]))) # Build the dual support - ptch_idcs1 = [i for (i,tetr) in enumerate(cells(tetrs)) if Edge[1] in tetr] - ptch_idcs2 = [i for (i,tetr) in enumerate(cells(tetrs)) if Edge[2] in tetr] + # ptch_idcs1 = [i for (i,tetr) in enumerate(cells(tetrs)) if Edge[1] in tetr] + # ptch_idcs2 = [i for (i,tetr) in enumerate(cells(tetrs)) if Edge[2] in tetr] + + ptch_idcs1 = v2t[Edge[1],1:v2n[Edge[1]]] + ptch_idcs2 = v2t[Edge[2],1:v2n[Edge[2]]] + patch1 = Mesh(vertices(tetrs), cells(tetrs)[ptch_idcs1]) patch2 = Mesh(vertices(tetrs), cells(tetrs)[ptch_idcs2]) patch_bnd = boundary(patch1) # @assert CompScienceMeshes.isoriented(patch_bnd) - port = Mesh(vertices(tetrs), filter(c->port_vertex_idx in c, cells(patch_bnd))) + # port = Mesh(vertices(tetrs), filter(c->port_vertex_idx in c, cells(patch_bnd))) + bnd_patch1 = boundary(patch1) + bnd_patch2 = boundary(patch2) + + # set_bnd_patch1 = Set(sort.(bnd_patch1)) + set_bnd_patch2 = Set(sort.(bnd_patch2)) + + # port = submesh(face -> sort(face) in sort.(boundary(patch2)), boundary(patch1)) + port = submesh(face -> sort(face) in set_bnd_patch2, bnd_patch1) + # @assert CompScienceMeshes.isoriented(port) patch = CompScienceMeshes.union(patch1, patch2) @show numcells(patch_bnd) @@ -170,6 +206,9 @@ function dual2forms(Tetrs, Edges) end RT_int, RT_prt, x_int, x_prt = builddual2form(patch, port, dirichlet, prt_fluxes) + @show norm(x_int) + @show norm(x_prt) + ptch_idcs = vcat(ptch_idcs1, ptch_idcs2) for (m,bf) in enumerate(RT_int.fns) for sh in bf From a4d1c6010b50042aee1903d6d7775712fe7544f3 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 16 Jan 2020 10:04:20 +0100 Subject: [PATCH 012/528] use Makeitso to avoid having to recompute the dual baiss functions every time --- examples/ex_fem.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/ex_fem.jl b/examples/ex_fem.jl index fd51ef57..587580da 100644 --- a/examples/ex_fem.jl +++ b/examples/ex_fem.jl @@ -1,5 +1,6 @@ using CompScienceMeshes using BEAST +using Makeitso using SparseArrays function isdivconforming(space) @@ -123,8 +124,8 @@ TF, Idcs = isdivconforming(ttXplus) @show length(Idcs) # error("stop") -Q = BEAST.dual2forms(tetrs, skeleton(tetrs,1), Dir) - +@target Q ()->BEAST.dual2forms(tetrs, skeleton(tetrs,1), Dir) +Q = @make Q QXplus = assemble(Id, Q, Xplus) From e54bec61eb966a60948d00d8075213a7d735b3e5 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 5 Feb 2020 16:10:04 +0100 Subject: [PATCH 013/528] ex_fem charge density comp using dual space --- examples/ex_fem.jl | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/examples/ex_fem.jl b/examples/ex_fem.jl index 587580da..d1afb6c6 100644 --- a/examples/ex_fem.jl +++ b/examples/ex_fem.jl @@ -42,6 +42,42 @@ function isdivconforming(space) return TotalFlux, I end + +import Plotly +function showfn(space,i) + geo = geometry(space) + T = coordtype(geo) + X = Dict{Int,T}() + Y = Dict{Int,T}() + Z = Dict{Int,T}() + U = Dict{Int,T}() + V = Dict{Int,T}() + W = Dict{Int,T}() + for sh in space.fns[i] + chrt = chart(geo, cells(geo)[sh.cellid]) + nbd = CompScienceMeshes.center(chrt) + vals = refspace(space)(nbd) + x,y,z = cartesian(nbd) + # @show vals[sh.refid].value + u,v,w = vals[sh.refid].value + # @show x, y, z + # @show u, v, w + X[sh.cellid] = x + Y[sh.cellid] = y + Z[sh.cellid] = z + U[sh.cellid] = get(U,sh.cellid,zero(T)) + sh.coeff * u + V[sh.cellid] = get(V,sh.cellid,zero(T)) + sh.coeff * v + W[sh.cellid] = get(W,sh.cellid,zero(T)) + sh.coeff * w + end + X = collect(values(X)) + Y = collect(values(Y)) + Z = collect(values(Z)) + U = collect(values(U)) + V = collect(values(V)) + W = collect(values(W)) + Plotly.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) +end + tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.15) @show numcells(tetrs) @@ -135,6 +171,8 @@ QcurlX = assemble(Id, Q, curlX) v = QXplus \ (QcurlX * u) fcr1, geo1 = facecurrents(v, BEAST.ttrace(Xplus, bnd_tetrs)); fcr2, geo2 = facecurrents(u, BEAST.ttrace(curl(X), bnd_tetrs)); +fcr3, geo3 = facecurrents(v, divergence(ttXplus)); + # tetrs1 = skeleton(tetrs,1) # tetrs2 = skeleton(tetrs,2) From d8a636f65cc988152e78f1a19aedc7f915d4c898 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 7 Feb 2020 13:05:11 +0100 Subject: [PATCH 014/528] rely on vector api for meshes --- examples/ex_dual2forms.jl | 9 ++-- src/bases/dual3d.jl | 101 +++++++++----------------------------- 2 files changed, 28 insertions(+), 82 deletions(-) diff --git a/examples/ex_dual2forms.jl b/examples/ex_dual2forms.jl index 451d8b8f..224c4963 100644 --- a/examples/ex_dual2forms.jl +++ b/examples/ex_dual2forms.jl @@ -26,7 +26,7 @@ function compress!(space) end end -Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.35) +Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.15) tetrs = barycentric_refinement(Tetrs) cells_Bndry = [sort(c) for c in cells(skeleton(boundary(Tetrs),1))] @@ -208,11 +208,12 @@ function showfn(space,i) Plotly.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) end -error("stop") +# error("stop") Dir = Mesh(vertices(Tetrs), CompScienceMeshes.celltype(int_faces)[]) -error() +# error() tetrs, bnd, v2t, v2n = BEAST.dual2forms_init(Tetrs) Y = BEAST.dual2forms_body(Tetrs, Edges[collect(1:10)], Dir, tetrs, bnd, v2t, v2n) -Y = BEAST.dual2forms(Tetrs, Edges, Dir) +# Y = BEAST.dual2forms(Tetrs, Edges, Dir) +nothing diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 2cfe2c25..1fd971af 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -2,88 +2,54 @@ using LinearAlgebra function builddual2form(support, port, dirichlet, prt_fluxes) - println() + # println() faces = CompScienceMeshes.skeleton_fast(support,2) edges = CompScienceMeshes.skeleton_fast(support,1) verts = CompScienceMeshes.skeleton_fast(support,0) @assert length(verts) - length(edges) + length(faces) - length(support) == 1 bndry = boundary(support) - # @show numcells(faces) - cells_bndry = [sort(c) for c in cells(bndry)] + cells_bndry = sort.(bndry) dirbnd = submesh(dirichlet) do face sort(face) in cells_bndry ? true : false end - # @show numcells(support) - # @show numcells(dirbnd) - # @assert numcells(dirbnd) ≤ 4 bnd_dirbnd = boundary(dirbnd) edges_dirbnd = CompScienceMeshes.skeleton_fast(dirbnd,1) - cells_bnd_dirbnd = [sort(c) for c in cells(bnd_dirbnd)] + cells_bnd_dirbnd = sort.(bnd_dirbnd) int_edges_dirbnd = submesh(edges_dirbnd) do edge sort(edge) in cells(bnd_dirbnd) ? false : true end - # @show numcells(int_edges_dirbnd) - # @assert numcells(int_edges_dirbnd) ≤ 2 - int_pred = interior_tpredicate(support) num_faces_on_port = 0 num_faces_on_dirc = 0 - # @assert numcells(submesh(!int_pred, faces)) == numcells(boundary(support)) - - - # for edge in cells(faces) - # println(edge) - # end - # println() - # for edge in cells(dirbnd) - # println(edge) - # end - # println() - # for edge in cells(port) - # println(edge) - # end - - cells_port = [sort(c) for c in cells(port)] - cells_dirbnd = [sort(c) for c in cells(dirbnd)] + cells_port = sort.(port) + cells_dirbnd = sort.(dirbnd) int_faces = submesh(faces) do face sort(face) in cells_port && return false sort(face) in cells_dirbnd && return true - int_pred(face) ? true : false + sort(face) in cells_bndry && return false + return true end - # println() - # for edge in cells(int_faces) - # println(edge) - # end - # @show numcells(int_faces) - # @show num_faces_on_port - # @show num_faces_on_dirc bnd_edges = CompScienceMeshes.skeleton_fast(bndry,1) prt_edges = CompScienceMeshes.skeleton_fast(port,1) - cells_int_edges_dirbnd = [sort(c) for c in cells(int_edges_dirbnd)] - cells_bnd_edges = [sort(c) for c in cells(bnd_edges)] - cells_prt_edges = [sort(c) for c in cells(prt_edges)] + cells_int_edges_dirbnd = sort.(int_edges_dirbnd) + cells_bnd_edges = sort.(bnd_edges) + cells_prt_edges = sort.(prt_edges) - # @show length(cells_int_edges_dirbnd) - # @show length(cells_bnd_edges) - # @show length(cells_prt_edges) int_edges = submesh(edges) do edge sort(edge) in cells_int_edges_dirbnd && return true sort(edge) in cells_bnd_edges && return false sort(edge) in cells_prt_edges && return false return true end - # @show numcells(int_edges) RT_int = nedelecd3d(support, int_faces) RT_prt = nedelecd3d(support, port) - # vertex_list = [c[1] for c in cells(int_edges)] - # L0_int = lagrangec0d1(suppport, vertex_list, Val{4}) L0_int = nedelecc3d(support, int_edges) Id = BEAST.Identity() @@ -93,8 +59,6 @@ function builddual2form(support, port, dirichlet, prt_fluxes) Q = assemble(Id, div_RT_int, div_RT_prt) d = -Q * prt_fluxes - # @show numfunctions(L0_int) - # @assert numfunctions(L0_int) in [1,2] curl_L0_int = curl(L0_int) div_curl_L0_int = divergence(curl_L0_int) ZZ = real(assemble(Id, div_curl_L0_int, div_curl_L0_int)) @@ -104,15 +68,10 @@ function builddual2form(support, port, dirichlet, prt_fluxes) x1 = pinv(D) * d N = nullspace(D) - # @show size(N) - # @show rank(C) @assert size(N,2) == rank(C) - # @show size(C) - # @assert rank(C) == size(C,1) p = (C*N) \ (c - C*x1) x = x1 + N*p - # D*x ≈ d atol=1e-8 if !isapprox(C*x, c, atol=1e-8) || !isapprox(D*x, d, atol=1e-6) @show norm(D*x-d) @show norm(C*x-c) @@ -121,10 +80,10 @@ function builddual2form(support, port, dirichlet, prt_fluxes) error("error") end - if rank(C) != size(C,1) - @show rank(C) - @show size(C) - end + # if rank(C) != size(C,1) + # @show rank(C) + # @show size(C) + # end return RT_int, RT_prt, x, prt_fluxes @@ -149,9 +108,6 @@ end function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) - # Bndry = boundary(Tetrs) - # tetrs, bnd, v2t, v2n = dual2forms_init(Tetrs) - T = coordtype(Tetrs) bfs = Vector{Vector{Shape{T}}}(undef, numcells(Edges)) pos = Vector{vertextype(Edges)}(undef, numcells(Edges)) @@ -160,44 +116,34 @@ function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) dirichlet = submesh(bnd) do face gpred(chart(bnd,face)) end - @show numcells(dirichlet) + # @show numcells(dirichlet) for (F,Edge) in enumerate(cells(Edges)) - @show F + # @show F + println("Constructing dual 2-forms: $F out of $(length(Edges)).") bfs[F] = Vector{Shape{T}}() pos[F] = cartesian(center(chart(Edges,Edge))) port_vertex_idx = argmin(norm.(vertices(tetrs) .- Ref(pos[F]))) - # Build the dual support - # ptch_idcs1 = [i for (i,tetr) in enumerate(cells(tetrs)) if Edge[1] in tetr] - # ptch_idcs2 = [i for (i,tetr) in enumerate(cells(tetrs)) if Edge[2] in tetr] - ptch_idcs1 = v2t[Edge[1],1:v2n[Edge[1]]] ptch_idcs2 = v2t[Edge[2],1:v2n[Edge[2]]] patch1 = Mesh(vertices(tetrs), cells(tetrs)[ptch_idcs1]) patch2 = Mesh(vertices(tetrs), cells(tetrs)[ptch_idcs2]) patch_bnd = boundary(patch1) - # @assert CompScienceMeshes.isoriented(patch_bnd) - # port = Mesh(vertices(tetrs), filter(c->port_vertex_idx in c, cells(patch_bnd))) + bnd_patch1 = boundary(patch1) bnd_patch2 = boundary(patch2) - # set_bnd_patch1 = Set(sort.(bnd_patch1)) set_bnd_patch2 = Set(sort.(bnd_patch2)) - # port = submesh(face -> sort(face) in sort.(boundary(patch2)), boundary(patch1)) port = submesh(face -> sort(face) in set_bnd_patch2, bnd_patch1) - # @assert CompScienceMeshes.isoriented(port) patch = CompScienceMeshes.union(patch1, patch2) - @show numcells(patch_bnd) - @show numcells(patch) - @show numcells(port) - # @assert numcells(skeleton(patch,1))+2 == numcells(skeleton(patch1,1)) + numcells(skeleton(patch2,1)) + # @show numcells(patch_bnd) + # @show numcells(patch) + # @show numcells(port) - # @assert numcells(patch) >= 6 - # @assert numcells(port) == 2 prt_fluxes = ones(T, numcells(port)) / numcells(port) tgt = vertices(Edges)[Edge[1]] - vertices(Edges)[Edge[2]] for (i,face) in enumerate(cells(port)) @@ -206,8 +152,8 @@ function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) end RT_int, RT_prt, x_int, x_prt = builddual2form(patch, port, dirichlet, prt_fluxes) - @show norm(x_int) - @show norm(x_prt) + # @show norm(x_int) + # @show norm(x_prt) ptch_idcs = vcat(ptch_idcs1, ptch_idcs2) for (m,bf) in enumerate(RT_int.fns) @@ -224,7 +170,6 @@ function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) end end - # F == 25 && error("stop") end NDLCDBasis(tetrs, bfs, pos) From 98ec41e781c918ce2735e42a02ef46769aee3d45 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 7 Feb 2020 14:32:54 +0100 Subject: [PATCH 015/528] Take a shot at the bottom up construction of dual 1-forms --- examples/ex_dual1forms_bis.jl | 206 ++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 examples/ex_dual1forms_bis.jl diff --git a/examples/ex_dual1forms_bis.jl b/examples/ex_dual1forms_bis.jl new file mode 100644 index 00000000..c71f4973 --- /dev/null +++ b/examples/ex_dual1forms_bis.jl @@ -0,0 +1,206 @@ +using CompScienceMeshes +using BEAST +using LinearAlgebra + +const Id = BEAST.Identity() +function Base.in(mesh::CompScienceMeshes.AbstractMesh) + cells_mesh = sort.(mesh) + function f(cell) + sort(cell) in cells_mesh + end +end + +function add!(fn, x, space) + for (m,bf) in enumerate(space.fns) + for sh in bf + BEAST.add!(fn, sh.cellid, sh.refid, sh.coeff * x[m]) + end + end +end + + +function compress!(space) + T = scalartype(space) + for (i,fn) in pairs(space.fns) + shapes = Dict{Tuple{Int,Int},T}() + for shape in fn + v = get(shapes, (shape.cellid, shape.refid), zero(T)) + shapes[(shape.cellid, shape.refid)] = v + shape.coeff + # set!(shapes, (shape.cellid, shape.refid), v + shape.coeff) + end + space.fns[i] = [BEAST.Shape(k[1], k[2], v) for (k,v) in shapes] + end +end + +import Plotly +function showfn(space,i) + geo = geometry(space) + T = coordtype(geo) + X = Dict{Int,T}() + Y = Dict{Int,T}() + Z = Dict{Int,T}() + U = Dict{Int,T}() + V = Dict{Int,T}() + W = Dict{Int,T}() + for sh in space.fns[i] + chrt = chart(geo, cells(geo)[sh.cellid]) + nbd = center(chrt) + vals = refspace(space)(nbd) + x,y,z = cartesian(nbd) + # @show vals[sh.refid].value + u,v,w = vals[sh.refid].value + # @show x, y, z + # @show u, v, w + X[sh.cellid] = x + Y[sh.cellid] = y + Z[sh.cellid] = z + U[sh.cellid] = get(U,sh.cellid,zero(T)) + sh.coeff * u + V[sh.cellid] = get(V,sh.cellid,zero(T)) + sh.coeff * v + W[sh.cellid] = get(W,sh.cellid,zero(T)) + sh.coeff * w + end + X = collect(values(X)) + Y = collect(values(Y)) + Z = collect(values(Z)) + U = collect(values(U)) + V = collect(values(V)) + W = collect(values(W)) + Plotly.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) +end + +Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.45) +tetrs = barycentric_refinement(Tetrs) + +bnd_Tetrs = boundary(Tetrs) +bnd_tetrs = boundary(tetrs) +srt_bnd_Tetrs = sort.(bnd_Tetrs) +srt_bnd_tetrs = sort.(bnd_tetrs) + +Faces = submesh(Face -> !(sort(Face) in srt_bnd_Tetrs), skeleton(Tetrs,2)) +@show length(Faces) + +F = 396 +Face = cells(Faces)[F] +pos = cartesian(center(chart(Faces, Face))) + +supp1 = submesh(tet -> Face[1] in tet, tetrs.mesh) +supp2 = submesh(tet -> Face[2] in tet, tetrs.mesh) +supp3 = submesh(tet -> Face[3] in tet, tetrs.mesh) + +faces_supp1 = CompScienceMeshes.skeleton_fast(supp1, 2) +faces_supp2 = CompScienceMeshes.skeleton_fast(supp2, 2) +faces_supp3 = CompScienceMeshes.skeleton_fast(supp3, 2) + +supp = CompScienceMeshes.union(supp1, supp2) +supp = CompScienceMeshes.union(supp, supp3) +# PlotlyJS.plot(patch(boundary(supp))) + +port = skeleton(supp1, 1) +# port = submesh(edge -> sort(edge) in sort.(skeleton(supp2,1)), port) +# port = submesh(edge -> sort(edge) in sort.(skeleton(supp3,1)), port) +port = submesh(in(skeleton(supp2,1)), port) +port = submesh(in(skeleton(supp3,1)), port) +@show length(port) +@assert 1 ≤ length(port) ≤ 2 + +bnd_supp = boundary(supp) +dir = submesh(in(bnd_tetrs), bnd_supp) +# dir = submesh(face -> sort(face) in srt_bnd_tetrs, bnd_supp) +rim = boundary(dir) +@show length(dir) + +# Step 1: step port flux and extend +x0 = ones(length(port)) / length(port) +for (i,edge) in enumerate(port) + tgt = tangents(center(chart(port,edge)),1) + if dot(normal(chart(Faces,Face)), tgt) < 0 + x0[i] *= -1 + end +end + +# cells_faces_supp1 = sort.(faces_supp1) +# cells_faces_supp2 = sort.(faces_supp2) +# cells_faces_supp3 = sort.(faces_supp3) + +dFace1 = submesh(in(faces_supp2), faces_supp3) +dFace2 = submesh(in(faces_supp3), faces_supp1) +dFace3 = submesh(in(faces_supp1), faces_supp2) + + + +srt_port = sort.(port) +srt_dir_edges = sort.(skeleton(dir,1)) +srt_bnd_edges = sort.(skeleton(boundary(supp),1)) +srt_rim = sort.(rim) +int_edges = submesh(skeleton(supp,1)) do edge + sort(edge) in srt_port && return false + sort(edge) in srt_rim && return false + sort(edge) in srt_dir_edges && return true + sort(edge) in srt_bnd_edges && return false + return true +end +@show length(int_edges) +@assert length(skeleton(supp,0)) - length(skeleton(supp,1)) + + length(skeleton(supp,2)) - length(supp) == 1 + +Nd_prt = BEAST.nedelecc3d(supp, port) +Nd_int = BEAST.nedelecc3d(supp, int_edges) +@show numfunctions(Nd_int) + + + +curl_Nd_int = curl(Nd_int) +A = assemble(Id, curl_Nd_int, curl_Nd_int) +N = nullspace(A) +@show size(N,2) +a = -assemble(Id, curl_Nd_int, curl(Nd_prt)) * x0 +x1 = pinv(A) * a +# x1 = A \ a +@show norm(N'*a) +@show norm(A*x1-a) + +srt_bnd_verts = sort.(skeleton(boundary(supp),0)) +srt_prt_verts = sort.(skeleton(port,0)) +int_verts = submesh(skeleton(supp,0)) do vert + sort(vert) in srt_bnd_verts && return false + sort(vert) in srt_prt_verts && return false + return true +end +@show length(int_verts) + +L0_int = lagrangec0d1(supp, int_verts) +@show numfunctions(L0_int) +grad_L0_int = BEAST.gradient(L0_int) +@assert numfunctions(grad_L0_int) == numfunctions(L0_int) +B = assemble(Id, grad_L0_int, Nd_int) + +B2, store = BEAST.allocatestorage(Id, grad_L0_int, Nd_int, + Val{:bandedstorage}, BEAST.LongDelays{:ignore}) +BEAST.assemble_local_mixed!(Id, grad_L0_int, Nd_int, store) +@assert isapprox(B, B2, atol=1e-8) +@show rank(B2) + +b = -assemble(Id, grad_L0_int, Nd_prt) * x0 +p = (B*N) \ (b-B*x1) +x1 = x1 + N*p + +@show norm(A*x1-a) +@show norm(B*x1-b) + +fn = BEAST.Shape{Float64}[] +add!(fn, x0, Nd_prt) +add!(fn, x1, Nd_int) + +Y1 = BEAST.NDLCCBasis(supp, [fn],[pos]) +curlY1 = curl(Y1); compress!(curl(Y1)); + +include(joinpath(@__DIR__, "utils/edge_values.jl")) +EV = edge_values(Y1,1) +check_edge_values(EV) + +error("stop") + +Y = BEAST.dual1forms(Tetrs, Faces) +curlY = curl(Y) +CC = assemble(Id, curlY, curlY) + +Plotly.plot(svdvals(CC)) From 27387462a45b093162cbb364b95528a2d3373219 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 20 Feb 2020 16:11:08 +0100 Subject: [PATCH 016/528] dual1form example implemented --- examples/ex_dual1forms_bis.jl | 330 ++++++++++++++++++++++------------ src/bases/rtspace.jl | 10 ++ 2 files changed, 222 insertions(+), 118 deletions(-) diff --git a/examples/ex_dual1forms_bis.jl b/examples/ex_dual1forms_bis.jl index c71f4973..2b996bfc 100644 --- a/examples/ex_dual1forms_bis.jl +++ b/examples/ex_dual1forms_bis.jl @@ -3,6 +3,8 @@ using BEAST using LinearAlgebra const Id = BEAST.Identity() +const Nx = BEAST.NCross() + function Base.in(mesh::CompScienceMeshes.AbstractMesh) cells_mesh = sort.(mesh) function f(cell) @@ -10,10 +12,11 @@ function Base.in(mesh::CompScienceMeshes.AbstractMesh) end end -function add!(fn, x, space) +function add!(fn, x, space, idcs) for (m,bf) in enumerate(space.fns) for sh in bf - BEAST.add!(fn, sh.cellid, sh.refid, sh.coeff * x[m]) + cellid = idcs[sh.cellid] + BEAST.add!(fn, cellid, sh.refid, sh.coeff * x[m]) end end end @@ -26,7 +29,6 @@ function compress!(space) for shape in fn v = get(shapes, (shape.cellid, shape.refid), zero(T)) shapes[(shape.cellid, shape.refid)] = v + shape.coeff - # set!(shapes, (shape.cellid, shape.refid), v + shape.coeff) end space.fns[i] = [BEAST.Shape(k[1], k[2], v) for (k,v) in shapes] end @@ -47,10 +49,7 @@ function showfn(space,i) nbd = center(chrt) vals = refspace(space)(nbd) x,y,z = cartesian(nbd) - # @show vals[sh.refid].value u,v,w = vals[sh.refid].value - # @show x, y, z - # @show u, v, w X[sh.cellid] = x Y[sh.cellid] = y Z[sh.cellid] = z @@ -70,137 +69,232 @@ end Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.45) tetrs = barycentric_refinement(Tetrs) -bnd_Tetrs = boundary(Tetrs) -bnd_tetrs = boundary(tetrs) -srt_bnd_Tetrs = sort.(bnd_Tetrs) -srt_bnd_tetrs = sort.(bnd_tetrs) +v2t, v2n = CompScienceMeshes.vertextocellmap(tetrs) + +Bnd = boundary(Tetrs) +bnd = boundary(tetrs) -Faces = submesh(Face -> !(sort(Face) in srt_bnd_Tetrs), skeleton(Tetrs,2)) +Dir = Bnd +dir = bnd + +Faces = submesh(!in(Bnd), CompScienceMeshes.skeleton_fast(Tetrs,2)) @show length(Faces) F = 396 Face = cells(Faces)[F] pos = cartesian(center(chart(Faces, Face))) -supp1 = submesh(tet -> Face[1] in tet, tetrs.mesh) -supp2 = submesh(tet -> Face[2] in tet, tetrs.mesh) -supp3 = submesh(tet -> Face[3] in tet, tetrs.mesh) - -faces_supp1 = CompScienceMeshes.skeleton_fast(supp1, 2) -faces_supp2 = CompScienceMeshes.skeleton_fast(supp2, 2) -faces_supp3 = CompScienceMeshes.skeleton_fast(supp3, 2) - -supp = CompScienceMeshes.union(supp1, supp2) -supp = CompScienceMeshes.union(supp, supp3) -# PlotlyJS.plot(patch(boundary(supp))) - -port = skeleton(supp1, 1) -# port = submesh(edge -> sort(edge) in sort.(skeleton(supp2,1)), port) -# port = submesh(edge -> sort(edge) in sort.(skeleton(supp3,1)), port) -port = submesh(in(skeleton(supp2,1)), port) -port = submesh(in(skeleton(supp3,1)), port) -@show length(port) -@assert 1 ≤ length(port) ≤ 2 - -bnd_supp = boundary(supp) -dir = submesh(in(bnd_tetrs), bnd_supp) -# dir = submesh(face -> sort(face) in srt_bnd_tetrs, bnd_supp) -rim = boundary(dir) -@show length(dir) - -# Step 1: step port flux and extend -x0 = ones(length(port)) / length(port) -for (i,edge) in enumerate(port) - tgt = tangents(center(chart(port,edge)),1) - if dot(normal(chart(Faces,Face)), tgt) < 0 - x0[i] *= -1 - end -end +idcs1 = v2t[Face[1],1:v2n[Face[1]]] +idcs2 = v2t[Face[2],1:v2n[Face[2]]] +idcs3 = v2t[Face[3],1:v2n[Face[3]]] -# cells_faces_supp1 = sort.(faces_supp1) -# cells_faces_supp2 = sort.(faces_supp2) -# cells_faces_supp3 = sort.(faces_supp3) +supp1 = tetrs.mesh[idcs1] +supp2 = tetrs.mesh[idcs2] +supp3 = tetrs.mesh[idcs3] -dFace1 = submesh(in(faces_supp2), faces_supp3) -dFace2 = submesh(in(faces_supp3), faces_supp1) -dFace3 = submesh(in(faces_supp1), faces_supp2) +supp1_edges = CompScienceMeshes.skeleton_fast(supp1, 1) +supp2_edges = CompScienceMeshes.skeleton_fast(supp2, 1) +supp3_edges = CompScienceMeshes.skeleton_fast(supp3, 1) +supp1_nodes = CompScienceMeshes.skeleton_fast(supp1, 0) +supp2_nodes = CompScienceMeshes.skeleton_fast(supp2, 0) +supp3_nodes = CompScienceMeshes.skeleton_fast(supp3, 0) +port_edges = supp1_edges +port_edges = submesh(in(supp2_edges), port_edges) +port_edges = submesh(in(supp3_edges), port_edges) +@show length(port_edges) +@assert 1 ≤ length(port_edges) ≤ 2 -srt_port = sort.(port) -srt_dir_edges = sort.(skeleton(dir,1)) -srt_bnd_edges = sort.(skeleton(boundary(supp),1)) -srt_rim = sort.(rim) -int_edges = submesh(skeleton(supp,1)) do edge - sort(edge) in srt_port && return false - sort(edge) in srt_rim && return false - sort(edge) in srt_dir_edges && return true - sort(edge) in srt_bnd_edges && return false - return true -end -@show length(int_edges) -@assert length(skeleton(supp,0)) - length(skeleton(supp,1)) + - length(skeleton(supp,2)) - length(supp) == 1 - -Nd_prt = BEAST.nedelecc3d(supp, port) -Nd_int = BEAST.nedelecc3d(supp, int_edges) -@show numfunctions(Nd_int) - - - -curl_Nd_int = curl(Nd_int) -A = assemble(Id, curl_Nd_int, curl_Nd_int) -N = nullspace(A) -@show size(N,2) -a = -assemble(Id, curl_Nd_int, curl(Nd_prt)) * x0 -x1 = pinv(A) * a -# x1 = A \ a -@show norm(N'*a) -@show norm(A*x1-a) - -srt_bnd_verts = sort.(skeleton(boundary(supp),0)) -srt_prt_verts = sort.(skeleton(port,0)) -int_verts = submesh(skeleton(supp,0)) do vert - sort(vert) in srt_bnd_verts && return false - sort(vert) in srt_prt_verts && return false - return true -end -@show length(int_verts) -L0_int = lagrangec0d1(supp, int_verts) -@show numfunctions(L0_int) -grad_L0_int = BEAST.gradient(L0_int) -@assert numfunctions(grad_L0_int) == numfunctions(L0_int) -B = assemble(Id, grad_L0_int, Nd_int) -B2, store = BEAST.allocatestorage(Id, grad_L0_int, Nd_int, - Val{:bandedstorage}, BEAST.LongDelays{:ignore}) -BEAST.assemble_local_mixed!(Id, grad_L0_int, Nd_int, store) -@assert isapprox(B, B2, atol=1e-8) -@show rank(B2) +dir1_faces = submesh(in(dir), boundary(supp1)) +dir2_faces = submesh(in(dir), boundary(supp2)) +dir3_faces = submesh(in(dir), boundary(supp3)) -b = -assemble(Id, grad_L0_int, Nd_prt) * x0 -p = (B*N) \ (b-B*x1) -x1 = x1 + N*p +dir1_compl = submesh(!in(dir), boundary(supp1)) +dir2_compl = submesh(!in(dir), boundary(supp2)) +dir3_compl = submesh(!in(dir), boundary(supp3)) -@show norm(A*x1-a) -@show norm(B*x1-b) +dir1_compl_edges = CompScienceMeshes.skeleton_fast(dir1_compl, 1) +dir2_compl_edges = CompScienceMeshes.skeleton_fast(dir2_compl, 1) +dir3_compl_edges = CompScienceMeshes.skeleton_fast(dir3_compl, 1) -fn = BEAST.Shape{Float64}[] -add!(fn, x0, Nd_prt) -add!(fn, x1, Nd_int) +dir1_compl_nodes = CompScienceMeshes.skeleton_fast(dir1_compl, 0) +dir2_compl_nodes = CompScienceMeshes.skeleton_fast(dir2_compl, 1) +dir3_compl_nodes = CompScienceMeshes.skeleton_fast(dir3_compl, 2) + +supp23 = submesh(in(boundary(supp2)), boundary(supp3)) +supp31 = submesh(in(boundary(supp3)), boundary(supp1)) +supp12 = submesh(in(boundary(supp1)), boundary(supp2)) + +supp23_edges = CompScienceMeshes.skeleton_fast(supp23, 1) +supp31_edges = CompScienceMeshes.skeleton_fast(supp31, 1) +supp12_edges = CompScienceMeshes.skeleton_fast(supp12, 1) + +supp23_nodes = CompScienceMeshes.skeleton_fast(supp23, 0) +supp31_nodes = CompScienceMeshes.skeleton_fast(supp31, 0) +supp12_nodes = CompScienceMeshes.skeleton_fast(supp12, 0) + +dir23_edges = submesh(in(boundary(dir2_faces)), boundary(dir3_faces)) +dir31_edges = submesh(in(boundary(dir3_faces)), boundary(dir1_faces)) +dir12_edges = submesh(in(boundary(dir1_faces)), boundary(dir2_faces)) + +dir23_compl_edges = submesh(!in(dir23_edges), boundary(supp23)) +dir31_compl_edges = submesh(!in(dir31_edges), boundary(supp31)) +dir12_compl_edges = submesh(!in(dir12_edges), boundary(supp12)) -Y1 = BEAST.NDLCCBasis(supp, [fn],[pos]) -curlY1 = curl(Y1); compress!(curl(Y1)); +dir23_compl_nodes = CompScienceMeshes.skeleton_fast(dir23_compl_edges, 0) +dir31_compl_nodes = CompScienceMeshes.skeleton_fast(dir31_compl_edges, 0) +dir12_compl_nodes = CompScienceMeshes.skeleton_fast(dir12_compl_edges, 0) -include(joinpath(@__DIR__, "utils/edge_values.jl")) -EV = edge_values(Y1,1) -check_edge_values(EV) +supp23_int_edges = submesh(!in(dir23_compl_edges), supp23_edges) +supp31_int_edges = submesh(!in(dir31_compl_edges), supp31_edges) +supp12_int_edges = submesh(!in(dir12_compl_edges), supp12_edges) + +supp23_int_nodes = submesh(!in(dir23_compl_nodes), supp23_nodes) +supp31_int_nodes = submesh(!in(dir31_compl_nodes), supp31_nodes) +supp12_int_nodes = submesh(!in(dir12_compl_nodes), supp12_nodes) + +X23_prt = BEAST.nedelec(supp23, port_edges) +X31_prt = BEAST.nedelec(supp31, port_edges) +X12_prt = BEAST.nedelec(supp12, port_edges) + +X23_int = BEAST.nedelec(supp23, supp23_int_edges) +X31_int = BEAST.nedelec(supp31, supp31_int_edges) +X12_int = BEAST.nedelec(supp12, supp12_int_edges) + +curl_X23_prt = divergence(n × X23_prt) +curl_X31_prt = divergence(n × X31_prt) +curl_X12_prt = divergence(n × X12_prt) + +curl_X23_int = divergence(n × X23_int) +curl_X31_int = divergence(n × X31_int) +curl_X12_int = divergence(n × X12_int) + +Y23 = lagrangec0d1(supp23, supp23_int_nodes) +Y31 = lagrangec0d1(supp31, supp31_int_nodes) +Y12 = lagrangec0d1(supp12, supp12_int_nodes) + +grad_Y23 = n × curl(Y23) +grad_Y31 = n × curl(Y31) +grad_Y12 = n × curl(Y12) + +# Step 1: set port flux and extend to dual faces +x0 = ones(length(port_edges)) / length(port_edges) +for (i,edge) in enumerate(port_edges) + tgt = tangents(center(chart(port_edges, edge)),1) + if dot(normal(chart(Faces, Face)), tgt) < 0 + x0[i] *= -1 + end +end + +A = assemble(Id, curl_X23_int, curl_X23_int) +a = -assemble(Id, curl_X23_int, curl_X23_prt) * x0 +B = assemble(Id, grad_Y23, X23_int) +b = -assemble(Id, grad_Y23, X23_prt) * x0 +nb = length(b) +Z = zeros(eltype(B), nb, nb) +u = [A B'; B Z] \ [a; b] +x23 = u[1:end-nb] + +A = assemble(Id, curl_X31_int, curl_X31_int) +a = -assemble(Id, curl_X31_int, curl_X31_prt) * x0 +B = assemble(Id, grad_Y31, X31_int) +b = -assemble(Id, grad_Y31, X31_prt) * x0 +nb = length(b) +Z = zeros(eltype(B), nb, nb) +u = [A B'; B Z] \ [a; b] +x31 = u[1:end-nb] + +A = assemble(Id, curl_X12_int, curl_X12_int) +a = -assemble(Id, curl_X12_int, curl_X12_prt) * x0 +B = assemble(Id, grad_Y12, X12_int) +b = -assemble(Id, grad_Y12, X12_prt) * x0 +nb = length(b) +Z = zeros(eltype(B), nb, nb) +u = [A B'; B Z] \ [a; b] +x12 = u[1:end-nb] + + +# Step 2: extend to the volume +supp1_int_edges = submesh(!in(dir1_compl_edges), supp1_edges) +supp2_int_edges = submesh(!in(dir2_compl_edges), supp2_edges) +supp3_int_edges = submesh(!in(dir3_compl_edges), supp3_edges) + +supp1_int_nodes = submesh(!in(dir1_compl_nodes), supp1_nodes) +supp2_int_nodes = submesh(!in(dir2_compl_nodes), supp2_nodes) +supp3_int_nodes = submesh(!in(dir3_compl_nodes), supp3_nodes) + +supp1_drv_edges = CompScienceMeshes.union(port_edges, supp31_int_edges) +supp1_drv_edges = CompScienceMeshes.union(supp1_drv_edges, supp12_int_edges) +supp2_drv_edges = CompScienceMeshes.union(port_edges, supp12_int_edges) +supp2_drv_edges = CompScienceMeshes.union(supp2_drv_edges, supp23_int_edges) +supp3_drv_edges = CompScienceMeshes.union(port_edges, supp23_int_edges) +supp3_drv_edges = CompScienceMeshes.union(supp3_drv_edges, supp31_int_edges) + +X1_drv = BEAST.nedelecc3d(supp1, supp1_drv_edges) +X2_drv = BEAST.nedelecc3d(supp2, supp2_drv_edges) +X3_drv = BEAST.nedelecc3d(supp3, supp3_drv_edges) + +X1_int = BEAST.nedelecc3d(supp1, supp1_int_edges) +X2_int = BEAST.nedelecc3d(supp2, supp2_int_edges) +X3_int = BEAST.nedelecc3d(supp3, supp3_int_edges) + +curl_X1_drv = curl(X1_drv) +curl_X2_drv = curl(X2_drv) +curl_X3_drv = curl(X3_drv) + +curl_X1_int = curl(X1_int) +curl_X2_int = curl(X2_int) +curl_X3_int = curl(X3_int) + +Y1 = BEAST.lagrangec0d1(supp1, supp1_int_nodes) +Y2 = BEAST.lagrangec0d1(supp2, supp2_int_nodes) +Y3 = BEAST.lagrangec0d1(supp3, supp3_int_nodes) + +grad_Y1 = BEAST.gradient(Y1) +grad_Y2 = BEAST.gradient(Y2) +grad_Y3 = BEAST.gradient(Y3) + +A = assemble(Id, curl_X1_int, curl_X1_int) +a = -assemble(Id, curl_X1_int, curl_X1_drv) * [x0; x31; x12] +B = assemble(Id, grad_Y1, X1_int) +b = -assemble(Id, grad_Y1, X1_drv) * [x0; x31; x12] +nb = length(b) +Z = zeros(eltype(B), nb, nb) +u = [A B'; B Z] \ [a; b] +x1 = u[1:end-nb] + +A = assemble(Id, curl_X2_int, curl_X2_int) +a = -assemble(Id, curl_X2_int, curl_X2_drv) * [x0; x12; x23] +B = assemble(Id, grad_Y2, X2_int) +b = -assemble(Id, grad_Y2, X2_drv) * [x0; x12; x23] +nb = length(b) +Z = zeros(eltype(B), nb, nb) +u = [A B'; B Z] \ [a; b] +x2 = u[1:end-nb] + +A = assemble(Id, curl_X3_int, curl_X3_int) +a = -assemble(Id, curl_X3_int, curl_X3_drv) * [x0; x23; x31] +B = assemble(Id, grad_Y3, X3_int) +b = -assemble(Id, grad_Y3, X3_drv) * [x0; x23; x31] +nb = length(b) +Z = zeros(eltype(B), nb, nb) +u = [A B'; B Z] \ [a; b] +x3 = u[1:end-nb] + + +# inject in the global space +fn = BEAST.Shape{Float64}[] +add!(fn, [x0; x31; x12], X1_drv, idcs1) +add!(fn, x1, X1_int, idcs1) -error("stop") +add!(fn, [x0; x12; x23], X2_drv, idcs2) +add!(fn, x2, X2_int, idcs2) -Y = BEAST.dual1forms(Tetrs, Faces) -curlY = curl(Y) -CC = assemble(Id, curlY, curlY) +add!(fn, [x0; x23; x31], X3_drv, idcs3) +add!(fn, x3, X3_int, idcs3) -Plotly.plot(svdvals(CC)) +pos = cartesian(CompScienceMeshes.center(chart(Faces, Face))) +space = BEAST.NDLCCBasis(tetrs, [fn], [pos]) diff --git a/src/bases/rtspace.jl b/src/bases/rtspace.jl index 6de46989..b442813d 100644 --- a/src/bases/rtspace.jl +++ b/src/bases/rtspace.jl @@ -239,3 +239,13 @@ end divergence(X::RTBasis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) ntrace(X::RTBasis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) + + +function LinearAlgebra.cross(::NormalVector, s::RTBasis) + @assert CompScienceMeshes.isoriented(s.geo) + fns = similar(s.fns) + for (i,fn) in pairs(s.fns) + fns[i] = [Shape(sh.cellid, sh.refid, -sh.coeff) for sh in fn] + end + NDBasis(s.geo, fns, s.pos) +end From efadca0f648ece253c5fc1ed8479751d6a82174e Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 21 Feb 2020 10:52:33 +0100 Subject: [PATCH 017/528] use saddle point solution instead of svd decomposition in construction of dual 2-forms --- src/bases/dual3d.jl | 59 ++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 1fd971af..603445ad 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -1,5 +1,16 @@ using LinearAlgebra + +function addf!(fn::Vector{<:Shape}, x::Vector, space::Space, idcs::Vector{Int}) + for (m,bf) in enumerate(space.fns) + for sh in bf + cellid = idcs[sh.cellid] + BEAST.add!(fn, cellid, sh.refid, sh.coeff * x[m]) + end + end +end + + function builddual2form(support, port, dirichlet, prt_fluxes) # println() @@ -66,11 +77,17 @@ function builddual2form(support, port, dirichlet, prt_fluxes) C = assemble(Id, curl_L0_int, RT_int) c = real(assemble(Id, curl_L0_int, RT_prt)) * prt_fluxes - x1 = pinv(D) * d - N = nullspace(D) - @assert size(N,2) == rank(C) - p = (C*N) \ (c - C*x1) - x = x1 + N*p + # x1 = pinv(D) * d + # N = nullspace(D) + # @assert size(N,2) == rank(C) + # p = (C*N) \ (c - C*x1) + # x = x1 + N*p + + T = eltype(D) + nz = length(c) + QQ = [D C'; C zeros(T,nz,nz)] + qq = [d;c] + x = (QQ \ qq)[1:end-nz] if !isapprox(C*x, c, atol=1e-8) || !isapprox(D*x, d, atol=1e-6) @show norm(D*x-d) @@ -140,9 +157,6 @@ function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) port = submesh(face -> sort(face) in set_bnd_patch2, bnd_patch1) patch = CompScienceMeshes.union(patch1, patch2) - # @show numcells(patch_bnd) - # @show numcells(patch) - # @show numcells(port) prt_fluxes = ones(T, numcells(port)) / numcells(port) tgt = vertices(Edges)[Edge[1]] - vertices(Edges)[Edge[2]] @@ -152,23 +166,9 @@ function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) end RT_int, RT_prt, x_int, x_prt = builddual2form(patch, port, dirichlet, prt_fluxes) - # @show norm(x_int) - # @show norm(x_prt) - - ptch_idcs = vcat(ptch_idcs1, ptch_idcs2) - for (m,bf) in enumerate(RT_int.fns) - for sh in bf - cellid = ptch_idcs[sh.cellid] - BEAST.add!(bfs[F], cellid, sh.refid, sh.coeff * x_int[m]) - end - end - - for (m,bf) in enumerate(RT_prt.fns) - for sh in bf - cellid = ptch_idcs[sh.cellid] - BEAST.add!(bfs[F],cellid, sh.refid, sh.coeff * x_prt[m]) - end - end + ptch_idcs = [ptch_idcs1; ptch_idcs2] + addf!(bfs[F], x_int, RT_int, ptch_idcs) + addf!(bfs[F], x_prt, RT_prt, ptch_idcs) end @@ -176,14 +176,7 @@ function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) end -function addf!(fn::Vector{<:Shape}, x::Vector, space::Space, idcs::Vector{Int}) - for (m,bf) in enumerate(space.fns) - for sh in bf - cellid = idcs[sh.cellid] - BEAST.add!(fn, cellid, sh.refid, sh.coeff * x[m]) - end - end -end + function builddual1form(supp, port, dir, x0) From ee6a2073204f9c3c19ecd63d2c7b432addba8961 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 21 Feb 2020 11:49:01 +0100 Subject: [PATCH 018/528] construct dual 2-forms on each dual cell separately --- src/bases/dual3d.jl | 55 +++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 603445ad..88ec233d 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -10,10 +10,15 @@ function addf!(fn::Vector{<:Shape}, x::Vector, space::Space, idcs::Vector{Int}) end end +function Base.in(mesh::CompScienceMeshes.AbstractMesh) + cells_mesh = sort.(mesh) + function f(cell) + sort(cell) in cells_mesh + end +end -function builddual2form(support, port, dirichlet, prt_fluxes) - # println() +function builddual2form(support, port, dirichlet, prt_fluxes) faces = CompScienceMeshes.skeleton_fast(support,2) edges = CompScienceMeshes.skeleton_fast(support,1) @@ -77,12 +82,6 @@ function builddual2form(support, port, dirichlet, prt_fluxes) C = assemble(Id, curl_L0_int, RT_int) c = real(assemble(Id, curl_L0_int, RT_prt)) * prt_fluxes - # x1 = pinv(D) * d - # N = nullspace(D) - # @assert size(N,2) == rank(C) - # p = (C*N) \ (c - C*x1) - # x = x1 + N*p - T = eltype(D) nz = length(c) QQ = [D C'; C zeros(T,nz,nz)] @@ -97,11 +96,6 @@ function builddual2form(support, port, dirichlet, prt_fluxes) error("error") end - # if rank(C) != size(C,1) - # @show rank(C) - # @show size(C) - # end - return RT_int, RT_prt, x, prt_fluxes end @@ -124,18 +118,17 @@ end function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) - T = coordtype(Tetrs) bfs = Vector{Vector{Shape{T}}}(undef, numcells(Edges)) pos = Vector{vertextype(Edges)}(undef, numcells(Edges)) - # dirichlet = boundary(tetrs) + gpred = CompScienceMeshes.overlap_gpredicate(Dir) dirichlet = submesh(bnd) do face gpred(chart(bnd,face)) end - # @show numcells(dirichlet) + for (F,Edge) in enumerate(cells(Edges)) - # @show F + println("Constructing dual 2-forms: $F out of $(length(Edges)).") bfs[F] = Vector{Shape{T}}() @@ -147,16 +140,13 @@ function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) patch1 = Mesh(vertices(tetrs), cells(tetrs)[ptch_idcs1]) patch2 = Mesh(vertices(tetrs), cells(tetrs)[ptch_idcs2]) + patch = CompScienceMeshes.union(patch1, patch2) + patch_bnd = boundary(patch1) bnd_patch1 = boundary(patch1) bnd_patch2 = boundary(patch2) - - set_bnd_patch2 = Set(sort.(bnd_patch2)) - - port = submesh(face -> sort(face) in set_bnd_patch2, bnd_patch1) - - patch = CompScienceMeshes.union(patch1, patch2) + port = submesh(in(bnd_patch2), bnd_patch1) prt_fluxes = ones(T, numcells(port)) / numcells(port) tgt = vertices(Edges)[Edge[1]] - vertices(Edges)[Edge[2]] @@ -164,11 +154,22 @@ function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) chrt = chart(port, face) prt_fluxes[i] *= sign(dot(normal(chrt), tgt)) end - RT_int, RT_prt, x_int, x_prt = builddual2form(patch, port, dirichlet, prt_fluxes) - ptch_idcs = [ptch_idcs1; ptch_idcs2] - addf!(bfs[F], x_int, RT_int, ptch_idcs) - addf!(bfs[F], x_prt, RT_prt, ptch_idcs) + # RT_int, RT_prt, x_int, x_prt = builddual2form(patch, port, dirichlet, prt_fluxes) + # ptch_idcs = [ptch_idcs1; ptch_idcs2] + # addf!(bfs[F], x_int, RT_int, ptch_idcs) + # addf!(bfs[F], x_prt, RT_prt, ptch_idcs) + + RT1_int, RT1_prt, x1_int, x_prt = builddual2form( + patch1, port, dirichlet, +prt_fluxes) + RT2_int, RT2_prt, x2_int, x_prt = builddual2form( + patch2, port, dirichlet, prt_fluxes) + + addf!(bfs[F], x1_int, RT1_int, ptch_idcs1) + addf!(bfs[F], +x_prt, RT1_prt, ptch_idcs1) + + addf!(bfs[F], x2_int, RT2_int, ptch_idcs2) + addf!(bfs[F], x_prt, RT2_prt, ptch_idcs2) end From 773755a26dde9c279beb04a34665a2174b52a33b Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 21 Feb 2020 14:21:08 +0100 Subject: [PATCH 019/528] clean up ex_dual1forms_bis.jl --- examples/ex_dual1forms_bis.jl | 276 ++++++++++++---------------------- 1 file changed, 100 insertions(+), 176 deletions(-) diff --git a/examples/ex_dual1forms_bis.jl b/examples/ex_dual1forms_bis.jl index 2b996bfc..ed4501be 100644 --- a/examples/ex_dual1forms_bis.jl +++ b/examples/ex_dual1forms_bis.jl @@ -66,6 +66,73 @@ function showfn(space,i) Plotly.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) end +function extend_edge_to_face(supp, dirichlet, x_prt, port_edges) + bnd_supp = boundary(supp) + supp_edges = CompScienceMeshes.skeleton_fast(supp, 1) + supp_nodes = CompScienceMeshes.skeleton_fast(supp, 0) + + dir_compl_edges = submesh(!in(dirichlet), bnd_supp) + dir_compl_nodes = CompScienceMeshes.skeleton_fast(dir_compl_edges, 0) + + int_edges = submesh(!in(dir_compl_edges), supp_edges) + int_nodes = submesh(!in(dir_compl_nodes), supp_nodes) + + Nd_prt = BEAST.nedelec(supp, port_edges) + Nd_int = BEAST.nedelec(supp, int_edges) + Lg_int = BEAST.lagrangec0d1(supp, int_nodes) + + curl_Nd_prt = divergence(n × Nd_prt) + curl_Nd_int = divergence(n × Nd_int) + grad_Lg_int = n × curl(Lg_int) + + A = assemble(Id, curl_Nd_int, curl_Nd_int) + B = assemble(Id, grad_Lg_int, Nd_int) + + a = -assemble(Id, curl_Nd_int, curl_Nd_prt) * x_prt + b = -assemble(Id, grad_Lg_int, Nd_prt) * x_prt + + Z = zeros(eltype(b), length(b), length(b)) + u = [A B'; B Z] \ [a;b] + x_int = u[1:end-length(b)] + + return x_int, int_edges, Nd_int +end + + + +function extend_face_to_tetr(supp, dirichlet, x_prt, port_edges) + bnd_supp = boundary(supp) + supp_edges = CompScienceMeshes.skeleton_fast(supp, 1) + supp_nodes = CompScienceMeshes.skeleton_fast(supp, 0) + + dir_compl_faces = submesh(!in(dirichlet), bnd_supp) + dir_compl_edges = CompScienceMeshes.skeleton_fast(dir_compl_faces, 1) + dir_compl_nodes = CompScienceMeshes.skeleton_fast(dir_compl_faces, 0) + + int_edges = submesh(!in(dir_compl_edges), supp_edges) + int_nodes = submesh(!in(dir_compl_nodes), supp_nodes) + + Nd_prt = BEAST.nedelecc3d(supp, port_edges) + Nd_int = BEAST.nedelecc3d(supp, int_edges) + Lg_int = BEAST.lagrangec0d1(supp, int_nodes) + + curl_Nd_prt = curl(Nd_prt) + curl_Nd_int = curl(Nd_int) + grad_Lg_int = BEAST.gradient(Lg_int) + + A = assemble(Id, curl_Nd_int, curl_Nd_int) + B = assemble(Id, grad_Lg_int, Nd_int) + + a = -assemble(Id, curl_Nd_int, curl_Nd_prt) * x_prt + b = -assemble(Id, grad_Lg_int, Nd_prt) * x_prt + + Z = zeros(eltype(b), length(b), length(b)) + u = [A B'; B Z] \ [a;b] + x_int = u[1:end-length(b)] + + return x_int, int_edges, Nd_int +end + Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.45) tetrs = barycentric_refinement(Tetrs) @@ -92,93 +159,21 @@ supp1 = tetrs.mesh[idcs1] supp2 = tetrs.mesh[idcs2] supp3 = tetrs.mesh[idcs3] -supp1_edges = CompScienceMeshes.skeleton_fast(supp1, 1) -supp2_edges = CompScienceMeshes.skeleton_fast(supp2, 1) -supp3_edges = CompScienceMeshes.skeleton_fast(supp3, 1) - -supp1_nodes = CompScienceMeshes.skeleton_fast(supp1, 0) -supp2_nodes = CompScienceMeshes.skeleton_fast(supp2, 0) -supp3_nodes = CompScienceMeshes.skeleton_fast(supp3, 0) - -port_edges = supp1_edges -port_edges = submesh(in(supp2_edges), port_edges) -port_edges = submesh(in(supp3_edges), port_edges) -@show length(port_edges) -@assert 1 ≤ length(port_edges) ≤ 2 - - - dir1_faces = submesh(in(dir), boundary(supp1)) dir2_faces = submesh(in(dir), boundary(supp2)) dir3_faces = submesh(in(dir), boundary(supp3)) -dir1_compl = submesh(!in(dir), boundary(supp1)) -dir2_compl = submesh(!in(dir), boundary(supp2)) -dir3_compl = submesh(!in(dir), boundary(supp3)) - -dir1_compl_edges = CompScienceMeshes.skeleton_fast(dir1_compl, 1) -dir2_compl_edges = CompScienceMeshes.skeleton_fast(dir2_compl, 1) -dir3_compl_edges = CompScienceMeshes.skeleton_fast(dir3_compl, 1) - -dir1_compl_nodes = CompScienceMeshes.skeleton_fast(dir1_compl, 0) -dir2_compl_nodes = CompScienceMeshes.skeleton_fast(dir2_compl, 1) -dir3_compl_nodes = CompScienceMeshes.skeleton_fast(dir3_compl, 2) - supp23 = submesh(in(boundary(supp2)), boundary(supp3)) supp31 = submesh(in(boundary(supp3)), boundary(supp1)) supp12 = submesh(in(boundary(supp1)), boundary(supp2)) -supp23_edges = CompScienceMeshes.skeleton_fast(supp23, 1) -supp31_edges = CompScienceMeshes.skeleton_fast(supp31, 1) -supp12_edges = CompScienceMeshes.skeleton_fast(supp12, 1) - -supp23_nodes = CompScienceMeshes.skeleton_fast(supp23, 0) -supp31_nodes = CompScienceMeshes.skeleton_fast(supp31, 0) -supp12_nodes = CompScienceMeshes.skeleton_fast(supp12, 0) - dir23_edges = submesh(in(boundary(dir2_faces)), boundary(dir3_faces)) dir31_edges = submesh(in(boundary(dir3_faces)), boundary(dir1_faces)) dir12_edges = submesh(in(boundary(dir1_faces)), boundary(dir2_faces)) - -dir23_compl_edges = submesh(!in(dir23_edges), boundary(supp23)) -dir31_compl_edges = submesh(!in(dir31_edges), boundary(supp31)) -dir12_compl_edges = submesh(!in(dir12_edges), boundary(supp12)) - -dir23_compl_nodes = CompScienceMeshes.skeleton_fast(dir23_compl_edges, 0) -dir31_compl_nodes = CompScienceMeshes.skeleton_fast(dir31_compl_edges, 0) -dir12_compl_nodes = CompScienceMeshes.skeleton_fast(dir12_compl_edges, 0) - -supp23_int_edges = submesh(!in(dir23_compl_edges), supp23_edges) -supp31_int_edges = submesh(!in(dir31_compl_edges), supp31_edges) -supp12_int_edges = submesh(!in(dir12_compl_edges), supp12_edges) - -supp23_int_nodes = submesh(!in(dir23_compl_nodes), supp23_nodes) -supp31_int_nodes = submesh(!in(dir31_compl_nodes), supp31_nodes) -supp12_int_nodes = submesh(!in(dir12_compl_nodes), supp12_nodes) - -X23_prt = BEAST.nedelec(supp23, port_edges) -X31_prt = BEAST.nedelec(supp31, port_edges) -X12_prt = BEAST.nedelec(supp12, port_edges) - -X23_int = BEAST.nedelec(supp23, supp23_int_edges) -X31_int = BEAST.nedelec(supp31, supp31_int_edges) -X12_int = BEAST.nedelec(supp12, supp12_int_edges) - -curl_X23_prt = divergence(n × X23_prt) -curl_X31_prt = divergence(n × X31_prt) -curl_X12_prt = divergence(n × X12_prt) - -curl_X23_int = divergence(n × X23_int) -curl_X31_int = divergence(n × X31_int) -curl_X12_int = divergence(n × X12_int) - -Y23 = lagrangec0d1(supp23, supp23_int_nodes) -Y31 = lagrangec0d1(supp31, supp31_int_nodes) -Y12 = lagrangec0d1(supp12, supp12_int_nodes) - -grad_Y23 = n × curl(Y23) -grad_Y31 = n × curl(Y31) -grad_Y12 = n × curl(Y12) +port_edges = boundary(supp23) +port_edges = submesh(in(boundary(supp31)), port_edges) +port_edges = submesh(in(boundary(supp12)), port_edges) +@assert 1 ≤ length(port_edges) ≤ 2 # Step 1: set port flux and extend to dual faces x0 = ones(length(port_edges)) / length(port_edges) @@ -189,112 +184,41 @@ for (i,edge) in enumerate(port_edges) end end -A = assemble(Id, curl_X23_int, curl_X23_int) -a = -assemble(Id, curl_X23_int, curl_X23_prt) * x0 -B = assemble(Id, grad_Y23, X23_int) -b = -assemble(Id, grad_Y23, X23_prt) * x0 -nb = length(b) -Z = zeros(eltype(B), nb, nb) -u = [A B'; B Z] \ [a; b] -x23 = u[1:end-nb] - -A = assemble(Id, curl_X31_int, curl_X31_int) -a = -assemble(Id, curl_X31_int, curl_X31_prt) * x0 -B = assemble(Id, grad_Y31, X31_int) -b = -assemble(Id, grad_Y31, X31_prt) * x0 -nb = length(b) -Z = zeros(eltype(B), nb, nb) -u = [A B'; B Z] \ [a; b] -x31 = u[1:end-nb] - -A = assemble(Id, curl_X12_int, curl_X12_int) -a = -assemble(Id, curl_X12_int, curl_X12_prt) * x0 -B = assemble(Id, grad_Y12, X12_int) -b = -assemble(Id, grad_Y12, X12_prt) * x0 -nb = length(b) -Z = zeros(eltype(B), nb, nb) -u = [A B'; B Z] \ [a; b] -x12 = u[1:end-nb] - - -# Step 2: extend to the volume -supp1_int_edges = submesh(!in(dir1_compl_edges), supp1_edges) -supp2_int_edges = submesh(!in(dir2_compl_edges), supp2_edges) -supp3_int_edges = submesh(!in(dir3_compl_edges), supp3_edges) - -supp1_int_nodes = submesh(!in(dir1_compl_nodes), supp1_nodes) -supp2_int_nodes = submesh(!in(dir2_compl_nodes), supp2_nodes) -supp3_int_nodes = submesh(!in(dir3_compl_nodes), supp3_nodes) - -supp1_drv_edges = CompScienceMeshes.union(port_edges, supp31_int_edges) -supp1_drv_edges = CompScienceMeshes.union(supp1_drv_edges, supp12_int_edges) -supp2_drv_edges = CompScienceMeshes.union(port_edges, supp12_int_edges) -supp2_drv_edges = CompScienceMeshes.union(supp2_drv_edges, supp23_int_edges) -supp3_drv_edges = CompScienceMeshes.union(port_edges, supp23_int_edges) -supp3_drv_edges = CompScienceMeshes.union(supp3_drv_edges, supp31_int_edges) - -X1_drv = BEAST.nedelecc3d(supp1, supp1_drv_edges) -X2_drv = BEAST.nedelecc3d(supp2, supp2_drv_edges) -X3_drv = BEAST.nedelecc3d(supp3, supp3_drv_edges) - -X1_int = BEAST.nedelecc3d(supp1, supp1_int_edges) -X2_int = BEAST.nedelecc3d(supp2, supp2_int_edges) -X3_int = BEAST.nedelecc3d(supp3, supp3_int_edges) - -curl_X1_drv = curl(X1_drv) -curl_X2_drv = curl(X2_drv) -curl_X3_drv = curl(X3_drv) - -curl_X1_int = curl(X1_int) -curl_X2_int = curl(X2_int) -curl_X3_int = curl(X3_int) - -Y1 = BEAST.lagrangec0d1(supp1, supp1_int_nodes) -Y2 = BEAST.lagrangec0d1(supp2, supp2_int_nodes) -Y3 = BEAST.lagrangec0d1(supp3, supp3_int_nodes) - -grad_Y1 = BEAST.gradient(Y1) -grad_Y2 = BEAST.gradient(Y2) -grad_Y3 = BEAST.gradient(Y3) - -A = assemble(Id, curl_X1_int, curl_X1_int) -a = -assemble(Id, curl_X1_int, curl_X1_drv) * [x0; x31; x12] -B = assemble(Id, grad_Y1, X1_int) -b = -assemble(Id, grad_Y1, X1_drv) * [x0; x31; x12] -nb = length(b) -Z = zeros(eltype(B), nb, nb) -u = [A B'; B Z] \ [a; b] -x1 = u[1:end-nb] - -A = assemble(Id, curl_X2_int, curl_X2_int) -a = -assemble(Id, curl_X2_int, curl_X2_drv) * [x0; x12; x23] -B = assemble(Id, grad_Y2, X2_int) -b = -assemble(Id, grad_Y2, X2_drv) * [x0; x12; x23] -nb = length(b) -Z = zeros(eltype(B), nb, nb) -u = [A B'; B Z] \ [a; b] -x2 = u[1:end-nb] - -A = assemble(Id, curl_X3_int, curl_X3_int) -a = -assemble(Id, curl_X3_int, curl_X3_drv) * [x0; x23; x31] -B = assemble(Id, grad_Y3, X3_int) -b = -assemble(Id, grad_Y3, X3_drv) * [x0; x23; x31] -nb = length(b) -Z = zeros(eltype(B), nb, nb) -u = [A B'; B Z] \ [a; b] -x3 = u[1:end-nb] +x23, supp23_int_edges = extend_edge_to_face(supp23, dir23_edges, x0, port_edges) +x31, supp31_int_edges = extend_edge_to_face(supp31, dir31_edges, x0, port_edges) +x12, supp12_int_edges = extend_edge_to_face(supp12, dir12_edges, x0, port_edges) + +port1_edges = CompScienceMeshes.union(port_edges, supp31_int_edges) +port1_edges = CompScienceMeshes.union(port1_edges, supp12_int_edges) + +port2_edges = CompScienceMeshes.union(port_edges, supp12_int_edges) +port2_edges = CompScienceMeshes.union(port2_edges, supp23_int_edges) + +port3_edges = CompScienceMeshes.union(port_edges, supp23_int_edges) +port3_edges = CompScienceMeshes.union(port3_edges, supp31_int_edges) + +x1_prt = [x0; x31; x12] +x2_prt = [x0; x12; x23] +x3_prt = [x0; x23; x31] + +Nd1_prt = BEAST.nedelecc3d(supp1, port1_edges) +Nd2_prt = BEAST.nedelecc3d(supp2, port2_edges) +Nd3_prt = BEAST.nedelecc3d(supp3, port3_edges) +x1_int, _, Nd1_int = extend_face_to_tetr(supp1, dir1_faces, x1_prt, port1_edges) +x2_int, _, Nd2_int = extend_face_to_tetr(supp2, dir2_faces, x2_prt, port2_edges) +x3_int, _, Nd3_int = extend_face_to_tetr(supp3, dir3_faces, x3_prt, port3_edges) # inject in the global space fn = BEAST.Shape{Float64}[] -add!(fn, [x0; x31; x12], X1_drv, idcs1) -add!(fn, x1, X1_int, idcs1) +add!(fn, x1_prt, Nd1_prt, idcs1) +add!(fn, x1_int, Nd1_int, idcs1) -add!(fn, [x0; x12; x23], X2_drv, idcs2) -add!(fn, x2, X2_int, idcs2) +add!(fn, x2_prt, Nd2_prt, idcs2) +add!(fn, x2_int, Nd2_int, idcs2) -add!(fn, [x0; x23; x31], X3_drv, idcs3) -add!(fn, x3, X3_int, idcs3) +add!(fn, x3_prt, Nd3_prt, idcs3) +add!(fn, x3_int, Nd3_int, idcs3) pos = cartesian(CompScienceMeshes.center(chart(Faces, Face))) space = BEAST.NDLCCBasis(tetrs, [fn], [pos]) From 7248106273d7fe6f154750772b7a70adcaac313d Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 21 Feb 2020 16:56:13 +0100 Subject: [PATCH 020/528] Checked the disappearing block property of the mixed gram matrix --- examples/ex_dual1forms_bis.jl | 25 ++++ src/bases/dual3d.jl | 234 +++++++++++++++++++++------------- src/identityop.jl | 7 + 3 files changed, 178 insertions(+), 88 deletions(-) diff --git a/examples/ex_dual1forms_bis.jl b/examples/ex_dual1forms_bis.jl index ed4501be..ec8d01c4 100644 --- a/examples/ex_dual1forms_bis.jl +++ b/examples/ex_dual1forms_bis.jl @@ -133,6 +133,7 @@ function extend_face_to_tetr(supp, dirichlet, x_prt, port_edges) return x_int, int_edges, Nd_int end + Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.45) tetrs = barycentric_refinement(Tetrs) @@ -222,3 +223,27 @@ add!(fn, x3_int, Nd3_int, idcs3) pos = cartesian(CompScienceMeshes.center(chart(Faces, Face))) space = BEAST.NDLCCBasis(tetrs, [fn], [pos]) + + +tetrs, bnd, dir, v2t, v2n = BEAST.dual1forms_init(Tetrs, Dir) +D1 = BEAST.dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) +P2 = BEAST.nedelecd3d(Tetrs, Faces) +# S = BEAST.dual1forms(Tetrs, Faces[collect(396:405)], Dir) + +int_Edges = submesh(!in(skeleton(boundary(Tetrs),1)), Edges) +P1 = BEAST.nedelecc3d(Tetrs, int_Edges) + +curl_D1 = curl(D1) +curl_P1 = curl(P1) +div_P2 = divergence(P2) + +G = assemble(Id, curl_D1, curl_D1) +GP = assemble(Id, div_P2, div_P2) +M = assemble(Id, D1, P2) + +CDP = assemble(Id, D1, curl_P1) +CPD = CDP' +GDD = assemble(Id, D1, D1) + +A1 = CPD * inv(GDD) * CDP; +A2 = assemble(Id, curl_P1, curl_P1) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 88ec233d..3fbb209f 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -18,6 +18,7 @@ function Base.in(mesh::CompScienceMeshes.AbstractMesh) end + function builddual2form(support, port, dirichlet, prt_fluxes) faces = CompScienceMeshes.skeleton_fast(support,2) @@ -177,126 +178,183 @@ function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) end +function extend_edge_to_face(supp, dirichlet, x_prt, port_edges) + + Id = BEAST.Identity() + + bnd_supp = boundary(supp) + supp_edges = CompScienceMeshes.skeleton_fast(supp, 1) + supp_nodes = CompScienceMeshes.skeleton_fast(supp, 0) + + dir_compl_edges = submesh(!in(dirichlet), bnd_supp) + dir_compl_nodes = CompScienceMeshes.skeleton_fast(dir_compl_edges, 0) + + int_edges = submesh(!in(dir_compl_edges), supp_edges) + int_nodes = submesh(!in(dir_compl_nodes), supp_nodes) + + Nd_prt = BEAST.nedelec(supp, port_edges) + Nd_int = BEAST.nedelec(supp, int_edges) + Lg_int = BEAST.lagrangec0d1(supp, int_nodes) + + curl_Nd_prt = divergence(n × Nd_prt) + curl_Nd_int = divergence(n × Nd_int) + grad_Lg_int = n × curl(Lg_int) + + A = assemble(Id, curl_Nd_int, curl_Nd_int) + B = assemble(Id, grad_Lg_int, Nd_int) + a = -assemble(Id, curl_Nd_int, curl_Nd_prt) * x_prt + b = -assemble(Id, grad_Lg_int, Nd_prt) * x_prt -function builddual1form(supp, port, dir, x0) + Z = zeros(eltype(b), length(b), length(b)) + u = [A B'; B Z] \ [a;b] + x_int = u[1:end-length(b)] + + return x_int, int_edges, Nd_int +end + + + +function extend_face_to_tetr(supp, dirichlet, x_prt, port_edges) Id = BEAST.Identity() - rim = boundary(dir) - bnd = boundary(supp) - supp_edges = CompScienceMeshes.skeleton_fast(supp,1) - supp_nodes = CompScienceMeshes.skeleton_fast(supp,0) - dir_edges = CompScienceMeshes.skeleton_fast(dir,1) - bnd_edges = CompScienceMeshes.skeleton_fast(bnd,1) - bnd_nodes = CompScienceMeshes.skeleton_fast(bnd,0) - prt_nodes = CompScienceMeshes.skeleton_fast(port,0) - - srt_rim = sort.(rim) - srt_port = sort.(port) - srt_dir_edges = sort.(dir_edges) - srt_bnd_edges = sort.(bnd_edges) - srt_bnd_verts = sort.(bnd_nodes) - srt_prt_verts = sort.(prt_nodes) - - int_edges = submesh(supp_edges) do edge - sort(edge) in srt_port && return false - sort(edge) in srt_rim && return false - sort(edge) in srt_dir_edges && return true - sort(edge) in srt_bnd_edges && return false - return true - end - # @assert length(supp_nodes) - length(supp_edges) + length(skeleton(supp,2)) - length(supp) == 1 + bnd_supp = boundary(supp) + supp_edges = CompScienceMeshes.skeleton_fast(supp, 1) + supp_nodes = CompScienceMeshes.skeleton_fast(supp, 0) + + dir_compl_faces = submesh(!in(dirichlet), bnd_supp) + dir_compl_edges = CompScienceMeshes.skeleton_fast(dir_compl_faces, 1) + dir_compl_nodes = CompScienceMeshes.skeleton_fast(dir_compl_faces, 0) + + int_edges = submesh(!in(dir_compl_edges), supp_edges) + int_nodes = submesh(!in(dir_compl_nodes), supp_nodes) - Nd_prt = BEAST.nedelecc3d(supp, port) + Nd_prt = BEAST.nedelecc3d(supp, port_edges) Nd_int = BEAST.nedelecc3d(supp, int_edges) + Lg_int = BEAST.lagrangec0d1(supp, int_nodes) + curl_Nd_prt = curl(Nd_prt) curl_Nd_int = curl(Nd_int) + grad_Lg_int = BEAST.gradient(Lg_int) + A = assemble(Id, curl_Nd_int, curl_Nd_int) - N = nullspace(A) - a = -assemble(Id, curl(Nd_int), curl(Nd_prt)) * x0 - x1 = pinv(A) * a - # x1 = A \ a - - int_verts = submesh(supp_nodes) do vert - sort(vert) in srt_bnd_verts && return false - sort(vert) in srt_prt_verts && return false - return true - end + B = assemble(Id, grad_Lg_int, Nd_int) - L0_int = lagrangec0d1(supp, int_verts) - grad_L0_int = BEAST.gradient(L0_int) - @assert numfunctions(grad_L0_int) == numfunctions(L0_int) + a = -assemble(Id, curl_Nd_int, curl_Nd_prt) * x_prt + b = -assemble(Id, grad_Lg_int, Nd_prt) * x_prt - B = assemble(Id, grad_L0_int, Nd_int) - b = -assemble(Id, grad_L0_int, Nd_prt) * x0 - p = (B*N) \ (b-B*x1) - x1 = x1 + N*p + Z = zeros(eltype(b), length(b), length(b)) + u = [A B'; B Z] \ [a;b] + x_int = u[1:end-length(b)] - @assert isapprox(A*x1, a, atol=1e-8) - @assert isapprox(B*x1, b, atol=1e-8) + return x_int, int_edges, Nd_int +end - return Nd_int, Nd_prt, x1, x0 +function dual1forms(Tetrs, Faces, Dir) + tetrs, bnd, dir, v2t, v2n = dual1forms_init(Tetrs, Dir) + dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) end -function dual1forms(Tetrs, Faces) +function dual1forms_init(Tetrs, Dir) + tetrs = barycentric_refinement(Tetrs) + v2t, v2n = CompScienceMeshes.vertextocellmap(tetrs) + bnd = boundary(tetrs) + gpred = CompScienceMeshes.overlap_gpredicate(Dir) + dir = submesh(face -> gpred(chart(bnd,face)), bnd) + return tetrs, bnd, dir, v2t, v2n +end - T = coordtype(Tetrs) - P = vertextype(Tetrs) - S = Shape{T} +function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) - tetrs = CompScienceMeshes.barycentric_refinement(Tetrs) - bnd_tetrs = boundary(tetrs) - srt_bnd_tetrs = sort.(bnd_tetrs) + T = coordtype(tetrs) + bfs = Vector{Vector{Shape{T}}}(undef, length(Faces)) + pos = Vector{vertextype(Faces)}(undef, length(Faces)) - fns = Vector{Vector{S}}() - pos = Vector{P}() for (F,Face) in enumerate(Faces) - @show F - po = cartesian(center(chart(Faces, Face))) + println("Constructing dual 2-forms: $F out of $(length(Faces)).") + + idcs1 = v2t[Face[1],1:v2n[Face[1]]] + idcs2 = v2t[Face[2],1:v2n[Face[2]]] + idcs3 = v2t[Face[3],1:v2n[Face[3]]] + + supp1 = tetrs.mesh[idcs1] + supp2 = tetrs.mesh[idcs2] + supp3 = tetrs.mesh[idcs3] - idcs1 = [i for (i,tet) in enumerate(tetrs) if Face[1] in tet] - idcs2 = [i for (i,tet) in enumerate(tetrs) if Face[2] in tet] - idcs3 = [i for (i,tet) in enumerate(tetrs) if Face[3] in tet] - idcs = vcat(idcs1, idcs2, idcs3) - # @show idcs + bnd_supp1 = boundary(supp1) + bnd_supp2 = boundary(supp2) + bnd_supp3 = boundary(supp3) - supp1 = Mesh(vertices(tetrs), cells(tetrs)[idcs1]) - supp2 = Mesh(vertices(tetrs), cells(tetrs)[idcs1]) - supp3 = Mesh(vertices(tetrs), cells(tetrs)[idcs1]) + dir1_faces = submesh(in(dir), bnd_supp1) + dir2_faces = submesh(in(dir), bnd_supp2) + dir3_faces = submesh(in(dir), bnd_supp3) - supp1 = submesh(tet -> Face[1] in tet, tetrs.mesh) - supp2 = submesh(tet -> Face[2] in tet, tetrs.mesh) - supp3 = submesh(tet -> Face[3] in tet, tetrs.mesh) + bnd_dir1 = boundary(dir1_faces) + bnd_dir2 = boundary(dir2_faces) + bnd_dir3 = boundary(dir3_faces) - supp = CompScienceMeshes.union(supp1, supp2) - supp = CompScienceMeshes.union(supp, supp3) + supp23 = submesh(in(bnd_supp2), bnd_supp3) + supp31 = submesh(in(bnd_supp3), bnd_supp1) + supp12 = submesh(in(bnd_supp1), bnd_supp2) - dir = submesh(face -> sort(face) in srt_bnd_tetrs, boundary(supp)) + dir23_edges = submesh(in(bnd_dir2), bnd_dir3) + dir31_edges = submesh(in(bnd_dir3), bnd_dir1) + dir12_edges = submesh(in(bnd_dir1), bnd_dir2) - port = skeleton(supp1, 1) - port = submesh(edge -> sort(edge) in sort.(skeleton(supp2,1)), port) - port = submesh(edge -> sort(edge) in sort.(skeleton(supp3,1)), port) - @assert 1 ≤ length(port) ≤ 2 + port_edges = boundary(supp23) + port_edges = submesh(in(boundary(supp31)), port_edges) + port_edges = submesh(in(boundary(supp12)), port_edges) + @assert 1 ≤ length(port_edges) ≤ 2 - x0 = ones(length(port)) / length(port) - for (i,edge) in enumerate(port) - tgt = tangents(center(chart(port,edge)),1) - dot(normal(chart(Faces,Face)), tgt) < 0 && (x0[i] *= -1) + # Step 1: set port flux and extend to dual faces + x0 = ones(length(port_edges)) / length(port_edges) + for (i,edge) in enumerate(port_edges) + tgt = tangents(center(chart(port_edges, edge)),1) + if dot(normal(chart(Faces, Face)), tgt) < 0 + x0[i] *= -1 + end end - Nd_int, Nd_prt, x_int, x_prt = builddual1form(supp, port, dir, x0) - @show norm(x_int) - @show norm(x_prt) + x23, supp23_int_edges = extend_edge_to_face(supp23, dir23_edges, x0, port_edges) + x31, supp31_int_edges = extend_edge_to_face(supp31, dir31_edges, x0, port_edges) + x12, supp12_int_edges = extend_edge_to_face(supp12, dir12_edges, x0, port_edges) + + port1_edges = CompScienceMeshes.union(port_edges, supp31_int_edges) + port1_edges = CompScienceMeshes.union(port1_edges, supp12_int_edges) + port2_edges = CompScienceMeshes.union(port_edges, supp12_int_edges) + port2_edges = CompScienceMeshes.union(port2_edges, supp23_int_edges) + port3_edges = CompScienceMeshes.union(port_edges, supp23_int_edges) + port3_edges = CompScienceMeshes.union(port3_edges, supp31_int_edges) + + x1_prt = [x0; x31; x12] + x2_prt = [x0; x12; x23] + x3_prt = [x0; x23; x31] + + Nd1_prt = BEAST.nedelecc3d(supp1, port1_edges) + Nd2_prt = BEAST.nedelecc3d(supp2, port2_edges) + Nd3_prt = BEAST.nedelecc3d(supp3, port3_edges) + + x1_int, _, Nd1_int = extend_face_to_tetr(supp1, dir1_faces, x1_prt, port1_edges) + x2_int, _, Nd2_int = extend_face_to_tetr(supp2, dir2_faces, x2_prt, port2_edges) + x3_int, _, Nd3_int = extend_face_to_tetr(supp3, dir3_faces, x3_prt, port3_edges) + + # inject in the global space + fn = BEAST.Shape{Float64}[] + addf!(fn, x1_prt, Nd1_prt, idcs1) + addf!(fn, x1_int, Nd1_int, idcs1) + + addf!(fn, x2_prt, Nd2_prt, idcs2) + addf!(fn, x2_int, Nd2_int, idcs2) - fn = Vector{S}() - addf!(fn, x_prt, Nd_prt, idcs) - addf!(fn, x_int, Nd_int, idcs) + addf!(fn, x3_prt, Nd3_prt, idcs3) + addf!(fn, x3_int, Nd3_int, idcs3) - push!(fns, fn) - push!(pos, po) + pos[F] = cartesian(CompScienceMeshes.center(chart(Faces, Face))) + bfs[F] = fn + # space = BEAST.NDLCCBasis(tetrs, [fn], [pos]) end - NDLCCBasis(tetrs, fns, pos) + NDLCCBasis(tetrs, bfs, pos) end diff --git a/src/identityop.jl b/src/identityop.jl index e0318f7b..ae9382d0 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -57,6 +57,13 @@ function quaddata(op::LocalOperator, g::NDLCDRefSpace, f::NDLCCRefSpace, tels, b [(w, parametric(p)) for (p,w) in qps] end +function quaddata(op::LocalOperator, g::NDLCCRefSpace, f::NDLCDRefSpace, tels, bels) + o, x, y, z = CompScienceMeshes.euclidianbasis(3) + reftet = simplex(x,y,z,o) + qps = quadpoints(reftet, 6) + [(w, parametric(p)) for (p,w) in qps] +end + quaddata(op::LocalOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, From 7bd4fe2f22dbdfe3985d25bcd66cf3b54ffe9bf4 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 24 Feb 2020 13:56:44 +0100 Subject: [PATCH 021/528] dual 1 forms use geometric normalisations at port --- examples/ex_dual1forms_bis.jl | 1 + src/bases/dual3d.jl | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/examples/ex_dual1forms_bis.jl b/examples/ex_dual1forms_bis.jl index ec8d01c4..79431df2 100644 --- a/examples/ex_dual1forms_bis.jl +++ b/examples/ex_dual1forms_bis.jl @@ -230,6 +230,7 @@ D1 = BEAST.dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) P2 = BEAST.nedelecd3d(Tetrs, Faces) # S = BEAST.dual1forms(Tetrs, Faces[collect(396:405)], Dir) +Edges = CompScienceMeshes.skeleton_fast(Tetrs, 1) int_Edges = submesh(!in(skeleton(boundary(Tetrs),1)), Edges) P1 = BEAST.nedelecc3d(Tetrs, int_Edges) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 3fbb209f..4c841d6f 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -310,11 +310,15 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) # Step 1: set port flux and extend to dual faces x0 = ones(length(port_edges)) / length(port_edges) + total_lgt = sum(volume(chart(port_edges, edge)) for edge in port_edges) for (i,edge) in enumerate(port_edges) tgt = tangents(center(chart(port_edges, edge)),1) - if dot(normal(chart(Faces, Face)), tgt) < 0 - x0[i] *= -1 - end + lgt = volume(chart(port_edges, edge)) + sgn = sign(dot(normal(chart(Faces, Face)), tgt)) + x0[i] = sgn * lgt / total_lgt + # if dot(normal(chart(Faces, Face)), tgt) < 0 + # x0[i] *= -1 + # end end x23, supp23_int_edges = extend_edge_to_face(supp23, dir23_edges, x0, port_edges) From eb790874fd32a073ce22eb5354164260e07be4c8 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 25 Feb 2020 10:55:20 +0100 Subject: [PATCH 022/528] pointwise scaling of fluxes --- examples/ex_dual1forms_bis.jl | 2 ++ src/bases/dual3d.jl | 25 ++++++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/examples/ex_dual1forms_bis.jl b/examples/ex_dual1forms_bis.jl index 79431df2..2012a16f 100644 --- a/examples/ex_dual1forms_bis.jl +++ b/examples/ex_dual1forms_bis.jl @@ -246,5 +246,7 @@ CDP = assemble(Id, D1, curl_P1) CPD = CDP' GDD = assemble(Id, D1, D1) +iM = inv(M) A1 = CPD * inv(GDD) * CDP; A2 = assemble(Id, curl_P1, curl_P1) +A3 = CPD * iM' * inv(GDD) * iM * CDP; diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 4c841d6f..264ef3d5 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -149,11 +149,15 @@ function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) bnd_patch2 = boundary(patch2) port = submesh(in(bnd_patch2), bnd_patch1) - prt_fluxes = ones(T, numcells(port)) / numcells(port) + total_area = sum(volume(chart(port, fc)) for fc in port) + # prt_fluxes = ones(T, numcells(port)) / numcells(port) + prt_fluxes = zeros(length(port)) tgt = vertices(Edges)[Edge[1]] - vertices(Edges)[Edge[2]] for (i,face) in enumerate(cells(port)) chrt = chart(port, face) - prt_fluxes[i] *= sign(dot(normal(chrt), tgt)) + sgn = sign(dot(normal(chrt), tgt)) + # prt_fluxes[i] *= sgn + prt_fluxes[i] = sgn * volume(chrt) / total_area end # RT_int, RT_prt, x_int, x_prt = builddual2form(patch, port, dirichlet, prt_fluxes) @@ -186,8 +190,10 @@ function extend_edge_to_face(supp, dirichlet, x_prt, port_edges) supp_edges = CompScienceMeshes.skeleton_fast(supp, 1) supp_nodes = CompScienceMeshes.skeleton_fast(supp, 0) - dir_compl_edges = submesh(!in(dirichlet), bnd_supp) - dir_compl_nodes = CompScienceMeshes.skeleton_fast(dir_compl_edges, 0) + dir_compl = submesh(!in(dirichlet), bnd_supp) + # dir_compl_edges = submesh(!in(dirichlet), bnd_supp) + dir_compl_edges = CompScienceMeshes.skeleton_fast(dir_compl, 1) + dir_compl_nodes = CompScienceMeshes.skeleton_fast(dir_compl, 0) int_edges = submesh(!in(dir_compl_edges), supp_edges) int_nodes = submesh(!in(dir_compl_nodes), supp_nodes) @@ -223,9 +229,9 @@ function extend_face_to_tetr(supp, dirichlet, x_prt, port_edges) supp_edges = CompScienceMeshes.skeleton_fast(supp, 1) supp_nodes = CompScienceMeshes.skeleton_fast(supp, 0) - dir_compl_faces = submesh(!in(dirichlet), bnd_supp) - dir_compl_edges = CompScienceMeshes.skeleton_fast(dir_compl_faces, 1) - dir_compl_nodes = CompScienceMeshes.skeleton_fast(dir_compl_faces, 0) + dir_compl = submesh(!in(dirichlet), bnd_supp) + dir_compl_edges = CompScienceMeshes.skeleton_fast(dir_compl, 1) + dir_compl_nodes = CompScienceMeshes.skeleton_fast(dir_compl, 0) int_edges = submesh(!in(dir_compl_edges), supp_edges) int_nodes = submesh(!in(dir_compl_nodes), supp_nodes) @@ -309,16 +315,13 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) @assert 1 ≤ length(port_edges) ≤ 2 # Step 1: set port flux and extend to dual faces - x0 = ones(length(port_edges)) / length(port_edges) + x0 = zeros(length(port_edges)) total_lgt = sum(volume(chart(port_edges, edge)) for edge in port_edges) for (i,edge) in enumerate(port_edges) tgt = tangents(center(chart(port_edges, edge)),1) lgt = volume(chart(port_edges, edge)) sgn = sign(dot(normal(chart(Faces, Face)), tgt)) x0[i] = sgn * lgt / total_lgt - # if dot(normal(chart(Faces, Face)), tgt) < 0 - # x0[i] *= -1 - # end end x23, supp23_int_edges = extend_edge_to_face(supp23, dir23_edges, x0, port_edges) From 11269a8165d63bf2acb978641451dd6bd198c51d Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 26 Feb 2020 14:34:09 +0100 Subject: [PATCH 023/528] build curlcurl from Id connectivity --- examples/ex_duals.jl | 37 +++++++++++++++++++++++++++++++++++++ examples/utils/showfn.jl | 34 ++++++++++++++++++++++++++++++++++ src/bases/dual3d.jl | 2 +- 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 examples/ex_duals.jl create mode 100644 examples/utils/showfn.jl diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl new file mode 100644 index 00000000..6e5060cb --- /dev/null +++ b/examples/ex_duals.jl @@ -0,0 +1,37 @@ +using LinearAlgebra + +using CompScienceMeshes +using BEAST + +include("utils/showfn.jl") + +Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.35) +Faces = CompScienceMeshes.skeleton_fast(Tetrs, 2) +Edges = CompScienceMeshes.skeleton_fast(Tetrs, 1) + +bnd_Faces = boundary(Tetrs) +bnd_Edges = CompScienceMeshes.skeleton_fast(bnd_Faces, 1) + +int_Faces = submesh(!in(bnd_Faces), Faces) +int_Edges = submesh(!in(bnd_Edges), Edges) + +primal1 = BEAST.nedelecc3d(Tetrs, int_Edges) +primal2 = BEAST.nedelecd3d(Tetrs, int_Faces) + +Dir = bnd_Faces +Neu = submesh(!in(Dir), bnd_Faces) +dual1 = BEAST.dual1forms(Tetrs, int_Faces, Dir) +dual2 = BEAST.dual2forms(Tetrs, int_Edges, Neu) + +TF = connectivity(int_Faces, Tetrs) +FE = connectivity(int_Edges, int_Faces) + +Id = BEAST.Identity() +G = assemble(Id, dual1, primal2) +A = assemble(Id, primal2, primal2) +B = assemble(Id, curl(primal1), curl(primal1)) +@show norm(G) +@show norm(TF*G*FE) + +B2 = FE'*A*FE; +@show norm(B - B2) diff --git a/examples/utils/showfn.jl b/examples/utils/showfn.jl new file mode 100644 index 00000000..f776b615 --- /dev/null +++ b/examples/utils/showfn.jl @@ -0,0 +1,34 @@ +import Plotly +function showfn(space,i) + geo = geometry(space) + T = coordtype(geo) + X = Dict{Int,T}() + Y = Dict{Int,T}() + Z = Dict{Int,T}() + U = Dict{Int,T}() + V = Dict{Int,T}() + W = Dict{Int,T}() + for sh in space.fns[i] + chrt = chart(geo, cells(geo)[sh.cellid]) + nbd = CompScienceMeshes.center(chrt) + vals = refspace(space)(nbd) + x,y,z = cartesian(nbd) + # @show vals[sh.refid].value + u,v,w = vals[sh.refid].value + # @show x, y, z + # @show u, v, w + X[sh.cellid] = x + Y[sh.cellid] = y + Z[sh.cellid] = z + U[sh.cellid] = get(U,sh.cellid,zero(T)) + sh.coeff * u + V[sh.cellid] = get(V,sh.cellid,zero(T)) + sh.coeff * v + W[sh.cellid] = get(W,sh.cellid,zero(T)) + sh.coeff * w + end + X = collect(values(X)) + Y = collect(values(Y)) + Z = collect(values(Z)) + U = collect(values(U)) + V = collect(values(V)) + W = collect(values(W)) + Plotly.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) +end diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 264ef3d5..60697677 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -279,7 +279,7 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) for (F,Face) in enumerate(Faces) - println("Constructing dual 2-forms: $F out of $(length(Faces)).") + println("Constructing dual 1-forms: $F out of $(length(Faces)).") idcs1 = v2t[Face[1],1:v2n[Face[1]]] idcs2 = v2t[Face[2],1:v2n[Face[2]]] From d531934128ef2a4f11e7a02f24344cc1ff16e4e7 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 26 Feb 2020 14:41:43 +0100 Subject: [PATCH 024/528] build curlcurl from Id connectivity --- examples/ex_duals.jl | 45 ++++++++++++++++++++++++++++++++++++++++ examples/utils/showfn.jl | 34 ++++++++++++++++++++++++++++++ src/bases/dual3d.jl | 2 +- 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 examples/ex_duals.jl create mode 100644 examples/utils/showfn.jl diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl new file mode 100644 index 00000000..3c2ed168 --- /dev/null +++ b/examples/ex_duals.jl @@ -0,0 +1,45 @@ +using LinearAlgebra + +using CompScienceMeshes +using BEAST + +include("utils/showfn.jl") + +Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.35) +Faces = CompScienceMeshes.skeleton_fast(Tetrs, 2) +Edges = CompScienceMeshes.skeleton_fast(Tetrs, 1) + +bnd_Faces = boundary(Tetrs) +bnd_Edges = CompScienceMeshes.skeleton_fast(bnd_Faces, 1) + +int_Faces = submesh(!in(bnd_Faces), Faces) +int_Edges = submesh(!in(bnd_Edges), Edges) + +primal1 = BEAST.nedelecc3d(Tetrs, int_Edges) +primal2 = BEAST.nedelecd3d(Tetrs, int_Faces) + +Dir = bnd_Faces +Neu = submesh(!in(Dir), bnd_Faces) +dual1 = BEAST.dual1forms(Tetrs, int_Faces, Dir) +dual2 = BEAST.dual2forms(Tetrs, int_Edges, Neu) + +TF = connectivity(int_Faces, Tetrs) +FE = connectivity(int_Edges, int_Faces) + +Id = BEAST.Identity() +G = assemble(Id, dual1, primal2) +A = assemble(Id, primal2, primal2) +B = assemble(Id, curl(primal1), curl(primal1)) +@show norm(G) +@show norm(TF*G*FE) + +B2 = FE'*A*FE; +@show norm(B - B2) + +EV = eigen(B) +idx = findfirst(abs.(EV.values) .> 1e-6) +u = real(EV.vectors[:,idx]) + +hemi = submesh(Tetrs) do Tetr + cartesian(center(chart(Tetrs, Tetr)))[3] < 0 +end diff --git a/examples/utils/showfn.jl b/examples/utils/showfn.jl new file mode 100644 index 00000000..f776b615 --- /dev/null +++ b/examples/utils/showfn.jl @@ -0,0 +1,34 @@ +import Plotly +function showfn(space,i) + geo = geometry(space) + T = coordtype(geo) + X = Dict{Int,T}() + Y = Dict{Int,T}() + Z = Dict{Int,T}() + U = Dict{Int,T}() + V = Dict{Int,T}() + W = Dict{Int,T}() + for sh in space.fns[i] + chrt = chart(geo, cells(geo)[sh.cellid]) + nbd = CompScienceMeshes.center(chrt) + vals = refspace(space)(nbd) + x,y,z = cartesian(nbd) + # @show vals[sh.refid].value + u,v,w = vals[sh.refid].value + # @show x, y, z + # @show u, v, w + X[sh.cellid] = x + Y[sh.cellid] = y + Z[sh.cellid] = z + U[sh.cellid] = get(U,sh.cellid,zero(T)) + sh.coeff * u + V[sh.cellid] = get(V,sh.cellid,zero(T)) + sh.coeff * v + W[sh.cellid] = get(W,sh.cellid,zero(T)) + sh.coeff * w + end + X = collect(values(X)) + Y = collect(values(Y)) + Z = collect(values(Z)) + U = collect(values(U)) + V = collect(values(V)) + W = collect(values(W)) + Plotly.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) +end diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 264ef3d5..60697677 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -279,7 +279,7 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) for (F,Face) in enumerate(Faces) - println("Constructing dual 2-forms: $F out of $(length(Faces)).") + println("Constructing dual 1-forms: $F out of $(length(Faces)).") idcs1 = v2t[Face[1],1:v2n[Face[1]]] idcs2 = v2t[Face[2],1:v2n[Face[2]]] From cb2153520e622369636b5d7033b6ddd281afc9ec Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 11 Mar 2020 15:52:36 +0100 Subject: [PATCH 025/528] Remove Base.in from BEAST --- src/bases/dual3d.jl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 60697677..d5f72baf 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -10,12 +10,12 @@ function addf!(fn::Vector{<:Shape}, x::Vector, space::Space, idcs::Vector{Int}) end end -function Base.in(mesh::CompScienceMeshes.AbstractMesh) - cells_mesh = sort.(mesh) - function f(cell) - sort(cell) in cells_mesh - end -end +# function Base.in(mesh::CompScienceMeshes.AbstractMesh) +# cells_mesh = sort.(mesh) +# function f(cell) +# sort(cell) in cells_mesh +# end +# end @@ -310,8 +310,11 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) dir12_edges = submesh(in(bnd_dir1), bnd_dir2) port_edges = boundary(supp23) + @show length(port_edges) port_edges = submesh(in(boundary(supp31)), port_edges) + @show length(port_edges) port_edges = submesh(in(boundary(supp12)), port_edges) + @show length(port_edges) @assert 1 ≤ length(port_edges) ≤ 2 # Step 1: set port flux and extend to dual faces From 84be7c9352c44932c225572631e3a0b0e0c619e0 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 24 Mar 2020 12:40:24 +0100 Subject: [PATCH 026/528] Remove comments --- examples/ex_duals.jl | 12 ++++++++++-- src/bases/dual3d.jl | 11 ----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl index 3c2ed168..92a988fd 100644 --- a/examples/ex_duals.jl +++ b/examples/ex_duals.jl @@ -5,7 +5,7 @@ using BEAST include("utils/showfn.jl") -Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.35) +Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.40) Faces = CompScienceMeshes.skeleton_fast(Tetrs, 2) Edges = CompScienceMeshes.skeleton_fast(Tetrs, 1) @@ -15,6 +15,9 @@ bnd_Edges = CompScienceMeshes.skeleton_fast(bnd_Faces, 1) int_Faces = submesh(!in(bnd_Faces), Faces) int_Edges = submesh(!in(bnd_Edges), Edges) +@show length(int_Edges) +@show length(int_Faces) + primal1 = BEAST.nedelecc3d(Tetrs, int_Edges) primal2 = BEAST.nedelecd3d(Tetrs, int_Faces) @@ -38,8 +41,13 @@ B2 = FE'*A*FE; EV = eigen(B) idx = findfirst(abs.(EV.values) .> 1e-6) -u = real(EV.vectors[:,idx]) +u = real(EV.vectors[:,idx+1]) hemi = submesh(Tetrs) do Tetr cartesian(center(chart(Tetrs, Tetr)))[3] < 0 end + +primal1_hemi = restrict(primal1, hemi) +trace_primal1_hemi = BEAST.ttrace(primal1_hemi, boundary(hemi)) +fcr, geo = facecurrents(u, trace_primal1_hemi) +Plotly.plot(patch(geo, norm.(fcr))) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index d5f72baf..178b2262 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -150,21 +150,14 @@ function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) port = submesh(in(bnd_patch2), bnd_patch1) total_area = sum(volume(chart(port, fc)) for fc in port) - # prt_fluxes = ones(T, numcells(port)) / numcells(port) prt_fluxes = zeros(length(port)) tgt = vertices(Edges)[Edge[1]] - vertices(Edges)[Edge[2]] for (i,face) in enumerate(cells(port)) chrt = chart(port, face) sgn = sign(dot(normal(chrt), tgt)) - # prt_fluxes[i] *= sgn prt_fluxes[i] = sgn * volume(chrt) / total_area end - # RT_int, RT_prt, x_int, x_prt = builddual2form(patch, port, dirichlet, prt_fluxes) - # ptch_idcs = [ptch_idcs1; ptch_idcs2] - # addf!(bfs[F], x_int, RT_int, ptch_idcs) - # addf!(bfs[F], x_prt, RT_prt, ptch_idcs) - RT1_int, RT1_prt, x1_int, x_prt = builddual2form( patch1, port, dirichlet, +prt_fluxes) RT2_int, RT2_prt, x2_int, x_prt = builddual2form( @@ -191,7 +184,6 @@ function extend_edge_to_face(supp, dirichlet, x_prt, port_edges) supp_nodes = CompScienceMeshes.skeleton_fast(supp, 0) dir_compl = submesh(!in(dirichlet), bnd_supp) - # dir_compl_edges = submesh(!in(dirichlet), bnd_supp) dir_compl_edges = CompScienceMeshes.skeleton_fast(dir_compl, 1) dir_compl_nodes = CompScienceMeshes.skeleton_fast(dir_compl, 0) @@ -310,11 +302,8 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) dir12_edges = submesh(in(bnd_dir1), bnd_dir2) port_edges = boundary(supp23) - @show length(port_edges) port_edges = submesh(in(boundary(supp31)), port_edges) - @show length(port_edges) port_edges = submesh(in(boundary(supp12)), port_edges) - @show length(port_edges) @assert 1 ≤ length(port_edges) ≤ 2 # Step 1: set port flux and extend to dual faces From 6fd6fa98f7942544d4db2abe32c3be821c9ac35f Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sun, 29 Mar 2020 15:19:49 +0200 Subject: [PATCH 027/528] Build CN step matrix and check eigvals --- examples/ex_duals.jl | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl index 92a988fd..bcf792ba 100644 --- a/examples/ex_duals.jl +++ b/examples/ex_duals.jl @@ -44,10 +44,36 @@ idx = findfirst(abs.(EV.values) .> 1e-6) u = real(EV.vectors[:,idx+1]) hemi = submesh(Tetrs) do Tetr - cartesian(center(chart(Tetrs, Tetr)))[3] < 0 + cartesian(CompScienceMeshes.center(chart(Tetrs, Tetr)))[3] < 0 end primal1_hemi = restrict(primal1, hemi) trace_primal1_hemi = BEAST.ttrace(primal1_hemi, boundary(hemi)) fcr, geo = facecurrents(u, trace_primal1_hemi) Plotly.plot(patch(geo, norm.(fcr))) + + +tetrs, bnd, dir, v2t, v2n = BEAST.dual1forms_init(Tetrs, Dir) +duals11 = BEAST.dual1forms_body(int_Faces[collect(1:10)], tetrs, bnd, dir, v2t, v2n) + +Ggf = assemble(Id, dual1, primal2); +Cgg = assemble(Id, dual1, curl(primal1)) + + +Cfg = Ggf \ Cgg; +Conn = Matrix(connectivity(int_Edges, int_Faces)) +@show norm(Cfg - Conn) + +Zpp = zeros(numfunctions(primal1), numfunctions(primal1)) +Zdd = zeros(numfunctions(dual1), numfunctions(dual1)) + +Ipp = assemble(Id, primal1, primal1) +Idd = assemble(Id, dual1, dual1) + +Zpd = zeros(numfunctions(primal1), numfunctions(dual1)) + +C = [Zpp Cgg'; -Cgg Zdd]; +J = [Ipp Zpd; Zpd' Idd]; + +P = inv(2J - C) * (2J + C); +Q = (2J - C) * inv(2J + C); From d0f43ef3525f427a7e32e3c59a507218107c1cd1 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 30 Mar 2020 12:31:06 +0200 Subject: [PATCH 028/528] extension to dual faces and dual cells is done by a single function --- src/BEAST.jl | 1 + src/bases/dual3d.jl | 94 ++++++++++++++++++++----------------- src/bases/lagrange.jl | 12 +++++ src/bases/local/laglocal.jl | 20 ++++++++ src/bases/ndlccspace.jl | 7 +++ src/bases/ndspace.jl | 7 +++ test/test_basis.jl | 47 +++++++++++++++++-- test/test_ndspace.jl | 6 +++ 8 files changed, 147 insertions(+), 47 deletions(-) diff --git a/src/BEAST.jl b/src/BEAST.jl index b63c4fd6..c4d814c8 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -55,6 +55,7 @@ export MWDoubleLayer3D export PlaneWaveMW export TangTraceMW, CrossTraceMW export curl +export gradient export MWSingleLayerField3D export SingleLayerTrace export DoubleLayerRotatedMW3D diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 178b2262..9d380a7d 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -194,9 +194,9 @@ function extend_edge_to_face(supp, dirichlet, x_prt, port_edges) Nd_int = BEAST.nedelec(supp, int_edges) Lg_int = BEAST.lagrangec0d1(supp, int_nodes) - curl_Nd_prt = divergence(n × Nd_prt) - curl_Nd_int = divergence(n × Nd_int) - grad_Lg_int = n × curl(Lg_int) + curl_Nd_prt = curl(Nd_prt) + curl_Nd_int = curl(Nd_int) + grad_Lg_int = gradient(Lg_int) A = assemble(Id, curl_Nd_int, curl_Nd_int) B = assemble(Id, grad_Lg_int, Nd_int) @@ -213,41 +213,43 @@ end -function extend_face_to_tetr(supp, dirichlet, x_prt, port_edges) - - Id = BEAST.Identity() - - bnd_supp = boundary(supp) - supp_edges = CompScienceMeshes.skeleton_fast(supp, 1) - supp_nodes = CompScienceMeshes.skeleton_fast(supp, 0) - - dir_compl = submesh(!in(dirichlet), bnd_supp) - dir_compl_edges = CompScienceMeshes.skeleton_fast(dir_compl, 1) - dir_compl_nodes = CompScienceMeshes.skeleton_fast(dir_compl, 0) - - int_edges = submesh(!in(dir_compl_edges), supp_edges) - int_nodes = submesh(!in(dir_compl_nodes), supp_nodes) - - Nd_prt = BEAST.nedelecc3d(supp, port_edges) - Nd_int = BEAST.nedelecc3d(supp, int_edges) - Lg_int = BEAST.lagrangec0d1(supp, int_nodes) - - curl_Nd_prt = curl(Nd_prt) - curl_Nd_int = curl(Nd_int) - grad_Lg_int = BEAST.gradient(Lg_int) - - A = assemble(Id, curl_Nd_int, curl_Nd_int) - B = assemble(Id, grad_Lg_int, Nd_int) - - a = -assemble(Id, curl_Nd_int, curl_Nd_prt) * x_prt - b = -assemble(Id, grad_Lg_int, Nd_prt) * x_prt - - Z = zeros(eltype(b), length(b), length(b)) - u = [A B'; B Z] \ [a;b] - x_int = u[1:end-length(b)] - - return x_int, int_edges, Nd_int -end +# function extend_face_to_tetr(supp, dirichlet, x_prt, port_edges) +# +# Id = BEAST.Identity() +# +# bnd_supp = boundary(supp) +# supp_edges = CompScienceMeshes.skeleton_fast(supp, 1) +# supp_nodes = CompScienceMeshes.skeleton_fast(supp, 0) +# +# dir_compl = submesh(!in(dirichlet), bnd_supp) +# dir_compl_edges = CompScienceMeshes.skeleton_fast(dir_compl, 1) +# dir_compl_nodes = CompScienceMeshes.skeleton_fast(dir_compl, 0) +# +# int_edges = submesh(!in(dir_compl_edges), supp_edges) +# int_nodes = submesh(!in(dir_compl_nodes), supp_nodes) +# +# # Nd_prt = BEAST.nedelecc3d(supp, port_edges) +# # Nd_int = BEAST.nedelecc3d(supp, int_edges) +# Nd_prt = BEAST.nedelec(supp, port_edges) +# Nd_int = BEAST.nedelec(supp, int_edges) +# Lg_int = BEAST.lagrangec0d1(supp, int_nodes) +# +# curl_Nd_prt = curl(Nd_prt) +# curl_Nd_int = curl(Nd_int) +# grad_Lg_int = BEAST.gradient(Lg_int) +# +# A = assemble(Id, curl_Nd_int, curl_Nd_int) +# B = assemble(Id, grad_Lg_int, Nd_int) +# +# a = -assemble(Id, curl_Nd_int, curl_Nd_prt) * x_prt +# b = -assemble(Id, grad_Lg_int, Nd_prt) * x_prt +# +# Z = zeros(eltype(b), length(b), length(b)) +# u = [A B'; B Z] \ [a;b] +# x_int = u[1:end-length(b)] +# +# return x_int, int_edges, Nd_int +# end function dual1forms(Tetrs, Faces, Dir) tetrs, bnd, dir, v2t, v2n = dual1forms_init(Tetrs, Dir) @@ -316,9 +318,9 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) x0[i] = sgn * lgt / total_lgt end - x23, supp23_int_edges = extend_edge_to_face(supp23, dir23_edges, x0, port_edges) - x31, supp31_int_edges = extend_edge_to_face(supp31, dir31_edges, x0, port_edges) - x12, supp12_int_edges = extend_edge_to_face(supp12, dir12_edges, x0, port_edges) + x23, supp23_int_edges, _ = extend_edge_to_face(supp23, dir23_edges, x0, port_edges) + x31, supp31_int_edges, _ = extend_edge_to_face(supp31, dir31_edges, x0, port_edges) + x12, supp12_int_edges, _ = extend_edge_to_face(supp12, dir12_edges, x0, port_edges) port1_edges = CompScienceMeshes.union(port_edges, supp31_int_edges) port1_edges = CompScienceMeshes.union(port1_edges, supp12_int_edges) @@ -335,9 +337,13 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) Nd2_prt = BEAST.nedelecc3d(supp2, port2_edges) Nd3_prt = BEAST.nedelecc3d(supp3, port3_edges) - x1_int, _, Nd1_int = extend_face_to_tetr(supp1, dir1_faces, x1_prt, port1_edges) - x2_int, _, Nd2_int = extend_face_to_tetr(supp2, dir2_faces, x2_prt, port2_edges) - x3_int, _, Nd3_int = extend_face_to_tetr(supp3, dir3_faces, x3_prt, port3_edges) + # x1_int, _, Nd1_int = extend_face_to_tetr(supp1, dir1_faces, x1_prt, port1_edges) + # x2_int, _, Nd2_int = extend_face_to_tetr(supp2, dir2_faces, x2_prt, port2_edges) + # x3_int, _, Nd3_int = extend_face_to_tetr(supp3, dir3_faces, x3_prt, port3_edges) + + x1_int, _, Nd1_int = extend_edge_to_face(supp1, dir1_faces, x1_prt, port1_edges) + x2_int, _, Nd2_int = extend_edge_to_face(supp2, dir2_faces, x2_prt, port2_edges) + x3_int, _, Nd3_int = extend_edge_to_face(supp3, dir3_faces, x3_prt, port3_edges) # inject in the global space fn = BEAST.Shape{Float64}[] diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 2168956a..1c6a36c6 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -471,8 +471,20 @@ function duallagrangec0d1(mesh, mesh2, pred, ::Type{Val{2}}) end gradient(space::LagrangeBasis{1,0}, geo, fns) = NDLCCBasis(geo, fns) +gradient(space::LagrangeBasis{1,0}, geo::CompScienceMeshes.AbstractMesh{U,3} where {U}, fns) = NDBasis(geo, fns) + curl(space::LagrangeBasis{1,0}, geo, fns) = RTBasis(geo, fns) +# function gradient(space::LagrangeBasis{1,0}) +# crl = curl(space) +# for fn in crl.fns +# for (i,sh) in enumerate(fn) +# fn[i] = Shape(sh.cellid, sh.refid, -sh.coeff) +# end +# end +# return n × crl +# end + # # Sclar trace for Laggrange element based spaces # diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 4ff1cc88..e775a29a 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -89,6 +89,26 @@ function gradient(ref::LagrangeRefSpace{T,1,4}, sh, tet) where {T} return output end +function gradient(ref::LagrangeRefSpace{T,1,3} where {T}, sh, el) + sh1 = Shape(sh.cellid, mod1(sh.refid+1,3), -sh.coeff) + sh2 = Shape(sh.cellid, mod1(sh.refid+2,3), +sh.coeff) + return [sh1, sh2] +end + +# function gradient(phi::LagrangeRefSpace{T,1,3}, shape, chart) +# +# r = shape.refid +# vert = chart.vertices[r] +# face = faces(chart)[r] +# n = normal(face) +# h = dot(vert - cartesian(center(face)),n) +# gradphi = h*n +# +# output = Shape{T}[] +# for s in faces(chart) +# +# end + function strace(x::LagrangeRefSpace, cell, localid, face) diff --git a/src/bases/ndlccspace.jl b/src/bases/ndlccspace.jl index c9827458..32cfeb4e 100644 --- a/src/bases/ndlccspace.jl +++ b/src/bases/ndlccspace.jl @@ -51,6 +51,13 @@ function nedelecc3d(mesh) nedelecc3d(mesh, edges) end + +function nedelec(mesh::CompScienceMeshes.AbstractMesh{U,4} where {U}, + edges=skeleton(mesh,1)) + + nedelecc3d(mesh, edges) +end + curl(space::NDLCCBasis, geo, fns) = NDLCDBasis(geo, fns, space.pos) ttrace(X::NDLCCBasis, geo, fns) = RTBasis(geo, fns, deepcopy(X.pos)) diff --git a/src/bases/ndspace.jl b/src/bases/ndspace.jl index b0c83a88..5f2dc9b1 100644 --- a/src/bases/ndspace.jl +++ b/src/bases/ndspace.jl @@ -4,6 +4,8 @@ struct NDBasis{T,M,P} <: Space{T} pos::Vector{P} end +NDBasis(geo, fns) = NDBasis(geo, fns, Vector{vertextype(geo)}(undef,length(fns))) + refspace(s::NDBasis) = NDRefSpace{scalartype(s)}() @@ -42,3 +44,8 @@ function LinearAlgebra.cross(::NormalVector, s::NDBasis) @assert CompScienceMeshes.isoriented(s.geo) RTBasis(s.geo, s.fns, s.pos) end + + +function curl(space::NDBasis) + divergence(n × space) +end diff --git a/test/test_basis.jl b/test/test_basis.jl index e4ae3b82..9f33ce53 100644 --- a/test/test_basis.jl +++ b/test/test_basis.jl @@ -1,5 +1,6 @@ ## Preamble using Test +using LinearAlgebra using CompScienceMeshes using BEAST @@ -51,9 +52,9 @@ t = neighborhood(s, [1,1]/3) v = f(t, Val{:withcurl}) A = volume(s) -@test v[1][2] == (s[3]-s[2])/2A -@test v[2][2] == (s[1]-s[3])/2A -@test v[3][2] == (s[2]-s[1])/2A +@test v[1][2] ≈ (s[3]-s[2])/2A +@test v[2][2] ≈ (s[1]-s[3])/2A +@test v[3][2] ≈ (s[2]-s[1])/2A @test v[1][1] ≈ 1/3 @test v[2][1] ≈ 1/3 @@ -188,6 +189,46 @@ for _p in 1:numcells(m) end end +## Test gradient and curl of continuous lagrange elements +m = meshrectangle(1.0, 1.0, 0.5, 3) +int_nodes = submesh(!in(skeleton(boundary(m),0)), skeleton(m,0)) +@test length(int_nodes) == 1 + +lag = lagrangec0d1(m, int_nodes) +@test numfunctions(lag) == 1 + +rs = refspace(lag) +cl = cells(m)[2] +ch = chart(m, cl) +nbd = neighborhood(ch, carttobary(ch, [0.5, 0.5, 0])) +vals = getfield.(rs(nbd), :value) +@test vals ≈ [0, 1, 0] + +crl = BEAST.curl(lag) +# BEAST.gradient(lag) +nxgrad = BEAST.n × BEAST.gradient(lag) + +for i in eachindex(crl.fns) + for j in eachindex(crl.fns[i]) + @test crl.fns[i][j] == nxgrad.fns[i][j] + end +end + + +m = Mesh([ + point(1,0,0), + point(0,1,0), + point(0,0,1), + point(0,0,0)], + [index(1,2,3,4)]) + +lag = lagrangec0d1(m, skeleton(m,0)) +@test numfunctions(lag) == 4 + +gradlag = gradient(lag) +edg = skeleton(m,1) +nd3d1 = BEAST.nedelec(m,edg) +nd3d2 = BEAST.nedelecc3d(m,edg) ## end of file diff --git a/test/test_ndspace.jl b/test/test_ndspace.jl index 64cbf85e..b50feffd 100644 --- a/test/test_ndspace.jl +++ b/test/test_ndspace.jl @@ -1,6 +1,8 @@ using CompScienceMeshes using BEAST + using Test +using LinearAlgebra fn = joinpath(dirname(@__FILE__),"assets","rect1.in") @@ -32,3 +34,7 @@ fn = ND.fns[3] v1 = dot(fn[1].coeff*ndlocal(nbd1)[1][1],ut) v2 = dot(fn[2].coeff*ndlocal(nbd2)[2][1],ut) @test v1 ≈ v2 ≈ -1/√2 ≈ -1/volume(charts[3]) + +curlND = curl(ND) +@test curlND.fns[3][1].coeff ≈ +2 +@test curlND.fns[3][2].coeff ≈ -2 From 4fa62b7d4fc73ae340f03d05c2e41de17445538a Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 2 Apr 2020 12:04:37 +0200 Subject: [PATCH 029/528] Rewrite 2-form computation to be more uniform with 1-form comp. --- Project.toml | 1 + examples/ex_duals.jl | 9 ++- examples/utils/showfn.jl | 13 ++++ src/bases/dual3d.jl | 134 +++++++++++++++++++++++++++++++++------ src/bases/ndlcdspace.jl | 4 ++ 5 files changed, 140 insertions(+), 21 deletions(-) diff --git a/Project.toml b/Project.toml index 16a1dd25..9d282ed4 100644 --- a/Project.toml +++ b/Project.toml @@ -20,6 +20,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] +BlockArrays = "^0.11" CollisionDetection = "^0.1" Combinatorics = "^0.7" CompScienceMeshes = "^0.2.5" diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl index bcf792ba..084dc9f4 100644 --- a/examples/ex_duals.jl +++ b/examples/ex_duals.jl @@ -8,12 +8,15 @@ include("utils/showfn.jl") Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.40) Faces = CompScienceMeshes.skeleton_fast(Tetrs, 2) Edges = CompScienceMeshes.skeleton_fast(Tetrs, 1) +Nodes = CompScienceMeshes.skeleton_fast(Tetrs, 0) bnd_Faces = boundary(Tetrs) bnd_Edges = CompScienceMeshes.skeleton_fast(bnd_Faces, 1) +bnd_Nodes = CompScienceMeshes.skeleton_fast(bnd_Faces, 0) int_Faces = submesh(!in(bnd_Faces), Faces) int_Edges = submesh(!in(bnd_Edges), Edges) +int_Nodes = submesh(!in(bnd_Nodes), Nodes) @show length(int_Edges) @show length(int_Faces) @@ -24,17 +27,21 @@ primal2 = BEAST.nedelecd3d(Tetrs, int_Faces) Dir = bnd_Faces Neu = submesh(!in(Dir), bnd_Faces) dual1 = BEAST.dual1forms(Tetrs, int_Faces, Dir) -dual2 = BEAST.dual2forms(Tetrs, int_Edges, Neu) +dual2_old = BEAST.dual2forms(Tetrs, int_Edges, Neu) +dual2 = BEAST.dual2forms_new(Tetrs, int_Edges, Dir) TF = connectivity(int_Faces, Tetrs) FE = connectivity(int_Edges, int_Faces) +EN = connectivity(int_Nodes, int_Edges) Id = BEAST.Identity() G = assemble(Id, dual1, primal2) +H = assemble(Id, dual2, primal1) A = assemble(Id, primal2, primal2) B = assemble(Id, curl(primal1), curl(primal1)) @show norm(G) @show norm(TF*G*FE) +@show norm(FE*H*EN) B2 = FE'*A*FE; @show norm(B - B2) diff --git a/examples/utils/showfn.jl b/examples/utils/showfn.jl index f776b615..f1ea7480 100644 --- a/examples/utils/showfn.jl +++ b/examples/utils/showfn.jl @@ -32,3 +32,16 @@ function showfn(space,i) W = collect(values(W)) Plotly.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) end + +function compress!(space) + T = scalartype(space) + for (i,fn) in pairs(space.fns) + shapes = Dict{Tuple{Int,Int},T}() + for shape in fn + v = get(shapes, (shape.cellid, shape.refid), zero(T)) + shapes[(shape.cellid, shape.refid)] = v + shape.coeff + # set!(shapes, (shape.cellid, shape.refid), v + shape.coeff) + end + space.fns[i] = [BEAST.Shape(k[1], k[2], v) for (k,v) in shapes] + end +end diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 9d380a7d..70e92818 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -174,8 +174,63 @@ function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) NDLCDBasis(tetrs, bfs, pos) end +function dual2forms_body_new(Edges, tetrs, bnd, dir, v2t, v2n) + + T = coordtype(tetrs) + bfs = Vector{Vector{Shape{T}}}(undef, numcells(Edges)) + pos = Vector{vertextype(Edges)}(undef, numcells(Edges)) + + for (F,Edge) in enumerate(cells(Edges)) + + println("Constructing dual 2-forms: $F out of $(length(Edges)).") + + idcs1 = v2t[Edge[1],1:v2n[Edge[1]]] + idcs2 = v2t[Edge[2],1:v2n[Edge[2]]] + + supp1 = tetrs.mesh[idcs1] + supp2 = tetrs.mesh[idcs2] + + bnd_supp1 = boundary(supp1) + bnd_supp2 = boundary(supp2) + + dir1_faces = submesh(in(dir), bnd_supp1) + dir2_faces = submesh(in(dir), bnd_supp2) + + port_faces = bnd_supp1 + port_faces = submesh(in(bnd_supp2), port_faces) + + x0 = zeros(length(port_faces)) + total_vol = sum(volume(chart(port_faces, fc)) for fc in port_faces) + tgt = tangents(center(chart(Edges, Edge)),1) + for (i,face) in enumerate(port_faces) + chrt = chart(port_faces, face) + nrm = normal(chrt) + vol = volume(chrt) + sgn = sign(dot(nrm, tgt)) + x0[i] = sgn * vol / total_vol + end + + RT1_prt = BEAST.raviartthomas(supp1, port_faces) + RT2_prt = BEAST.raviartthomas(supp2, port_faces) + + x1_int, _, RT1_int = extend_2_form(supp1, dir1_faces, x0, port_faces) + x2_int, _, RT2_int = extend_2_form(supp2, dir2_faces, x0, port_faces) + + bfs[F] = Vector{Shape{T}}() + pos[F] = cartesian(center(chart(Edges,Edge))) + addf!(bfs[F], x1_int, RT1_int, idcs1) + addf!(bfs[F], x0, RT1_prt, idcs1) + + addf!(bfs[F], x2_int, RT2_int, idcs2) + addf!(bfs[F], x0, RT2_prt, idcs2) + + end + + NDLCDBasis(tetrs, bfs, pos) +end -function extend_edge_to_face(supp, dirichlet, x_prt, port_edges) + +function extend_1_form(supp, dirichlet, x_prt, port_edges) Id = BEAST.Identity() @@ -212,6 +267,43 @@ function extend_edge_to_face(supp, dirichlet, x_prt, port_edges) end +function extend_2_form(supp, dirichlet, x_prt, port_faces) + + Id = BEAST.Identity() + + bnd_supp = boundary(supp) + supp_faces = CompScienceMeshes.skeleton_fast(supp, 2) + supp_edges = CompScienceMeshes.skeleton_fast(supp, 1) + + dir_compl = submesh(!in(dirichlet), bnd_supp) + dir_compl_faces = CompScienceMeshes.skeleton_fast(dir_compl, 2) + dir_compl_edges = CompScienceMeshes.skeleton_fast(dir_compl, 1) + + int_faces = submesh(!in(dir_compl_faces), supp_faces) + int_edges = submesh(!in(dir_compl_edges), supp_edges) + + RT_prt = BEAST.raviartthomas(supp, port_faces) + RT_int = BEAST.raviartthomas(supp, int_faces) + Nd_int = BEAST.nedelec(supp, int_edges) + + div_RT_prt = divergence(RT_prt) + div_RT_int = divergence(RT_int) + curl_Nd_int = curl(Nd_int) + + A = assemble(Id, div_RT_int, div_RT_int) + B = assemble(Id, curl_Nd_int, RT_int) + + a = -assemble(Id, div_RT_int, div_RT_prt) * x_prt + b = -assemble(Id, curl_Nd_int, RT_prt) * x_prt + + Z = zeros(eltype(b), length(b), length(b)) + u = [A B'; B Z] \ [a;b] + x_int = u[1:end-length(b)] + + return x_int, int_faces, RT_int +end + + # function extend_face_to_tetr(supp, dirichlet, x_prt, port_edges) # @@ -256,6 +348,11 @@ function dual1forms(Tetrs, Faces, Dir) dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) end +function dual2forms_new(Tetrs, Faces, Dir) + tetrs, bnd, dir, v2t, v2n = dual1forms_init(Tetrs, Dir) + dual2forms_body_new(Faces, tetrs, bnd, dir, v2t, v2n) +end + function dual1forms_init(Tetrs, Dir) tetrs = barycentric_refinement(Tetrs) v2t, v2n = CompScienceMeshes.vertextocellmap(tetrs) @@ -310,17 +407,19 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) # Step 1: set port flux and extend to dual faces x0 = zeros(length(port_edges)) - total_lgt = sum(volume(chart(port_edges, edge)) for edge in port_edges) + total_vol = sum(volume(chart(port_edges, edge)) for edge in port_edges) + nrm = normal(chart(Faces, Face)) for (i,edge) in enumerate(port_edges) - tgt = tangents(center(chart(port_edges, edge)),1) - lgt = volume(chart(port_edges, edge)) - sgn = sign(dot(normal(chart(Faces, Face)), tgt)) - x0[i] = sgn * lgt / total_lgt + cht = chart(port_edges, edge) + tgt = tangents(center(cht),1) + vol = volume(cht) + sgn = sign(dot(nrm, tgt)) + x0[i] = sgn * vol / total_vol end - x23, supp23_int_edges, _ = extend_edge_to_face(supp23, dir23_edges, x0, port_edges) - x31, supp31_int_edges, _ = extend_edge_to_face(supp31, dir31_edges, x0, port_edges) - x12, supp12_int_edges, _ = extend_edge_to_face(supp12, dir12_edges, x0, port_edges) + x23, supp23_int_edges, _ = extend_1_form(supp23, dir23_edges, x0, port_edges) + x31, supp31_int_edges, _ = extend_1_form(supp31, dir31_edges, x0, port_edges) + x12, supp12_int_edges, _ = extend_1_form(supp12, dir12_edges, x0, port_edges) port1_edges = CompScienceMeshes.union(port_edges, supp31_int_edges) port1_edges = CompScienceMeshes.union(port1_edges, supp12_int_edges) @@ -333,17 +432,13 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) x2_prt = [x0; x12; x23] x3_prt = [x0; x23; x31] - Nd1_prt = BEAST.nedelecc3d(supp1, port1_edges) - Nd2_prt = BEAST.nedelecc3d(supp2, port2_edges) - Nd3_prt = BEAST.nedelecc3d(supp3, port3_edges) - - # x1_int, _, Nd1_int = extend_face_to_tetr(supp1, dir1_faces, x1_prt, port1_edges) - # x2_int, _, Nd2_int = extend_face_to_tetr(supp2, dir2_faces, x2_prt, port2_edges) - # x3_int, _, Nd3_int = extend_face_to_tetr(supp3, dir3_faces, x3_prt, port3_edges) + Nd1_prt = BEAST.nedelec(supp1, port1_edges) + Nd2_prt = BEAST.nedelec(supp2, port2_edges) + Nd3_prt = BEAST.nedelec(supp3, port3_edges) - x1_int, _, Nd1_int = extend_edge_to_face(supp1, dir1_faces, x1_prt, port1_edges) - x2_int, _, Nd2_int = extend_edge_to_face(supp2, dir2_faces, x2_prt, port2_edges) - x3_int, _, Nd3_int = extend_edge_to_face(supp3, dir3_faces, x3_prt, port3_edges) + x1_int, _, Nd1_int = extend_1_form(supp1, dir1_faces, x1_prt, port1_edges) + x2_int, _, Nd2_int = extend_1_form(supp2, dir2_faces, x2_prt, port2_edges) + x3_int, _, Nd3_int = extend_1_form(supp3, dir3_faces, x3_prt, port3_edges) # inject in the global space fn = BEAST.Shape{Float64}[] @@ -358,7 +453,6 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) pos[F] = cartesian(CompScienceMeshes.center(chart(Faces, Face))) bfs[F] = fn - # space = BEAST.NDLCCBasis(tetrs, [fn], [pos]) end NDLCCBasis(tetrs, bfs, pos) diff --git a/src/bases/ndlcdspace.jl b/src/bases/ndlcdspace.jl index 2299384d..419ef8b9 100644 --- a/src/bases/ndlcdspace.jl +++ b/src/bases/ndlcdspace.jl @@ -41,6 +41,10 @@ function nedelecd3d(mesh) nedelecd3d(mesh, faces) end +function raviartthomas(m::CompScienceMeshes.AbstractMesh{U,4} where {U}, faces=skeleton(m,2)) + nedelecd3d(m, faces) +end + ntrace(X::NDLCDBasis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(X.pos)) ttrace(X::NDLCDBasis, geo, fns) = NDBasis{}(geo, fns, deepcopy(X.pos)) From 7f7f8d0d168234a2c74d18f1852d9288c98cf372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cedric=20M=C3=BCnger?= Date: Mon, 21 Sep 2020 14:04:57 +0200 Subject: [PATCH 030/528] 2d/3d BDM elements --- examples/ex_interpolation.jl | 150 +++++++++++++++++ src/BEAST.jl | 5 + src/bases/bdm3dspace.jl | 114 +++++++++++++ src/bases/local/bdm3dlocal.jl | 35 ++++ src/identityop.jl | 11 ++ src/interpolation.jl | 303 ++++++++++++++++++++++++++++++++++ 6 files changed, 618 insertions(+) create mode 100644 examples/ex_interpolation.jl create mode 100644 src/bases/bdm3dspace.jl create mode 100644 src/bases/local/bdm3dlocal.jl create mode 100644 src/interpolation.jl diff --git a/examples/ex_interpolation.jl b/examples/ex_interpolation.jl new file mode 100644 index 00000000..815bbb97 --- /dev/null +++ b/examples/ex_interpolation.jl @@ -0,0 +1,150 @@ +using CompScienceMeshes +using BEAST +using LinearAlgebra + +trias = CompScienceMeshes.meshrectangle(1.0,1.0,0.05) +trias = CompScienceMeshes.meshcircle(1.0,0.5) +tetrs = CompScienceMeshes.tetmeshcuboid(1,1,1,0.2) + +f = BEAST.ScalarTrace(x -> sin(pi*x[1]*x[2])*sin(pi*x[2])) +f2 = BEAST.ScalarTrace(x -> (-x[2]*sin(2*pi*(x[1]^2+x[2]^2))*sin(atan(x[2],x[1])), x[1]*sin(2*pi*(x[1]^2+x[2]^2))*sin(atan(x[2],x[1])), 0) ) +g = BEAST.ScalarTrace(x -> (sin(pi*x[1])*cos(pi*x[2]), -cos(pi*x[1])*sin(pi*x[2]), 0 )) +g = BEAST.ScalarTrace(x -> (sin(pi*x[1])*cos(pi*x[2]), sin(pi*x[2])*cos(pi*x[2]), sin(pi*x[3])*cos(pi*x[2]))) +g2 = BEAST.ScalarTrace(x -> ( sin(pi*x[1])*cos(pi*x[3]), 0, -cos(pi*x[1])*sin(pi*x[3]))) + + +N = 4 + +h = Vector(undef, N) +errorL = Vector(undef, N) +errorBDM = Vector(undef, N) +errorRT = Vector(undef, N) + +for n in 1:N + + h[n] = 0.5^(n) + + trias2 = CompScienceMeshes.meshrectangle(1.0,1.0, h[n]) + + trias = CompScienceMeshes.meshcircle(1.0, h[n], 3) + + Z = BEAST.lagrangec0d1(trias, dirichlet=false) + Y = BEAST.brezzidouglasmarini(trias) + X = BEAST.raviartthomas(trias) + + u_exact = DofInterpolate(Z,f) + b_exact = DofInterpolate(Y,f2) + r_exact = DofInterpolate(X,f2) + + identity = BEAST.Identity() + + M = assemble(identity, Z, Z) + b = assemble(f,Z) + u_n = M \ b + + errorL[n] = sqrt((u_n-u_exact)'*M*(u_n-u_exact)) + + + M = assemble(identity, Y, Y) + b = assemble(f2,Y) + b_n = M \ b + + errorBDM[n] = sqrt((b_n-b_exact)'*M*(b_n-b_exact)) + + + M = assemble(identity, X, X) + b = assemble(f2,X) + r_n = M \ b + + errorRT[n] = sqrt((r_n-r_exact)'*M*(r_n-r_exact)) + +end + +N = 15 + +delta = Vector(undef, N) +error = Vector(undef, N) + + +for i in 1:N + + delta[i] = 0.5-0.025*i + + tetrs = tetmeshcuboid(1.0,1.0,1.0, delta[i]) + + bndry = boundary(tetrs) + faces = skeleton(tetrs, 2) + + bndry_faces = [sort(c) for c in cells(skeleton(bndry, 2))] + function is_interior(faces) + !(sort(faces) in bndry_faces) + end + + interior_faces = submesh(is_interior, faces) + + W = BEAST.brezzidouglasmarini3d(tetrs, interior_faces) + + u_exact = DofInterpolate(W,g2) + + identity = BEAST.Identity() + + M = assemble(identity, W, W) + + b = assemble(g2,W) + u_n = M \ b + + error[i] = real(sqrt((u_n-u_exact)'*M*(u_n-u_exact))) +end + + +N=15 + +delta2 = Vector(undef, N) +error2 = Vector(undef, N) + +for i in 1:N + + delta2[i] =0.5-0.025*i + + tetrs = tetmeshcuboid(1.0,1.0,1.0, delta2[i]) + + bndry = boundary(tetrs) + faces = skeleton(tetrs, 2) + + bndry_faces = [sort(c) for c in cells(skeleton(bndry, 2))] + function is_interior(faces) + !(sort(faces) in bndry_faces) + end + + interior_faces = submesh(is_interior, faces) + + V = BEAST.nedelecd3d(tetrs, interior_faces) + + + + q_exact = DofInterpolate(V,g) + + identity = BEAST.Identity() + + M2 = assemble(identity, V, V) + + + b2 = assemble(g,V) + + q_n = M2 \ b2 + + + error2[i] = real(sqrt((q_n-q_exact)'*M2*(q_n-q_exact))) +end + +using Plots + +p2 = plot(delta,error, label="BDM3D", title="L2-Error on [0,1]^3",xlabel="h", ylabel="Error", legend=:bottomright, xaxis=:log, yaxis=:log) +plot!(p2, delta2, error2, label="Nedelec Div", xlims=[0.075,0.55]) + +p1 = plot(h,real(errorL), label="Lagrange", title="L2-Error on [0,1]^2",xlabel="h", ylabel="Error", legend=:bottomright, xaxis=:log, yaxis=:log, xlims=[0.025,0.5]) +plot!(p1,h,real(errorBDM),label="BDM") +plot!(p1,h,real(errorRT),label="Raviart-Thomas") + +plot(p1,p2, layout=2) + diff --git a/src/BEAST.jl b/src/BEAST.jl index 444e0fd4..97f18211 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -25,6 +25,7 @@ export lagdimension export restrict export raviartthomas, raowiltonglisson, positions export brezzidouglasmarini +export brezzidouglasmarini3d export portcells, rt_ports, getindex_rtg, subset export StagedTimeStep export subdsurface,subdBasis,assemblydata,refspace @@ -85,6 +86,7 @@ export potential export get_scatter_parameters export quaddata +export DofInterpolate export kernelvals @@ -123,6 +125,7 @@ include("bases/local/ndlocal.jl") include("bases/local/bdmlocal.jl") include("bases/local/ndlcclocal.jl") include("bases/local/ndlcdlocal.jl") +include("bases/local/bdm3dlocal.jl") include("bases/lagrange.jl") include("bases/rtspace.jl") @@ -132,6 +135,7 @@ include("bases/ndspace.jl") include("bases/bdmdiv.jl") include("bases/ndlccspace.jl") include("bases/ndlcdspace.jl") +include("bases/bdm3dspace.jl") include("bases/subdbasis.jl") @@ -146,6 +150,7 @@ include("localop.jl") include("multiplicativeop.jl") include("identityop.jl") include("integralop.jl") +include("interpolation.jl") include("quaddata.jl") include("postproc.jl") include("postproc/segcurrents.jl") diff --git a/src/bases/bdm3dspace.jl b/src/bases/bdm3dspace.jl new file mode 100644 index 00000000..bdfc8a05 --- /dev/null +++ b/src/bases/bdm3dspace.jl @@ -0,0 +1,114 @@ +mutable struct BDM3DBasis{T,M,P} <: Space{T} + geo::M + fns::Vector{Vector{Shape{T}}} + pos::Vector{P} +end + +refspace(s::BDM3DBasis{T}) where {T} = BDM3DRefSpace{T}() + + +function brezzidouglasmarini3d(mesh, faces) + T = coordtype(mesh) + P = vertextype(mesh) + num_faces = numcells(faces) + + C = connectivity(faces, mesh, identity) + + #println(C) + rows = rowvals(C) + vals = nonzeros(C) + + #println(rows) + + fns = Vector{Vector{Shape{T}}}(undef, 3*num_faces) + pos = Vector{P}(undef, 3*num_faces) + + #println(cells(faces)) + + + for (i,face) in enumerate(cells(faces)) + + fns[3*(i-1)+1] = Vector{Shape{T}}() + fns[3*(i-1)+2] = Vector{Shape{T}}() + fns[3*(i-1)+3] = Vector{Shape{T}}() + + pos[3*(i-1)+1] = cartesian(center(chart(faces,face))) + pos[3*(i-1)+2] = cartesian(center(chart(faces,face))) + pos[3*(i-1)+3] = cartesian(center(chart(faces,face))) + + #println( cartesian(center(chart(faces,face)))) + #println(face) + + # add shape function for each cell to the basis + for k in nzrange(C,i) + + #Cell index + j = rows[k] + #face index (sign gives local orientation) + s = vals[k] + + cell = cells(mesh)[j] + + #local face index + lf = abs(s); + #local vertex index + lv1 = mod1(lf+1,4) + lv2 = mod1(lf+2,4) + lv3 = mod1(lf+3,4) + + #println("\t",lv1," ",lv2," ",lv3) + #println("\t=>",cell[lv1]," ",cell[lv2]," ",cell[lv3]) + #global vertex index + gv1 = cell[lv1] + gv2 = cell[lv2] + gv3 = cell[lv3] + + if (gv1 < gv2) && (gv2 < gv3) + push!(fns[3*(i-1)+1], Shape{T}(j, 3*(abs(s)-1)+1, sign(s))) + push!(fns[3*(i-1)+2], Shape{T}(j, 3*(abs(s)-1)+2, sign(s))) + push!(fns[3*(i-1)+3], Shape{T}(j, 3*(abs(s)-1)+3, sign(s))) + #println("Case 1") + elseif (gv1 < gv3) && (gv3 < gv2) + push!(fns[3*(i-1)+1], Shape{T}(j, 3*(abs(s)-1)+1, sign(s))) + push!(fns[3*(i-1)+2], Shape{T}(j, 3*(abs(s)-1)+3, sign(s))) + push!(fns[3*(i-1)+3], Shape{T}(j, 3*(abs(s)-1)+2, sign(s))) + #println("Case 2") + elseif (gv2 < gv1) && (gv1 < gv3) + push!(fns[3*(i-1)+1], Shape{T}(j, 3*(abs(s)-1)+2, sign(s))) + push!(fns[3*(i-1)+2], Shape{T}(j, 3*(abs(s)-1)+1, sign(s))) + push!(fns[3*(i-1)+3], Shape{T}(j, 3*(abs(s)-1)+3, sign(s))) + #println("Case 3") + elseif (gv2 < gv3) && (gv3 < gv1) + push!(fns[3*(i-1)+1], Shape{T}(j, 3*(abs(s)-1)+2, sign(s))) + push!(fns[3*(i-1)+2], Shape{T}(j, 3*(abs(s)-1)+3, sign(s))) + push!(fns[3*(i-1)+3], Shape{T}(j, 3*(abs(s)-1)+1, sign(s))) + #println("Case 4") + elseif (gv3 < gv2) && (gv2 < gv1) + push!(fns[3*(i-1)+1], Shape{T}(j, 3*(abs(s)-1)+3, sign(s))) + push!(fns[3*(i-1)+2], Shape{T}(j, 3*(abs(s)-1)+2, sign(s))) + push!(fns[3*(i-1)+3], Shape{T}(j, 3*(abs(s)-1)+1, sign(s))) + #println("Case 5") + elseif (gv3 < gv1) && (gv1 < gv2) + push!(fns[3*(i-1)+1], Shape{T}(j, 3*(abs(s)-1)+3, sign(s))) + push!(fns[3*(i-1)+2], Shape{T}(j, 3*(abs(s)-1)+1, sign(s))) + push!(fns[3*(i-1)+3], Shape{T}(j, 3*(abs(s)-1)+2, sign(s))) + #println("Case 6") + end + + + #println(s) + + #push!(fns[3*(i-1)+1], Shape{T}(j, 3*(abs(s)-1)+1, sign(s))) + #push!(fns[3*(i-1)+2], Shape{T}(j, 3*(abs(s)-1)+2, sign(s))) + #push!(fns[3*(i-1)+3], Shape{T}(j, 3*(abs(s)-1)+3, sign(s))) + end + + end + + BDM3DBasis(mesh, fns, pos) +end + +function brezzidouglasmarini3d(mesh) + faces = skeleton(mesh, 2) + brezzidouglasmarini3d(mesh, faces) +end diff --git a/src/bases/local/bdm3dlocal.jl b/src/bases/local/bdm3dlocal.jl new file mode 100644 index 00000000..9b1694ea --- /dev/null +++ b/src/bases/local/bdm3dlocal.jl @@ -0,0 +1,35 @@ +struct BDM3DRefSpace{T} <: RefSpace{T,12} end + +function (f::BDM3DRefSpace)(p) + + u,v,w = parametric(p) + + tu = tangents(p,1) + tv = tangents(p,2) + tw = tangents(p,3) + + j = jacobian(p) + d=1/j + + return SVector(( + + + (value= 2*(-v*tu+v*tv)/j , divergence= 2*d), + (value= 2*(-w*tu+w*tw)/j , divergence= 2*d), + (value= 2*((u+v+w-1)*tu)/j , divergence= 2*d), + + (value= 2*(-w*tv+w*tw)/j , divergence= 2*d), + (value= 2*((u+v+w-1)*tv)/j , divergence= 2*d), + (value= 2*(u*tu-u*tv)/j , divergence= 2*d), + + + (value= 2*((u+v+w-1)*tw)/j , divergence= 2*d), + (value= 2*(u*tu-u*tw)/j , divergence= 2*d), + (value= 2*(v*tv-v*tw)/j , divergence= 2*d), + + (value= 2*(u*tu)/j , divergence= 2*d), + (value= 2*(v*tv)/j , divergence= 2*d), + (value= 2*(w*tw)/j , divergence= 2*d) + + )) +end \ No newline at end of file diff --git a/src/identityop.jl b/src/identityop.jl index a3b13b47..03cc06d3 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -23,6 +23,11 @@ function quaddata(op::LocalOperator, g::NDRefSpace, f::NDRefSpace, tels, bels) return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] end +function quaddata(op::LocalOperator, g::BDMRefSpace, f::BDMRefSpace, tels, bels) + u, w = trgauss(6) + return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] +end + function quaddata(op::LocalOperator, g::subReferenceSpace, f::subReferenceSpace, tels, bels) u, w = trgauss(6) return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] @@ -57,6 +62,12 @@ function quaddata(op::LocalOperator, g::NDLCDRefSpace, f::NDLCCRefSpace, tels, b [(w, parametric(p)) for (p,w) in qps] end +function quaddata(op::LocalOperator, g::BDM3DRefSpace, f::BDM3DRefSpace, tels, bels) + o, x, y, z, = CompScienceMeshes.euclidianbasis(3) + reftet = simplex(x,y,z,o) + qps = quadpoints(reftet, 6) + [(w, parametric(p)) for (p,w) in qps] +end quaddata(op::LocalOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, diff --git a/src/interpolation.jl b/src/interpolation.jl new file mode 100644 index 00000000..16c05cc3 --- /dev/null +++ b/src/interpolation.jl @@ -0,0 +1,303 @@ + +function DofInterpolate(basis::LagrangeBasis, field) + + num_bfs = numfunctions(basis) + + res = Vector(undef, num_bfs) + + for b in 1 : num_bfs + bfs = basis.fns[b] + + shape = bfs[1] + + cellid = shape.cellid + refid = shape.refid + + cell = cells(basis.geo)[cellid] + + tria = chart(basis.geo, cell) + + vid = cell[refid] + + if refid == 1 + v = neighborhood(tria, [1, 0]) + elseif refid == 2 + v = neighborhood(tria, [0, 1]) + else + v = neighborhood(tria, [0, 0]) + end + + res[b] = field(v) + end + + return res +end + +function DofInterpolate(basis::RTBasis, field) + + num_bfs = numfunctions(basis) + + res = Vector(undef, num_bfs) + + for b in 1 : num_bfs + + bfs = basis.fns[b] + + shape = bfs[2] + + cellid = shape.cellid + refid = shape.refid + coeff = shape.coeff + + cell = cells(basis.geo)[cellid] + + e = refid + + v1 = cell[mod1(e+1,3)] + v2 = cell[mod1(e+2,3)] + + edge = simplex(basis.geo.vertices[[v1,v2]]...) + t = tangents(center(edge),1) + tria = chart(basis.geo, cell) + + n = normal(center(tria)) + + bn = normalize(cross(n,t)) + + v = center(edge) + + res[b] = volume(edge)*coeff*dot(field(v),bn) + + end + + return res + +end + +function DofInterpolate(basis::BDMBasis, field) + + num_bfs = numfunctions(basis) + + res = Vector(undef, num_bfs) + + for b in 1 : num_bfs + + bfs = basis.fns[b] + + shape = bfs[2] + + cellid = shape.cellid + refid = shape.refid + coeff = shape.coeff + + cell = cells(basis.geo)[cellid] + + e = (refid-1)÷2+1 + + v1 = cell[mod1(e+1,3)] + v2 = cell[mod1(e+2,3)] + + edge = simplex(basis.geo.vertices[[v1,v2]]...) + t = tangents(center(edge),1) + tria = chart(basis.geo, cell) + + n = normal(center(tria)) + + bn = normalize(cross(n,t)) + + if refid == 1 + v = neighborhood(tria, [0, 1]) + elseif refid == 2 + v = neighborhood(tria, [0, 0]) + elseif refid == 3 + v = neighborhood(tria, [0, 0]) + elseif refid == 4 + v = neighborhood(tria, [1, 0]) + elseif refid == 5 + v = neighborhood(tria, [1, 0]) + else + v = neighborhood(tria, [0, 1]) + end + + res[b] = volume(edge)*coeff*dot(field(v),bn) + + end + + return res + +end + +function DofInterpolate(basis::NDLCDBasis, field) + + num_bfs = numfunctions(basis) + + res = Vector(undef, num_bfs) + + for b in 1 : num_bfs + + bfs = basis.fns[b] + + shape = bfs[1] + + cellid = shape.cellid + refid = shape.refid + coeff = shape.coeff + + cell = cells(basis.geo)[cellid] + + f = refid + + v1 = cell[mod1(f+1,4)] + v2 = cell[mod1(f+2,4)] + v3 = cell[mod1(f+3,4)] + + v4 = cell[mod1(f,4)] + + face = simplex(basis.geo.vertices[[v1,v2,v3]]...) + + n = normal(center(face)) + + inside = dot(n, basis.geo.vertices[v4]-basis.geo.vertices[v1]) + + n *= -sign(inside) + + v = center(face) + + res[b] = volume(face)*coeff*dot(field(v),n) + + end + + return res + +end + +function DofInterpolate(basis::BEAST.BDM3DBasis, field) + + num_bfs = numfunctions(basis) + + res = Vector(undef, num_bfs) + + for b in 1 : num_bfs + + bfs = basis.fns[b] + + shape = bfs[1] + + cellid = shape.cellid + refid = shape.refid + coeff = shape.coeff + + cell = cells(basis.geo)[cellid] + + f = (refid-1)÷3+1 + + v1 = cell[mod1(f+1,4)] + v2 = cell[mod1(f+2,4)] + v3 = cell[mod1(f+3,4)] + + v4 = cell[mod1(f,4)] + + face = simplex(basis.geo.vertices[[v1,v2,v3]]...) + + n = normal(center(face)) + + inside = dot(n, basis.geo.vertices[v4]-basis.geo.vertices[v1]) + + n *= -sign(inside) + + tet = simplex(basis.geo.vertices[cell]...) + + if refid == 1 + v = neighborhood(tet, [0, 1, 0]) + elseif refid == 2 + v = neighborhood(tet, [0, 0, 1]) + elseif refid == 3 + v = neighborhood(tet, [0, 0, 0]) + elseif refid == 4 + v = neighborhood(tet, [0, 0, 1]) + elseif refid == 5 + v = neighborhood(tet, [0, 0, 0]) + elseif refid == 6 + v = neighborhood(tet, [1, 0, 0]) + elseif refid == 7 + v = neighborhood(tet, [0, 0, 0]) + elseif refid == 8 + v = neighborhood(tet, [1, 0, 0]) + elseif refid == 9 + v = neighborhood(tet, [0, 1, 0]) + elseif refid == 10 + v = neighborhood(tet, [1, 0, 0]) + elseif refid == 11 + v = neighborhood(tet, [0, 1, 0]) + else + v = neighborhood(tet, [0, 0, 1]) + end + + res[b] = volume(face)*coeff*dot(field(v),n) + + end + + return res + +end + +function Centervalues(mesh::Mesh,f::Functional) + num_cells = numcells(mesh) + + res = Vector{SVector{3,Complex{Float64}}}(undef, num_cells) + + for i in 1:num_cells + cell = cells(mesh)[i] + + tet = chart(mesh, cell) + + v = center(tet) + + res[i] = f(v) + + end + + return res + +end + +function EvalCenter(basis::Space, coeff) + + mesh = basis.geo + num_cells = numcells(mesh) + num_bfs = numfunctions(basis) + ref_space = refspace(basis) + + res = Vector{SVector{3,Complex{Float64}}}(undef, num_cells) + + for i in 1:num_cells + res[i] = [0,0,0] + end + + for b in 1 : num_bfs + + bfs = basis.fns[b] + + for shape in bfs + + + cellid = shape.cellid + refid = shape.refid + a = shape.coeff + + cell = cells(mesh)[cellid] + + tet = chart(mesh, cell) + + v = center(tet) + + local_bfs = ref_space(v) + + res[cellid] += a*coeff[b]*local_bfs[refid].value + end + + end + + return res + +end \ No newline at end of file From f422d628a54b097bb3eecb9c9ec8df49597a4732 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 22 Oct 2020 10:55:53 +0200 Subject: [PATCH 031/528] Modified boundary conditions in ex_duals --- examples/ex_duals.jl | 88 +++++++++++++++++++++++++---------------- src/multiplicativeop.jl | 2 +- 2 files changed, 56 insertions(+), 34 deletions(-) diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl index 084dc9f4..1f13b79c 100644 --- a/examples/ex_duals.jl +++ b/examples/ex_duals.jl @@ -5,11 +5,15 @@ using BEAST include("utils/showfn.jl") -Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.40) +Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.20) Faces = CompScienceMeshes.skeleton_fast(Tetrs, 2) Edges = CompScienceMeshes.skeleton_fast(Tetrs, 1) Nodes = CompScienceMeshes.skeleton_fast(Tetrs, 0) +hemi = submesh(Tetrs) do Tetr + cartesian(CompScienceMeshes.center(chart(Tetrs, Tetr)))[3] < 0 +end + bnd_Faces = boundary(Tetrs) bnd_Edges = CompScienceMeshes.skeleton_fast(bnd_Faces, 1) bnd_Nodes = CompScienceMeshes.skeleton_fast(bnd_Faces, 0) @@ -22,65 +26,83 @@ int_Nodes = submesh(!in(bnd_Nodes), Nodes) @show length(int_Faces) primal1 = BEAST.nedelecc3d(Tetrs, int_Edges) -primal2 = BEAST.nedelecd3d(Tetrs, int_Faces) +primal2 = BEAST.nedelecd3d(Tetrs, Faces) Dir = bnd_Faces Neu = submesh(!in(Dir), bnd_Faces) -dual1 = BEAST.dual1forms(Tetrs, int_Faces, Dir) -dual2_old = BEAST.dual2forms(Tetrs, int_Edges, Neu) + +dual1 = BEAST.dual1forms(Tetrs, Faces, Dir) dual2 = BEAST.dual2forms_new(Tetrs, int_Edges, Dir) -TF = connectivity(int_Faces, Tetrs) -FE = connectivity(int_Edges, int_Faces) -EN = connectivity(int_Nodes, int_Edges) +@assert numfunctions(primal1) == numfunctions(dual2) +@assert numfunctions(primal2) == numfunctions(dual1) Id = BEAST.Identity() G = assemble(Id, dual1, primal2) H = assemble(Id, dual2, primal1) -A = assemble(Id, primal2, primal2) -B = assemble(Id, curl(primal1), curl(primal1)) + +TF = connectivity(Faces, Tetrs) +FE = connectivity(int_Edges, Faces) +EN = connectivity(int_Nodes, int_Edges) + @show norm(G) +@show norm(H) @show norm(TF*G*FE) @show norm(FE*H*EN) -B2 = FE'*A*FE; -@show norm(B - B2) +# A = assemble(Id, primal2, primal2) +# B = assemble(Id, curl(primal1), curl(primal1)) +# B2 = FE'*A*FE; +# @show norm(B - B2) -EV = eigen(B) -idx = findfirst(abs.(EV.values) .> 1e-6) -u = real(EV.vectors[:,idx+1]) +# EV = eigen(B) +# idx = findfirst(abs.(EV.values) .> 1e-6) +# u = real(EV.vectors[:,idx+1]) -hemi = submesh(Tetrs) do Tetr - cartesian(CompScienceMeshes.center(chart(Tetrs, Tetr)))[3] < 0 -end - -primal1_hemi = restrict(primal1, hemi) -trace_primal1_hemi = BEAST.ttrace(primal1_hemi, boundary(hemi)) -fcr, geo = facecurrents(u, trace_primal1_hemi) -Plotly.plot(patch(geo, norm.(fcr))) +# primal1_hemi = restrict(primal1, hemi) +# trace_primal1_hemi = BEAST.ttrace(primal1_hemi, boundary(hemi)) +# fcr, geo = facecurrents(u, trace_primal1_hemi) +# Plotly.plot(patch(geo, norm.(fcr))) -tetrs, bnd, dir, v2t, v2n = BEAST.dual1forms_init(Tetrs, Dir) -duals11 = BEAST.dual1forms_body(int_Faces[collect(1:10)], tetrs, bnd, dir, v2t, v2n) +# tetrs, bnd, dir, v2t, v2n = BEAST.dual1forms_init(Tetrs, Dir) +# duals11 = BEAST.dual1forms_body(int_Faces[collect(1:10)], tetrs, bnd, dir, v2t, v2n) -Ggf = assemble(Id, dual1, primal2); +# Ggf = assemble(Id, dual1, primal2); Cgg = assemble(Id, dual1, curl(primal1)) - -Cfg = Ggf \ Cgg; -Conn = Matrix(connectivity(int_Edges, int_Faces)) -@show norm(Cfg - Conn) +# Cfg = Ggf \ Cgg; +# Conn = Matrix(connectivity(int_Edges, Faces)) +# @show norm(Cfg - Conn) Zpp = zeros(numfunctions(primal1), numfunctions(primal1)) Zdd = zeros(numfunctions(dual1), numfunctions(dual1)) -Ipp = assemble(Id, primal1, primal1) -Idd = assemble(Id, dual1, dual1) +ϵ = BEAST.Multiplicative(p -> 1.0) +μ = BEAST.Multiplicative(p -> 1.0) + +Ipp = assemble(ϵ, primal1, primal1) +Idd = assemble(μ, dual1, dual1) Zpd = zeros(numfunctions(primal1), numfunctions(dual1)) C = [Zpp Cgg'; -Cgg Zdd]; J = [Ipp Zpd; Zpd' Idd]; +γ = 1.0 * im + +# P = inv(2J - C) * (2J + C); +# Q = (2J - C) * inv(2J + C); -P = inv(2J - C) * (2J + C); -Q = (2J - C) * inv(2J + C); +jg = assemble(BEAST.ScalarTrace(p -> point(1,0,0)), primal1) +mG = zeros(numfunctions(dual1)) + +b = [jg; mG] +u = (C - γ*J) \ b + +eg = u[1:numfunctions(primal1)] +hG = u[numfunctions(primal1)+1:end] + +primal1_hemi = restrict(primal1, hemi) +trace_primal1_hemi = BEAST.ttrace(primal1_hemi, boundary(hemi)) +fcr, geo = facecurrents(eg, trace_primal1_hemi) +Plotly.plot(patch(geo, norm.(fcr))) \ No newline at end of file diff --git a/src/multiplicativeop.jl b/src/multiplicativeop.jl index 06920208..2834590d 100644 --- a/src/multiplicativeop.jl +++ b/src/multiplicativeop.jl @@ -2,6 +2,6 @@ struct Multiplicative{f} <: LocalOperator field::f end -kernelvals(biop::Multiplicative, x) = Nothing +kernelvals(biop::Multiplicative, x) = nothing integrand(op::Multiplicative, kernel, x, g, f) = dot(f[1], op.field(cartesian(x))*g[1]) scalartype(op::Multiplicative) = Float64 From a5959abbd0bec230ed0b0aaec2230a97379e6121 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 26 Oct 2020 14:52:38 +0100 Subject: [PATCH 032/528] Reverted from SharedArray to Matrix for localop storage --- examples/ex_duals.jl | 12 +- src/bases/dual3d.jl | 319 +++++++++++++++++++------------------------ src/localop.jl | 15 +- src/operator.jl | 9 +- 4 files changed, 159 insertions(+), 196 deletions(-) diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl index 1f13b79c..62d0d622 100644 --- a/examples/ex_duals.jl +++ b/examples/ex_duals.jl @@ -5,7 +5,7 @@ using BEAST include("utils/showfn.jl") -Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.20) +Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.40) Faces = CompScienceMeshes.skeleton_fast(Tetrs, 2) Edges = CompScienceMeshes.skeleton_fast(Tetrs, 1) Nodes = CompScienceMeshes.skeleton_fast(Tetrs, 0) @@ -31,8 +31,9 @@ primal2 = BEAST.nedelecd3d(Tetrs, Faces) Dir = bnd_Faces Neu = submesh(!in(Dir), bnd_Faces) -dual1 = BEAST.dual1forms(Tetrs, Faces, Dir) -dual2 = BEAST.dual2forms_new(Tetrs, int_Edges, Dir) +@time dual1 = BEAST.dual1forms(Tetrs, Faces, Dir) +error("stop here") +dual2 = BEAST.dual2forms(Tetrs, int_Edges, Dir) @assert numfunctions(primal1) == numfunctions(dual2) @assert numfunctions(primal2) == numfunctions(dual1) @@ -105,4 +106,7 @@ hG = u[numfunctions(primal1)+1:end] primal1_hemi = restrict(primal1, hemi) trace_primal1_hemi = BEAST.ttrace(primal1_hemi, boundary(hemi)) fcr, geo = facecurrents(eg, trace_primal1_hemi) -Plotly.plot(patch(geo, norm.(fcr))) \ No newline at end of file +Plotly.plot(patch(geo, norm.(fcr))) + +# tetrs, bnd, dir, v2t, v2n = BEAST.dualforms_init(Tetrs, Dir) +# @profview BEAST.dual1forms_body(Faces[collect(1:10)], tetrs, bnd, dir, v2t, v2n) \ No newline at end of file diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 70e92818..d25c8810 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -10,171 +10,156 @@ function addf!(fn::Vector{<:Shape}, x::Vector, space::Space, idcs::Vector{Int}) end end -# function Base.in(mesh::CompScienceMeshes.AbstractMesh) -# cells_mesh = sort.(mesh) -# function f(cell) -# sort(cell) in cells_mesh -# end -# end - - -function builddual2form(support, port, dirichlet, prt_fluxes) +# function builddual2form(support, port, dirichlet, prt_fluxes) - faces = CompScienceMeshes.skeleton_fast(support,2) - edges = CompScienceMeshes.skeleton_fast(support,1) - verts = CompScienceMeshes.skeleton_fast(support,0) - @assert length(verts) - length(edges) + length(faces) - length(support) == 1 - bndry = boundary(support) +# faces = CompScienceMeshes.skeleton_fast(support,2) +# edges = CompScienceMeshes.skeleton_fast(support,1) +# verts = CompScienceMeshes.skeleton_fast(support,0) +# @assert length(verts) - length(edges) + length(faces) - length(support) == 1 +# bndry = boundary(support) - cells_bndry = sort.(bndry) - dirbnd = submesh(dirichlet) do face - sort(face) in cells_bndry ? true : false - end - - bnd_dirbnd = boundary(dirbnd) - edges_dirbnd = CompScienceMeshes.skeleton_fast(dirbnd,1) - cells_bnd_dirbnd = sort.(bnd_dirbnd) - int_edges_dirbnd = submesh(edges_dirbnd) do edge - sort(edge) in cells(bnd_dirbnd) ? false : true - end +# cells_bndry = sort.(bndry) +# dirbnd = submesh(dirichlet) do face +# sort(face) in cells_bndry ? true : false +# end - num_faces_on_port = 0 - num_faces_on_dirc = 0 +# bnd_dirbnd = boundary(dirbnd) +# edges_dirbnd = CompScienceMeshes.skeleton_fast(dirbnd,1) +# cells_bnd_dirbnd = sort.(bnd_dirbnd) +# int_edges_dirbnd = submesh(edges_dirbnd) do edge +# sort(edge) in cells(bnd_dirbnd) ? false : true +# end - cells_port = sort.(port) - cells_dirbnd = sort.(dirbnd) - int_faces = submesh(faces) do face - sort(face) in cells_port && return false - sort(face) in cells_dirbnd && return true - sort(face) in cells_bndry && return false - return true - end +# num_faces_on_port = 0 +# num_faces_on_dirc = 0 - bnd_edges = CompScienceMeshes.skeleton_fast(bndry,1) - prt_edges = CompScienceMeshes.skeleton_fast(port,1) +# cells_port = sort.(port) +# cells_dirbnd = sort.(dirbnd) +# int_faces = submesh(faces) do face +# sort(face) in cells_port && return false +# sort(face) in cells_dirbnd && return true +# sort(face) in cells_bndry && return false +# return true +# end - cells_int_edges_dirbnd = sort.(int_edges_dirbnd) - cells_bnd_edges = sort.(bnd_edges) - cells_prt_edges = sort.(prt_edges) +# bnd_edges = CompScienceMeshes.skeleton_fast(bndry,1) +# prt_edges = CompScienceMeshes.skeleton_fast(port,1) - int_edges = submesh(edges) do edge - sort(edge) in cells_int_edges_dirbnd && return true - sort(edge) in cells_bnd_edges && return false - sort(edge) in cells_prt_edges && return false - return true - end +# cells_int_edges_dirbnd = sort.(int_edges_dirbnd) +# cells_bnd_edges = sort.(bnd_edges) +# cells_prt_edges = sort.(prt_edges) - RT_int = nedelecd3d(support, int_faces) - RT_prt = nedelecd3d(support, port) - L0_int = nedelecc3d(support, int_edges) +# int_edges = submesh(edges) do edge +# sort(edge) in cells_int_edges_dirbnd && return true +# sort(edge) in cells_bnd_edges && return false +# sort(edge) in cells_prt_edges && return false +# return true +# end - Id = BEAST.Identity() - div_RT_int = divergence(RT_int) - div_RT_prt = divergence(RT_prt) - D = assemble(Id, div_RT_int, div_RT_int) - Q = assemble(Id, div_RT_int, div_RT_prt) - d = -Q * prt_fluxes - - curl_L0_int = curl(L0_int) - div_curl_L0_int = divergence(curl_L0_int) - ZZ = real(assemble(Id, div_curl_L0_int, div_curl_L0_int)) - @assert isapprox(norm(ZZ), 0.0, atol=1e-8) - C = assemble(Id, curl_L0_int, RT_int) - c = real(assemble(Id, curl_L0_int, RT_prt)) * prt_fluxes - - T = eltype(D) - nz = length(c) - QQ = [D C'; C zeros(T,nz,nz)] - qq = [d;c] - x = (QQ \ qq)[1:end-nz] - - if !isapprox(C*x, c, atol=1e-8) || !isapprox(D*x, d, atol=1e-6) - @show norm(D*x-d) - @show norm(C*x-c) - @show rank(C) - @show size(C,1) - error("error") - end +# RT_int = nedelecd3d(support, int_faces) +# RT_prt = nedelecd3d(support, port) +# L0_int = nedelecc3d(support, int_edges) - return RT_int, RT_prt, x, prt_fluxes +# Id = BEAST.Identity() +# div_RT_int = divergence(RT_int) +# div_RT_prt = divergence(RT_prt) +# D = assemble(Id, div_RT_int, div_RT_int) +# Q = assemble(Id, div_RT_int, div_RT_prt) +# d = -Q * prt_fluxes + +# curl_L0_int = curl(L0_int) +# div_curl_L0_int = divergence(curl_L0_int) +# ZZ = real(assemble(Id, div_curl_L0_int, div_curl_L0_int)) +# @assert isapprox(norm(ZZ), 0.0, atol=1e-8) +# C = assemble(Id, curl_L0_int, RT_int) +# c = real(assemble(Id, curl_L0_int, RT_prt)) * prt_fluxes + +# T = eltype(D) +# nz = length(c) +# QQ = [D C'; C zeros(T,nz,nz)] +# qq = [d;c] +# x = (QQ \ qq)[1:end-nz] + +# if !isapprox(C*x, c, atol=1e-8) || !isapprox(D*x, d, atol=1e-6) +# @show norm(D*x-d) +# @show norm(C*x-c) +# @show rank(C) +# @show size(C,1) +# error("error") +# end -end +# return RT_int, RT_prt, x, prt_fluxes +# end -function dual2forms_init(Tetrs) - tetrs = barycentric_refinement(Tetrs) - v2t, v2n = CompScienceMeshes.vertextocellmap(tetrs) - bnd = boundary(tetrs) - return tetrs, bnd, v2t, v2n -end -function dual2forms(Tetrs, Edges, Dir) - tetrs, bnd, v2t, v2n = dual2forms_init(Tetrs) - dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) -end +# function dual2forms(Tetrs, Edges, Dir) +# tetrs, bnd, v2t, v2n = dual2forms_init(Tetrs) +# dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) +# end -function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) +# function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) - T = coordtype(Tetrs) - bfs = Vector{Vector{Shape{T}}}(undef, numcells(Edges)) - pos = Vector{vertextype(Edges)}(undef, numcells(Edges)) +# T = coordtype(Tetrs) +# bfs = Vector{Vector{Shape{T}}}(undef, numcells(Edges)) +# pos = Vector{vertextype(Edges)}(undef, numcells(Edges)) - gpred = CompScienceMeshes.overlap_gpredicate(Dir) - dirichlet = submesh(bnd) do face - gpred(chart(bnd,face)) - end +# gpred = CompScienceMeshes.overlap_gpredicate(Dir) +# dirichlet = submesh(bnd) do face +# gpred(chart(bnd,face)) +# end - for (F,Edge) in enumerate(cells(Edges)) +# for (F,Edge) in enumerate(cells(Edges)) - println("Constructing dual 2-forms: $F out of $(length(Edges)).") - bfs[F] = Vector{Shape{T}}() +# println("Constructing dual 2-forms: $F out of $(length(Edges)).") +# bfs[F] = Vector{Shape{T}}() - pos[F] = cartesian(center(chart(Edges,Edge))) - port_vertex_idx = argmin(norm.(vertices(tetrs) .- Ref(pos[F]))) +# pos[F] = cartesian(center(chart(Edges,Edge))) +# port_vertex_idx = argmin(norm.(vertices(tetrs) .- Ref(pos[F]))) - ptch_idcs1 = v2t[Edge[1],1:v2n[Edge[1]]] - ptch_idcs2 = v2t[Edge[2],1:v2n[Edge[2]]] +# ptch_idcs1 = v2t[Edge[1],1:v2n[Edge[1]]] +# ptch_idcs2 = v2t[Edge[2],1:v2n[Edge[2]]] - patch1 = Mesh(vertices(tetrs), cells(tetrs)[ptch_idcs1]) - patch2 = Mesh(vertices(tetrs), cells(tetrs)[ptch_idcs2]) - patch = CompScienceMeshes.union(patch1, patch2) +# patch1 = Mesh(vertices(tetrs), cells(tetrs)[ptch_idcs1]) +# patch2 = Mesh(vertices(tetrs), cells(tetrs)[ptch_idcs2]) +# patch = CompScienceMeshes.union(patch1, patch2) - patch_bnd = boundary(patch1) +# patch_bnd = boundary(patch1) - bnd_patch1 = boundary(patch1) - bnd_patch2 = boundary(patch2) - port = submesh(in(bnd_patch2), bnd_patch1) +# bnd_patch1 = boundary(patch1) +# bnd_patch2 = boundary(patch2) +# port = submesh(in(bnd_patch2), bnd_patch1) - total_area = sum(volume(chart(port, fc)) for fc in port) - prt_fluxes = zeros(length(port)) - tgt = vertices(Edges)[Edge[1]] - vertices(Edges)[Edge[2]] - for (i,face) in enumerate(cells(port)) - chrt = chart(port, face) - sgn = sign(dot(normal(chrt), tgt)) - prt_fluxes[i] = sgn * volume(chrt) / total_area - end +# total_area = sum(volume(chart(port, fc)) for fc in port) +# prt_fluxes = zeros(length(port)) +# tgt = vertices(Edges)[Edge[1]] - vertices(Edges)[Edge[2]] +# for (i,face) in enumerate(cells(port)) +# chrt = chart(port, face) +# sgn = sign(dot(normal(chrt), tgt)) +# prt_fluxes[i] = sgn * volume(chrt) / total_area +# end - RT1_int, RT1_prt, x1_int, x_prt = builddual2form( - patch1, port, dirichlet, +prt_fluxes) - RT2_int, RT2_prt, x2_int, x_prt = builddual2form( - patch2, port, dirichlet, prt_fluxes) +# RT1_int, RT1_prt, x1_int, x_prt = builddual2form( +# patch1, port, dirichlet, +prt_fluxes) +# RT2_int, RT2_prt, x2_int, x_prt = builddual2form( +# patch2, port, dirichlet, prt_fluxes) - addf!(bfs[F], x1_int, RT1_int, ptch_idcs1) - addf!(bfs[F], +x_prt, RT1_prt, ptch_idcs1) +# addf!(bfs[F], x1_int, RT1_int, ptch_idcs1) +# addf!(bfs[F], +x_prt, RT1_prt, ptch_idcs1) - addf!(bfs[F], x2_int, RT2_int, ptch_idcs2) - addf!(bfs[F], x_prt, RT2_prt, ptch_idcs2) +# addf!(bfs[F], x2_int, RT2_int, ptch_idcs2) +# addf!(bfs[F], x_prt, RT2_prt, ptch_idcs2) - end +# end - NDLCDBasis(tetrs, bfs, pos) -end +# NDLCDBasis(tetrs, bfs, pos) +# end -function dual2forms_body_new(Edges, tetrs, bnd, dir, v2t, v2n) +function dual2forms_body(Edges, tetrs, bnd, dir, v2t, v2n) T = coordtype(tetrs) bfs = Vector{Vector{Shape{T}}}(undef, numcells(Edges)) @@ -253,11 +238,11 @@ function extend_1_form(supp, dirichlet, x_prt, port_edges) curl_Nd_int = curl(Nd_int) grad_Lg_int = gradient(Lg_int) - A = assemble(Id, curl_Nd_int, curl_Nd_int) - B = assemble(Id, grad_Lg_int, Nd_int) + A = assemble(Id, curl_Nd_int, curl_Nd_int, threading=Threading{:single}) + B = assemble(Id, grad_Lg_int, Nd_int, threading=Threading{:single}) - a = -assemble(Id, curl_Nd_int, curl_Nd_prt) * x_prt - b = -assemble(Id, grad_Lg_int, Nd_prt) * x_prt + a = -assemble(Id, curl_Nd_int, curl_Nd_prt, threading=Threading{:single}) * x_prt + b = -assemble(Id, grad_Lg_int, Nd_prt, threading=Threading{:single}) * x_prt Z = zeros(eltype(b), length(b), length(b)) u = [A B'; B Z] \ [a;b] @@ -303,57 +288,17 @@ function extend_2_form(supp, dirichlet, x_prt, port_faces) return x_int, int_faces, RT_int end - - -# function extend_face_to_tetr(supp, dirichlet, x_prt, port_edges) -# -# Id = BEAST.Identity() -# -# bnd_supp = boundary(supp) -# supp_edges = CompScienceMeshes.skeleton_fast(supp, 1) -# supp_nodes = CompScienceMeshes.skeleton_fast(supp, 0) -# -# dir_compl = submesh(!in(dirichlet), bnd_supp) -# dir_compl_edges = CompScienceMeshes.skeleton_fast(dir_compl, 1) -# dir_compl_nodes = CompScienceMeshes.skeleton_fast(dir_compl, 0) -# -# int_edges = submesh(!in(dir_compl_edges), supp_edges) -# int_nodes = submesh(!in(dir_compl_nodes), supp_nodes) -# -# # Nd_prt = BEAST.nedelecc3d(supp, port_edges) -# # Nd_int = BEAST.nedelecc3d(supp, int_edges) -# Nd_prt = BEAST.nedelec(supp, port_edges) -# Nd_int = BEAST.nedelec(supp, int_edges) -# Lg_int = BEAST.lagrangec0d1(supp, int_nodes) -# -# curl_Nd_prt = curl(Nd_prt) -# curl_Nd_int = curl(Nd_int) -# grad_Lg_int = BEAST.gradient(Lg_int) -# -# A = assemble(Id, curl_Nd_int, curl_Nd_int) -# B = assemble(Id, grad_Lg_int, Nd_int) -# -# a = -assemble(Id, curl_Nd_int, curl_Nd_prt) * x_prt -# b = -assemble(Id, grad_Lg_int, Nd_prt) * x_prt -# -# Z = zeros(eltype(b), length(b), length(b)) -# u = [A B'; B Z] \ [a;b] -# x_int = u[1:end-length(b)] -# -# return x_int, int_edges, Nd_int -# end - function dual1forms(Tetrs, Faces, Dir) - tetrs, bnd, dir, v2t, v2n = dual1forms_init(Tetrs, Dir) + tetrs, bnd, dir, v2t, v2n = dualforms_init(Tetrs, Dir) dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) end -function dual2forms_new(Tetrs, Faces, Dir) - tetrs, bnd, dir, v2t, v2n = dual1forms_init(Tetrs, Dir) - dual2forms_body_new(Faces, tetrs, bnd, dir, v2t, v2n) +function dual2forms(Tetrs, Faces, Dir) + tetrs, bnd, dir, v2t, v2n = dualforms_init(Tetrs, Dir) + dual2forms_body(Faces, tetrs, bnd, dir, v2t, v2n) end -function dual1forms_init(Tetrs, Dir) +function dualforms_init(Tetrs, Dir) tetrs = barycentric_refinement(Tetrs) v2t, v2n = CompScienceMeshes.vertextocellmap(tetrs) bnd = boundary(tetrs) @@ -362,15 +307,27 @@ function dual1forms_init(Tetrs, Dir) return tetrs, bnd, dir, v2t, v2n end +# function dual2forms_init(Tetrs) + +# tetrs = barycentric_refinement(Tetrs) +# v2t, v2n = CompScienceMeshes.vertextocellmap(tetrs) +# bnd = boundary(tetrs) + +# return tetrs, bnd, v2t, v2n +# end + function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) T = coordtype(tetrs) bfs = Vector{Vector{Shape{T}}}(undef, length(Faces)) pos = Vector{vertextype(Faces)}(undef, length(Faces)) - for (F,Face) in enumerate(Faces) + Cells = cells(Faces) + Threads.@threads for F in 1:length(Faces) + Face = Cells[F] - println("Constructing dual 1-forms: $F out of $(length(Faces)).") + myid = Threads.threadid() + myid == 1 && println("Constructing dual 1-forms: $F out of $(length(Faces)).") idcs1 = v2t[Face[1],1:v2n[Face[1]]] idcs2 = v2t[Face[2],1:v2n[Face[2]]] diff --git a/src/localop.jl b/src/localop.jl index 7b27cb72..cf98b80c 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -11,7 +11,7 @@ abstract type LocalOperator <: Operator end function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, - threading::Type{Threading{:multi}}) + threading::Type{Threading{:single}}) if geometry(tfs) == geometry(bfs) return assemble_local_matched!(biop, tfs, bfs, store) @@ -21,15 +21,16 @@ function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, return assemble_local_refines!(biop, tfs, bfs, store) end - # tels, tad = assemblydata(tfs) - # bels, bad = assemblydata(bfs) - # - # length(tels) != numcells(geometry(tfs)) && return assemble_local_mixed!(biop, tfs, bfs, store) - # length(bels) != numcells(geometry(bfs)) && return assemble_local_mixed!(biop, tfs, bfs, store) - return assemble_local_mixed!(biop, tfs, bfs, store) end +function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, + threading::Type{Threading{:multi}}) + + assemble!(biop, tfs, bfs, store, Threading{:single}) + +end + function assemble_local_matched!(biop::LocalOperator, tfs::Space, bfs::Space, store) tels, tad, ta2g = assemblydata(tfs) diff --git a/src/operator.jl b/src/operator.jl index 0b8aa5e1..323f1b93 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -116,10 +116,11 @@ function allocatestorage(operator::AbstractOperator, test_functions, trial_funct scalartype(test_functions) , scalartype(trial_functions), ) - Z = SharedArray{T}( - numfunctions(test_functions) , - numfunctions(trial_functions), - ) + # Z = SharedArray{T}( + # numfunctions(test_functions) , + # numfunctions(trial_functions), + # ) + Z = Matrix{T}(undef, numfunctions(test_functions), numfunctions(trial_functions)) fill!(Z, 0) store(v,m,n) = (Z[m,n] += v) return Z, store From 2a8952d9414d44d6915d73aa5508e455e1c5995a Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 27 Oct 2020 12:42:08 +0100 Subject: [PATCH 033/528] Reduce output during duals1 ctr --- examples/ex_duals.jl | 4 +- src/bases/dual3d.jl | 164 ++----------------------------------------- 2 files changed, 7 insertions(+), 161 deletions(-) diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl index 62d0d622..662f4975 100644 --- a/examples/ex_duals.jl +++ b/examples/ex_duals.jl @@ -32,8 +32,8 @@ Dir = bnd_Faces Neu = submesh(!in(Dir), bnd_Faces) @time dual1 = BEAST.dual1forms(Tetrs, Faces, Dir) -error("stop here") -dual2 = BEAST.dual2forms(Tetrs, int_Edges, Dir) +# error() +@time dual2 = BEAST.dual2forms(Tetrs, int_Edges, Dir) @assert numfunctions(primal1) == numfunctions(dual2) @assert numfunctions(primal2) == numfunctions(dual1) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index d25c8810..edbde713 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -10,155 +10,6 @@ function addf!(fn::Vector{<:Shape}, x::Vector, space::Space, idcs::Vector{Int}) end end - -# function builddual2form(support, port, dirichlet, prt_fluxes) - -# faces = CompScienceMeshes.skeleton_fast(support,2) -# edges = CompScienceMeshes.skeleton_fast(support,1) -# verts = CompScienceMeshes.skeleton_fast(support,0) -# @assert length(verts) - length(edges) + length(faces) - length(support) == 1 -# bndry = boundary(support) - -# cells_bndry = sort.(bndry) -# dirbnd = submesh(dirichlet) do face -# sort(face) in cells_bndry ? true : false -# end - -# bnd_dirbnd = boundary(dirbnd) -# edges_dirbnd = CompScienceMeshes.skeleton_fast(dirbnd,1) -# cells_bnd_dirbnd = sort.(bnd_dirbnd) -# int_edges_dirbnd = submesh(edges_dirbnd) do edge -# sort(edge) in cells(bnd_dirbnd) ? false : true -# end - -# num_faces_on_port = 0 -# num_faces_on_dirc = 0 - -# cells_port = sort.(port) -# cells_dirbnd = sort.(dirbnd) -# int_faces = submesh(faces) do face -# sort(face) in cells_port && return false -# sort(face) in cells_dirbnd && return true -# sort(face) in cells_bndry && return false -# return true -# end - -# bnd_edges = CompScienceMeshes.skeleton_fast(bndry,1) -# prt_edges = CompScienceMeshes.skeleton_fast(port,1) - -# cells_int_edges_dirbnd = sort.(int_edges_dirbnd) -# cells_bnd_edges = sort.(bnd_edges) -# cells_prt_edges = sort.(prt_edges) - -# int_edges = submesh(edges) do edge -# sort(edge) in cells_int_edges_dirbnd && return true -# sort(edge) in cells_bnd_edges && return false -# sort(edge) in cells_prt_edges && return false -# return true -# end - -# RT_int = nedelecd3d(support, int_faces) -# RT_prt = nedelecd3d(support, port) -# L0_int = nedelecc3d(support, int_edges) - -# Id = BEAST.Identity() -# div_RT_int = divergence(RT_int) -# div_RT_prt = divergence(RT_prt) -# D = assemble(Id, div_RT_int, div_RT_int) -# Q = assemble(Id, div_RT_int, div_RT_prt) -# d = -Q * prt_fluxes - -# curl_L0_int = curl(L0_int) -# div_curl_L0_int = divergence(curl_L0_int) -# ZZ = real(assemble(Id, div_curl_L0_int, div_curl_L0_int)) -# @assert isapprox(norm(ZZ), 0.0, atol=1e-8) -# C = assemble(Id, curl_L0_int, RT_int) -# c = real(assemble(Id, curl_L0_int, RT_prt)) * prt_fluxes - -# T = eltype(D) -# nz = length(c) -# QQ = [D C'; C zeros(T,nz,nz)] -# qq = [d;c] -# x = (QQ \ qq)[1:end-nz] - -# if !isapprox(C*x, c, atol=1e-8) || !isapprox(D*x, d, atol=1e-6) -# @show norm(D*x-d) -# @show norm(C*x-c) -# @show rank(C) -# @show size(C,1) -# error("error") -# end - -# return RT_int, RT_prt, x, prt_fluxes - -# end - - - - - -# function dual2forms(Tetrs, Edges, Dir) -# tetrs, bnd, v2t, v2n = dual2forms_init(Tetrs) -# dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) -# end - -# function dual2forms_body(Tetrs, Edges, Dir, tetrs, bnd, v2t, v2n) - -# T = coordtype(Tetrs) -# bfs = Vector{Vector{Shape{T}}}(undef, numcells(Edges)) -# pos = Vector{vertextype(Edges)}(undef, numcells(Edges)) - -# gpred = CompScienceMeshes.overlap_gpredicate(Dir) -# dirichlet = submesh(bnd) do face -# gpred(chart(bnd,face)) -# end - -# for (F,Edge) in enumerate(cells(Edges)) - -# println("Constructing dual 2-forms: $F out of $(length(Edges)).") -# bfs[F] = Vector{Shape{T}}() - -# pos[F] = cartesian(center(chart(Edges,Edge))) -# port_vertex_idx = argmin(norm.(vertices(tetrs) .- Ref(pos[F]))) - -# ptch_idcs1 = v2t[Edge[1],1:v2n[Edge[1]]] -# ptch_idcs2 = v2t[Edge[2],1:v2n[Edge[2]]] - -# patch1 = Mesh(vertices(tetrs), cells(tetrs)[ptch_idcs1]) -# patch2 = Mesh(vertices(tetrs), cells(tetrs)[ptch_idcs2]) -# patch = CompScienceMeshes.union(patch1, patch2) - -# patch_bnd = boundary(patch1) - -# bnd_patch1 = boundary(patch1) -# bnd_patch2 = boundary(patch2) -# port = submesh(in(bnd_patch2), bnd_patch1) - -# total_area = sum(volume(chart(port, fc)) for fc in port) -# prt_fluxes = zeros(length(port)) -# tgt = vertices(Edges)[Edge[1]] - vertices(Edges)[Edge[2]] -# for (i,face) in enumerate(cells(port)) -# chrt = chart(port, face) -# sgn = sign(dot(normal(chrt), tgt)) -# prt_fluxes[i] = sgn * volume(chrt) / total_area -# end - -# RT1_int, RT1_prt, x1_int, x_prt = builddual2form( -# patch1, port, dirichlet, +prt_fluxes) -# RT2_int, RT2_prt, x2_int, x_prt = builddual2form( -# patch2, port, dirichlet, prt_fluxes) - -# addf!(bfs[F], x1_int, RT1_int, ptch_idcs1) -# addf!(bfs[F], +x_prt, RT1_prt, ptch_idcs1) - -# addf!(bfs[F], x2_int, RT2_int, ptch_idcs2) -# addf!(bfs[F], x_prt, RT2_prt, ptch_idcs2) - -# end - -# NDLCDBasis(tetrs, bfs, pos) -# end - function dual2forms_body(Edges, tetrs, bnd, dir, v2t, v2n) T = coordtype(tetrs) @@ -245,7 +96,8 @@ function extend_1_form(supp, dirichlet, x_prt, port_edges) b = -assemble(Id, grad_Lg_int, Nd_prt, threading=Threading{:single}) * x_prt Z = zeros(eltype(b), length(b), length(b)) - u = [A B'; B Z] \ [a;b] + S = [A B'; B Z] + u = S \ [a;b] x_int = u[1:end-length(b)] return x_int, int_edges, Nd_int @@ -307,14 +159,6 @@ function dualforms_init(Tetrs, Dir) return tetrs, bnd, dir, v2t, v2n end -# function dual2forms_init(Tetrs) - -# tetrs = barycentric_refinement(Tetrs) -# v2t, v2n = CompScienceMeshes.vertextocellmap(tetrs) -# bnd = boundary(tetrs) - -# return tetrs, bnd, v2t, v2n -# end function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) @@ -327,7 +171,9 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) Face = Cells[F] myid = Threads.threadid() - myid == 1 && println("Constructing dual 1-forms: $F out of $(length(Faces)).") + myid == 1 && + F % 20 == 0 && + println("Constructing dual 1-forms: $F out of $(length(Faces)).") idcs1 = v2t[Face[1],1:v2n[Face[1]]] idcs2 = v2t[Face[2],1:v2n[Face[2]]] From c66305773e63c69140c06aaa226b07fa6b2effc4 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 10 Nov 2020 12:52:35 +0100 Subject: [PATCH 034/528] Multithreaded construction of 2-forms --- examples/ex_duals.jl | 10 ++++++++++ src/bases/dual3d.jl | 15 ++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl index 662f4975..5109f92f 100644 --- a/examples/ex_duals.jl +++ b/examples/ex_duals.jl @@ -108,5 +108,15 @@ trace_primal1_hemi = BEAST.ttrace(primal1_hemi, boundary(hemi)) fcr, geo = facecurrents(eg, trace_primal1_hemi) Plotly.plot(patch(geo, norm.(fcr))) +tetrs = geometry(dual1) +bary_hemi = submesh(tetrs) do tetr + cartesian(CompScienceMeshes.center(chart(tetrs, tetr)))[3] < 0 +end + +dual1_hemi = restrict(dual1, bary_hemi) +bnd_bary_hemi = boundary(bary_hemi) +trace_dual1_hemi = BEAST.ttrace(dual1_hemi, bnd_bary_hemi) + +fcr, geo = facecurrents(hG, trace_dual1_hemi) # tetrs, bnd, dir, v2t, v2n = BEAST.dualforms_init(Tetrs, Dir) # @profview BEAST.dual1forms_body(Faces[collect(1:10)], tetrs, bnd, dir, v2t, v2n) \ No newline at end of file diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index edbde713..d9e8e7b1 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -16,9 +16,14 @@ function dual2forms_body(Edges, tetrs, bnd, dir, v2t, v2n) bfs = Vector{Vector{Shape{T}}}(undef, numcells(Edges)) pos = Vector{vertextype(Edges)}(undef, numcells(Edges)) - for (F,Edge) in enumerate(cells(Edges)) + Cells = cells(Edges) + num_threads = Threads.nthreads() + Threads.@threads for F in eachindex(Cells) + Edge = Cells[F] - println("Constructing dual 2-forms: $F out of $(length(Edges)).") + myid = Threads.threadid() + myid == 1 && (F % 20 == 0) && + println("Constructing dual 2-forms: $(F*num_threads) out of $(length(Edges)).") idcs1 = v2t[Edge[1],1:v2n[Edge[1]]] idcs2 = v2t[Edge[2],1:v2n[Edge[2]]] @@ -167,13 +172,13 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) pos = Vector{vertextype(Faces)}(undef, length(Faces)) Cells = cells(Faces) + num_threads = Threads.nthreads() Threads.@threads for F in 1:length(Faces) Face = Cells[F] myid = Threads.threadid() - myid == 1 && - F % 20 == 0 && - println("Constructing dual 1-forms: $F out of $(length(Faces)).") + myid == 1 && F % 20 == 0 && + println("Constructing dual 1-forms: $(F*num_threads) out of $(length(Faces)).") idcs1 = v2t[Face[1],1:v2n[Face[1]]] idcs2 = v2t[Face[2],1:v2n[Face[2]]] From b9a762435c18d457ce0c30c7508e860e8725f103 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 11 Nov 2020 12:37:44 +0100 Subject: [PATCH 035/528] print nomral fluxes in ex_duals --- examples/ex_duals.jl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl index 5109f92f..9af7d869 100644 --- a/examples/ex_duals.jl +++ b/examples/ex_duals.jl @@ -108,6 +108,11 @@ trace_primal1_hemi = BEAST.ttrace(primal1_hemi, boundary(hemi)) fcr, geo = facecurrents(eg, trace_primal1_hemi) Plotly.plot(patch(geo, norm.(fcr))) +curl_primal1_hemi = BEAST.curl(primal1_hemi) +ncurl_primal1_hemi = BEAST.ntrace(curl_primal1_hemi, boundary(hemi)) +fcr, geo = facecurrents(eg, ncurl_primal1_hemi) +Plotly.plot(patch(geo, norm.(fcr))) + tetrs = geometry(dual1) bary_hemi = submesh(tetrs) do tetr cartesian(CompScienceMeshes.center(chart(tetrs, tetr)))[3] < 0 @@ -116,7 +121,10 @@ end dual1_hemi = restrict(dual1, bary_hemi) bnd_bary_hemi = boundary(bary_hemi) trace_dual1_hemi = BEAST.ttrace(dual1_hemi, bnd_bary_hemi) - fcr, geo = facecurrents(hG, trace_dual1_hemi) +Plotly.plot(patch(geo, norm.(fcr))) + + + # tetrs, bnd, dir, v2t, v2n = BEAST.dualforms_init(Tetrs, Dir) # @profview BEAST.dual1forms_body(Faces[collect(1:10)], tetrs, bnd, dir, v2t, v2n) \ No newline at end of file From 76e1d34e20ccdcd7a7287d6e3f1ddb66c21e22c4 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 13 Nov 2020 12:46:49 +0100 Subject: [PATCH 036/528] Regularistion extension of 2-forms --- examples/ex_duals.jl | 2 +- src/bases/dual3d.jl | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl index 9af7d869..4d4a5727 100644 --- a/examples/ex_duals.jl +++ b/examples/ex_duals.jl @@ -5,7 +5,7 @@ using BEAST include("utils/showfn.jl") -Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.40) +Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.26) Faces = CompScienceMeshes.skeleton_fast(Tetrs, 2) Edges = CompScienceMeshes.skeleton_fast(Tetrs, 1) Nodes = CompScienceMeshes.skeleton_fast(Tetrs, 0) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index d9e8e7b1..d754a009 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -116,13 +116,21 @@ function extend_2_form(supp, dirichlet, x_prt, port_faces) bnd_supp = boundary(supp) supp_faces = CompScienceMeshes.skeleton_fast(supp, 2) supp_edges = CompScienceMeshes.skeleton_fast(supp, 1) + supp_nodes = CompScienceMeshes.skeleton_fast(supp, 0) dir_compl = submesh(!in(dirichlet), bnd_supp) dir_compl_faces = CompScienceMeshes.skeleton_fast(dir_compl, 2) dir_compl_edges = CompScienceMeshes.skeleton_fast(dir_compl, 1) + dir_compl_nodes = CompScienceMeshes.skeleton_fast(dir_compl, 0) int_faces = submesh(!in(dir_compl_faces), supp_faces) int_edges = submesh(!in(dir_compl_edges), supp_edges) + int_nodes = submesh(!in(dir_compl_nodes), supp_nodes) + + if length(int_nodes) > 0 + @assert length(int_nodes) == 1 + int_edges = int_edges[collect(1:length(int_edges)-length(int_nodes))] + end RT_prt = BEAST.raviartthomas(supp, port_faces) RT_int = BEAST.raviartthomas(supp, int_faces) From 8a85fe50d1c0fa840900eb52b66a72bbecfc529d Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 20 Nov 2020 17:11:49 +0100 Subject: [PATCH 037/528] Store duals-duals discs in dense storage --- examples/ex_duals.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl index 4d4a5727..04afc4ea 100644 --- a/examples/ex_duals.jl +++ b/examples/ex_duals.jl @@ -83,7 +83,7 @@ Zdd = zeros(numfunctions(dual1), numfunctions(dual1)) μ = BEAST.Multiplicative(p -> 1.0) Ipp = assemble(ϵ, primal1, primal1) -Idd = assemble(μ, dual1, dual1) +Idd = assemble(μ, dual1, dual1, storage_policy=Val{:densestorage}) Zpd = zeros(numfunctions(primal1), numfunctions(dual1)) From 268b5fa79c201f59351cfc25bbc821d7df7bd076 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sun, 22 Nov 2020 16:24:09 +0100 Subject: [PATCH 038/528] Zero int_nodes border case fixed --- examples/ex_duals.jl | 4 ++-- src/bases/dual3d.jl | 24 +++++++++++++----------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl index 04afc4ea..1c77fb4f 100644 --- a/examples/ex_duals.jl +++ b/examples/ex_duals.jl @@ -5,7 +5,7 @@ using BEAST include("utils/showfn.jl") -Tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.26) +Tetrs = CompScienceMeshes.meshball(radius=1.0, h=0.26) Faces = CompScienceMeshes.skeleton_fast(Tetrs, 2) Edges = CompScienceMeshes.skeleton_fast(Tetrs, 1) Nodes = CompScienceMeshes.skeleton_fast(Tetrs, 0) @@ -115,7 +115,7 @@ Plotly.plot(patch(geo, norm.(fcr))) tetrs = geometry(dual1) bary_hemi = submesh(tetrs) do tetr - cartesian(CompScienceMeshes.center(chart(tetrs, tetr)))[3] < 0 + cartesian(CompScienceMeshes.center(chart(tetrs, tetr)))[1] < 0 end dual1_hemi = restrict(dual1, bary_hemi) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index d754a009..99c9fc1b 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -88,23 +88,25 @@ function extend_1_form(supp, dirichlet, x_prt, port_edges) Nd_prt = BEAST.nedelec(supp, port_edges) Nd_int = BEAST.nedelec(supp, int_edges) - Lg_int = BEAST.lagrangec0d1(supp, int_nodes) - + curl_Nd_prt = curl(Nd_prt) curl_Nd_int = curl(Nd_int) - grad_Lg_int = gradient(Lg_int) - A = assemble(Id, curl_Nd_int, curl_Nd_int, threading=Threading{:single}) - B = assemble(Id, grad_Lg_int, Nd_int, threading=Threading{:single}) - a = -assemble(Id, curl_Nd_int, curl_Nd_prt, threading=Threading{:single}) * x_prt - b = -assemble(Id, grad_Lg_int, Nd_prt, threading=Threading{:single}) * x_prt + + if length(int_nodes) > 0 + Lg_int = BEAST.lagrangec0d1(supp, int_nodes) + grad_Lg_int = gradient(Lg_int) + B = assemble(Id, grad_Lg_int, Nd_int, threading=Threading{:single}) + b = -assemble(Id, grad_Lg_int, Nd_prt, threading=Threading{:single}) * x_prt + Z = zeros(eltype(b), length(b), length(b)) + S = [A B'; B Z] + u = S \ [a;b] + else + u = A \ a + end - Z = zeros(eltype(b), length(b), length(b)) - S = [A B'; B Z] - u = S \ [a;b] x_int = u[1:end-length(b)] - return x_int, int_edges, Nd_int end From c365fc070ee0c03323d62657666da69e7a5c765a Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 24 Nov 2020 17:37:40 +0100 Subject: [PATCH 039/528] Deal with special border case in extend 1-form --- src/bases/dual3d.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 99c9fc1b..03b9d56e 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -106,7 +106,7 @@ function extend_1_form(supp, dirichlet, x_prt, port_edges) u = A \ a end - x_int = u[1:end-length(b)] + x_int = u[1:numfunctions(Nd_int)] return x_int, int_edges, Nd_int end @@ -177,6 +177,8 @@ end function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) + @assert dimension(Faces) == 2 + T = coordtype(tetrs) bfs = Vector{Vector{Shape{T}}}(undef, length(Faces)) pos = Vector{vertextype(Faces)}(undef, length(Faces)) From 0a32fedb86bd14854d925b947cbe30d51f723f5f Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 15 Dec 2020 16:10:42 +0000 Subject: [PATCH 040/528] local assembly verbose from threshold size --- src/bases/ndspace.jl | 2 +- src/localop.jl | 12 +++++++++++- test/test_assemblerow.jl | 2 ++ test/test_local_assembly.jl | 1 + test/test_nitsche.jl | 5 ++++- 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/bases/ndspace.jl b/src/bases/ndspace.jl index 5f2dc9b1..cfb7403f 100644 --- a/src/bases/ndspace.jl +++ b/src/bases/ndspace.jl @@ -41,7 +41,7 @@ function nedelec(surface, edges=skeleton(surface,1)) end function LinearAlgebra.cross(::NormalVector, s::NDBasis) - @assert CompScienceMeshes.isoriented(s.geo) + # @assert CompScienceMeshes.isoriented(s.geo) RTBasis(s.geo, s.fns, s.pos) end diff --git a/src/localop.jl b/src/localop.jl index 8f0baac1..7f906997 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -94,6 +94,10 @@ function assemble_local_matched!(biop::LocalOperator, tfs::Space, bfs::Space, st brefs = refspace(bfs) qd = quaddata(biop, trefs, brefs, tels, bels) + + verbose = length(tels) > 10_000 + verbose && print("dots out of 20: ") + todo, done, pctg = length(tels), 0, 0 for (p,cell) in enumerate(tels) P = ta2g[p] q = bg2a[P] @@ -105,7 +109,13 @@ function assemble_local_matched!(biop::LocalOperator, tfs::Space, bfs::Space, st for i in 1 : size(locmat, 1), j in 1 : size(locmat, 2) for (m,a) in tad[p,i], (n,b) in bad[q,j] store(a * locmat[i,j] * b, m, n) -end end end end + + end end + + new_pctg = round(Int, (done += 1) / todo * 100) + verbose && new_pctg > pctg + 4 && (print("."); pctg = new_pctg) + end +end function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, store) diff --git a/test/test_assemblerow.jl b/test/test_assemblerow.jl index de27e4e7..a6fad553 100644 --- a/test/test_assemblerow.jl +++ b/test/test_assemblerow.jl @@ -1,3 +1,5 @@ +@info "Executing test_assemblerow.jl" + using CompScienceMeshes, BEAST using Test diff --git a/test/test_local_assembly.jl b/test/test_local_assembly.jl index a99ca345..702eeb3e 100644 --- a/test/test_local_assembly.jl +++ b/test/test_local_assembly.jl @@ -1,3 +1,4 @@ +@info "Executing test_local_assembly.jl" using CompScienceMeshes using BEAST diff --git a/test/test_nitsche.jl b/test/test_nitsche.jl index d748d22a..5106a751 100644 --- a/test/test_nitsche.jl +++ b/test/test_nitsche.jl @@ -1,4 +1,7 @@ +@info "Executing test_nitsche.jl" + using Test +using LinearAlgebra #using LinearForms using CompScienceMeshes using BEAST @@ -18,7 +21,7 @@ X = raviartthomas(Γ, γ) x = divergence(X) y = ntrace(X,γ) -Z = assemble(S,y,x) +Z = assemble(S,y,x,threading=BEAST.Threading{:single}) # test for the correct sparsity pattern # I, J, V = findall(!iszero, Z) From 20dad371b3571b0d08cc3a1d33bde4bca2f34b76 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 26 Apr 2021 16:15:27 +0200 Subject: [PATCH 041/528] left and right hand of varform can be constructed separately --- src/solvers/solver.jl | 58 ++++++++++++++++++++++++++++++++++++++++ src/utils/variational.jl | 2 +- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 4ab8d3c4..248b7085 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -8,6 +8,61 @@ mutable struct DiscreteEquation test_space_dict # dictionary mapping indices into test space to FE spaces end +struct DiscreteBilform + bilform + trial_space_dict # dictionary mapping indices into trial space to FE spaces + test_space_dict # dictionary mapping indices into test space to FE spaces +end + +struct DiscreteLinform + linform + test_space_dict +end + + +function discretise(bf::BilForm, space_mappings::Pair...) + trial_space_dict = Dict() + test_space_dict = Dict() + for sm in space_mappings + + found = false + sm.first.space == bf.trial_space && (dict = trial_space_dict; found = true) + sm.first.space == bf.test_space && (dict = test_space_dict; found = true) + @assert found "Vector $(sm.first) neither in test nor in trial space" + + @assert !haskey(dict, sm.first.idx) "multiple mappings for $(sm.first)" + dict[sm.first.idx] = sm.second + end + + # check that all symbols where mapped + for p in eachindex(bf.trial_space) @assert haskey(trial_space_dict,p) end + for p in eachindex(bf.test_space) @assert haskey(test_space_dict, p) end + + DiscreteBilform(bf, trial_space_dict, test_space_dict) +end + + +function discretise(lf::LinForm, space_mappings::Pair...) + # trial_space_dict = Dict() + test_space_dict = Dict() + for sm in space_mappings + + found = false + # sm.first.space == bf.trial_space && (dict = trial_space_dict; found = true) + sm.first.space == lf.test_space && (dict = test_space_dict; found = true) + @assert found "Vector $(sm.first) not found in test space" + + @assert !haskey(dict, sm.first.idx) "multiple mappings for $(sm.first)" + dict[sm.first.idx] = sm.second + end + + # check that all symbols where mapped + # for p in eachindex(bf.trial_space) @assert haskey(trial_space_dict,p) end + for p in eachindex(lf.test_space) @assert haskey(test_space_dict, p) end + + DiscreteLinform(lf, test_space_dict) +end + function discretise(eq, space_mappings::Pair...) trial_space_dict = Dict() @@ -54,6 +109,9 @@ end sysmatrix(eq::DiscreteEquation) = assemble(eq.equation.lhs, eq.test_space_dict, eq.trial_space_dict) rhs(eq::DiscreteEquation) = assemble(eq.equation.rhs, eq.test_space_dict) +assemble(dbf::DiscreteBilform) = assemble(dbf.bilform, dbf.test_space_dict, dbf.trial_space_dict) +assemble(dlf::DiscreteLinform) = assemble(dlf.linform, dlf.test_space_dict) + function assemble(lform::LinForm, test_space_dict) terms = lform.terms diff --git a/src/utils/variational.jl b/src/utils/variational.jl index 9888ea2f..eb16ebce 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -179,7 +179,7 @@ macro hilbertspace(syms...) $vars = $rhs end for (i,s) in enumerate(syms) - push!(xp.args, :(global $s = $vars[$i])) + push!(xp.args, :($(esc(s)) = $vars[$i])) end xp From b645a97f5e5aa1f278fb3202d87b742b2bd474ec Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 30 Apr 2021 13:23:09 +0200 Subject: [PATCH 042/528] Arrays of hilbertspace generators can be declared --- examples/stgalerkin.jl | 3 + src/integralop.jl | 61 +------------------- src/maxwell/sauterschwabints_rt.jl | 5 +- src/utils/variational.jl | 93 +++++++++++++++++++++++------- 4 files changed, 81 insertions(+), 81 deletions(-) diff --git a/examples/stgalerkin.jl b/examples/stgalerkin.jl index aeb95319..d4079493 100644 --- a/examples/stgalerkin.jl +++ b/examples/stgalerkin.jl @@ -12,6 +12,9 @@ X = raviartthomas(Γ) T = timebasisc0d1(Δt, Nt) U = timebasiscxd0(Δt, Nt) +# T = timebasisshiftedlagrange(Δt, Nt, 2) +# U = timebasisdelta(Δt, Nt) + V = X ⊗ T W = X ⊗ U diff --git a/src/integralop.jl b/src/integralop.jl index 73ea92ce..2553f85c 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -82,6 +82,7 @@ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store) bshapes, bsis_elements, bad, qd, zlocal, store) else + @info "assemblechunk for nested meshes" assemblechunk_body_nested_meshes!(biop, tshapes, test_elements, tad, bshapes, bsis_elements, bad, @@ -354,63 +355,3 @@ end end end end end - -# mutable struct SauterSchwabStrategy -# hits::Int64 -# end -# function momintegrals!(biop, tshs::subReferenceSpace, bshs::subReferenceSpace, tcell, bcell, z, strat::SauterSchwabStrategy) -# -# A = biop.alpha -# k = biop.gamma -# -# M, N = size(z) -# -# hits = strat.hits -# print(hits) -# acc = 3 -# -# if hits == 0 -# ssm = PositiveDistance(acc) -# elseif hits == 1 -# ssm = CommonVertex(acc) -# elseif hits == 2 -# ssm = CommonEdge(acc) -# elseif hits == 3 -# ssm = CommonFace(acc) -# else -# error("hits can not exceed 3") -# end -# M= tcell.N -# N= bcell.N -# print("M = $(M) and N = $(N) \n") -# z = Array{Complex{Float64},2}(M,N) -# for i = 1:M -# for j = 1:N -# # print("i = $(i) and j = $(j) \n") -# function integrand(u,v) -# upt = neighborhood(tcell,u) -# vpt = neighborhood(bcell,v) -# tshape = shapefuns(upt) -# bshape = shapefuns(vpt) -# y = cartesian(upt) -# x = cartesian(vpt) -# kernel = A * exp(-complex(0,1)*k*norm(x-y))/(4.0*π*norm(x-y)) -# # kernel = kernelvals(biop, upt, vpt) -# ujac = jacobian(upt) -# vjac = jacobian(vpt) -# return tshape[i]*bshape[j]*kernel* ujac * vjac -# # return kernel* ujac * vjac -# end -# z[i,j] = (sauterschwab_parameterized(tcell,bcell,integrand,ssm)) -# end -# end -# -# return z -# end - - - -# mutable struct QuadData{WPV1,WPV2} -# tpoints::Matrix{Vector{WPV1}} -# bpoints::Matrix{Vector{WPV2}} -# end diff --git a/src/maxwell/sauterschwabints_rt.jl b/src/maxwell/sauterschwabints_rt.jl index 7095570f..1813e127 100644 --- a/src/maxwell/sauterschwabints_rt.jl +++ b/src/maxwell/sauterschwabints_rt.jl @@ -131,11 +131,14 @@ function momintegrals_nested!(op::MWOperator3D, # 1. Refine the trial_chart p1, p2, p3 = trial_chart.vertices - e1 = cartesian(neighborhood(trial_chart, (0,1/2))) + # TODO: generalise this to include more general refinements + e1 = cartesian(neighborhood(trial_chart, (0,1/2))) e2 = cartesian(neighborhood(trial_chart, (1/2,0))) e3 = cartesian(neighborhood(trial_chart, (1/2,1/2))) + ct = cartesian(center(trial_chart)) + refined_trial_chart = [ simplex(ct, p1, e3), simplex(ct, e3, p2), diff --git a/src/utils/variational.jl b/src/utils/variational.jl index eb16ebce..e10aebb7 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -155,36 +155,89 @@ Build an equation from a left hand and right hand side -""" - hilbert_space(type, g1, g2, ...) +# """ +# hilbert_space(type, g1, g2, ...) -Returns generators defining a Hilbert space of field `type` -""" -hilbertspace(vars::Symbol...) = [HilbertVector(i, [vars...], []) for i in 1:length(vars)] +# Returns generators defining a Hilbert space of field `type` +# """ +# hilbertspace(vars::Symbol...) = [HilbertVector(i, [vars...], []) for i in 1:length(vars)] +function genspace(syms...) -macro hilbertspace(syms...) - + space = Vector{Symbol}() + lengths = Int[] + starts = Int[] + stops = Int[] for sym in syms - @assert isa(sym, Symbol) "@hilbertspace takes a list of Symbols" - end - rhs = :(hilbertspace()) - for sym in syms - push!(rhs.args, QuoteNode(sym)) + if sym isa Symbol + push!(space, sym) + push!(lengths,1) + push!(starts,1) + push!(stops,1) + elseif sym isa Expr && sym.head == :ref + base = sym.args[1] + start = sym.args[2].args[2] + stop = sym.args[2].args[3] + for k in start:stop + sym = Symbol(base,k) + push!(space, sym) + end + push!(lengths,stop-start+1) + push!(starts,start) + push!(stops,stop) + end end + + return space, starts, stops +end - vars = gensym() - xp = quote - $vars = $rhs - end - for (i,s) in enumerate(syms) - push!(xp.args, :($(esc(s)) = $vars[$i])) - end +macro hilbertspace(syms...) + + space, starts, stops = genspace(syms...) + + ex = quote end + k = 1 + for (s, (start,stop)) in enumerate(zip(starts,stops)) + + + len = stop-start+1 + if len == 1 + sym = syms[s] + push!(ex.args, :($(esc(sym)) = HilbertVector($k,$space,[]))) + k += 1 + else + sym = syms[s].args[1] + push!(ex.args, :($(esc(sym)) = [HilbertVector(i,$space,[]) for i in $k:$(k+len-1)])) + k += len + end - xp + end + return ex end +# macro hilbertspace(syms...) + +# for sym in syms +# @assert isa(sym, Symbol) "@hilbertspace takes a list of Symbols" +# end + +# rhs = :(hilbertspace()) +# for sym in syms +# push!(rhs.args, QuoteNode(sym)) +# end + +# vars = gensym() +# xp = quote +# $vars = $rhs +# end +# for (i,s) in enumerate(syms) +# push!(xp.args, :($(esc(s)) = $vars[$i])) +# end + +# xp +# end + """ call(u::HilbertVector, f, params...) From 950321c9193b4a214a0ca3781885be3c57bf8bdd Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Thu, 6 May 2021 23:29:17 +0200 Subject: [PATCH 043/528] Added electric and magnetic dipole excitation This adds an electric and magnetic dipole excitation. --- src/BEAST.jl | 2 + src/maxwell/mwexc.jl | 167 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) diff --git a/src/BEAST.jl b/src/BEAST.jl index 5d861afb..97961b1c 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -55,6 +55,8 @@ export MWFarField3D export MWSingleLayer3D, MWHyperSingular, MWWeaklySingular export MWDoubleLayer3D export PlaneWaveMW +export electricdipole, ElectricDipole +export magneticdipole, MagneticDipole export TangTraceMW, CrossTraceMW export curl export MWSingleLayerField3D diff --git a/src/maxwell/mwexc.jl b/src/maxwell/mwexc.jl index cb22aa47..6f58119a 100644 --- a/src/maxwell/mwexc.jl +++ b/src/maxwell/mwexc.jl @@ -43,6 +43,172 @@ end *(a::Number, e::PlaneWaveMW) = PlaneWaveMW(e.direction, e.polarisation, e.wavenumber, a*e.amplitude) +abstract type Dipole end + +mutable struct ElectricDipole{T,P} <: Dipole + location::P + orientation::P + wavenumber::T + ε::T + μ::T +end + +function ElectricDipole(l,o,k,ε,μ) + T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) + P = similar_type(typeof(l), T) + ElectricDipole{T,P}(l,o,k,ε,μ) +end + +mutable struct curlElectricDipole{T,P} <: Dipole + location::P + orientation::P + wavenumber::T + ε::T + μ::T +end + +function curlElectricDipole(l,o,k,ε,μ) + T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) + P = similar_type(typeof(l), T) + curlElectricDipole{T,P}(l,o,k,ε,μ) +end + +mutable struct MagneticDipole{T,P} <: Dipole + location::P + orientation::P + wavenumber::T + ε::T + μ::T +end + +function MagneticDipole(l,o,k,ε,μ) + T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) + P = similar_type(typeof(l), T) + MagneticDipole{T,P}(l,o,k,ε,μ) +end + +mutable struct curlMagneticDipole{T,P} <: Dipole + location::P + orientation::P + wavenumber::T + ε::T + μ::T +end + +function curlMagneticDipole(l,o,k,ε,μ) + T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) + P = similar_type(typeof(l), T) + curlMagneticDipole{T,P}(l,o,k,ε,μ) +end + +""" + electricdipole(;location, orientation, wavenumber, permittivity, permeability) + +Create an electric dipole solution to Maxwell's equations representing the electric +field part. Implementation is based on (9.18) of Jackson's “Classical electrodynamics”, +with the notable difference that the ``\exp(ikr)`` is used. +""" +electricdipole(; + location = error("missing arguement `location`"), + orientation = error("missing arguement `orientation`"), + wavenumber = error("missing arguement `wavenumber`"), + permittivity = error("missing arguement `permittivity`"), + permeability = error("missing arguement `permeability`")) = + ElectricDipole(location, orientation, wavenumber, permittivity, permeability) + +function (hd::ElectricDipole)(x; isfarfield=false) + k = hd.wavenumber + x_0 = hd.location + p = hd.orientation + r = norm(x-x_0) + n = (x - x_0)/r + η = sqrt(hd.μ/hd.ε) + if isfarfield + # postfactor (4*π*im)/k to be consistent with BEAST far field computation + # and, of course, omitted exp(-im*k*r)/r factor in (9.19) + return η*k^2/(4*π*sqrt(hd.ε*hd.μ))*cross(cross(n,p),n)*(4*π*im)/k + else + return (1/(4*π*hd.ε))*exp(-im*k*r)*(k^2/r*cross(cross(n,p),n) + (1/r^3 + im*k/r^2)*(3*n*dot(n,p) - p)) + end +end + +function (hd::curlElectricDipole)(x; isfarfield=false) + k = hd.wavenumber + x_0 = hd.location + p = hd.orientation + r = norm(x-x_0) + n = (x - x_0)/r + c = 1/sqrt(hd.ε*hd.μ) + if isfarfield + # prefactor (-im*hd.μ*c*k), because this is the curl + # postfactor (4*π*im)/k to be consistent with BEAST far field computation + return (-im*hd.μ*c*k)*k^2/(4*π*sqrt(hd.ε*hd.μ))*cross(n,p)*(4*π*im)/k + else + #return (k^2/(4*π*sqrt(hd.ε*hd.μ)))*cross(n,p)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) + return -im*hd.μ*c^2*(k^3)/(4*π)*cross(n,p)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) + end +end + +function curl(ehd::ElectricDipole) + return curlElectricDipole(ehd.location,ehd.orientation,ehd.wavenumber,ehd.ε,ehd.μ) +end + +""" + magneticdipole(;location, orientation, wavenumber, permittivity, permeability) + +Create a magnetic dipole solution to Maxwell's equations representing the electric +field part. Implementation is based on (9.36) of Jackson's “Classical electrodynamics”, +with the notable difference that the ``\exp(ikr)`` is used. +""" +magneticdipole(; + location = error("missing arguement `location`"), + orientation = error("missing arguement `orientation`"), + wavenumber = error("missing arguement `wavenumber`"), + permittivity = error("missing arguement `permittivity`"), + permeability = error("missing arguement `permeability`")) = + MagneticDipole(location, orientation, wavenumber, permittivity, permeability) + +function (hd::MagneticDipole)(x; isfarfield=false) + k = hd.wavenumber + x_0 = hd.location + m = hd.orientation + r = norm(x-x_0) + n = (x - x_0)/r + η = sqrt(hd.μ/hd.ε) + if isfarfield + # Jackson (9.36) without exp(-im*k*r)/r, r → ∞ and with (im*4*π)/k to match BEAST's farfield + return -η/(4*π)*k^2*cross(n,m)*(im*4*π)/k + else + # Jackson (9.36) + return -η/(4*π)*k^2*cross(n,m)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) + end +end + +function (hd::curlMagneticDipole)(x; isfarfield=false) + k = hd.wavenumber + x_0 = hd.location + m = hd.orientation + r = norm(x-x_0) + n = (x - x_0)/r + c = 1/sqrt(hd.ε*hd.μ) + if isfarfield + # Jackson (9.35) without exp(-im*k*r)/r, r → ∞ and with (im*4*π)/k to match BEAST's farfield + return -im*hd.μ*c*k/(4π)*(k^2*cross(cross(n,m),n))*(im*4*π)/k + else + # Jackson (9.35) + return -im*hd.μ*c*k/(4π)*exp(-im*k*r)*(k^2*cross(cross(n,m),n)/r + (3*n*dot(n,m)-m)*(1/r^3 + im*k/r^2)) + end +end + +function curl(ehd::MagneticDipole) + return curlMagneticDipole(ehd.location,ehd.orientation,ehd.wavenumber,ehd.ε,ehd.μ) +end + +*(a::Number, e::ElectricDipole) = ElectricDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) +*(a::Number, e::curlElectricDipole) = curlElectricDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) +*(a::Number, e::MagneticDipole) = MagneticDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) +*(a::Number, e::curlMagneticDipole) = curlMagneticDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) + mutable struct CrossTraceMW{F} <: Functional field::F end @@ -53,6 +219,7 @@ end cross(::NormalVector, p::Function) = CrossTraceMW(p) cross(::NormalVector, p::PlaneWaveMW) = CrossTraceMW(p) +cross(::NormalVector, p::Dipole) = CrossTraceMW(p) cross(t::CrossTraceMW, ::NormalVector) = TangTraceMW(t.field) function (ϕ::CrossTraceMW)(p) From 5f52fe662d9b39e322dae6dc106c7b47f5b1684b Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Fri, 7 May 2021 20:37:30 +0200 Subject: [PATCH 044/528] Test for dipole excitation The test is an example of using an dipole as a manufactured solution, i.e., we have an analytical solution for arbitrary geometries. --- test/runtests.jl | 2 + test/test_dipole.jl | 106 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 test/test_dipole.jl diff --git a/test/runtests.jl b/test/runtests.jl index 6bb6afe2..7bab9663 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -46,6 +46,8 @@ include("test_laminated.jl") include("test_assemblerow.jl") include("test_local_assembly.jl") +include("test_dipole.jl") + include("test_wiltonints.jl") include("test_sauterschwabints.jl") include("test_ss_nested_meshes.jl") diff --git a/test/test_dipole.jl b/test/test_dipole.jl new file mode 100644 index 00000000..b58f6dd3 --- /dev/null +++ b/test/test_dipole.jl @@ -0,0 +1,106 @@ +using Test + +using CompScienceMeshes +using BEAST +using StaticArrays +using LinearAlgebra + +c = 3e8 +μ = 4*π*1e-7 +ε = 1/(μ*c^2) +f = 1e8 +λ = c/f +k = 2*π/λ +ω = k*c +η = sqrt(μ/ε) + +a = 1 +Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.3) +Γ = translate(Γ_orig,SVector(-a/2,-a/2,-a/2)) + +Φ, Θ = [0.0], range(0,stop=π,length=100) +pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] + +E = electricdipole(location=SVector(0,0,0), + orientation=1e-9.*SVector(0.5,0.5,0), + wavenumber=k, + permittivity=ε, + permeability=μ) + +n = BEAST.NormalVector() + +𝒆 = (n × E) × n +H = (-1/(im*μ*ω))*curl(E) +𝒉 = (n × H) × n + +𝓣 = Maxwell3D.singlelayer(wavenumber=k) +𝓝 = BEAST.NCross() +𝓚 = Maxwell3D.doublelayer(wavenumber=k) + +X = raviartthomas(Γ) +Y = buffachristiansen(Γ) + +T = Matrix(assemble(𝓣,X,X)) +e = Vector(assemble(𝒆,X)) +j_EFIE = T\e + +nf_E_EFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_EFIE, X) +nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_EFIE, X) ./ η +ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X) + +@test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 +@test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 +@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.001 + +K_bc = Matrix(assemble(𝓚,Y,X)) +G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) +h_bc = η*Vector(assemble(𝒉,Y)) +M_bc = -0.5*G_nxbc_rt + K_bc +j_BCMFIE = M_bc\h_bc + +nf_E_BCMFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) +nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) ./ η +ff_E_BCMFIE = potential(MWFarField3D(wavenumber=k), pts, j_BCMFIE, X) + +@test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 +@test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 +@test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 + +E = magneticdipole(location=SVector(0,0,0), + orientation=1e-9.*SVector(0.5,0.5,0), + wavenumber=k, + permittivity=ε, + permeability=μ) + +𝒆 = (n × E) × n +H = (-1/(im*μ*ω))*curl(E) +𝒉 = (n × H) × n + +X = raviartthomas(Γ) +Y = buffachristiansen(Γ) + +T = Matrix(assemble(𝓣,X,X)) +e = Vector(assemble(𝒆,X)) +j_EFIE = T\e + +nf_E_EFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_EFIE, X) +nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_EFIE, X) ./ η +ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X) + +@test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 +@test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 +@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 + +K_bc = Matrix(assemble(𝓚,Y,X)) +G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) +h_bc = η*Vector(assemble(𝒉,Y)) +M_bc = -0.5*G_nxbc_rt + K_bc +j_BCMFIE = M_bc\h_bc + +nf_E_BCMFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) +nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) ./ η +ff_E_BCMFIE = potential(MWFarField3D(wavenumber=k), pts, j_BCMFIE, X) + +@test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 +@test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 +@test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 \ No newline at end of file From 53f5c0d7bf3d39ef710e9f0e6f92a5ab409e7036 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Mon, 31 May 2021 17:23:12 +0200 Subject: [PATCH 045/528] Removed physical units from dipole definition Instead of distinguishing between an electric and a magnetic dipole there exists now a single data type DipoleMW (and curlDipoleMW) This reduces the code duplication and resembles more closely the PlaneWaveMW type The test "test_dipole.jl" shows how to obtain an electric and a magnetic dipole corresponding to the formulas in Jackson's Classical Electrodynamics --- src/BEAST.jl | 3 +- src/maxwell/mwexc.jl | 151 +++++++++---------------------------------- test/test_dipole.jl | 29 ++++----- 3 files changed, 46 insertions(+), 137 deletions(-) diff --git a/src/BEAST.jl b/src/BEAST.jl index 97961b1c..2f91e7f0 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -55,8 +55,7 @@ export MWFarField3D export MWSingleLayer3D, MWHyperSingular, MWWeaklySingular export MWDoubleLayer3D export PlaneWaveMW -export electricdipole, ElectricDipole -export magneticdipole, MagneticDipole +export dipolemw3d, DipoleMW export TangTraceMW, CrossTraceMW export curl export MWSingleLayerField3D diff --git a/src/maxwell/mwexc.jl b/src/maxwell/mwexc.jl index 6f58119a..e6e7bda9 100644 --- a/src/maxwell/mwexc.jl +++ b/src/maxwell/mwexc.jl @@ -45,169 +45,80 @@ end abstract type Dipole end -mutable struct ElectricDipole{T,P} <: Dipole +mutable struct DipoleMW{T,P} <: Dipole location::P orientation::P wavenumber::T - ε::T - μ::T end -function ElectricDipole(l,o,k,ε,μ) - T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) +function DipoleMW(l,o,k) + T = promote_type(eltype(l), eltype(o), typeof(k)) P = similar_type(typeof(l), T) - ElectricDipole{T,P}(l,o,k,ε,μ) + DipoleMW{T,P}(l,o,k) end -mutable struct curlElectricDipole{T,P} <: Dipole +mutable struct curlDipoleMW{T,P} <: Dipole location::P orientation::P wavenumber::T - ε::T - μ::T end -function curlElectricDipole(l,o,k,ε,μ) - T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) +function curlDipoleMW(l,o,k) + T = promote_type(eltype(l), eltype(o), typeof(k)) P = similar_type(typeof(l), T) - curlElectricDipole{T,P}(l,o,k,ε,μ) -end - -mutable struct MagneticDipole{T,P} <: Dipole - location::P - orientation::P - wavenumber::T - ε::T - μ::T -end - -function MagneticDipole(l,o,k,ε,μ) - T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) - P = similar_type(typeof(l), T) - MagneticDipole{T,P}(l,o,k,ε,μ) -end - -mutable struct curlMagneticDipole{T,P} <: Dipole - location::P - orientation::P - wavenumber::T - ε::T - μ::T -end - -function curlMagneticDipole(l,o,k,ε,μ) - T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) - P = similar_type(typeof(l), T) - curlMagneticDipole{T,P}(l,o,k,ε,μ) + curlDipoleMW{T,P}(l,o,k) end """ - electricdipole(;location, orientation, wavenumber, permittivity, permeability) + dipolemw3d(;location, orientation, wavenumber) Create an electric dipole solution to Maxwell's equations representing the electric field part. Implementation is based on (9.18) of Jackson's “Classical electrodynamics”, with the notable difference that the ``\exp(ikr)`` is used. """ -electricdipole(; +dipolemw3d(; location = error("missing arguement `location`"), orientation = error("missing arguement `orientation`"), wavenumber = error("missing arguement `wavenumber`"), - permittivity = error("missing arguement `permittivity`"), - permeability = error("missing arguement `permeability`")) = - ElectricDipole(location, orientation, wavenumber, permittivity, permeability) - -function (hd::ElectricDipole)(x; isfarfield=false) - k = hd.wavenumber - x_0 = hd.location - p = hd.orientation + ) = DipoleMW(location, orientation, wavenumber) + +function (d::DipoleMW)(x; isfarfield=false) + k = d.wavenumber + x_0 = d.location + p = d.orientation r = norm(x-x_0) - n = (x - x_0)/r - η = sqrt(hd.μ/hd.ε) + n = (x - x_0)/r if isfarfield # postfactor (4*π*im)/k to be consistent with BEAST far field computation # and, of course, omitted exp(-im*k*r)/r factor in (9.19) - return η*k^2/(4*π*sqrt(hd.ε*hd.μ))*cross(cross(n,p),n)*(4*π*im)/k + # of Jackson's Classical Electrodynamics + return k^2/(4*π)*cross(cross(n,p),n)*(4*π*im)/k else - return (1/(4*π*hd.ε))*exp(-im*k*r)*(k^2/r*cross(cross(n,p),n) + (1/r^3 + im*k/r^2)*(3*n*dot(n,p) - p)) + return 1/(4*π)*exp(-im*k*r)*(k^2/r*cross(cross(n,p),n) + + (1/r^3 + im*k/r^2)*(3*n*dot(n,p) - p)) end end -function (hd::curlElectricDipole)(x; isfarfield=false) - k = hd.wavenumber - x_0 = hd.location - p = hd.orientation +function (d::curlDipoleMW)(x; isfarfield=false) + k = d.wavenumber + x_0 = d.location + p = d.orientation r = norm(x-x_0) n = (x - x_0)/r - c = 1/sqrt(hd.ε*hd.μ) if isfarfield - # prefactor (-im*hd.μ*c*k), because this is the curl # postfactor (4*π*im)/k to be consistent with BEAST far field computation - return (-im*hd.μ*c*k)*k^2/(4*π*sqrt(hd.ε*hd.μ))*cross(n,p)*(4*π*im)/k - else - #return (k^2/(4*π*sqrt(hd.ε*hd.μ)))*cross(n,p)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) - return -im*hd.μ*c^2*(k^3)/(4*π)*cross(n,p)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) - end -end - -function curl(ehd::ElectricDipole) - return curlElectricDipole(ehd.location,ehd.orientation,ehd.wavenumber,ehd.ε,ehd.μ) -end - -""" - magneticdipole(;location, orientation, wavenumber, permittivity, permeability) - -Create a magnetic dipole solution to Maxwell's equations representing the electric -field part. Implementation is based on (9.36) of Jackson's “Classical electrodynamics”, -with the notable difference that the ``\exp(ikr)`` is used. -""" -magneticdipole(; - location = error("missing arguement `location`"), - orientation = error("missing arguement `orientation`"), - wavenumber = error("missing arguement `wavenumber`"), - permittivity = error("missing arguement `permittivity`"), - permeability = error("missing arguement `permeability`")) = - MagneticDipole(location, orientation, wavenumber, permittivity, permeability) - -function (hd::MagneticDipole)(x; isfarfield=false) - k = hd.wavenumber - x_0 = hd.location - m = hd.orientation - r = norm(x-x_0) - n = (x - x_0)/r - η = sqrt(hd.μ/hd.ε) - if isfarfield - # Jackson (9.36) without exp(-im*k*r)/r, r → ∞ and with (im*4*π)/k to match BEAST's farfield - return -η/(4*π)*k^2*cross(n,m)*(im*4*π)/k - else - # Jackson (9.36) - return -η/(4*π)*k^2*cross(n,m)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) - end -end - -function (hd::curlMagneticDipole)(x; isfarfield=false) - k = hd.wavenumber - x_0 = hd.location - m = hd.orientation - r = norm(x-x_0) - n = (x - x_0)/r - c = 1/sqrt(hd.ε*hd.μ) - if isfarfield - # Jackson (9.35) without exp(-im*k*r)/r, r → ∞ and with (im*4*π)/k to match BEAST's farfield - return -im*hd.μ*c*k/(4π)*(k^2*cross(cross(n,m),n))*(im*4*π)/k + return (-im*k)*k^2/(4*π)*cross(n,p)*(4*π*im)/k else - # Jackson (9.35) - return -im*hd.μ*c*k/(4π)*exp(-im*k*r)*(k^2*cross(cross(n,m),n)/r + (3*n*dot(n,m)-m)*(1/r^3 + im*k/r^2)) + return -im*(k^3)/(4*π)*cross(n,p)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) end end -function curl(ehd::MagneticDipole) - return curlMagneticDipole(ehd.location,ehd.orientation,ehd.wavenumber,ehd.ε,ehd.μ) +function curl(d::DipoleMW) + return curlDipoleMW(d.location, d.orientation, d.wavenumber) end -*(a::Number, e::ElectricDipole) = ElectricDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) -*(a::Number, e::curlElectricDipole) = curlElectricDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) -*(a::Number, e::MagneticDipole) = MagneticDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) -*(a::Number, e::curlMagneticDipole) = curlMagneticDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) +*(a::Number, d::DipoleMW) = DipoleMW(d.location, a .* d.orientation, d.wavenumber) +*(a::Number, d::curlDipoleMW) = curlDipoleMW(d.location, a .* d.orientation, d.wavenumber) mutable struct CrossTraceMW{F} <: Functional field::F diff --git a/test/test_dipole.jl b/test/test_dipole.jl index b58f6dd3..4e2641a1 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -8,24 +8,25 @@ using LinearAlgebra c = 3e8 μ = 4*π*1e-7 ε = 1/(μ*c^2) -f = 1e8 +f = 5e7 λ = c/f k = 2*π/λ ω = k*c η = sqrt(μ/ε) a = 1 -Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.3) +Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.2) Γ = translate(Γ_orig,SVector(-a/2,-a/2,-a/2)) Φ, Θ = [0.0], range(0,stop=π,length=100) pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] -E = electricdipole(location=SVector(0,0,0), - orientation=1e-9.*SVector(0.5,0.5,0), - wavenumber=k, - permittivity=ε, - permeability=μ) +# This is an electric dipole +# The pre-factor (1/ε) is used to resemble +# (9.18) in Jackson's Classical Electrodynamics +E = (1/ε) * dipolemw3d(location=SVector(0,0,0), + orientation=1e-9.*SVector(0.5,0.5,0), + wavenumber=k) n = BEAST.NormalVector() @@ -50,7 +51,7 @@ ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X) @test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 @test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 -@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.001 +@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 K_bc = Matrix(assemble(𝓚,Y,X)) G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) @@ -66,15 +67,13 @@ ff_E_BCMFIE = potential(MWFarField3D(wavenumber=k), pts, j_BCMFIE, X) @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 -E = magneticdipole(location=SVector(0,0,0), - orientation=1e-9.*SVector(0.5,0.5,0), - wavenumber=k, - permittivity=ε, - permeability=μ) +H = dipolemw3d(location=SVector(0,0,0), + orientation=1e-9.*SVector(0.5,0.5,0), + wavenumber=k) -𝒆 = (n × E) × n -H = (-1/(im*μ*ω))*curl(E) 𝒉 = (n × H) × n +E = (1/(im*ε*ω))*curl(H) +𝒆 = (n × E) × n X = raviartthomas(Γ) Y = buffachristiansen(Γ) From 319bef5bfa80f123a616b090e817abd6cac4efc9 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 2 Jun 2021 10:21:45 +0200 Subject: [PATCH 046/528] GMRESSolver isa LinearMap --- Project.toml | 2 ++ src/BEAST.jl | 2 +- src/solvers/itsolver.jl | 19 ++++++++++++++----- src/timedomain/convop.jl | 2 +- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index cde04326..b36f3353 100644 --- a/Project.toml +++ b/Project.toml @@ -14,6 +14,7 @@ FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" @@ -32,6 +33,7 @@ FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" FillArrays = "0.11" IterativeSolvers = "^0.9" +LinearMaps = "^3.3" SauterSchwabQuadrature = "^2.1.1, 2.1" SparseMatrixDicts = "0.2" SpecialFunctions = "^0.7, ^0.8, ^0.9, ^0.10, ^1" diff --git a/src/BEAST.jl b/src/BEAST.jl index 5d861afb..d4700d57 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -2,7 +2,6 @@ module BEAST using Distributed using LinearAlgebra -# using Pkg using SharedArrays using SparseArrays using FillArrays @@ -11,6 +10,7 @@ using SparseMatrixDicts using SauterSchwabQuadrature using FastGaussQuadrature +using LinearMaps import LinearAlgebra: cross, dot import LinearAlgebra: ×, ⋅ diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index 77f2ecbb..2d98c303 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -3,11 +3,11 @@ import IterativeSolvers -struct GMRESSolver{L,R} +struct GMRESSolver{L,T} <: LinearMap{T} linear_operator::L maxiter::Int restart::Int - tol::R + tol::T end @@ -34,11 +34,20 @@ function solve(solver::GMRESSolver, b) end -function Base.:*(solver::GMRESSolver, b) - x, ch = solve(solver, b) +# function Base.:*(solver::GMRESSolver, b) +# x, ch = solve(solver, b) +# println("Number of iterations: ", ch.iters) +# ch.isconverged || error("Iterative solver did not converge.") +# return x +# end + +Base.size(solver::GMRESSolver) = reverse(size(solver.linear_operator)) + +function LinearAlgebra.mul!(y::AbstractVecOrMat, solver::GMRESSolver, x::AbstractVector) + temp, ch = solve(solver, x) println("Number of iterations: ", ch.iters) ch.isconverged || error("Iterative solver did not converge.") - return x + y .= temp end diff --git a/src/timedomain/convop.jl b/src/timedomain/convop.jl index 25ae7740..57a613e0 100644 --- a/src/timedomain/convop.jl +++ b/src/timedomain/convop.jl @@ -6,7 +6,7 @@ struct ConvOp{T} <: AbstractArray{T,3} length::Int end -function Base.size(obj) +function Base.size(obj::ConvOp) return (size(obj.data)[2:3]...,obj.length) end From 3e3155d0518dc7ab62d0e55516aca971096d495a Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 11 Jun 2021 15:39:02 +0200 Subject: [PATCH 047/528] facecurrents fixed for scalar values FEM spaces --- examples/helmholtz3d_neumann.jl | 10 ++++++++-- src/postproc.jl | 6 ++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/helmholtz3d_neumann.jl b/examples/helmholtz3d_neumann.jl index 9d14db8a..fe6fc58f 100644 --- a/examples/helmholtz3d_neumann.jl +++ b/examples/helmholtz3d_neumann.jl @@ -1,6 +1,9 @@ using CompScienceMeshes, BEAST +using LinearAlgebra, Pkg -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Pkg.activate(@__DIR__) +# Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) X = lagrangec0d1(Γ) @show numfunctions(X) @@ -26,7 +29,10 @@ fcr1, geo1 = facecurrents(x1, X) fcr2, geo2 = facecurrents(x2, X) using Plots -using LinearAlgebra plot(title="Comparse 1st and 2nd kind eqs.") plot!(norm.(fcr1),c=:blue,label="1st") scatter!(norm.(fcr2),c=:red,label="2nd") + +import Plotly +Plotly.plot(patch(Γ, norm.(fcr1))) +Plotly.plot(patch(Γ, norm.(fcr2))) \ No newline at end of file diff --git a/src/postproc.jl b/src/postproc.jl index 3e09edc5..0ee1907a 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -20,8 +20,10 @@ function facecurrents(coeffs, basis) # U = D+1 U = 3 - # TODO: express relative to input types - PT = SVector{U, T} + # TODO: remove ugliness + vals = refs(center(first(cells))) + PT = typeof(first(coeffs)*vals[1][1]) + fcr = zeros(PT, numcells(mesh)) for (t,cell) in enumerate(cells) From 7c59a2480bdd106a0ae7c1bc73b4bc724ab8591a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 20 Jun 2021 00:22:55 +0000 Subject: [PATCH 048/528] CompatHelper: bump compat for "BlockArrays" to "0.16" --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index b36f3353..555b3aa6 100644 --- a/Project.toml +++ b/Project.toml @@ -24,7 +24,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] -BlockArrays = "^0.10, ^0.11, ^0.12, ^0.13, ^0.14, 0.15" +BlockArrays = "^0.10, ^0.11, ^0.12, ^0.13, ^0.14, 0.15, 0.16" CollisionDetection = "^0.1" Combinatorics = "^0.7, ^1" CompScienceMeshes = "^0.2.8" From c357a1424d71970073c6cf61874d999bc4ffe40f Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 7 Jul 2021 17:05:03 +0200 Subject: [PATCH 049/528] iterative solvers log convergence --- examples/mthelmholtz.jl | 26 -------------------- examples/mtjunction.jl | 54 ++++++++++++++++++++++++++++++++++++++--- src/solvers/itsolver.jl | 2 +- 3 files changed, 51 insertions(+), 31 deletions(-) diff --git a/examples/mthelmholtz.jl b/examples/mthelmholtz.jl index 17e2b26b..cbd30b06 100644 --- a/examples/mthelmholtz.jl +++ b/examples/mthelmholtz.jl @@ -73,30 +73,4 @@ using Plots plot() scatter!(w1) scatter!(w2) -# u = gmres(mtefie) -# -# offset = 1; u12 = u[offset:offset+numfunctions(X12)-1] -# offset += numfunctions(X12); u23 = u[offset:offset+numfunctions(X23)-1] -# offset += numfunctions(X23); u31 = u[offset:offset+numfunctions(X31)-1] -# -# fcr12, _ = facecurrents(u12, X12) -# fcr23, _ = facecurrents(u23, X23) -# fcr31, _ = facecurrents(u31, X31) - -# import PlotlyJS -using LinearAlgebra -# p1 = PlotlyJS.Plot(patch(G12, norm.(fcr12))) -# p2 = PlotlyJS.Plot(patch(G23, norm.(fcr23))) -# p3 = PlotlyJS.Plot(patch(G31, norm.(fcr31))) - -# PlotlyJS.plot([p1, p2, p3]) - - - - -Sxx = BEAST.sysmatrix(mtefie) - -Syy = assemble(SL, Y, Y) -iNxy = inv(Nxy) -# gmres() diff --git a/examples/mtjunction.jl b/examples/mtjunction.jl index ea35748a..1b207ee2 100644 --- a/examples/mtjunction.jl +++ b/examples/mtjunction.jl @@ -43,7 +43,7 @@ mtefie = @discretise( # fcr23, _ = facecurrents(u23, X23) # fcr31, _ = facecurrents(u31, X31) -import PlotlyJS +import Plotly using LinearAlgebra # p1 = PlotlyJS.Plot(patch(G12, norm.(fcr12))) # p2 = PlotlyJS.Plot(patch(G23, norm.(fcr23))) @@ -76,10 +76,11 @@ Nxy = blkdiagm(N1,N2,N3) Syy = assemble(SL, Y, Y) iNxy = inv(Nxy) -cond(Sxx) +# cond(Matrix(Sxx)) ex = assemble(e,X) -Q = transpose(iNxy) * Syy * iNxy * Sxx; -R = transpose(iNxy) * Syy * iNxy * ex; +P = transpose(iNxy) * Syy * iNxy +Q = P * Sxx; +R = P * ex; u1, ch1 = solve(BEAST.GMRESSolver(Sxx),ex) @@ -88,3 +89,48 @@ u2, ch2 = solve(BEAST.GMRESSolver(Q),R) @show ch1.iters @show ch2.iters # gmres() + +ns = [ + 0, + numfunctions(X12), + numfunctions(X23), + numfunctions(X31)] + +cns = cumsum(ns) +u12 = u1[cns[1]+1:cns[2]] +u23 = u1[cns[2]+1:cns[3]] +u31 = u1[cns[3]+1:cns[4]] + +fcr1, geo1 = facecurrents(u12, X12) +fcr2, geo2 = facecurrents(u23, X23) +fcr3, geo3 = facecurrents(u31, X31) + +p1 = patch(geo1, norm.(fcr1)) +p2 = patch(geo2, norm.(fcr2)) +p3 = patch(geo3, norm.(fcr3)) + +Plotly.plot([p1,p2,p3]) + +G123 = weld(G1,G2,G3) +X123 = raviartthomas(G123) + +stefie = @discretise( + SL[k,j] == e[k], + j ∈ X123, k ∈ X123) + +Sst = assemble(SL, X123, X123) +bst = assemble(e, X123) +ust, chst = solve(BEAST.GMRESSolver(Sst),bst) + +fcrst, geost = facecurrents(ust, X123) +Plotly.plot(patch(geost, norm.(fcrst))) + + +Φ, Θ = [0.0], range(0,stop=π,length=100) +pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] + +ffd_mt = potential(MWFarField3D(wavenumber=κ), pts, u1, X) +ffd_st = potential(MWFarField3D(wavenumber=κ), pts, ust, X123) + +plot(norm.(ffd_mt)) +scatter!(norm.(ffd_st)) \ No newline at end of file diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index 2d98c303..c96ec31b 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -29,7 +29,7 @@ operator(solver::GMRESSolver) = solver.linear_operator function solve(solver::GMRESSolver, b) op = operator(solver) x, ch = IterativeSolvers.gmres(op, b, log=true, maxiter=solver.maxiter, - restart=solver.restart, reltol=solver.tol) + restart=solver.restart, reltol=solver.tol, verbose=true) return x, ch end From 05f823c772a6c4bb4970f712ad859ec45130300b Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 8 Jul 2021 14:25:56 +0200 Subject: [PATCH 050/528] utils to lift LinearMaps added --- src/BEAST.jl | 1 + src/utils/liftmap.jl | 17 +++++++++++++++++ src/utils/variational.jl | 3 +++ 3 files changed, 21 insertions(+) create mode 100644 src/utils/liftmap.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 04f03f31..ef11c050 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -115,6 +115,7 @@ include("utils/combinatorics.jl") include("utils/linearspace.jl") include("utils/matrixconv.jl") include("utils/polyeig.jl") +include("utils/liftmap.jl") include("bases/basis.jl") include("bases/lincomb.jl") diff --git a/src/utils/liftmap.jl b/src/utils/liftmap.jl new file mode 100644 index 00000000..cf196918 --- /dev/null +++ b/src/utils/liftmap.jl @@ -0,0 +1,17 @@ +# Utils to lift LinearMaps to LinearMaps acting on larger vector spaces +import LinearMaps +import LinearAlgebra + +struct LiftedMap{T,TI,TJ} <: LinearMap{T} + A::LinearMap{T} + I::TI + J::TJ +end + +function LinearAlgebra.mul!(y::AbstractVector, L::LiftedMap, x::AbstractVector, α::Number, β::Number) + + yI = view(y, L.I) + xJ = view(x, L.J) + AIJ = L.A + LinearAlgebra.mul!(yI, AIJ, xJ, α, β) +end \ No newline at end of file diff --git a/src/utils/variational.jl b/src/utils/variational.jl index e10aebb7..6818067d 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -114,6 +114,9 @@ Base.getindex(u::AbstractBlockArray, p::HilbertVector) = u[Block(Int(p))] Base.setindex!(A::AbstractBlockArray, v, p::HilbertVector, q::HilbertVector) = setindex!(A, v, Block(Int(p),Int(q))) Base.setindex!(A::AbstractBlockArray, v, p::HilbertVector) = setindex!(A, v, Block(Int(p))) +Base.view(A::AbstractBlockArray, p::HilbertVector, q::HilbertVector) = view(A, Block(Int(p), Int(q))) +Base.view(A::AbstractBlockArray, p::HilbertVector) = view(A, Block(Int(p))) + mutable struct LinForm test_space terms From 5cc64b2f981f2bd811c1b9fc38460afc3dfc08e5 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Wed, 18 Aug 2021 00:22:46 +0000 Subject: [PATCH 051/528] CompatHelper: bump compat for FillArrays to 0.12, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 555b3aa6..f7f51650 100644 --- a/Project.toml +++ b/Project.toml @@ -31,7 +31,7 @@ CompScienceMeshes = "^0.2.8" Compat = "^2, ^3" FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" -FillArrays = "0.11" +FillArrays = "0.11, 0.12" IterativeSolvers = "^0.9" LinearMaps = "^3.3" SauterSchwabQuadrature = "^2.1.1, 2.1" From 12e670617ab0feefe2d725fb8d1ce945653ac8c0 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 24 Aug 2021 18:16:43 +0200 Subject: [PATCH 052/528] fixed ex_dual2forms --- Project.toml | 12 ++++++------ examples/ex_dual2forms.jl | 9 +++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Project.toml b/Project.toml index 9019bada..b1dd184e 100644 --- a/Project.toml +++ b/Project.toml @@ -22,17 +22,17 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] -BlockArrays = "^0.10, ^0.11, ^0.12" +BlockArrays = "^0.10, ^0.11, ^0.12, ^0.13, ^0.14, ^0.15, ^0.16" CollisionDetection = "^0.1" -Combinatorics = "^0.7" +Combinatorics = "^0.7, ^1" CompScienceMeshes = "^0.2.5" -Compat = "^2" +Compat = "^2, ^3" FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" -IterativeSolvers = "^0.7, ^0.8" +IterativeSolvers = "^0.7, ^0.8, ^0.9" SauterSchwabQuadrature = "^2.1" -SpecialFunctions = "^0.7, ^0.8" -StaticArrays = "^0.8.3, ^0.9, ^0.10, ^0.11, ^0.12" +SpecialFunctions = "^0.7, ^0.8, ^1" +StaticArrays = "^0.8.3, ^0.9, ^0.10, ^0.11, ^0.12, ^1" WiltonInts84 = "^0.2.0" julia = "^1" diff --git a/examples/ex_dual2forms.jl b/examples/ex_dual2forms.jl index 224c4963..e94825f8 100644 --- a/examples/ex_dual2forms.jl +++ b/examples/ex_dual2forms.jl @@ -108,9 +108,10 @@ end Nd_int = BEAST.nedelecc3d(support, int_edges) Q = assemble(Id, Nd_int, Nd_int) -Q2, store = BEAST.allocatestorage(Id, Nd_int, Nd_int, +freeze, store = BEAST.allocatestorage(Id, Nd_int, Nd_int, Val{:bandedstorage}, BEAST.LongDelays{:ignore}) BEAST.assemble_local_mixed!(Id, Nd_int, Nd_int, store) +Q2 = freeze() @assert Q ≈ Q2 @@ -156,7 +157,7 @@ for (i,face) in enumerate(port) end Q6 = assemble(Id, divergence(RT_int), divergence(RT_prt)) d = -Q6 * x0 -x1 = pinv(Q5) * d +x1 = pinv(Matrix(Q5)) * d # L0 = lagrangecxd0(support) # Q7 = assemble(Id, L0, divergence(RT_int)) @@ -212,8 +213,8 @@ end Dir = Mesh(vertices(Tetrs), CompScienceMeshes.celltype(int_faces)[]) # error() -tetrs, bnd, v2t, v2n = BEAST.dual2forms_init(Tetrs) -Y = BEAST.dual2forms_body(Tetrs, Edges[collect(1:10)], Dir, tetrs, bnd, v2t, v2n) +tetrs, bnd, dir, v2t, v2n = BEAST.dualforms_init(Tetrs, Dir) +Y = BEAST.dual2forms_body(Edges[collect(1:10)], tetrs, bnd, dir, v2t, v2n) # Y = BEAST.dual2forms(Tetrs, Edges, Dir) nothing From a8acddbaaa81d8db0870989248f74ec9866d899f Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Thu, 26 Aug 2021 13:36:58 +0200 Subject: [PATCH 053/528] Fix of the far field computation of Maxwell dipole The previous formula assumed that the dipole is located in the origin. - Now the phase-term is correctly included - Also the n x ( ) x n operation is performed stripping away the radial component. --- src/maxwell/mwexc.jl | 20 ++++++++++++-------- test/test_dipole.jl | 9 +++++---- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/maxwell/mwexc.jl b/src/maxwell/mwexc.jl index e6e7bda9..048d8b78 100644 --- a/src/maxwell/mwexc.jl +++ b/src/maxwell/mwexc.jl @@ -86,14 +86,16 @@ function (d::DipoleMW)(x; isfarfield=false) k = d.wavenumber x_0 = d.location p = d.orientation - r = norm(x-x_0) - n = (x - x_0)/r if isfarfield # postfactor (4*π*im)/k to be consistent with BEAST far field computation - # and, of course, omitted exp(-im*k*r)/r factor in (9.19) - # of Jackson's Classical Electrodynamics - return k^2/(4*π)*cross(cross(n,p),n)*(4*π*im)/k + # and, of course, adapted phase factor exp(im*k*dot(n,x_0)) with + # respect to (9.19) of Jackson's Classical Electrodynamics + r = norm(x) + n = x/r + return cross(cross(n,1/(4*π)*(k^2*cross(cross(n,p),n))*exp(im*k*dot(n,x_0))*(4*π*im)/k),n) else + r = norm(x-x_0) + n = (x - x_0)/r return 1/(4*π)*exp(-im*k*r)*(k^2/r*cross(cross(n,p),n) + (1/r^3 + im*k/r^2)*(3*n*dot(n,p) - p)) end @@ -103,12 +105,14 @@ function (d::curlDipoleMW)(x; isfarfield=false) k = d.wavenumber x_0 = d.location p = d.orientation - r = norm(x-x_0) - n = (x - x_0)/r if isfarfield # postfactor (4*π*im)/k to be consistent with BEAST far field computation - return (-im*k)*k^2/(4*π)*cross(n,p)*(4*π*im)/k + r = norm(x) + n = x/r + return (-im*k)*k^2/(4*π)*cross(n,p)*(4*π*im)/k*exp(im*k*dot(n,x_0)) else + r = norm(x-x_0) + n = (x - x_0)/r return -im*(k^3)/(4*π)*cross(n,p)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) end end diff --git a/test/test_dipole.jl b/test/test_dipole.jl index 4e2641a1..0742a0f7 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -15,7 +15,7 @@ k = 2*π/λ η = sqrt(μ/ε) a = 1 -Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.2) +Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.1) Γ = translate(Γ_orig,SVector(-a/2,-a/2,-a/2)) Φ, Θ = [0.0], range(0,stop=π,length=100) @@ -24,7 +24,7 @@ pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in # This is an electric dipole # The pre-factor (1/ε) is used to resemble # (9.18) in Jackson's Classical Electrodynamics -E = (1/ε) * dipolemw3d(location=SVector(0,0,0), +E = (1/ε) * dipolemw3d(location=SVector(0.4,0.2,0), orientation=1e-9.*SVector(0.5,0.5,0), wavenumber=k) @@ -51,7 +51,7 @@ ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X) @test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 @test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 -@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 +@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.001 K_bc = Matrix(assemble(𝓚,Y,X)) G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) @@ -67,7 +67,7 @@ ff_E_BCMFIE = potential(MWFarField3D(wavenumber=k), pts, j_BCMFIE, X) @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 -H = dipolemw3d(location=SVector(0,0,0), +H = dipolemw3d(location=SVector(0.0,0.0,0.3), orientation=1e-9.*SVector(0.5,0.5,0), wavenumber=k) @@ -100,6 +100,7 @@ nf_E_BCMFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) ./ η ff_E_BCMFIE = potential(MWFarField3D(wavenumber=k), pts, j_BCMFIE, X) +@test norm(j_BCMFIE - j_EFIE)/norm(j_EFIE) ≈ 0 atol=0.02 @test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 \ No newline at end of file From 04134ab0b81caa5b5543a1d34e7ca521e9457312 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 26 Aug 2021 16:01:01 +0200 Subject: [PATCH 054/528] migrate to GH actions for CI --- .github/workflows/CI.yml | 68 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/workflows/CI.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 00000000..20dde54e --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,68 @@ +name: CI +on: + pull_request: + branches: + - master + push: + branches: + - master + tags: '*' +jobs: + test: + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1.3' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'. + - '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia. + - 'nightly' + os: + - ubuntu-latest + arch: + - x64 + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: actions/cache@v1 + env: + cache-name: cache-artifacts + with: + path: ~/.julia/artifacts + key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }} + restore-keys: | + ${{ runner.os }}-test-${{ env.cache-name }}- + ${{ runner.os }}-test- + ${{ runner.os }}- + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v1 + with: + file: lcov.info + docs: + name: Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@v1 + with: + version: '1' + - run: | + julia --project=docs -e ' + using Pkg + Pkg.develop(PackageSpec(path=pwd())) + Pkg.instantiate()' + - run: | + julia --project=docs -e ' + using Documenter: doctest + using BEAST + doctest(BEAST)' # change MYPACKAGE to the name of your package + - run: julia --project=docs docs/make.jl + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} \ No newline at end of file From ea2b92ff744b0cc93a97708c3d1617af0ceec544 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 27 Aug 2021 14:36:29 +0200 Subject: [PATCH 055/528] narrow CSM compat to 0.3 onwards --- Project.toml | 2 +- docs/Project.toml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 docs/Project.toml diff --git a/Project.toml b/Project.toml index f7f51650..b592bdc4 100644 --- a/Project.toml +++ b/Project.toml @@ -27,7 +27,7 @@ WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" BlockArrays = "^0.10, ^0.11, ^0.12, ^0.13, ^0.14, 0.15, 0.16" CollisionDetection = "^0.1" Combinatorics = "^0.7, ^1" -CompScienceMeshes = "^0.2.8" +CompScienceMeshes = "^0.3" Compat = "^2, ^3" FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" diff --git a/docs/Project.toml b/docs/Project.toml new file mode 100644 index 00000000..6a233f0c --- /dev/null +++ b/docs/Project.toml @@ -0,0 +1,3 @@ +[deps] +BEAST = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" From fcff728c5bc1bcbec98230c3e77ca2193fcc3e92 Mon Sep 17 00:00:00 2001 From: krcools Date: Fri, 27 Aug 2021 14:50:26 +0200 Subject: [PATCH 056/528] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dcb060af..37ad8863 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Boundary Element Analysis and Simulation Toolkit -[![Build Status](https://travis-ci.org/krcools/BEAST.jl.svg?branch=master)](https://travis-ci.org/krcools/BEAST.jl) +[![Build status](https://github.com/krcools/BEAST.jl/workflows/CI/badge.svg)](https://github.com/krcools/BEAST.jl/actions) [![codecov.io](http://codecov.io/github/krcools/BEAST.jl/coverage.svg?branch=master)](http://codecov.io/github/krcools/BEAST.jl?branch=master) [![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://krcools.github.io/BEAST.jl/latest/) [![DOI](https://zenodo.org/badge/87720391.svg)](https://zenodo.org/badge/latestdoi/87720391) From 59318f42afd8f5a880e062c83f9e3fe9d87cd137 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 27 Aug 2021 15:15:15 +0200 Subject: [PATCH 057/528] removed travis config file --- .travis.yml | 13 ------------- TODO.md | 5 ----- 2 files changed, 18 deletions(-) delete mode 100644 .travis.yml delete mode 100644 TODO.md diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c3be022e..00000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -# Documentation: http://docs.travis-ci.com/user/languages/julia/ -language: julia -os: - - linux - - osx - - windows -julia: - - 1.3 - - 1.5 -notifications: - email: false -after_success: - - julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())' diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 686ee4fd..00000000 --- a/TODO.md +++ /dev/null @@ -1,5 +0,0 @@ -[x] Create wrapper type around Array{T,3} to make it behave like a convolution operator - -[] Properly implement the convolution of two piecewise polynomial time basis functions - -[] assemble and allocatestorage for tensor operators need to be more robust From 77920d020e470f398b374f769441d1c26ea3a090 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 3 Sep 2021 17:13:56 +0200 Subject: [PATCH 058/528] Added Zero linear map to use as stub --- src/solvers/solver.jl | 25 +++++++++++++++++++++++++ src/utils/zeromap.jl | 13 +++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/utils/zeromap.jl diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 248b7085..80361214 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -234,6 +234,31 @@ function assemble(bilform::BilForm, test_space_dict, trial_space_dict) end +# function assemble(bilform::BilForm, test_space_dict, trial_space_dict) + +# @assert !isempty(terms) +# Z = ZeroMap{Float32} +# for term in bilform.terms + +# x = test_space_dict[term.test_id] +# for op in reverse(term.test_ops) +# x = op[end](op[1:end-1]..., x) +# end + +# y = trial_space_dict[term.trial_id] +# for op in reverse(t.trial_ops) +# y = op[end](op[1:end-1]..., y) +# end + +# end + + + + + +# end + + function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict) diff --git a/src/utils/zeromap.jl b/src/utils/zeromap.jl new file mode 100644 index 00000000..e312cb74 --- /dev/null +++ b/src/utils/zeromap.jl @@ -0,0 +1,13 @@ +import LinearMaps + +struct ZeroMap{T} <: LinearMap{T} + num_rows::Int + num_cols::Int +end + +Base.size(A::ZeroMap) = (A.num_rows, A.num_cols,) + +function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector, α::Number, β::Number) + y .= β * y + # LinearAlgebra.mul!(yI, AIJ, xJ, α, β) +end \ No newline at end of file From 1824cf3a49d087d145c0934056c926e9a3a660e7 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 15 Sep 2021 11:05:22 +0200 Subject: [PATCH 059/528] depends on registered version of LiftedMaps --- Project.toml | 2 ++ examples/pmchwt.jl | 28 +++++++++++++++++----- src/BEAST.jl | 3 ++- src/solvers/itsolver.jl | 24 ++++++++++++++++--- src/solvers/solver.jl | 52 +++++++++++++++++++++++++++-------------- src/utils/liftmap.jl | 17 -------------- src/utils/zeromap.jl | 30 ++++++++++++++++++------ 7 files changed, 105 insertions(+), 51 deletions(-) delete mode 100644 src/utils/liftmap.jl diff --git a/Project.toml b/Project.toml index b592bdc4..515d35f8 100644 --- a/Project.toml +++ b/Project.toml @@ -13,6 +13,7 @@ FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" +LiftedMaps = "d22a30c1-52ac-4762-a8c9-5838452405e0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" @@ -33,6 +34,7 @@ FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" FillArrays = "0.11, 0.12" IterativeSolvers = "^0.9" +LiftedMaps = "0.2" LinearMaps = "^3.3" SauterSchwabQuadrature = "^2.1.1, 2.1" SparseMatrixDicts = "0.2" diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 177eb81b..fdf4b2d5 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -1,7 +1,8 @@ using CompScienceMeshes, BEAST using LinearAlgebra -Γ = meshcuboid(0.5, 1.0, 1.0, 0.075) +Γ = meshcuboid(0.5, 1.0, 1.0, 0.045) +# Γ = meshcuboid(0.5, 1.0, 1.0, 0.15) CompScienceMeshes.translate!(Γ, point(-0.25, -0.5, -0.5)) # Γ = meshsphere(1.0, 0.075) X = raviartthomas(Γ) @@ -29,7 +30,21 @@ pmchwt = @discretise( (K+K′)[l,j] + (α*T+α′*T′)[l,m] == -e[k] - h[l], j∈X, m∈X, k∈X, l∈X) -u = solve(pmchwt) +# Z = BEAST.sysmatrix(pmchwt) +# b = BEAST.rhs(pmchwt) +# W = BEAST.assemble_hide(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict) + +# using BEAST.BlockArrays +# using BEAST.IterativeSolvers + +# u = PseudoBlockVector{ComplexF64}(undef, numfunctions.([X,X])) +# fill!(u,0) + +# error() +# u, ch = IterativeSolvers.gmres!(u, Z, b, log=true, maxiter=1000, +# restart=1000, reltol=1e-5, verbose=true) + +u = BEAST.gmres(pmchwt, tol=1e-5) Θ, Φ = range(0.0,stop=2π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] @@ -80,9 +95,10 @@ E_tot = E_in + E_ex H_tot = H_in + H_ex contour(real.(getindex.(E_tot,1))) -heatmap(Z, Y, real.(getindex.(E_tot,1))) -plot(real.(getindex.(E_tot[:,51],1))) - contour(real.(getindex.(H_tot,2))) + +heatmap(Z, Y, real.(getindex.(E_tot,1))) heatmap(Z, Y, real.(getindex.(H_tot,2))) -plot(real.(getindex.(H_tot[:,51],2))) \ No newline at end of file + +plot(real.(getindex.(E_tot[:,51],1))) +plot!(real.(getindex.(H_tot[:,51],2))) \ No newline at end of file diff --git a/src/BEAST.jl b/src/BEAST.jl index ef11c050..238adac2 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -11,6 +11,7 @@ using SparseMatrixDicts using SauterSchwabQuadrature using FastGaussQuadrature using LinearMaps +using LiftedMaps import LinearAlgebra: cross, dot import LinearAlgebra: ×, ⋅ @@ -115,7 +116,7 @@ include("utils/combinatorics.jl") include("utils/linearspace.jl") include("utils/matrixconv.jl") include("utils/polyeig.jl") -include("utils/liftmap.jl") +include("utils/zeromap.jl") include("bases/basis.jl") include("bases/lincomb.jl") diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index c96ec31b..5436ff4f 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -34,6 +34,14 @@ function solve(solver::GMRESSolver, b) end +function solve!(x, solver::GMRESSolver, b) + op = operator(solver) + x, ch = IterativeSolvers.gmres!(x, op, b, log=true, maxiter=solver.maxiter, + restart=solver.restart, reltol=solver.tol, verbose=true) + return x, ch +end + + # function Base.:*(solver::GMRESSolver, b) # x, ch = solve(solver, b) # println("Number of iterations: ", ch.iters) @@ -44,10 +52,10 @@ end Base.size(solver::GMRESSolver) = reverse(size(solver.linear_operator)) function LinearAlgebra.mul!(y::AbstractVecOrMat, solver::GMRESSolver, x::AbstractVector) - temp, ch = solve(solver, x) + y, ch = solve!(y, solver, x) println("Number of iterations: ", ch.iters) ch.isconverged || error("Iterative solver did not converge.") - y .= temp + return y end @@ -62,10 +70,20 @@ function gmres(eq::DiscreteEquation; maxiter=0, restart=0, tol=0) b = assemble(rhs, test_space_dict) Z = assemble(lhs, test_space_dict, trial_space_dict) + block_sizes = zeros(Int, length(trial_space_dict)) + for (p,x) in eq.trial_space_dict + block_sizes[p] = numfunctions(x) + end + + T = promote_type(eltype(Z), eltype(b)) + x = PseudoBlockVector{T}(undef, block_sizes) + fill!(x, 0) + if tol == 0 invZ = GMRESSolver(Z, maxiter=maxiter, restart=restart) else invZ = GMRESSolver(Z, maxiter=maxiter, restart=restart, tol=tol) end - x = invZ * b + # x = invZ * b + mul!(x, invZ, b) end diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 80361214..47a835aa 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -106,7 +106,8 @@ macro discretise(eq, pairs...) end -sysmatrix(eq::DiscreteEquation) = assemble(eq.equation.lhs, eq.test_space_dict, eq.trial_space_dict) +sysmatrix(eq::DiscreteEquation, materialize=BEAST.assemble) = + assemble(eq.equation.lhs, eq.test_space_dict, eq.trial_space_dict, materialize=mt) rhs(eq::DiscreteEquation) = assemble(eq.equation.rhs, eq.test_space_dict) assemble(dbf::DiscreteBilform) = assemble(dbf.bilform, dbf.test_space_dict, dbf.trial_space_dict) @@ -188,7 +189,7 @@ function td_assemble(lform::LinForm, test_space_dict) return B end -function assemble(bilform::BilForm, test_space_dict, trial_space_dict) +function assemble_hide(bilform::BilForm, test_space_dict, trial_space_dict) lhterms = bilform.terms T = ComplexF64 # TDOD: Fix this @@ -227,6 +228,7 @@ function assemble(bilform::BilForm, test_space_dict, trial_space_dict) end z = assemble(a, x, y) + # @show m n norm(z) Z[Block(m,n)] += α * z end @@ -234,29 +236,45 @@ function assemble(bilform::BilForm, test_space_dict, trial_space_dict) end -# function assemble(bilform::BilForm, test_space_dict, trial_space_dict) +function assemble(bilform::BilForm, test_space_dict, trial_space_dict; + materialize=BEAST.assemble) -# @assert !isempty(terms) -# Z = ZeroMap{Float32} -# for term in bilform.terms - -# x = test_space_dict[term.test_id] -# for op in reverse(term.test_ops) -# x = op[end](op[1:end-1]..., x) -# end + @assert !isempty(bilform.terms) + + M = zeros(Int, length(test_space_dict)) + N = zeros(Int, length(trial_space_dict)) -# y = trial_space_dict[term.trial_id] -# for op in reverse(t.trial_ops) -# y = op[end](op[1:end-1]..., y) -# end + for (p,x) in test_space_dict M[p]=numfunctions(x) end + for (p,x) in trial_space_dict N[p]=numfunctions(x) end -# end + U = BlockArrays.blockedrange(M) + V = BlockArrays.blockedrange(N) + Z = ZeroMap{Float32}(U, V) + + for term in bilform.terms + + x = test_space_dict[term.test_id] + for op in reverse(term.test_ops) + x = op[end](op[1:end-1]..., x) + end + y = trial_space_dict[term.trial_id] + for op in reverse(term.trial_ops) + y = op[end](op[1:end-1]..., y) + end + z = materialize(term.kernel, x, y) + + I = Block(term.test_id) + J = Block(term.trial_id) + zlifted = LiftedMap(z,I,J,U,V) + Z = Z + term.coeff * zlifted + end -# end + return Z +end diff --git a/src/utils/liftmap.jl b/src/utils/liftmap.jl deleted file mode 100644 index cf196918..00000000 --- a/src/utils/liftmap.jl +++ /dev/null @@ -1,17 +0,0 @@ -# Utils to lift LinearMaps to LinearMaps acting on larger vector spaces -import LinearMaps -import LinearAlgebra - -struct LiftedMap{T,TI,TJ} <: LinearMap{T} - A::LinearMap{T} - I::TI - J::TJ -end - -function LinearAlgebra.mul!(y::AbstractVector, L::LiftedMap, x::AbstractVector, α::Number, β::Number) - - yI = view(y, L.I) - xJ = view(x, L.J) - AIJ = L.A - LinearAlgebra.mul!(yI, AIJ, xJ, α, β) -end \ No newline at end of file diff --git a/src/utils/zeromap.jl b/src/utils/zeromap.jl index e312cb74..769904b2 100644 --- a/src/utils/zeromap.jl +++ b/src/utils/zeromap.jl @@ -1,13 +1,29 @@ import LinearMaps -struct ZeroMap{T} <: LinearMap{T} - num_rows::Int - num_cols::Int +struct ZeroMap{T,U,V} <: LinearMap{T} + range::U + domain::V end -Base.size(A::ZeroMap) = (A.num_rows, A.num_cols,) +ZeroMap{T}(range::U, domain::V) where {T,U,V} = ZeroMap{T,U,V}(range, domain) +LinearMaps.MulStyle(A::ZeroMap) = LinearMaps.FiveArg() -function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector, α::Number, β::Number) - y .= β * y - # LinearAlgebra.mul!(yI, AIJ, xJ, α, β) +Base.size(A::ZeroMap) = (length(A.range), length(A.domain),) + +function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector, + α::Number, β::Number) + + y .*= β +end + +function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector) + y .= 0 +end + +function Base.:(*)(A::ZeroMap, x::AbstractVector) + # y = zero(A.image) + T = eltype(A) + y = similar(A.range, T) + fill!(y, zero(T)) + LinearAlgebra.mul!(y,A,x) end \ No newline at end of file From c28722b359e43527120cd2f976fb15b6865a33d8 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 15 Sep 2021 11:45:40 +0200 Subject: [PATCH 060/528] minver julia set to 1.6 to allow dep on BlockArrays 0.16 --- .github/workflows/CI.yml | 2 +- Project.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 20dde54e..fd462381 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: version: - - '1.3' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'. + - '1.6' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'. - '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia. - 'nightly' os: diff --git a/Project.toml b/Project.toml index 515d35f8..2bce6223 100644 --- a/Project.toml +++ b/Project.toml @@ -41,7 +41,7 @@ SparseMatrixDicts = "0.2" SpecialFunctions = "^0.7, ^0.8, ^0.9, ^0.10, ^1" StaticArrays = "^0.8.3, ^0.9, ^0.10, ^0.11, ^0.12, ^1" WiltonInts84 = "^0.2" -julia = "^1.3" +julia = "1.6" [extras] DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" From 06e348fa879e2cc5dfb9d9162c7454cd52c752d0 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 17 Sep 2021 13:13:16 +0200 Subject: [PATCH 061/528] sysmatrix allows specifying compression algo --- src/solvers/itsolver.jl | 34 ++++++++++++++++++++++------------ src/solvers/solver.jl | 4 ++-- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index 5436ff4f..f0ca771e 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -11,6 +11,9 @@ struct GMRESSolver{L,T} <: LinearMap{T} end +Base.axes(A::GMRESSolver) = reverse(axes(A.linear_operator)) + + function GMRESSolver(op; maxiter=0, restart=0, tol=sqrt(eps(real(eltype(op))))) m, n = size(op) @@ -26,12 +29,12 @@ end operator(solver::GMRESSolver) = solver.linear_operator -function solve(solver::GMRESSolver, b) - op = operator(solver) - x, ch = IterativeSolvers.gmres(op, b, log=true, maxiter=solver.maxiter, - restart=solver.restart, reltol=solver.tol, verbose=true) - return x, ch -end +# function solve(solver::GMRESSolver, b) +# op = operator(solver) +# x, ch = IterativeSolvers.gmres(op, b, log=true, maxiter=solver.maxiter, +# restart=solver.restart, reltol=solver.tol, verbose=true) +# return x, ch +# end function solve!(x, solver::GMRESSolver, b) @@ -42,16 +45,23 @@ function solve!(x, solver::GMRESSolver, b) end -# function Base.:*(solver::GMRESSolver, b) -# x, ch = solve(solver, b) -# println("Number of iterations: ", ch.iters) -# ch.isconverged || error("Iterative solver did not converge.") -# return x -# end +function Base.:*(A::GMRESSolver, b::AbstractVector) + + T = promote_type(eltype(A), eltype(b)) + y = PseudoBlockVector{T}(undef, BlockArrays.blocksizes(A)[1]) + + mul!(y, A, b) + + # x, ch = solve(solver, b) + # println("Number of iterations: ", ch.iters) + # ch.isconverged || error("Iterative solver did not converge.") + # return x +end Base.size(solver::GMRESSolver) = reverse(size(solver.linear_operator)) function LinearAlgebra.mul!(y::AbstractVecOrMat, solver::GMRESSolver, x::AbstractVector) + fill!(y,0) y, ch = solve!(y, solver, x) println("Number of iterations: ", ch.iters) ch.isconverged || error("Iterative solver did not converge.") diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 47a835aa..0d6e8090 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -106,8 +106,8 @@ macro discretise(eq, pairs...) end -sysmatrix(eq::DiscreteEquation, materialize=BEAST.assemble) = - assemble(eq.equation.lhs, eq.test_space_dict, eq.trial_space_dict, materialize=mt) +sysmatrix(eq::DiscreteEquation; materialize=BEAST.assemble) = + assemble(eq.equation.lhs, eq.test_space_dict, eq.trial_space_dict, materialize=materialize) rhs(eq::DiscreteEquation) = assemble(eq.equation.rhs, eq.test_space_dict) assemble(dbf::DiscreteBilform) = assemble(dbf.bilform, dbf.test_space_dict, dbf.trial_space_dict) From 3d99f92248294320d0e2781f1d39e34bc495003b Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Sun, 19 Sep 2021 17:24:43 +0200 Subject: [PATCH 062/528] Make zlocal per thread Currently zlocal is computed once. Thus if blockassembler is called in an openmp environment the same zlocal is used by all threads Now an array of zlocals is established (its length equal to #threads) to prevent this. NOTE: When multithreading is active, the unit test test_nitsche.jl fails. This has been true already before this change and should thus be fixed separately. --- src/integralop.jl | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/integralop.jl b/src/integralop.jl index 2553f85c..cdfed6d2 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -162,13 +162,13 @@ function blockassembler(biop::IntegralOperator, tfs::Space, bfs::Space) test_elements, test_assembly_data, trial_elements, trial_assembly_data, - quadrature_data, zlocal = assembleblock_primer(biop, tfs, bfs) + quadrature_data, zlocals = assembleblock_primer(biop, tfs, bfs) return function f(test_ids, trial_ids, store) assembleblock_body!(biop, tfs, test_ids, test_elements, test_assembly_data, bfs, trial_ids, trial_elements, trial_assembly_data, - quadrature_data, zlocal, store) + quadrature_data, zlocals, store) end end @@ -181,7 +181,7 @@ end function assembleblock!(biop::IntegralOperator, tfs::Space, bfs::Space, store) - test_elements, tad, trial_elements, bad, quadrature_data, zlocal = + test_elements, tad, trial_elements, bad, quadrature_data, zlocals = assembleblock_primer(biop, tfs, bfs) active_test_dofs = collect(1:numfunctions(tfs)) @@ -190,7 +190,7 @@ function assembleblock!(biop::IntegralOperator, tfs::Space, bfs::Space, store) assembleblock_body!(biop, tfs, active_test_dofs, test_elements, tad, bfs, active_trial_dofs, trial_elements, bad, - quadrature_data, zlocal, store) + quadrature_data, zlocals, store) end @@ -203,16 +203,20 @@ function assembleblock_primer(biop, tfs, bfs) bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes) qd = quaddata(biop, tshapes, bshapes, test_elements, bsis_elements) - zlocal = zeros(scalartype(biop, tfs, bfs), num_tshapes, num_bshapes) - return test_elements, tad, bsis_elements, bad, qd, zlocal + zlocals = Matrix{scalartype(biop, tfs, bfs)}[] + for i in 1:Threads.nthreads() + push!(zlocals, zeros(scalartype(biop, tfs, bfs), num_tshapes, num_bshapes)) + end + + return test_elements, tad, bsis_elements, bad, qd, zlocals end function assembleblock_body!(biop::IntegralOperator, tfs, test_ids, test_elements, test_assembly_data, bfs, trial_ids, bsis_elements, trial_assembly_data, - quadrature_data, zlocal, store) + quadrature_data, zlocals, store) test_shapes = refspace(tfs) trial_shapes = refspace(bfs) @@ -240,19 +244,19 @@ function assembleblock_body!(biop::IntegralOperator, for q in active_trial_el_ids bcell = bsis_elements[q] - fill!(zlocal, 0) + fill!(zlocals[Threads.threadid()], 0) strat = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data) - momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, strat) + momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocals[Threads.threadid()], strat) - for j in 1 : size(zlocal,2) - for i in 1 : size(zlocal,1) + for j in 1 : size(zlocals[Threads.threadid()],2) + for i in 1 : size(zlocals[Threads.threadid()],1) for (n,b) in trial_assembly_data[q,j] n′ = get(trial_id_in_blk, n, 0) n′ == 0 && continue for (m,a) in test_assembly_data[p,i] m′ = get(test_id_in_blk, m, 0) m′ == 0 && continue - store(a*zlocal[i,j]*b, m′, n′) + store(a*zlocals[Threads.threadid()][i,j]*b, m′, n′) end end end end end end end From 9aca8f5c1364ce0ed18eb429a66e3c52415bc996 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Sat, 18 Sep 2021 23:37:25 +0200 Subject: [PATCH 063/528] Allow to pass quaddata and quadrule for blockassembler --- src/integralop.jl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/integralop.jl b/src/integralop.jl index cdfed6d2..3907f4b3 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -158,17 +158,18 @@ end -function blockassembler(biop::IntegralOperator, tfs::Space, bfs::Space) +function blockassembler(biop::IntegralOperator, tfs::Space, bfs::Space; + quaddata=quaddata, quadrule=quadrule) test_elements, test_assembly_data, trial_elements, trial_assembly_data, - quadrature_data, zlocals = assembleblock_primer(biop, tfs, bfs) + quadrature_data, zlocals = assembleblock_primer(biop, tfs, bfs, quaddata=quaddata) return function f(test_ids, trial_ids, store) assembleblock_body!(biop, tfs, test_ids, test_elements, test_assembly_data, bfs, trial_ids, trial_elements, trial_assembly_data, - quadrature_data, zlocals, store) + quadrature_data, zlocals, store, quadrule=quadrule) end end @@ -194,7 +195,7 @@ function assembleblock!(biop::IntegralOperator, tfs::Space, bfs::Space, store) end -function assembleblock_primer(biop, tfs, bfs) +function assembleblock_primer(biop, tfs, bfs; quaddata=quaddata) test_elements, tad = assemblydata(tfs) bsis_elements, bad = assemblydata(bfs) @@ -216,7 +217,7 @@ end function assembleblock_body!(biop::IntegralOperator, tfs, test_ids, test_elements, test_assembly_data, bfs, trial_ids, bsis_elements, trial_assembly_data, - quadrature_data, zlocals, store) + quadrature_data, zlocals, store; quadrule=quadrule) test_shapes = refspace(tfs) trial_shapes = refspace(bfs) From d5d28cdc404ad2a2abb318f8a1477147325a4b52 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Sun, 19 Sep 2021 20:58:47 +0200 Subject: [PATCH 064/528] Enable keyword quadrature for all matrix assemblies --- src/integralop.jl | 40 ++++++++++++++----------- src/localop.jl | 14 +++++---- src/maxwell/mwops.jl | 3 -- src/maxwell/timedomain/mwtdops.jl | 2 +- src/operator.jl | 50 +++++++++++++++++++------------ src/timedomain/tdintegralop.jl | 7 +++-- 6 files changed, 68 insertions(+), 48 deletions(-) diff --git a/src/integralop.jl b/src/integralop.jl index 3907f4b3..20d106f1 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -65,7 +65,8 @@ elements(sp::Space) = elements(geometry(sp)) Computes the matrix of operator biop wrt the finite element spaces tfs and bfs """ -function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store) +function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; + quaddata=quaddata, quadrule=quadrule) test_elements, tad = assemblydata(tfs) bsis_elements, bad = assemblydata(bfs) @@ -80,13 +81,13 @@ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store) assemblechunk_body!(biop, tshapes, test_elements, tad, bshapes, bsis_elements, bad, - qd, zlocal, store) + qd, zlocal, store, quadrule=quadrule) else @info "assemblechunk for nested meshes" assemblechunk_body_nested_meshes!(biop, tshapes, test_elements, tad, bshapes, bsis_elements, bad, - qd, zlocal, store) + qd, zlocal, store, quadrule=quadrule) end end @@ -94,7 +95,7 @@ end function assemblechunk_body!(biop, test_shapes, test_elements, test_assembly_data, trial_shapes, trial_elements, trial_assembly_data, - qd, zlocal, store) + qd, zlocal, store; quadrule=quadrule) myid = Threads.threadid() myid == 1 && print("dots out of 10: ") @@ -129,7 +130,7 @@ end function assemblechunk_body_nested_meshes!(biop, test_shapes, test_elements, test_assembly_data, trial_shapes, trial_elements, trial_assembly_data, - qd, zlocal, store) + qd, zlocal, store; quadrule=quadrule) myid = Threads.threadid() myid == 1 && print("dots out of 10: ") @@ -159,7 +160,7 @@ end function blockassembler(biop::IntegralOperator, tfs::Space, bfs::Space; - quaddata=quaddata, quadrule=quadrule) + quaddata=quaddata, quadrule=quadrule) test_elements, test_assembly_data, trial_elements, trial_assembly_data, @@ -174,16 +175,19 @@ function blockassembler(biop::IntegralOperator, tfs::Space, bfs::Space; end -function assembleblock(operator::AbstractOperator, test_functions, trial_functions) +function assembleblock(operator::AbstractOperator, test_functions, trial_functions; + quaddata=quaddata, quadrule=quadrule) Z, store = allocatestorage(operator, test_functions, trial_functions) - assembleblock!(operator, test_functions, trial_functions, store) + assembleblock!(operator, test_functions, trial_functions, store, + quaddata=quaddata, quadrule=quadrule) sdata(Z) end -function assembleblock!(biop::IntegralOperator, tfs::Space, bfs::Space, store) +function assembleblock!(biop::IntegralOperator, tfs::Space, bfs::Space, store; + quaddata=quaddata, quadrule=quadrule) test_elements, tad, trial_elements, bad, quadrature_data, zlocals = - assembleblock_primer(biop, tfs, bfs) + assembleblock_primer(biop, tfs, bfs, quaddata=quaddata) active_test_dofs = collect(1:numfunctions(tfs)) active_trial_dofs = collect(1:numfunctions(bfs)) @@ -191,7 +195,7 @@ function assembleblock!(biop::IntegralOperator, tfs::Space, bfs::Space, store) assembleblock_body!(biop, tfs, active_test_dofs, test_elements, tad, bfs, active_trial_dofs, trial_elements, bad, - quadrature_data, zlocals, store) + quadrature_data, zlocals, store, quadrule=quadrule) end @@ -261,7 +265,8 @@ function assembleblock_body!(biop::IntegralOperator, end end end end end end end -function assemblerow!(biop::IntegralOperator, test_functions::Space, trial_functions::Space, store) +function assemblerow!(biop::IntegralOperator, test_functions::Space, trial_functions::Space, store; + quaddata=quaddata, quadrule=quadrule) test_elements = elements(geometry(test_functions)) trial_elements, trial_assembly_data = assemblydata(trial_functions) @@ -282,14 +287,14 @@ function assemblerow!(biop::IntegralOperator, test_functions::Space, trial_funct assemblerow_body!(biop, test_functions, test_elements, test_shapes, trial_assembly_data, trial_elements, trial_shapes, - zlocal, quadrature_data, store) + zlocal, quadrature_data, store, quadrule=quadrule) end function assemblerow_body!(biop, test_functions, test_elements, test_shapes, trial_assembly_data, trial_elements, trial_shapes, - zlocal, quadrature_data, store) + zlocal, quadrature_data, store; quadrule=quadrule) test_function = test_functions.fns[1] for shape in test_function @@ -309,7 +314,8 @@ function assemblerow_body!(biop, end end end end end -function assemblecol!(biop::IntegralOperator, test_functions::Space, trial_functions::Space, store) +function assemblecol!(biop::IntegralOperator, test_functions::Space, trial_functions::Space, store; + quaddata=quaddata, quadrule=quadrule) test_elements, test_assembly_data = assemblydata(test_functions) trial_elements = elements(geometry(trial_functions)) @@ -331,14 +337,14 @@ function assemblecol!(biop::IntegralOperator, test_functions::Space, trial_funct assemblecol_body!(biop, test_assembly_data, test_elements, test_shapes, trial_functions, trial_elements, trial_shapes, - zlocal, quadrature_data, store) + zlocal, quadrature_data, store, quadrule=quadrule) end function assemblecol_body!(biop, test_assembly_data, test_elements, test_shapes, trial_functions, trial_elements, trial_shapes, - zlocal, quadrature_data, store) + zlocal, quadrature_data, store; quadrule=quadrule) trial_function = trial_functions.fns[1] for shape in trial_function diff --git a/src/localop.jl b/src/localop.jl index 1b9a62e5..73b3a750 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -62,7 +62,7 @@ function allocatestorage(op::LocalOperator, test_functions, trial_functions, end function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, - threading::Type{Threading{:multi}}) + threading::Type{Threading{:multi}}; quaddata=quaddata, quadrule=quadrule) if geometry(tfs) == geometry(bfs) return assemble_local_matched!(biop, tfs, bfs, store) @@ -75,7 +75,8 @@ function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, return assemble_local_mixed!(biop, tfs, bfs, store) end -function assemble_local_matched!(biop::LocalOperator, tfs::Space, bfs::Space, store) +function assemble_local_matched!(biop::LocalOperator, tfs::Space, bfs::Space, store; + quaddata=quaddata, quadrule=quadrule) tels, tad, ta2g = assemblydata(tfs) bels, bad, ba2g = assemblydata(bfs) @@ -103,7 +104,8 @@ function assemble_local_matched!(biop::LocalOperator, tfs::Space, bfs::Space, st end end end end -function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, store) +function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, store; + quaddata=quaddata, quadrule=quadrule) println("Using 'refines' algorithm for local assembly:") @@ -167,7 +169,8 @@ function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, st end -function assemble_local_matched!(biop::LocalOperator, tfs::subdBasis, bfs::subdBasis, store) +function assemble_local_matched!(biop::LocalOperator, tfs::subdBasis, bfs::subdBasis, store; + quaddata=quaddata, quadrule=quadrule) tels, tad = assemblydata(tfs) bels, bad = assemblydata(bfs) @@ -223,7 +226,8 @@ end For use when basis and test functions are defined on different meshes """ -function assemble_local_mixed!(biop::LocalOperator, tfs::Space, bfs::Space, store) +function assemble_local_mixed!(biop::LocalOperator, tfs::Space, bfs::Space, store; + quaddata=quaddata, quadrule=quadrule) tol = sqrt(eps(Float64)) diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index b35f37bc..feefdf3b 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -100,9 +100,6 @@ function quaddata(op::MaxwellOperator3D, return (tpoints=tqd, bpoints=bqd, gausslegendre=leg) end - - - # use Union type so this code can be shared between the operator # and its regular part. MWSL3DGen = Union{MWSingleLayer3D,MWSingleLayer3DReg} diff --git a/src/maxwell/timedomain/mwtdops.jl b/src/maxwell/timedomain/mwtdops.jl index b0139e2b..c7daad61 100644 --- a/src/maxwell/timedomain/mwtdops.jl +++ b/src/maxwell/timedomain/mwtdops.jl @@ -137,7 +137,7 @@ end # end function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, store, - threading=Threading{:multi}) + threading=Threading{:multi}; quaddata=quaddata, quadrule=quadrule) X, T = spatialbasis(W), temporalbasis(W) Y, U = spatialbasis(V), temporalbasis(V) diff --git a/src/operator.jl b/src/operator.jl index d34d0a51..9a889430 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -78,23 +78,28 @@ transpose(op::Operator) = TransposedOperator(op) function assemble(operator::AbstractOperator, test_functions, trial_functions; storage_policy = Val{:bandedstorage}, long_delays_policy = LongDelays{:compress}, - threading = Threading{:multi}) + threading = Threading{:multi}, + quaddata=quaddata, + quadrule=quadrule) # This is a convenience function whose only job is to allocate # the storage for the interaction matrix. Further dispatch on # operator and space types is handled by the 4-argument version Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) - assemble!(operator, test_functions, trial_functions, store, threading) + assemble!(operator, test_functions, trial_functions, store, threading, + quaddata=quaddata, quadrule=quadrule) return Z() end function assemblerow(operator::AbstractOperator, test_functions, trial_functions, storage_policy = Val{:bandedstorage}, - long_delays_policy = LongDelays{:ignore}) + long_delays_policy = LongDelays{:ignore}; + quaddata=quaddata, quadrule=quadrule) Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) - assemblerow!(operator, test_functions, trial_functions, store) + assemblerow!(operator, test_functions, trial_functions, store, + quaddata=quaddata, quadrule=quadrule) Z() end @@ -104,7 +109,8 @@ function assemblecol(operator::AbstractOperator, test_functions, trial_functions Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) - assemblecol!(operator, test_functions, trial_functions, store) + assemblecol!(operator, test_functions, trial_functions, store, + quaddata=quaddata, quadrule=quadrule) Z() end @@ -149,7 +155,7 @@ end function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, - threading::Type{Threading{:multi}} = Threading{:multi}) + threading::Type{Threading{:multi}} = Threading{:multi}; quaddata=quaddata, quadrule=quadrule) @info "Multi-threaded assembly:" @@ -163,62 +169,68 @@ function assemble!(operator::Operator, test_functions::Space, trial_functions::S lo <= hi || continue test_functions_p = subset(test_functions, lo:hi) store1 = (v,m,n) -> store(v,lo+m-1,n) - assemblechunk!(operator, test_functions_p, trial_functions, store1) + assemblechunk!(operator, test_functions_p, trial_functions, store1, + quaddata=quaddata, quadrule=quadrule) end end function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, - threading::Type{Threading{:single}}) + threading::Type{Threading{:single}}; quaddata=quaddata, quadrule=quadrule) @info "Single-threaded assembly" - assemblechunk!(operator, test_functions, trial_functions, store) + assemblechunk!(operator, test_functions, trial_functions, store, + quaddata=quaddata, quadrule=quadrule) end -function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, store) +function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, store; + quaddata=quaddata, quadrule=quadrule) store1(v,m,n) = store(v,n,m) - assemble!(op.op, bfs, tfs, store1) + assemble!(op.op, bfs, tfs, store1, quaddata=quaddata, quadrule=quadrule) end function assemble!(op::LinearCombinationOfOperators, tfs::AbstractSpace, bfs::AbstractSpace, - store, threading = Threading{:multi}) + store, threading = Threading{:multi}; quaddata=quaddata, quadrule=quadrule) for (a,A) in zip(op.coeffs, op.ops) store1(v,m,n) = store(a*v,m,n) - assemble!(A, tfs, bfs, store1) + assemble!(A, tfs, bfs, store1, quaddata=quaddata, quadrule=quadrule) end end # Support for direct product spaces -function assemble!(op::Operator, tfs::DirectProductSpace, bfs::Space, store, threading = Threading{:multi}) +function assemble!(op::Operator, tfs::DirectProductSpace, bfs::Space, store, threading = Threading{:multi}; + quaddata=quaddata, quadrule=quadrule) I = Int[0] for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) store1(v,m,n) = store(v,m + I[i], n) - assemble!(op, s, bfs, store1) + assemble!(op, s, bfs, store1, quaddata=quaddata, quadrule=quadrule) end end -function assemble!(op::Operator, tfs::Space, bfs::DirectProductSpace, store, threading=Threading{:multi}) +function assemble!(op::Operator, tfs::Space, bfs::DirectProductSpace, store, threading=Threading{:multi}; + quaddata=quaddata, quadrule=quadrule) J = Int[0] for s in bfs.factors push!(J, last(J) + numfunctions(s)) end for (j,s) in enumerate(bfs.factors) store1(v,m,n) = store(v,m,n + J[j]) - assemble!(op, tfs, s, store1) + assemble!(op, tfs, s, store1, quaddata=quaddata, quadrule=quadrule) end end -function assemble!(op::Operator, tfs::DirectProductSpace, bfs::DirectProductSpace, store, threading=Threading{:multi}) +function assemble!(op::Operator, tfs::DirectProductSpace, bfs::DirectProductSpace, store, threading=Threading{:multi}; + quaddata=quaddata, quadrule=quadrule) I = Int[0] for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) store1(v,m,n) = store(v,m + I[i],n) - assemble!(op, s, bfs, store1) + assemble!(op, s, bfs, store1, quaddata=quaddata, quadrule=quadrule) end end diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index eaef6d95..51856718 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -129,7 +129,7 @@ function allocatestorage(op::RetardedPotential, testST, basisST, end function assemble!(op::LinearCombinationOfOperators, tfs::SpaceTimeBasis, bfs::SpaceTimeBasis, store, - threading=Threading{:multi}) + threading=Threading{:multi}; quaddata=quaddata, quadrule=quadrule) for (a,A) in zip(op.coeffs, op.ops) store1(v,m,n,k) = store(a*v,m,n,k) @@ -138,7 +138,7 @@ function assemble!(op::LinearCombinationOfOperators, tfs::SpaceTimeBasis, bfs::S end function assemble!(op::RetardedPotential, testST, trialST, store, - threading=Threading{:multi}) + threading=Threading{:multi}; quaddata=quaddata, quadrule=quadrule) Y, S = spatialbasis(testST), temporalbasis(testST) @@ -158,7 +158,8 @@ function assemble!(op::RetardedPotential, testST, trialST, store, assemble_chunk!(op, testST, trialST, store) end -function assemble_chunk!(op::RetardedPotential, testST, trialST, store) +function assemble_chunk!(op::RetardedPotential, testST, trialST, store; + quaddata=quaddata, quadrule=quadrule) myid = Threads.threadid() From 6c66a5b5389056991091807c12ec3cb4f3d92295 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Sun, 19 Sep 2021 21:13:44 +0200 Subject: [PATCH 065/528] Added RHS individual quadrature --- src/excitation.jl | 16 ++++++++++------ src/timedomain/tdexcitation.jl | 14 +++++++++----- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/excitation.jl b/src/excitation.jl index 6c139a87..49f7c35c 100644 --- a/src/excitation.jl +++ b/src/excitation.jl @@ -11,24 +11,27 @@ quadrule(fn::Functional, refs, p, cell, qd) = qd[1,p] Assemble the vector of test coefficients corresponding to functional `fn` and test functions `tfs`. """ -function assemble(field::Functional, tfs) +function assemble(field::Functional, tfs; quaddata=quaddata, quadrule=quadrule) b = zeros(ComplexF64, numfunctions(tfs)) store(v,m) = (b[m] += v) - assemble!(field, tfs, store) + assemble!(field, tfs, store, quaddata=quaddata, quadrule=quadrule) return b end -function assemble!(field::Functional, tfs::DirectProductSpace, store) +function assemble!(field::Functional, tfs::DirectProductSpace, store; + quaddata=quaddata, quadrule=quadrule) + I = Int[0] for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) store1(v,m) = store(v, m + I[i]) - assemble!(field, s, store1) + assemble!(field, s, store1, quaddata=quaddata, quadrule=quadrule) end end -function assemble!(field::Functional, tfs::Space, store) +function assemble!(field::Functional, tfs::Space, store; + quaddata=quaddata, quadrule=quadrule) tels, tad = assemblydata(tfs) @@ -51,7 +54,8 @@ function assemble!(field::Functional, tfs::Space, store) end -function assemble!(field::Functional, tfs::subdBasis, store) +function assemble!(field::Functional, tfs::subdBasis, store; + quaddata=quaddata, quadrule=quadrule) tels, tad = assemblydata(tfs) diff --git a/src/timedomain/tdexcitation.jl b/src/timedomain/tdexcitation.jl index 3b5a5122..b6a1f3d5 100644 --- a/src/timedomain/tdexcitation.jl +++ b/src/timedomain/tdexcitation.jl @@ -41,16 +41,18 @@ function quadrule(exc::TDFunctional, testrefs, timerefs::DiracBoundary, p, τ, r end -function assemble(exc::TDFunctional, testST) +function assemble(exc::TDFunctional, testST; quaddata=quaddata, quadrule=quadrule) testfns = spatialbasis(testST) timefns = temporalbasis(testST) Z = zeros(eltype(exc), numfunctions(testfns), numfunctions(timefns)) store(v,m,k) = (Z[m,k] += v) - assemble!(exc, testST, store) + assemble!(exc, testST, store, quaddata=quaddata, quadrule=quadrule) return Z end -function assemble(exc::TDFunctional, testST::StagedTimeStep) +function assemble(exc::TDFunctional, testST::StagedTimeStep; + quaddata=quaddata, quadrule=quadrule) + stageCount = length(testST.c); spatialBasis = testST.spatialBasis; Nt = testST.Nt; @@ -59,12 +61,14 @@ function assemble(exc::TDFunctional, testST::StagedTimeStep) for i = 1:stageCount store(v,m,k) = (Z[(m-1)*stageCount+i,k] += v); tbsd = TimeBasisDeltaShifted(timebasisdelta(Δt, Nt), testST.c[i]); - assemble!(exc, spatialBasis ⊗ tbsd, store); + assemble!(exc, spatialBasis ⊗ tbsd, store, + quaddata=quaddata, quadrule=quadrule); end return Z; end -function assemble!(exc::TDFunctional, testST, store) +function assemble!(exc::TDFunctional, testST, store; + quaddata=quaddata, quadrule=quadrule) testfns = spatialbasis(testST) timefns = temporalbasis(testST) From a1b75e3d4fc3f01d2ce672a949e6b9acdab91f33 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Sun, 19 Sep 2021 22:26:30 +0200 Subject: [PATCH 066/528] Added unit test TODO: Unit tests for timedomain should be added. --- test/test_quadrature.jl | 90 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 test/test_quadrature.jl diff --git a/test/test_quadrature.jl b/test/test_quadrature.jl new file mode 100644 index 00000000..2e5543df --- /dev/null +++ b/test/test_quadrature.jl @@ -0,0 +1,90 @@ +using Test + +using CompScienceMeshes +using BEAST +using StaticArrays +using LinearAlgebra + +c = 3e8 +μ = 4*π*1e-7 +ε = 1/(μ*c^2) +f = 5e7 +λ = c/f +k = 2*π/λ +ω = k*c +η = sqrt(μ/ε) + +a = 1 +Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.1) +Γ = translate(Γ_orig,SVector(-a/2,-a/2,-a/2)) + +Φ, Θ = [0.0], range(0,stop=π,length=100) +pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] + +# This is an electric dipole +# The pre-factor (1/ε) is used to resemble +# (9.18) in Jackson's Classical Electrodynamics +E = (1/ε) * dipolemw3d(location=SVector(0.4,0.2,0), + orientation=1e-9.*SVector(0.5,0.5,0), + wavenumber=k) + +n = BEAST.NormalVector() + +𝒆 = (n × E) × n +H = (-1/(im*μ*ω))*curl(E) +𝒉 = (n × H) × n + +𝓣 = Maxwell3D.singlelayer(wavenumber=k) + +X = raviartthomas(Γ) + +T = Matrix(assemble(𝓣,X,X)) +e = Vector(assemble(𝒆,X)) +j_EFIE = T\e + +nf_E_EFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_EFIE, X) +nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_EFIE, X) ./ η +ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X) + +accuracy_default_quadrature_nf_e = norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) +accuracy_default_quadrature_nf_h = norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) +accuracy_default_quadrature_ff_e = + norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) + +function fastquaddata(op::BEAST.MaxwellOperator3D, + test_local_space::BEAST.RefSpace, trial_local_space::BEAST.RefSpace, + test_charts, trial_charts) + + a, b = 0.0, 1.0 + # CommonVertex, CommonEdge, CommonFace rules + println("Fast quadrule is called") + tqd = quadpoints(test_local_space, test_charts, (1,2)) + bqd = quadpoints(trial_local_space, trial_charts, (1,2)) + leg = (BEAST._legendre(3,a,b), BEAST._legendre(4,a,b), BEAST._legendre(5,a,b),) + + return (tpoints=tqd, bpoints=bqd, gausslegendre=leg) +end + +function fastquaddata(fn::BEAST.Functional, refs, cells) + println("Fast RHS quadrature") + return quadpoints(refs, cells, [1]) +end + + +fastT = Matrix(assemble(𝓣,X,X, quaddata=fastquaddata)) +faste = Vector(assemble(𝒆,X, quaddata=fastquaddata)) +fastj_EFIE = fastT\faste + +fastnf_E_EFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, fastj_EFIE, X) +fastnf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, fastj_EFIE, X) ./ η +fastff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, fastj_EFIE, X) + +accuracy_fast_quadrature_nf_e = norm(fastnf_E_EFIE - E.(pts))/norm(E.(pts)) +accuracy_fast_quadrature_nf_h = norm(fastnf_H_EFIE - H.(pts))/norm(H.(pts)) +accuracy_fast_quadrature_ff_e = + norm(fastff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) + +@test accuracy_fast_quadrature_nf_e > accuracy_default_quadrature_nf_e +@test accuracy_fast_quadrature_nf_h > accuracy_default_quadrature_nf_h +@test accuracy_fast_quadrature_ff_e > accuracy_default_quadrature_ff_e + From df89459f43ebb58339066f10b498a6f39b950ae3 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 27 Sep 2021 13:58:33 +0100 Subject: [PATCH 067/528] potential for helmholtz problems --- examples/helmholtz3d_dirichlet.jl | 12 +++--- examples/helmholtz3d_neumann.jl | 19 +++++++-- examples/mtjunction.jl | 66 ++++++++++++++++++++----------- src/BEAST.jl | 1 + src/bases/lagrange.jl | 8 +++- src/helmholtz3d/hh3dnear.jl | 46 +++++++++++++++++++++ src/helmholtz3d/hh3dops.jl | 4 +- src/postproc.jl | 23 ++++++----- src/solvers/itsolver.jl | 12 +++--- 9 files changed, 139 insertions(+), 52 deletions(-) create mode 100644 src/helmholtz3d/hh3dnear.jl diff --git a/examples/helmholtz3d_dirichlet.jl b/examples/helmholtz3d_dirichlet.jl index 3f59e985..f77300db 100644 --- a/examples/helmholtz3d_dirichlet.jl +++ b/examples/helmholtz3d_dirichlet.jl @@ -25,15 +25,15 @@ g = ∂n(uⁱ) eq1 = @discretise a[v,u] == f[v] u∈X v∈X eq2 = @discretise b[v,u] == g[v] u∈X v∈X -x1 = solve(eq1) -x2 = solve(eq2) +x1 = gmres(eq1) +x2 = gmres(eq2) fcr1, geo1 = facecurrents(x1, X) fcr2, geo2 = facecurrents(x2, X) # include(Pkg.dir("CompScienceMeshes","examples","plotlyjs_patches.jl")) -# p1 = patch(geo1, real.(norm.(fcr1))) -# p2 = patch(geo2, real.(norm.(fcr2))) +patch(geo1, real.(norm.(fcr1))) +patch(geo2, real.(norm.(fcr2))) using LinearAlgebra using Test @@ -42,7 +42,7 @@ using Test Z = assemble(a,X,X); m1, m2 = 1, numfunctions(X) chm, chn = chart(Γ,cells(Γ)[m1]), chart(Γ,cells(Γ)[m2]) -ctm, ctn = center(chm), center(chn) +ctm, ctn = CompScienceMeshes.center(chm), CompScienceMeshes.center(chn) R = norm(cartesian(ctm)-cartesian(ctn)) G = exp(-im*κ*R)/(4π*R) Wmn = volume(chm) * volume(chn) * G @@ -52,7 +52,7 @@ Wmn = volume(chm) * volume(chn) * G r = assemble(f,X) m1 = 1 chm = chart(Γ,cells(Γ)[m1]) -ctm = center(chm) +ctm = CompScienceMeshes.center(chm) sm = volume(chm) * f(ctm) r[m1] @show abs(sm - r[m1]) / abs(r[m1]) diff --git a/examples/helmholtz3d_neumann.jl b/examples/helmholtz3d_neumann.jl index fe6fc58f..2acbd8f1 100644 --- a/examples/helmholtz3d_neumann.jl +++ b/examples/helmholtz3d_neumann.jl @@ -4,6 +4,7 @@ using LinearAlgebra, Pkg Pkg.activate(@__DIR__) # Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +# Γ = meshrectangle(1.0, 1.0, 0.05, 3) X = lagrangec0d1(Γ) @show numfunctions(X) @@ -20,10 +21,10 @@ g = ∂n(uⁱ) eq1 = @discretise a[v,u] == g[v] u∈X v∈X eq2 = @discretise b[v,u] == f[v] u∈X v∈X -x1 = solve(eq1) -x2 = solve(eq2) +x1 = gmres(eq1) +x2 = gmres(eq2) -@assert norm(x1-x2)/norm(x1+x2) < 0.5e-2 +# @assert norm(x1-x2)/norm(x1+x2) < 0.5e-2 fcr1, geo1 = facecurrents(x1, X) fcr2, geo2 = facecurrents(x2, X) @@ -35,4 +36,14 @@ scatter!(norm.(fcr2),c=:red,label="2nd") import Plotly Plotly.plot(patch(Γ, norm.(fcr1))) -Plotly.plot(patch(Γ, norm.(fcr2))) \ No newline at end of file +Plotly.plot(patch(Γ, norm.(fcr2))) + + +ys = range(-2,2,length=200) +zs = range(-2,2,length=200) +pts = [point(0.5, y, z) for y in ys, z in zs] + +nf = BEAST.HH3DDoubleLayerNear(wavenumber=κ) +near = BEAST.potential(nf, pts, x1, X, type=ComplexF64) +inc = uⁱ.(pts) +tot = near + inc \ No newline at end of file diff --git a/examples/mtjunction.jl b/examples/mtjunction.jl index 1b207ee2..ed615dd2 100644 --- a/examples/mtjunction.jl +++ b/examples/mtjunction.jl @@ -2,21 +2,27 @@ using CompScienceMeshes, BEAST width, height, h = 1.0, 0.5, 0.05 G1 = meshrectangle(width, height, h) -G2 = CompScienceMeshes.rotate(G1, 0.5π * x̂) -G3 = CompScienceMeshes.rotate(G1, 1.0π * x̂) +G2 = CompScienceMeshes.rotate(G1, 1.0π * x̂) +G3 = CompScienceMeshes.rotate(G1, 1.5π * x̂) G12 = weld(G1,-G2) G23 = weld(G2,-G3) G31 = weld(G3,-G1) +G21 = weld(G2,-G1) + X12 = raviartthomas(G12) X23 = raviartthomas(G23) X31 = raviartthomas(G31) +X21 = raviartthomas(G21) + Y12 = buffachristiansen(G12) Y23 = buffachristiansen(G23) Y31 = buffachristiansen(G31) +Y21 = buffachristiansen(G21) + κ = 1.0 SL = Maxwell3D.singlelayer(wavenumber=κ) N = NCross() @@ -24,6 +30,9 @@ N = NCross() X = X12 × X23 × X31 Y = Y12 × Y23 × Y31 +X = X12 × X21 +Y = Y12 × Y21 + E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) e = (n × E) × n @@ -74,7 +83,11 @@ N2 = assemble(N, X23, Y23) N3 = assemble(N, X31, Y31) Nxy = blkdiagm(N1,N2,N3) Syy = assemble(SL, Y, Y) -iNxy = inv(Nxy) +iNxy = blkdiagm(inv(Matrix(N1)), inv(Matrix(N2)), inv(Matrix(N3))) + +N21 = assemble(N, X21, Y21) +Nxy = blkdiagm(N1, N21) +iNxy = blkdiagm(inv(Matrix(N1)), inv(Matrix(N21))) # cond(Matrix(Sxx)) ex = assemble(e,X) @@ -83,33 +96,33 @@ Q = P * Sxx; R = P * ex; -u1, ch1 = solve(BEAST.GMRESSolver(Sxx),ex) -u2, ch2 = solve(BEAST.GMRESSolver(Q),R) +u1, ch1 = solve(BEAST.GMRESSolver(Sxx), ex) +u2, ch2 = solve(BEAST.GMRESSolver(Q), R) @show ch1.iters @show ch2.iters # gmres() -ns = [ - 0, - numfunctions(X12), - numfunctions(X23), - numfunctions(X31)] +# ns = [ +# 0, +# numfunctions(X12), +# numfunctions(X23), +# numfunctions(X31)] -cns = cumsum(ns) -u12 = u1[cns[1]+1:cns[2]] -u23 = u1[cns[2]+1:cns[3]] -u31 = u1[cns[3]+1:cns[4]] +# cns = cumsum(ns) +# u12 = u1[cns[1]+1:cns[2]] +# u23 = u1[cns[2]+1:cns[3]] +# u31 = u1[cns[3]+1:cns[4]] -fcr1, geo1 = facecurrents(u12, X12) -fcr2, geo2 = facecurrents(u23, X23) -fcr3, geo3 = facecurrents(u31, X31) +# fcr1, geo1 = facecurrents(u12, X12) +# fcr2, geo2 = facecurrents(u23, X23) +# fcr3, geo3 = facecurrents(u31, X31) -p1 = patch(geo1, norm.(fcr1)) -p2 = patch(geo2, norm.(fcr2)) -p3 = patch(geo3, norm.(fcr3)) +# p1 = patch(geo1, norm.(fcr1)) +# p2 = patch(geo2, norm.(fcr2)) +# p3 = patch(geo3, norm.(fcr3)) -Plotly.plot([p1,p2,p3]) +# Plotly.plot([p1,p2,p3]) G123 = weld(G1,G2,G3) X123 = raviartthomas(G123) @@ -129,8 +142,13 @@ Plotly.plot(patch(geost, norm.(fcrst))) Φ, Θ = [0.0], range(0,stop=π,length=100) pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] -ffd_mt = potential(MWFarField3D(wavenumber=κ), pts, u1, X) -ffd_st = potential(MWFarField3D(wavenumber=κ), pts, ust, X123) +ffd_mt = potential(MWFarField3D(wavenumber=κ), pts, u1, X) +ffd_mt_pc = potential(MWFarField3D(wavenumber=κ), pts, u2, X) +ffd_err = potential(MWFarField3D(wavenumber=κ), pts, u1-u2, X) +ffd_st = potential(MWFarField3D(wavenumber=κ), pts, ust, X123) +using Plots plot(norm.(ffd_mt)) -scatter!(norm.(ffd_st)) \ No newline at end of file +scatter!(norm.(ffd_mt_pc)) +scatter!(norm.(ffd_st)) +scatter!(norm.(ffd_err)) \ No newline at end of file diff --git a/src/BEAST.jl b/src/BEAST.jl index 238adac2..aa3c13f9 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -188,6 +188,7 @@ include("helmholtz2d/helmholtzop.jl") include("helmholtz3d/hh3dexc.jl") include("helmholtz3d/hh3dops.jl") include("helmholtz3d/nitsche.jl") +include("helmholtz3d/hh3dnear.jl") include("decoupled/dpops.jl") include("decoupled/potentials.jl") diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index bdbe7165..65d048b4 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -115,7 +115,7 @@ function duallagrangecxd0(mesh, jct=CompScienceMeshes.mesh(coordtype(mesh), dime end -function duallagrangecxd0(mesh, vertexlist::Vector) +function duallagrangecxd0(mesh, vertexlist::Vector{Int}) T = coordtype(mesh) @@ -136,6 +136,12 @@ function duallagrangecxd0(mesh, vertexlist::Vector) end +function duallagrangecxd0(mesh, vertices::CompScienceMeshes.AbstractMesh{U,1}) where {U} + vertexlist = Int[v[1] for v in vertices] + return duallagrangecxd0(mesh, vertexlist) +end + + """ singleduallagd0(fine, F, v) diff --git a/src/helmholtz3d/hh3dnear.jl b/src/helmholtz3d/hh3dnear.jl new file mode 100644 index 00000000..c6a949b3 --- /dev/null +++ b/src/helmholtz3d/hh3dnear.jl @@ -0,0 +1,46 @@ + +struct HH3DDoubleLayerNear{K} + gamma::K +end + +function HH3DDoubleLayerNear(;wavenumber=error("wavenumber is a required argument")) + if iszero(real(wavenumber)) + HH3DDoubleLayerNear(-imag(wavenumber)) + else + HH3DDoubleLayerNear(wavenumber*im) + end +end + +quaddata(op::HH3DDoubleLayerNear,rs,els) = quadpoints(rs,els,(3,)) +quadrule(op::HH3DDoubleLayerNear,refspace,p,y,q,el,qdata) = qdata[1,q] + +function kernelvals(op::HH3DDoubleLayerNear,y,p) + + γ = op.gamma + x = cartesian(p) + r = y - x + R = norm(r) + γR = γ*R + + inv_R = 1/R + + expn = exp(-γR) + green = expn * inv_R * inv_4pi + gradgreen = -(γ + inv_R) * green * inv_R * r + + nx = normal(p) + + (;γ, r, R, green, gradgreen, nx) +end + +function integrand(op::HH3DDoubleLayerNear,krn,y,f,p) + + ∇G = krn.gradgreen + nx = krn.nx + + fx = f.value + + ∂G∂n = nx ⋅ ∇G + + return ∂G∂n * fx +end diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index ac9e1238..74a8ca3e 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -63,8 +63,8 @@ function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, # test_qp = quadpoints(test_eval, test_elements, (6,)) # bssi_qp = quadpoints(trial_eval, trial_elements, (7,)) - test_qp = quadpoints(test_eval, test_elements, (4,)) - bsis_qp = quadpoints(trial_eval, trial_elements, (7,)) + test_qp = quadpoints(test_eval, test_elements, (2,)) + bsis_qp = quadpoints(trial_eval, trial_elements, (3,)) return test_qp, bsis_qp end diff --git a/src/postproc.jl b/src/postproc.jl index 0ee1907a..b2aec6ab 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -127,11 +127,12 @@ function facecurrents(u, X::DirectProductSpace) fcr, m end -function potential(op, points, coeffs, basis) - T = SVector{3,ComplexF64} +function potential(op, points, coeffs, basis; type=SVector{3,ComplexF64}) + # T = SVector{3,ComplexF64} + T = type ff = zeros(T, size(points)) store(v,m,n) = (ff[m] += v*coeffs[n]) - potential!(store, op, points, basis) + potential!(store, op, points, basis, type=T) return ff end @@ -143,25 +144,29 @@ function potential(op, points,coeffs, basis::SpaceTimeBasis) return ff end -function potential(op, points, coeffs, space::DirectProductSpace) - T = SVector{3,ComplexF64} - ff = zeros(T, length(points)) +function potential(op, points, coeffs, space::DirectProductSpace; type=SVector{3,ComplexF64}) + # T = SVector{3,ComplexF64} + T = type + ff = zeros(T, size(points)) + @show size(ff) @assert length(coeffs) == numfunctions(space) offset = 0 for fct in space.factors store(v,m,n) = (ff[m] += v*coeffs[offset+n]) - potential!(store, op, points, fct) + potential!(store, op, points, fct, type=T) offset += numfunctions(fct) end ff end -function potential!(store, op, points, basis) +function potential!(store, op, points, basis; type=SVector{3,ComplexF64}) - T = SVector{3,ComplexF64} + # T = SVector{3,ComplexF64} + T = type + # @show T z = zeros(T,length(points)) els, ad = assemblydata(basis) diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index f0ca771e..66b5876d 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -29,12 +29,12 @@ end operator(solver::GMRESSolver) = solver.linear_operator -# function solve(solver::GMRESSolver, b) -# op = operator(solver) -# x, ch = IterativeSolvers.gmres(op, b, log=true, maxiter=solver.maxiter, -# restart=solver.restart, reltol=solver.tol, verbose=true) -# return x, ch -# end +function solve(solver::GMRESSolver, b) + op = operator(solver) + x, ch = IterativeSolvers.gmres(op, b, log=true, maxiter=solver.maxiter, + restart=solver.restart, reltol=solver.tol, verbose=true) + return x, ch +end function solve!(x, solver::GMRESSolver, b) From c6918e01aeaf61135ed9d089389eb1f3f4ac003c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 28 Sep 2021 12:31:05 +0100 Subject: [PATCH 068/528] hoist computation from hotloop assemblechunk_nested! --- src/integralop.jl | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/integralop.jl b/src/integralop.jl index 20d106f1..3309d988 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -113,8 +113,7 @@ function assemblechunk_body!(biop, for (n,b) in trial_assembly_data[q][j] zb = zij*b for (m,a) in test_assembly_data[p][i] - azb = a*zb - store(azb, m, n) + store(a*zb, m, n) end end end end done += 1 @@ -138,22 +137,25 @@ function assemblechunk_body_nested_meshes!(biop, for (p,tcell) in enumerate(test_elements) for (q,bcell) in enumerate(trial_elements) - fill!(zlocal, 0) - strat = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, qd) - momintegrals_nested!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, strat) - I = length(test_assembly_data[p]) - J = length(trial_assembly_data[q]) - for j in 1 : J, i in 1 : I - for (n,b) in trial_assembly_data[q][j], (m,a) in test_assembly_data[p][i] - store(a*zlocal[i,j]*b, m, n) + fill!(zlocal, 0) + strat = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, qd) + momintegrals_nested!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, strat) + I = length(test_assembly_data[p]) + J = length(trial_assembly_data[q]) + for j in 1 : J, i in 1 : I + zij = zlocal[i,j] + for (n,b) in trial_assembly_data[q][j] + zb = zij*b + for (m,a) in test_assembly_data[p][i] + store(a*zb, m, n) + end end end + + done += 1 + new_pctg = round(Int, done / todo * 100) + if new_pctg > pctg + 9 + myid == 1 && print(".") + pctg = new_pctg end end end - - done += 1 - new_pctg = round(Int, done / todo * 100) - if new_pctg > pctg + 9 - myid == 1 && print(".") - pctg = new_pctg - end end myid == 1 && println("") end From f07bdcf3f147c43db2aa61c7db928bf9be2d5ea2 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 28 Sep 2021 14:17:56 +0100 Subject: [PATCH 069/528] return assembleblock_body_nested if appropriate --- src/integralop.jl | 73 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/src/integralop.jl b/src/integralop.jl index 3309d988..54d3dbbd 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -168,11 +168,27 @@ function blockassembler(biop::IntegralOperator, tfs::Space, bfs::Space; trial_elements, trial_assembly_data, quadrature_data, zlocals = assembleblock_primer(biop, tfs, bfs, quaddata=quaddata) - return function f(test_ids, trial_ids, store) - assembleblock_body!(biop, - tfs, test_ids, test_elements, test_assembly_data, - bfs, trial_ids, trial_elements, trial_assembly_data, - quadrature_data, zlocals, store, quadrule=quadrule) + # return function f(test_ids, trial_ids, store) + # assembleblock_body!(biop, + # tfs, test_ids, test_elements, test_assembly_data, + # bfs, trial_ids, trial_elements, trial_assembly_data, + # quadrature_data, zlocals, store, quadrule=quadrule) + # end + + if !CompScienceMeshes.refines(tfs.geo, bfs.geo) + return function f(test_ids, trial_ids, store) + assembleblock_body!(biop, + tfs, test_ids, test_elements, test_assembly_data, + bfs, trial_ids, trial_elements, trial_assembly_data, + quadrature_data, zlocals, store, quadrule=quadrule) + end + else + return function f(test_ids, trial_ids, store) + assembleblock_body_nested!(biop, + tfs, test_ids, test_elements, test_assembly_data, + bfs, trial_ids, trial_elements, trial_assembly_data, + quadrature_data, zlocals, store, quadrule=quadrule) + end end end @@ -232,8 +248,6 @@ function assembleblock_body!(biop::IntegralOperator, active_test_el_ids = Vector{Int}() active_trial_el_ids = Vector{Int}() - #test_id_in_blk = zeros(Int, numfunctions(tfs)) - #trial_id_in_blk = zeros(Int, numfunctions(bfs)) test_id_in_blk = Dict{Int,Int}() trial_id_in_blk = Dict{Int,Int}() @@ -267,6 +281,51 @@ function assembleblock_body!(biop::IntegralOperator, end end end end end end end +function assembleblock_body_nested!(biop::IntegralOperator, + tfs, test_ids, test_elements, test_assembly_data, + bfs, trial_ids, bsis_elements, trial_assembly_data, + quadrature_data, zlocals, store; quadrule=quadrule) + + test_shapes = refspace(tfs) + trial_shapes = refspace(bfs) + + # Enumerate all the active test elements + active_test_el_ids = Vector{Int}() + active_trial_el_ids = Vector{Int}() + + test_id_in_blk = Dict{Int,Int}() + trial_id_in_blk = Dict{Int,Int}() + + for (i,m) in enumerate(test_ids); test_id_in_blk[m] = i; end + for (i,m) in enumerate(trial_ids); trial_id_in_blk[m] = i; end + + for m in test_ids, sh in tfs.fns[m]; push!(active_test_el_ids, sh.cellid); end + for m in trial_ids, sh in bfs.fns[m]; push!(active_trial_el_ids, sh.cellid); end + + active_test_el_ids = unique(sort(active_test_el_ids)) + active_trial_el_ids = unique(sort(active_trial_el_ids)) + + for p in active_test_el_ids + tcell = test_elements[p] + for q in active_trial_el_ids + bcell = bsis_elements[q] + + fill!(zlocals[Threads.threadid()], 0) + strat = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data) + momintegrals_nested!(biop, test_shapes, trial_shapes, tcell, bcell, zlocals[Threads.threadid()], strat) + + for j in 1 : size(zlocals[Threads.threadid()],2) + for i in 1 : size(zlocals[Threads.threadid()],1) + for (n,b) in trial_assembly_data[q,j] + n′ = get(trial_id_in_blk, n, 0) + n′ == 0 && continue + for (m,a) in test_assembly_data[p,i] + m′ = get(test_id_in_blk, m, 0) + m′ == 0 && continue + store(a*zlocals[Threads.threadid()][i,j]*b, m′, n′) +end end end end end end end + + function assemblerow!(biop::IntegralOperator, test_functions::Space, trial_functions::Space, store; quaddata=quaddata, quadrule=quadrule) From 6920c28d8a6275080b410579ce7351530d939e36 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 28 Sep 2021 14:35:33 +0100 Subject: [PATCH 070/528] remove closure name conflict --- src/integralop.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/integralop.jl b/src/integralop.jl index 54d3dbbd..d21dbd63 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -176,14 +176,15 @@ function blockassembler(biop::IntegralOperator, tfs::Space, bfs::Space; # end if !CompScienceMeshes.refines(tfs.geo, bfs.geo) - return function f(test_ids, trial_ids, store) + # if true + return (test_ids, trial_ids, store) -> begin assembleblock_body!(biop, tfs, test_ids, test_elements, test_assembly_data, bfs, trial_ids, trial_elements, trial_assembly_data, quadrature_data, zlocals, store, quadrule=quadrule) end else - return function f(test_ids, trial_ids, store) + return (test_ids, trial_ids, store) -> begin assembleblock_body_nested!(biop, tfs, test_ids, test_elements, test_assembly_data, bfs, trial_ids, trial_elements, trial_assembly_data, From bfbd303f9000a2ca9753829cb9fcf106df4bd243 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 29 Sep 2021 09:27:30 +0100 Subject: [PATCH 071/528] docs on quadstrat proposal --- docs/mkdocs.yml | 1 + docs/src/quadstrat.md | 67 +++++++++++++++++++++++++++++++++++++++++++ src/integralop.jl | 8 ------ 3 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 docs/src/quadstrat.md diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 004c791e..9fae846d 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -15,3 +15,4 @@ pages: - Home: index.md - Tutorial: tutorial.md - Assembly: assemble.md + - Quadrature Strategies: quadstrat.md diff --git a/docs/src/quadstrat.md b/docs/src/quadstrat.md new file mode 100644 index 00000000..6711c517 --- /dev/null +++ b/docs/src/quadstrat.md @@ -0,0 +1,67 @@ +# Quadrature strategies + +## Introduction + +There are many ways to approximately compute the singular integrals that appear in boundary element discretisations of surface and volume integral euqations. + +BEAST.jl is configured to select reasonable defaults, but advanced users may want to select their own quadrature rules. This section provides information on how to do this. + +## quaddata and quadrule + +Numerical quadrature is governed by a pair of functions that need to be designed to work together: + +- `quaddata`: this function is executed before the assembly loop is entered. It's job is to compute all data needed for quadrature that the developer wants to be cached. Typically this is all geometric information such as the parametric and cartesian coordinates of all quadratures rules for all elemenents. It makes sense to cache this data as it will be used many times over in the double for loop that governs assembly. Typically, near singular interactions require more careful treatment than far interactions. This means that multiple quadrature rules per elements can be required. In such cases, the developer may want to opt to sture quadrature points and weights for all these rules. The function returns a quaddata object that holds all the cached data. +- `quadrule`: quadrule is executed inside the assembly hotloop. It receives a pair of elements and the quaddata object as its arguments. Based on this, the relevant cached data is extracted and stored in a quadrule object. The type of this object will determine the actual quadrature routined that will be called upon to do the numerical quadrature. + +## quadstrat + +The pair of quaddata and quadrule methods that is used is determined by the type of the operator and finite elements, and a `quadstrat` object. This object is passed to the assembly routine and passed on to `quaddata` and `quadstrat`, so it can be considered during dispatch. + +Parameters, such as those that determine the accuracy of the numerical quadrature, are part of the runtime payload of the quadstrat object. This is usefull when the user is interested on the impact of these parameters on the performance and the accuracy of the solver without having to supply a new pair of `quadstrat`/`quaddata` methods for each possible value of these parameters. + +Roughly this leads to the following (simplified) assembly routine: + +```julia +function assemble(op, tfs, bfs, store; quadstrat=QS) + + tad, tels = assemblydata(op, tfs) + bad, bels = assemblydata(op, bfs) + + qd = quadata(op,tels,bels,quadstrat) + for tel in tels + for bel in bels + qr = quadrule(op,tel,bel,qd,quadstrat) + zlocal = momintegrals(op,tel,bel,qr) + + for i in axes(zlocal,1) + for j in axes(zlocal,2) + m, a = tad[tel,i] + n, b = bad[bel,j] + store(a*zlocal[i,j]*b,m,n) +end end end end end +``` + +It is conceivable that the types and functions described above look like this: + +```julia +struct DoubleNumQS + test_precision + trial_precision +end + +function quaddata(op, tels, bels, quadstrat::DoubleNumQS) + tqps = [quadpoints(tel,precision=quadstrat.test_precision) for tel in tels] + bqps = [quadpoints(bel,precision=quadstrat.basis_precision) for bel in bels] + return (test_quadpoints=tqps, basis_quadpoints=bqps) +end + + +struct DoubleNumQR + test_quadpoints + trial_quadpoints +end + +function quadstrat(op, tel, bel, qd, quadstrat::DoubleNumQs) + return DoubleNumQR(qd.test_quadpoints[tel], qd.basis_quadpoints[bel]) +end +``` \ No newline at end of file diff --git a/src/integralop.jl b/src/integralop.jl index d21dbd63..e414b233 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -168,15 +168,7 @@ function blockassembler(biop::IntegralOperator, tfs::Space, bfs::Space; trial_elements, trial_assembly_data, quadrature_data, zlocals = assembleblock_primer(biop, tfs, bfs, quaddata=quaddata) - # return function f(test_ids, trial_ids, store) - # assembleblock_body!(biop, - # tfs, test_ids, test_elements, test_assembly_data, - # bfs, trial_ids, trial_elements, trial_assembly_data, - # quadrature_data, zlocals, store, quadrule=quadrule) - # end - if !CompScienceMeshes.refines(tfs.geo, bfs.geo) - # if true return (test_ids, trial_ids, store) -> begin assembleblock_body!(biop, tfs, test_ids, test_elements, test_assembly_data, From 6438c060415d88f8f90c62f28677c498c203f56d Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 30 Sep 2021 11:26:16 +0200 Subject: [PATCH 072/528] deploydocs reinserted --- docs/Project.toml | 1 + docs/make.jl | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index 6a233f0c..ad968743 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,3 +1,4 @@ [deps] BEAST = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" +CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" diff --git a/docs/make.jl b/docs/make.jl index 1e48f7a1..91931468 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -4,7 +4,8 @@ makedocs( clean=false, sitename="BEAST Documentation") -# deploydocs( -# deps = Deps.pip("mkdocs", "python-markdown-math"), -# repo = "github.com/krcools/BEAST.jl.git", -# julia = "1.0") +deploydocs( + # deps = Deps.pip("mkdocs", "python-markdown-math"), + repo = "github.com/krcools/BEAST.jl.git", + # julia = "1.0", +) From e76fc4f66a1ac60d97f4282d4295ad147ca4a834 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 30 Sep 2021 11:40:04 +0200 Subject: [PATCH 073/528] added to quadstrat.md --- docs/src/quadstrat.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/src/quadstrat.md b/docs/src/quadstrat.md index 6711c517..cc1a606d 100644 --- a/docs/src/quadstrat.md +++ b/docs/src/quadstrat.md @@ -61,7 +61,21 @@ struct DoubleNumQR trial_quadpoints end -function quadstrat(op, tel, bel, qd, quadstrat::DoubleNumQs) - return DoubleNumQR(qd.test_quadpoints[tel], qd.basis_quadpoints[bel]) +struct HighPrecisionQR end + +function quadrule(op, tel, bel, qd, quadstrat::DoubleNumQs) + if wellseparated(tel, bel) + return DoubleNumQR(qd.test_quadpoints[tel], qd.basis_quadpoints[bel]) + else + return HighPrecisionQR(tel, bel) + end +end + +function momintegrals(op, tel, bel, qr::DoubleNumQR) + ... +end + +function momintegrals(op, tel, bel, qr::HighPrecisionQR) + ... end ``` \ No newline at end of file From 5c129223ceb5bdafaaf4cc838cd1c2d2c76729b5 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 7 Oct 2021 15:43:05 +0200 Subject: [PATCH 074/528] sorting RT on construction can be toggled --- Project.toml | 2 +- examples/capacitor.jl | 2 +- examples/mthelmholtz.jl | 23 +++++++++++--- examples/mtjunction.jl | 53 +++++++------------------------ examples/pmchwt.jl | 13 +++++++- src/bases/bcspace.jl | 6 ++-- src/bases/rtspace.jl | 51 ++++++++++++++++++------------ src/solvers/lusolver.jl | 19 +++++++++-- src/utils/zeromap.jl | 5 +-- test/runtests.jl | 1 + test/test_directproduct.jl | 4 +-- test/test_dvg.jl | 10 +++++- test/test_mixed_blkassm.jl | 64 ++++++++++++++++++++++++++++++++++++++ test/test_nitsche.jl | 10 ++++-- 14 files changed, 182 insertions(+), 81 deletions(-) create mode 100644 test/test_mixed_blkassm.jl diff --git a/Project.toml b/Project.toml index 2bce6223..e09dab75 100644 --- a/Project.toml +++ b/Project.toml @@ -34,7 +34,7 @@ FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" FillArrays = "0.11, 0.12" IterativeSolvers = "^0.9" -LiftedMaps = "0.2" +LiftedMaps = "0.2.1" LinearMaps = "^3.3" SauterSchwabQuadrature = "^2.1.1, 2.1" SparseMatrixDicts = "0.2" diff --git a/examples/capacitor.jl b/examples/capacitor.jl index e18b0cd8..d7b57ec6 100644 --- a/examples/capacitor.jl +++ b/examples/capacitor.jl @@ -58,7 +58,7 @@ fcr,geo = facecurrents(u, RT); println("Face currents calculated") z = range(-0.5, stop=0.5, length=100) pts1 = point.(0.5,0.5,z) # pts1 = [point(0.5,0.5,a) for a in range(-0.5,stop=0.5,length=100)] -volt = η*potential(MWSingleLayerPotential3D(κ), pts1, u, RT) +volt = η*potential(MWSingleLayerPotential3D(κ), pts1, u, RT, type=ComplexF64) # Plot the Scalar potential using Plots diff --git a/examples/mthelmholtz.jl b/examples/mthelmholtz.jl index cbd30b06..553295bc 100644 --- a/examples/mthelmholtz.jl +++ b/examples/mthelmholtz.jl @@ -46,13 +46,13 @@ e = strace(E, G12) ex = assemble(e, X) Sxx = assemble(SL, X, X) -N1 = assemble(Identity(), X12, Y12) -N2 = assemble(Identity(), X23, Y23) -N3 = assemble(Identity(), X31, Y31) -Nxy = blkdiagm(N1,N2,N3) Syy = assemble(HS, Y, Y) -Dyx = inv(Nxy) +N1 = Matrix(assemble(Identity(), X12, Y12)) +N2 = Matrix(assemble(Identity(), X23, Y23)) +N3 = Matrix(assemble(Identity(), X31, Y31)) +Dyx = blkdiagm(inv(N1),inv(N2),inv(N3)) + Q = transpose(Dyx) * Syy * Dyx * Sxx R = transpose(Dyx) * Syy * Dyx * ex @@ -62,6 +62,19 @@ u2, ch2 = solve(BEAST.GMRESSolver(Q),R) @show ch1.iters @show ch2.iters +rad, ϕ = 10, π/2 +thetas = range(0,π,length=200) +pts = [rad*point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in thetas] + +DLp = BEAST.HH3DDoubleLayerNear(wavenumber=κ) +near1 = BEAST.potential(DLp, pts, u1, X, type=ComplexF64) +near2 = BEAST.potential(DLp, pts, u2, X, type=ComplexF64) +inc = E.(pts) + +using Plots +plot(thetas, abs.(vec(near1))) +scatter!(thetas, abs.(vec(near2))) + using LinearAlgebra cond(Sxx) cond(Q) diff --git a/examples/mtjunction.jl b/examples/mtjunction.jl index ed615dd2..88644bb4 100644 --- a/examples/mtjunction.jl +++ b/examples/mtjunction.jl @@ -11,15 +11,15 @@ G31 = weld(G3,-G1) G21 = weld(G2,-G1) -X12 = raviartthomas(G12) -X23 = raviartthomas(G23) -X31 = raviartthomas(G31) +X12 = raviartthomas(G12, sort=:none) +X23 = raviartthomas(G23, sort=:none) +X31 = raviartthomas(G31, sort=:none) -X21 = raviartthomas(G21) +X21 = raviartthomas(G21, sort=:none) -Y12 = buffachristiansen(G12) -Y23 = buffachristiansen(G23) -Y31 = buffachristiansen(G31) +Y12 = buffachristiansen(G12, sort=:none) +Y23 = buffachristiansen(G23, sort=:none) +Y31 = buffachristiansen(G31, sort=:none) Y21 = buffachristiansen(G21) @@ -30,8 +30,8 @@ N = NCross() X = X12 × X23 × X31 Y = Y12 × Y23 × Y31 -X = X12 × X21 -Y = Y12 × Y21 +# X = X12 × X21 +# Y = Y12 × Y21 E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) e = (n × E) × n @@ -54,12 +54,6 @@ mtefie = @discretise( import Plotly using LinearAlgebra -# p1 = PlotlyJS.Plot(patch(G12, norm.(fcr12))) -# p2 = PlotlyJS.Plot(patch(G23, norm.(fcr23))) -# p3 = PlotlyJS.Plot(patch(G31, norm.(fcr31))) - -# PlotlyJS.plot([p1, p2, p3]) - function blkdiagm(blks...) m = sum(size(b,1) for b in blks) @@ -85,11 +79,10 @@ Nxy = blkdiagm(N1,N2,N3) Syy = assemble(SL, Y, Y) iNxy = blkdiagm(inv(Matrix(N1)), inv(Matrix(N2)), inv(Matrix(N3))) -N21 = assemble(N, X21, Y21) -Nxy = blkdiagm(N1, N21) -iNxy = blkdiagm(inv(Matrix(N1)), inv(Matrix(N21))) +# N21 = assemble(N, X21, Y21) +# Nxy = blkdiagm(N1, N21) +# iNxy = blkdiagm(inv(Matrix(N1)), inv(Matrix(N21))) -# cond(Matrix(Sxx)) ex = assemble(e,X) P = transpose(iNxy) * Syy * iNxy Q = P * Sxx; @@ -101,28 +94,6 @@ u2, ch2 = solve(BEAST.GMRESSolver(Q), R) @show ch1.iters @show ch2.iters -# gmres() - -# ns = [ -# 0, -# numfunctions(X12), -# numfunctions(X23), -# numfunctions(X31)] - -# cns = cumsum(ns) -# u12 = u1[cns[1]+1:cns[2]] -# u23 = u1[cns[2]+1:cns[3]] -# u31 = u1[cns[3]+1:cns[4]] - -# fcr1, geo1 = facecurrents(u12, X12) -# fcr2, geo2 = facecurrents(u23, X23) -# fcr3, geo3 = facecurrents(u31, X31) - -# p1 = patch(geo1, norm.(fcr1)) -# p2 = patch(geo2, norm.(fcr2)) -# p3 = patch(geo3, norm.(fcr3)) - -# Plotly.plot([p1,p2,p3]) G123 = weld(G1,G2,G3) X123 = raviartthomas(G123) diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index fdf4b2d5..a7593105 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -1,6 +1,10 @@ using CompScienceMeshes, BEAST using LinearAlgebra +using BEAST.LinearMaps +Base.axes(A::LinearMaps.LinearCombination) = axes(A.maps[1]) +Base.axes(A::LinearMaps.ScaledMap) = axes(A.lmap) + Γ = meshcuboid(0.5, 1.0, 1.0, 0.045) # Γ = meshcuboid(0.5, 1.0, 1.0, 0.15) CompScienceMeshes.translate!(Γ, point(-0.25, -0.5, -0.5)) @@ -32,6 +36,9 @@ pmchwt = @discretise( # Z = BEAST.sysmatrix(pmchwt) # b = BEAST.rhs(pmchwt) +# M = BEAST.convert_to_dense(Z) +# u = M \ b +# error() # W = BEAST.assemble_hide(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict) # using BEAST.BlockArrays @@ -44,7 +51,11 @@ pmchwt = @discretise( # u, ch = IterativeSolvers.gmres!(u, Z, b, log=true, maxiter=1000, # restart=1000, reltol=1e-5, verbose=true) -u = BEAST.gmres(pmchwt, tol=1e-5) + + +# u = BEAST.gmres(pmchwt, tol=1e-5) +# error() +u = BEAST.solve(pmchwt) Θ, Φ = range(0.0,stop=2π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] diff --git a/src/bases/bcspace.jl b/src/bases/bcspace.jl index 5788d1e3..88fa0e49 100644 --- a/src/bases/bcspace.jl +++ b/src/bases/bcspace.jl @@ -87,18 +87,18 @@ isclosed(a, pred) = length(a)>2 && pred(a[end], a[1]) Construct the set of Buffa-Christiansen functions subject to mesh Γ and only enforcing zero normal components on ∂Γ ∖ γ. """ -function buffachristiansen(Γ, γ=mesh(coordtype(Γ),1,3); ibscaled=false) +function buffachristiansen(Γ, γ=mesh(coordtype(Γ),1,3); ibscaled=false, sort=:spacefillingcurve) @assert CompScienceMeshes.isoriented(Γ) T = coordtype(Γ) P = vertextype(Γ) - edges = skeleton(Γ, 1) + edges = skeleton(Γ, 1; sort) fine = if ibscaled CompScienceMeshes.lineofsight_refinement(Γ) else - barycentric_refinement(Γ) + barycentric_refinement(Γ; sort) end in_interior = interior_tpredicate(Γ) diff --git a/src/bases/rtspace.jl b/src/bases/rtspace.jl index 5ab29623..1565b72d 100644 --- a/src/bases/rtspace.jl +++ b/src/bases/rtspace.jl @@ -66,6 +66,18 @@ function raviartthomas(mesh, cellpairs::Array{Int,2}) end +function raviartthomas(mesh, edges::CompScienceMeshes.AbstractMesh{U,2} where {U}) + cps = CompScienceMeshes.cellpairs(mesh, edges) + # ids = findall(x -> x>0, cps[2,:]) + raviartthomas(mesh, cps) +end + +function raviartthomas(mesh::CompScienceMeshes.AbstractMesh{U,3} where {U}) + bnd = boundary(mesh) + edges = submesh(!in(bnd), skeleton(mesh,1)) + return raviartthomas(mesh, edges) +end + """ raviartthomas(mesh) @@ -78,8 +90,8 @@ Calls raviartthomas(mesh::Mesh, cellpairs::Array{Int,2}), which constructs Returns the RT basis object. """ -function raviartthomas(mesh) - edges = skeleton(mesh, 1) +function raviartthomas(mesh; sort=:spacefillingcurve) + edges = skeleton(mesh, 1; sort) cps = cellpairs(mesh, edges, dropjunctionpair=true) ids = findall(x -> x>0, cps[2,:]) raviartthomas(mesh, cps[:,ids]) @@ -87,31 +99,30 @@ end -""" - raviartthomas(Γ, γ) +# """ +# raviartthomas(Γ; neumann) -Constructs the RT space relative to boundary `γ` of an open surface, only - selecting cell pairs whose common edge does not lie on `γ` . (This prevents - the calculation of physically-impossible surface currents, such as those - flowing 'off the edge' of a surface.) +# Constructs the RT space relative to boundary `neumann` of an open surface, only +# selecting cell pairs whose common edge does lie on `γ` . (This prevents +# the calculation of physically-impossible surface currents, such as those +# flowing 'off the edge' of a surface.) +# """ +# function raviartthomas(Γ; neumann) -Calls raviartthomas(Γ, duals) which constructs - the RT basis on the `mesh`, using the cell pairs identified. -""" -function raviartthomas(Γ, γ) +# γ = neumann - in_interior = interior_tpredicate(Γ) - on_junction = overlap_gpredicate(γ) +# in_interior = interior_tpredicate(Γ) +# on_junction = overlap_gpredicate(γ) - pred = c -> (in_interior(c) || on_junction(chart(Γ,c))) +# pred = c -> (in_interior(c) || on_junction(chart(Γ,c))) - edges = skeleton(pred, Γ, 1) - cps = cellpairs(Γ, edges, dropjunctionpair=true) +# edges = skeleton(pred, Γ, 1) +# cps = cellpairs(Γ, edges, dropjunctionpair=true) - raviartthomas(Γ, cps) -end +# raviartthomas(Γ, cps) +# end -raowiltonglisson = raviartthomas +# raowiltonglisson = raviartthomas """ diff --git a/src/solvers/lusolver.jl b/src/solvers/lusolver.jl index c5de7991..c3944834 100644 --- a/src/solvers/lusolver.jl +++ b/src/solvers/lusolver.jl @@ -1,3 +1,16 @@ +function convert_to_dense(A::LinearMaps.LinearCombination) + + T = eltype(A) + # M = zeros(T, size(A)) + M = PseudoBlockMatrix{T}(undef, BlockArrays.blocksizes(A)...) + fill!(M,0) + for map in A.maps + M .+= Matrix(map) + end + + return M +end + """ Solves a variational equation by simply creating the full system matrix and calling a traditional lu decomposition. @@ -18,10 +31,12 @@ function solve(eq) b = assemble(rhs, test_space_dict) Z = assemble(lhs, test_space_dict, trial_space_dict) + M = convert_to_dense(Z) + println("Sysmatrix converted to dense.") - u = Z \ b + u = Matrix(M) \ Vector(b) - return u + return PseudoBlockVector(u, blocksizes(M,2)) end diff --git a/src/utils/zeromap.jl b/src/utils/zeromap.jl index 769904b2..cd3d64c6 100644 --- a/src/utils/zeromap.jl +++ b/src/utils/zeromap.jl @@ -21,9 +21,10 @@ function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector) end function Base.:(*)(A::ZeroMap, x::AbstractVector) - # y = zero(A.image) T = eltype(A) y = similar(A.range, T) fill!(y, zero(T)) LinearAlgebra.mul!(y,A,x) -end \ No newline at end of file +end + +Base.Matrix{T}(A::ZeroMap) where {T} = zeros(T, length(A.range), length(A.domain)) \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 7bab9663..ed8935ac 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -44,6 +44,7 @@ include("test_embedding.jl") include("test_laminated.jl") include("test_assemblerow.jl") +include("test_mixed_blkassm.jl") include("test_local_assembly.jl") include("test_dipole.jl") diff --git a/test/test_directproduct.jl b/test/test_directproduct.jl index 7eba3395..f9c87e66 100644 --- a/test/test_directproduct.jl +++ b/test/test_directproduct.jl @@ -4,8 +4,8 @@ using BEAST using Test m1 = meshrectangle(1.0, 1.0, 0.5) -m2 = translate!(meshrectangle(1.0, 1.0, 0.5), point(0,0,1)) -m3 = translate!(meshrectangle(1.0, 1.0, 0.5), point(0,0,2)) +m2 = CompScienceMeshes.translate!(meshrectangle(1.0, 1.0, 0.5), point(0,0,1)) +m3 = CompScienceMeshes.translate!(meshrectangle(1.0, 1.0, 0.5), point(0,0,2)) X1 = raviartthomas(m1) X2 = raviartthomas(m2) diff --git a/test/test_dvg.jl b/test/test_dvg.jl index 262d650d..259bdcb8 100644 --- a/test/test_dvg.jl +++ b/test/test_dvg.jl @@ -1,11 +1,19 @@ using BEAST using CompScienceMeshes +using SparseArrays using Test Γ = meshrectangle(1.0,1.0,1.0) γ = meshsegment(1.0,1.0,3) -X = raviartthomas(Γ, γ) + +in_interior = CompScienceMeshes.interior_tpredicate(Γ) +on_junction = CompScienceMeshes.overlap_gpredicate(γ) +edges = skeleton(Γ,1) do edge + in_interior(edge) ||on_junction(chart(Γ,edge)) +end +length(edges) +X = raviartthomas(Γ, edges) x = divergence(X) diff --git a/test/test_mixed_blkassm.jl b/test/test_mixed_blkassm.jl new file mode 100644 index 00000000..a251a747 --- /dev/null +++ b/test/test_mixed_blkassm.jl @@ -0,0 +1,64 @@ +# test resolutuion of #66 + +using BEAST +using Test +using CompScienceMeshes +using LinearAlgebra + +function hassemble(operator::BEAST.AbstractOperator, + test_functions, + trial_functions) + + blkasm = BEAST.blockassembler(operator, test_functions, trial_functions) + + function assembler(Z, tdata, sdata) + store(v,m,n) = (Z[m,n] += v) + blkasm(tdata,sdata,store) + end + + mat = zeros(scalartype(operator), + numfunctions(test_functions), + numfunctions(trial_functions)) + + assembler(mat, 1:numfunctions(test_functions), 1:numfunctions(trial_functions)) + return mat +end + +c = 3e8 +μ = 4π * 1e-7 +ε = 1/(μ*c^2) +f = 1e8 +λ = c/f +k = 2π/λ +ω = k*c +η = sqrt(μ/ε) + +a = 1 +Γ = CompScienceMeshes.meshcuboid(a,a,a,0.2) + +𝓣 = Maxwell3D.singlelayer(wavenumber=k) +𝓚 = Maxwell3D.doublelayer(wavenumber=k) + +X = raviartthomas(Γ) +Y = buffachristiansen(Γ) + +println("Number of RWG functions: ", numfunctions(X)) + +T_blockassembler = hassemble(𝓣, X, X) +T_standardassembler = assemble(𝓣, X, X) + +@test norm(T_blockassembler - T_standardassembler)/norm(T_standardassembler) ≈ 0.0 atol=1e-14 + +T_bc_blockassembler = hassemble(𝓣, Y, Y) +T_bc_standardassembler = assemble(𝓣, Y, Y) + +@test norm(T_bc_blockassembler - T_bc_standardassembler)/norm(T_bc_standardassembler) ≈ 0.0 atol=1e-14 + +K_mix_blockassembler = hassemble(𝓚,Y,X) +K_mix_standardassembler = assemble(𝓚,Y,X) + +T_mix_blockassembler = hassemble(𝓣, Y, X) +T_mix_standardassembler = assemble(𝓣, Y, X) + +@test norm(K_mix_blockassembler - K_mix_standardassembler)/norm(K_mix_standardassembler) ≈ 0.0 atol=1e-14 +@test norm(T_mix_blockassembler - T_mix_standardassembler)/norm(T_mix_standardassembler) ≈ 0.0 atol=1e-14 \ No newline at end of file diff --git a/test/test_nitsche.jl b/test/test_nitsche.jl index d748d22a..16994ac5 100644 --- a/test/test_nitsche.jl +++ b/test/test_nitsche.jl @@ -1,5 +1,6 @@ using Test -#using LinearForms +using LinearAlgebra + using CompScienceMeshes using BEAST @@ -14,7 +15,12 @@ h = 0.25 Γ = meshrectangle(1.0,1.0,h) γ = meshsegment(1.0,1.0,3) -X = raviartthomas(Γ, γ) +in_interior = CompScienceMeshes.interior_tpredicate(Γ) +on_junction = CompScienceMeshes.overlap_gpredicate(γ) +edges = skeleton(Γ,1) do edge + in_interior(edge) ||on_junction(chart(Γ,edge)) +end +X = raviartthomas(Γ, edges) x = divergence(X) y = ntrace(X,γ) From 80e92e2b38068dd855aa803679339cc048064e95 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 7 Oct 2021 16:55:41 +0200 Subject: [PATCH 075/528] Name changes --- docs/src/assemble.md | 4 ++-- src/helmholtz2d/helmholtzop.jl | 2 +- src/helmholtz3d/hh3dops.jl | 20 ++++++++-------- src/helmholtz3d/nitsche.jl | 2 +- src/localop.jl | 4 ---- src/maxwell/mwops.jl | 18 +++++++------- src/maxwell/nitsche.jl | 2 +- src/maxwell/nxdbllayer.jl | 2 +- src/maxwell/wiltonints.jl | 8 +++---- src/quaddata.jl | 4 ++-- src/quadrature/double_quadrature.jl | 4 ++-- src/quadrature/quadstrats.jl | 15 ++++++++++++ src/quadrature/singularity_extraction.jl | 6 ++--- test/test_bogaertints.jl | 30 ++++++++++++------------ test/test_sauterschwabints.jl | 12 +++++----- test/test_ss_nested_meshes.jl | 16 ++++++------- test/test_wiltonints.jl | 6 ++--- test/untested/test_mwops.jl | 2 +- 18 files changed, 84 insertions(+), 73 deletions(-) create mode 100644 src/quadrature/quadstrats.jl diff --git a/docs/src/assemble.md b/docs/src/assemble.md index f937e44f..00824548 100644 --- a/docs/src/assemble.md +++ b/docs/src/assemble.md @@ -134,7 +134,7 @@ function quaddata(operator::SingleLayerTrace, end function quadrule(op::SingleLayerTrace, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd) - DoubleQuadStrategy( + DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j] ) @@ -145,7 +145,7 @@ integrand(op::SingleLayerTrace, kernel, g, τ, f, σ) = f[1]*g[1]*kernel.green Every kernel corresponds with a type. Kernels can potentially depend on a set of parameters; these appear as fields in the type. Here our Nitsche kernel depends on the wavenumber. In quaddata we precompute quadrature points for all geometric cells in the supports of test and trial elements. This is fairly sloppy: only one rule for test and trial integration is considered. A high accuracy implementation would typically compute points for both low quality and high quality quadrature rules. -Also `quadrule` is sloppy: we always select a `DoubleQuadStrategy` to perform the computation of interactions between local shape functions. No singularity extraction or other advanced technique is considered for nearby interactions. Clearly amateurs at work here! +Also `quadrule` is sloppy: we always select a `DoubleQuadRule` to perform the computation of interactions between local shape functions. No singularity extraction or other advanced technique is considered for nearby interactions. Clearly amateurs at work here! `BEAST` provides a default implementation of an integration routine using double numerical quadrature. All that is required to tap into that implementation is a method overloading `integrand`. From the above formula it is clear what this method should look like. diff --git a/src/helmholtz2d/helmholtzop.jl b/src/helmholtz2d/helmholtzop.jl index 3b0c41ff..bc3fd942 100644 --- a/src/helmholtz2d/helmholtzop.jl +++ b/src/helmholtz2d/helmholtzop.jl @@ -40,7 +40,7 @@ end function quadrule(op::HelmholtzOperator2D, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd) - DoubleQuadStrategy( + DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j] ) diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index 74a8ca3e..0da0df32 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -94,13 +94,13 @@ function quadrule(op::HH3DSingleLayerFDBIO, test_quadpoints = quadrature_data[1] trial_quadpoints = quadrature_data[2] - hits != 0 && return WiltonSEStrategy( + hits != 0 && return WiltonSERule( test_quadpoints[1,i], - DoubleQuadStrategy( + DoubleQuadRule( test_quadpoints[1,i], trial_quadpoints[1,j])) - return DoubleQuadStrategy( + return DoubleQuadRule( quadrature_data[1][1,i], quadrature_data[2][1,j]) end @@ -118,20 +118,20 @@ function quadrule(op::HH3DSingleLayerFDBIO, test_refspace::subReferenceSpace, # test_quadpoints = quadrature_data[1] # trial_quadpoints = quadrature_data[2] - # hits != 0 && return WiltonSEStrategy( + # hits != 0 && return WiltonSERule( # test_quadpoints[1,i], - # DoubleQuadStrategy( + # DoubleQuadRule( # test_quadpoints[1,i], # trial_quadpoints[1,j])) - return DoubleQuadStrategy( + return DoubleQuadRule( quadrature_data[1][1,i], quadrature_data[2][1,j]) # return SauterSchwabStrategy(hits) # test_qd = qd[1] # trial_qd = qd[2] # - # DoubleQuadStrategy( + # DoubleQuadRule( # test_qd[1,i], # rule 1 on test element j # trial_qd[1,j] # rule 1 on trial element i # ) @@ -145,7 +145,7 @@ function quadrule(op::Helmholtz3DOp, test_quadpoints = quadrature_data[1] trial_quadpoints = quadrature_data[2] - return DoubleQuadStrategy( + return DoubleQuadRule( quadrature_data[1][1,i], quadrature_data[2][1,j]) end @@ -157,7 +157,7 @@ function quadrule(op::Helmholtz3DOp, test_quadpoints = quadrature_data[1] trial_quadpoints = quadrature_data[2] - return DoubleQuadStrategy( + return DoubleQuadRule( quadrature_data[1][1,i], quadrature_data[2][1,j]) end @@ -207,7 +207,7 @@ end function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, test_refspace::LagrangeRefSpace{T,0} where {T}, trial_refspace::LagrangeRefSpace{T,0} where {T}, - test_elements, trial_element, zlocal, quadrature_rule::WiltonSEStrategy, dx) + test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) γ = op.gamma α = op.alpha diff --git a/src/helmholtz3d/nitsche.jl b/src/helmholtz3d/nitsche.jl index 4c9e3927..13740939 100644 --- a/src/helmholtz3d/nitsche.jl +++ b/src/helmholtz3d/nitsche.jl @@ -17,7 +17,7 @@ function quaddata(operator::NitscheHH3, end function quadrule(op::NitscheHH3, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd) - DoubleQuadStrategy( + DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j] ) diff --git a/src/localop.jl b/src/localop.jl index 73b3a750..14ef7732 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -2,10 +2,6 @@ using CollisionDetection -# mutable struct SingleQuadStrategy{T} -# coords::Vector{T} -# weights::Vector{T} -# end abstract type LocalOperator <: Operator end diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index feefdf3b..f078d549 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -167,12 +167,12 @@ function qrss(op, g, f, i, τ, j, σ, qd) h2 = volume(σ) xtol2 = 0.2 * 0.2 k2 = abs2(op.gamma) - max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSEStrategy( + max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( qd.tpoints[2,i], - DoubleQuadStrategy( + DoubleQuadRule( qd.tpoints[2,i], qd.bpoints[2,j],),) - return DoubleQuadStrategy( + return DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j],) end @@ -204,14 +204,14 @@ function qrib(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, hits == 2 && return BogaertEdgePatchStrategy(8, 4) hits == 1 && return BogaertPointPatchStrategy(2, 3) rmin = xmin/k - xmin < xtol && return WiltonSEStrategy( + xmin < xtol && return WiltonSERule( qd.tpoints[1,i], - DoubleQuadStrategy( + DoubleQuadRule( qd.tpoints[2,i], qd.bpoints[2,j], ), ) - return DoubleQuadStrategy( + return DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j], ) @@ -241,14 +241,14 @@ function qrdf(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, end end - xmin < xtol && return WiltonSEStrategy( + xmin < xtol && return WiltonSERule( qd.tpoints[1,i], - DoubleQuadStrategy( + DoubleQuadRule( qd.tpoints[2,i], qd.bpoints[2,j], ), ) - return DoubleQuadStrategy( + return DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j], ) diff --git a/src/maxwell/nitsche.jl b/src/maxwell/nitsche.jl index d5499a8a..a833ee1a 100644 --- a/src/maxwell/nitsche.jl +++ b/src/maxwell/nitsche.jl @@ -28,7 +28,7 @@ end # Use numerical quadrature for now # Note: basis integral is over triangle, test over line function quadrule(op::SingleLayerTrace, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd) - DoubleQuadStrategy( + DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j] ) diff --git a/src/maxwell/nxdbllayer.jl b/src/maxwell/nxdbllayer.jl index b6ca769a..89ec92c8 100644 --- a/src/maxwell/nxdbllayer.jl +++ b/src/maxwell/nxdbllayer.jl @@ -29,7 +29,7 @@ function quadrule(operator::DoubleLayerRotatedMW3D, test_quad_rules = quad_data[1] trial_quad_rules = quad_data[2] - DoubleQuadStrategy( + DoubleQuadRule( test_quad_rules[1,test_id], trial_quad_rules[1,trial_id] ) diff --git a/src/maxwell/wiltonints.jl b/src/maxwell/wiltonints.jl index 77287d76..daa2407c 100644 --- a/src/maxwell/wiltonints.jl +++ b/src/maxwell/wiltonints.jl @@ -1,12 +1,12 @@ import WiltonInts84 -mutable struct WiltonSEStrategy{P,Q} <: SingularityExtractionStrategy +mutable struct WiltonSERule{P,Q} <: SingularityExtractionRule outer_quad_points::P - regularpart_quadrule::DoubleQuadStrategy{P,Q} + regularpart_quadrule::DoubleQuadRule{P,Q} end function innerintegrals!(op::MWSingleLayer3DSng, p, g, f, t, s, z, - strat::WiltonSEStrategy, dx) + strat::WiltonSERule, dx) γ = op.gamma x = cartesian(p) @@ -48,7 +48,7 @@ function innerintegrals!(op::MWSingleLayer3DSng, p, g, f, t, s, z, end -function innerintegrals!(op::MWDoubleLayer3DSng, p, g, f, t, s, z, strat::WiltonSEStrategy, dx) +function innerintegrals!(op::MWDoubleLayer3DSng, p, g, f, t, s, z, strat::WiltonSERule, dx) γ = op.gamma x = cartesian(p) diff --git a/src/quaddata.jl b/src/quaddata.jl index 1b6e79f3..8abd281c 100644 --- a/src/quaddata.jl +++ b/src/quaddata.jl @@ -56,10 +56,10 @@ function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - xmin < xtol && return DoubleQuadStrategy( + xmin < xtol && return DoubleQuadRule( qd.tpoints[2,i], qd.bpoints[2,j],) - return DoubleQuadStrategy( + return DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j],) end diff --git a/src/quadrature/double_quadrature.jl b/src/quadrature/double_quadrature.jl index 03da1cf6..ea14832f 100644 --- a/src/quadrature/double_quadrature.jl +++ b/src/quadrature/double_quadrature.jl @@ -1,4 +1,4 @@ -struct DoubleQuadStrategy{P,Q} +struct DoubleQuadRule{P,Q} outer_quad_points::P inner_quad_points::Q end @@ -9,7 +9,7 @@ end Function for the computation of moment integrals using simple double quadrature. """ -function momintegrals!(biop, tshs, bshs, tcell, bcell, z, strat::DoubleQuadStrategy) +function momintegrals!(biop, tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) # memory allocation here is a result from the type instability on strat # which is on purpose, i.e. the momintegrals! method is chosen based diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl new file mode 100644 index 00000000..32c60cca --- /dev/null +++ b/src/quadrature/quadstrats.jl @@ -0,0 +1,15 @@ +struct DoubleNumWiltonSauterQStrat{R,S} + outer_rule_far::R + inner_rule_far::R + outer_rule_near::R + inner_rule_near::R + sauter_schwab_common_tetr::S + sauter_schwab_common_face::S + sauter_schwab_common_edge::S + sauter_schwab_common_vert::S +end + +struct DoubleNum{R} + outer_rule::R + inner_rule::R +end \ No newline at end of file diff --git a/src/quadrature/singularity_extraction.jl b/src/quadrature/singularity_extraction.jl index debbdb4d..872f0293 100644 --- a/src/quadrature/singularity_extraction.jl +++ b/src/quadrature/singularity_extraction.jl @@ -1,7 +1,7 @@ -abstract type SingularityExtractionStrategy end -regularpart_quadrule(qr::SingularityExtractionStrategy) = qr.regularpart_quadrule +abstract type SingularityExtractionRule end +regularpart_quadrule(qr::SingularityExtractionRule) = qr.regularpart_quadrule -function momintegrals!(op, g, f, t, s, z, strat::SingularityExtractionStrategy) +function momintegrals!(op, g, f, t, s, z, strat::SingularityExtractionRule) womps = strat.outer_quad_points diff --git a/test/test_bogaertints.jl b/test/test_bogaertints.jl index 6da52012..e364025a 100644 --- a/test/test_bogaertints.jl +++ b/test/test_bogaertints.jl @@ -23,9 +23,9 @@ z2 = zeros(z1) tqd = BE.quadpoints(x, [t], (12,13)) bqd = BE.quadpoints(x, [t], (13,)) -SE_strategy = BE.WiltonSEStrategy( +SE_strategy = BE.WiltonSERule( tqd[2,1], - BE.DoubleQuadStrategy( + BE.DoubleQuadRule( tqd[1,1], bqd[1,1], ), @@ -47,9 +47,9 @@ BEAST.momintegrals!(op, x, x, t, s, z3, EE_strategy) z4 = zeros(ComplexF64, n, n) tqd = BE.quadpoints(x, [t], (12,13)) bqd = BE.quadpoints(x, [s], (13,)) -SE_strategy = BE.WiltonSEStrategy( +SE_strategy = BE.WiltonSERule( tqd[2,1], - BE.DoubleQuadStrategy( + BE.DoubleQuadRule( tqd[1,1], bqd[1,1], ), @@ -73,9 +73,9 @@ BEAST.momintegrals!(op, x, x, t, s, z5, EE_strategy) z6 = zeros(ComplexF64, n, n) tqd = BE.quadpoints(x, [t], (12,13)) bqd = BE.quadpoints(x, [s], (13,)) -SE_strategy = BE.WiltonSEStrategy( +SE_strategy = BE.WiltonSERule( tqd[2,1], - BE.DoubleQuadStrategy( + BE.DoubleQuadRule( tqd[1,1], bqd[1,1], ), @@ -115,14 +115,14 @@ z3 = zeros(ComplexF64, BE.numfunctions(x), BE.numfunctions(x)) s1 = BEAST.BogaertEdgePatchStrategy(13, 30) tqd = BE.quadpoints(x, [t1], (12,13)) bqd = BE.quadpoints(x, [t2], (13,)) -s2 = BE.WiltonSEStrategy( +s2 = BE.WiltonSERule( tqd[2,1], - BE.DoubleQuadStrategy( + BE.DoubleQuadRule( tqd[1,1], bqd[1,1], ), ) -s3 = BE.DoubleQuadStrategy( +s3 = BE.DoubleQuadRule( tqd[1,1], tqd[1,1], ) @@ -136,14 +136,14 @@ BEAST.momintegrals!(op, x, x, t1, t2, z3, s3) s1 = BEAST.BogaertPointPatchStrategy(9,10) tqd = BE.quadpoints(x, [t2], (12,13)) bqd = BE.quadpoints(x, [t3], (13,)) -s2 = BE.WiltonSEStrategy( +s2 = BE.WiltonSERule( tqd[2,1], - BE.DoubleQuadStrategy( + BE.DoubleQuadRule( tqd[1,1], bqd[1,1], ), ) -s3 = BE.DoubleQuadStrategy( +s3 = BE.DoubleQuadRule( tqd[1,1], tqd[1,1], ) @@ -157,14 +157,14 @@ fill!(z3, 0); BEAST.momintegrals!(op, x, x, t2, t3, z3, s3) s1 = BEAST.BogaertSelfPatchStrategy(20) tqd = BE.quadpoints(x, [t1], (12,13)) bqd = BE.quadpoints(x, [t1], (13,)) -s2 = BE.WiltonSEStrategy( +s2 = BE.WiltonSERule( tqd[2,1], - BE.DoubleQuadStrategy( + BE.DoubleQuadRule( tqd[1,1], bqd[1,1], ), ) -s3 = BE.DoubleQuadStrategy( +s3 = BE.DoubleQuadRule( tqd[1,1], tqd[1,1], ) diff --git a/test/test_sauterschwabints.jl b/test/test_sauterschwabints.jl index 29d5ef2f..e026c85e 100644 --- a/test/test_sauterschwabints.jl +++ b/test/test_sauterschwabints.jl @@ -31,9 +31,9 @@ op1 = BEAST.MWSingleLayer3D(1.0, 250.0, 1.0) op2 = BEAST.MWSingleLayer3D(1.0) op3 = BEAST.MWDoubleLayer3D(0.0) -SE_strategy = BEAST.WiltonSEStrategy( +SE_strategy = BEAST.WiltonSERule( tqd[1,1], - BEAST.DoubleQuadStrategy( + BEAST.DoubleQuadRule( tqd[1,1], bqd[1,1], ), @@ -57,9 +57,9 @@ BEAST.momintegrals!(op2, rt, rt, t1, t1, z_ss2, SS_strategy) @test z_se2 ≈ z_ss2 atol=1e-4 ## Common Vertex Case -SE_strategy = BEAST.WiltonSEStrategy( +SE_strategy = BEAST.WiltonSERule( tqd[1,1], - BEAST.DoubleQuadStrategy( + BEAST.DoubleQuadRule( tqd[1,1], bqd[1,2])) SS_strategy = SauterSchwabQuadrature.CommonVertex(BEAST._legendre(12,0.0,1.0)) @@ -103,9 +103,9 @@ t2 = simplex( tqd = BEAST.quadpoints(rt, [t1,t2], (12,)) bqd = BEAST.quadpoints(rt, [t1,t2], (13,)) -SE_strategy = BEAST.WiltonSEStrategy( +SE_strategy = BEAST.WiltonSERule( tqd[1,1], - BEAST.DoubleQuadStrategy( + BEAST.DoubleQuadRule( tqd[1,1], bqd[1,2])) SS_strategy = SauterSchwabQuadrature.CommonEdge(BEAST._legendre(12,0.0,1.0)) diff --git a/test/test_ss_nested_meshes.jl b/test/test_ss_nested_meshes.jl index 8d1558b7..31d75bcd 100644 --- a/test/test_ss_nested_meshes.jl +++ b/test/test_ss_nested_meshes.jl @@ -2,7 +2,7 @@ using Test # Write a test that compares the momintegrals_nested approach with applying the # Wilton singularity extraction to a non-conforming mesh. -# Conclusion: WiltonSEStrategy should not be applied to deal with overlapping +# Conclusion: WiltonSERule should not be applied to deal with overlapping # charts in the case of geometrically non-conforming meshes; the result of the # inner integral is not analytic, crippling the accuracy of the outer integration, # which is done with triangular Gauss-Legendre points. @@ -41,7 +41,7 @@ sauterschwab = BEAST.SauterSchwabQuadrature.CommonFace(nothing) out_ss = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) BEAST.momintegrals_nested!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss,sauterschwab) -wiltonsingext = BEAST.WiltonSEStrategy(test_quadpoints, BEAST.DoubleQuadStrategy(test_quadpoints, trial_quadpoints)) +wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) out_dw = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) BEAST.momintegrals_nested!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw,wiltonsingext) @@ -57,7 +57,7 @@ sauterschwab = BEAST.SauterSchwabQuadrature.CommonFace(BEAST._legendre(10,0.0,1. out_ss1 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss1,sauterschwab) -wiltonsingext = BEAST.WiltonSEStrategy(test_quadpoints, BEAST.DoubleQuadStrategy(test_quadpoints, trial_quadpoints)) +wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) out_dw1 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw1,wiltonsingext) @@ -74,7 +74,7 @@ sauterschwab = BEAST.SauterSchwabQuadrature.CommonEdge(BEAST._legendre(10,0.0,1. out_ss2 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss2,sauterschwab) -wiltonsingext = BEAST.WiltonSEStrategy(test_quadpoints, BEAST.DoubleQuadStrategy(test_quadpoints, trial_quadpoints)) +wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) out_dw2 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw2,wiltonsingext) @@ -91,7 +91,7 @@ sauterschwab = BEAST.SauterSchwabQuadrature.CommonEdge(BEAST._legendre(10,0.0,1. out_ss3 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss3,sauterschwab) -wiltonsingext = BEAST.WiltonSEStrategy(test_quadpoints, BEAST.DoubleQuadStrategy(test_quadpoints, trial_quadpoints)) +wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) out_dw3 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw3,wiltonsingext) @@ -109,7 +109,7 @@ sauterschwab = BEAST.SauterSchwabQuadrature.CommonVertex(BEAST._legendre(10,0.0, out_ss4 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss4,sauterschwab) -wiltonsingext = BEAST.WiltonSEStrategy(test_quadpoints, BEAST.DoubleQuadStrategy(test_quadpoints, trial_quadpoints)) +wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) out_dw4 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw4,wiltonsingext) @@ -126,7 +126,7 @@ sauterschwab = BEAST.SauterSchwabQuadrature.CommonVertex(BEAST._legendre(10,0.0, out_ss5 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss5,sauterschwab) -wiltonsingext = BEAST.WiltonSEStrategy(test_quadpoints, BEAST.DoubleQuadStrategy(test_quadpoints, trial_quadpoints)) +wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) out_dw5 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw5,wiltonsingext) @@ -143,7 +143,7 @@ sauterschwab = BEAST.SauterSchwabQuadrature.CommonVertex(BEAST._legendre(10,0.0, out_ss6 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss6,sauterschwab) -wiltonsingext = BEAST.WiltonSEStrategy(test_quadpoints, BEAST.DoubleQuadStrategy(test_quadpoints, trial_quadpoints)) +wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) out_dw6 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw6,wiltonsingext) diff --git a/test/test_wiltonints.jl b/test/test_wiltonints.jl index 04842375..612188e9 100644 --- a/test/test_wiltonints.jl +++ b/test/test_wiltonints.jl @@ -341,12 +341,12 @@ BE = BEAST tqd = BE.quadpoints(x, [t], (12,)) bqd = BE.quadpoints(x, [s], (13,)) -DQ_strategy = BE.DoubleQuadStrategy(tqd[1,1], bqd[1,1]) +DQ_strategy = BE.DoubleQuadRule(tqd[1,1], bqd[1,1]) BEAST.momintegrals!(op, x, x, t, s, z1, DQ_strategy) -SE_strategy = BE.WiltonSEStrategy( +SE_strategy = BE.WiltonSERule( tqd[1,1], - BE.DoubleQuadStrategy( + BE.DoubleQuadRule( tqd[1,1], bqd[1,1], ), diff --git a/test/untested/test_mwops.jl b/test/untested/test_mwops.jl index ce428b6b..b2ec8586 100644 --- a/test/untested/test_mwops.jl +++ b/test/untested/test_mwops.jl @@ -62,6 +62,6 @@ z = assemble(K, T, S) qd = quaddata(K, rt, rt, elements(τ), elements(σ)) zlocal = zeros(promote_type(scalartype(K), scalartype(S), scalartype(T)), 3, 3) strat = BEAST.quadrule(K, rt, rt, 1, t, 1, s, qd) -strat = BEAST.DoubleQuadStrategy(qd.tpoints[1,1], qd.bpoints[1,1]) +strat = BEAST.DoubleQuadRule(qd.tpoints[1,1], qd.bpoints[1,1]) BEAST.momintegrals!(K, rt, rt, t, s, zlocal, strat) z = zlocal[3,3] From 6c7b590a563a94510177e810330329ca66cc3e2f Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 12 Oct 2021 09:35:12 +0200 Subject: [PATCH 076/528] tests and examples work with new quadstrat dispatching --- Project.toml | 26 +++---- examples/calderon.jl | 2 +- examples/capacitor.jl | 4 +- examples/dot_tdmfie.jl | 5 +- examples/dpie.jl | 16 ++-- examples/ex_fem.jl | 5 +- examples/ex_restrict_space.jl | 2 +- examples/helmholtz3d_neumann.jl | 2 +- examples/mfie_bdm.jl | 10 ++- examples/nitsche.jl | 16 +++- examples/scomplex_efie.jl | 4 +- examples/tdefie_neumann.jl | 2 +- src/BEAST.jl | 7 +- src/bases/basis.jl | 4 + src/bases/bcspace.jl | 79 ++++++++++---------- src/helmholtz2d/helmholtzop.jl | 13 ++-- src/helmholtz3d/hh3dops.jl | 30 +++++--- src/helmholtz3d/nitsche.jl | 12 ++- src/helmholtz3d/timedomain/tdhh3dops.jl | 6 +- src/identityop.jl | 44 +++++++---- src/integralop.jl | 99 +++++++++++++------------ src/localop.jl | 33 +++++---- src/maxwell/mwops.jl | 55 +++++++++++--- src/maxwell/nitsche.jl | 12 +-- src/maxwell/nxdbllayer.jl | 19 +++-- src/maxwell/sauterschwabints_rt.jl | 5 +- src/maxwell/timedomain/mwtdops.jl | 24 ++++-- src/operator.jl | 62 ++++++++-------- src/quadrature/quadstrats.jl | 11 ++- src/timedomain/tdintegralop.jl | 11 +-- src/timedomain/tdtimeops.jl | 15 +++- test/test_assemblerow.jl | 5 +- test/test_tdassembly.jl | 5 +- test/test_tdmwdbl.jl | 8 +- 34 files changed, 391 insertions(+), 262 deletions(-) diff --git a/Project.toml b/Project.toml index e09dab75..3a639ba2 100644 --- a/Project.toml +++ b/Project.toml @@ -25,22 +25,22 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] -BlockArrays = "^0.10, ^0.11, ^0.12, ^0.13, ^0.14, 0.15, 0.16" -CollisionDetection = "^0.1" -Combinatorics = "^0.7, ^1" -CompScienceMeshes = "^0.3" -Compat = "^2, ^3" -FFTW = "^0.2.3, ^1" -FastGaussQuadrature = "^0.3, ^0.4" +BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" +CollisionDetection = "0.1" +Combinatorics = "0.7, 1" +CompScienceMeshes = "0.3.1" +Compat = "2, 3" +FFTW = "0.2.3, 1" +FastGaussQuadrature = "0.3, 0.4" FillArrays = "0.11, 0.12" -IterativeSolvers = "^0.9" +IterativeSolvers = "0.9" LiftedMaps = "0.2.1" -LinearMaps = "^3.3" -SauterSchwabQuadrature = "^2.1.1, 2.1" +LinearMaps = "3.3" +SauterSchwabQuadrature = "2.1.1, 2.1" SparseMatrixDicts = "0.2" -SpecialFunctions = "^0.7, ^0.8, ^0.9, ^0.10, ^1" -StaticArrays = "^0.8.3, ^0.9, ^0.10, ^0.11, ^0.12, ^1" -WiltonInts84 = "^0.2" +SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1" +StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" +WiltonInts84 = "0.2" julia = "1.6" [extras] diff --git a/examples/calderon.jl b/examples/calderon.jl index 71f81a90..ef52241f 100644 --- a/examples/calderon.jl +++ b/examples/calderon.jl @@ -15,7 +15,7 @@ N = NCross() Txx = assemble(T,X,X); println("primal discretisation assembled.") Tyy = assemble(T,Y,Y); println("dual discretisation assembled.") -Nxy = assemble(N,X,Y); println("duality form assembled.") +Nxy = Matrix(assemble(N,X,Y)); println("duality form assembled.") iNxy = inv(Nxy); println("duality form inverted.") A = iNxy' * Tyy * iNxy * Txx diff --git a/examples/capacitor.jl b/examples/capacitor.jl index d7b57ec6..9cf29290 100644 --- a/examples/capacitor.jl +++ b/examples/capacitor.jl @@ -23,11 +23,11 @@ println("λ = ", λₑ, "m; l = ", l ,"m") # Build and mesh the structure Γ₁ = meshrectangle(l,w,h); -translate!(Γ₁, point(0.0,0.0,d)) +CompScienceMeshes.translate!(Γ₁, point(0.0,0.0,d)) Γ₀ = meshrectangle(l,w,h) γ₁ = meshsegment(l, h, 3) -translate!(γ₁, point(0.0,0.0,d)) +CompScienceMeshes.translate!(γ₁, point(0.0,0.0,d)) γ₀ = meshsegment(l, h, 3) Γ = weld(Γ₁, Γ₀) diff --git a/examples/dot_tdmfie.jl b/examples/dot_tdmfie.jl index 380a81ac..82267942 100644 --- a/examples/dot_tdmfie.jl +++ b/examples/dot_tdmfie.jl @@ -9,15 +9,16 @@ o, x, y, z = euclidianbasis(3) X, Y = raviartthomas(Γ), buffachristiansen(Γ) # Δt, Nt = 0.11, 200 -Δt, Nt = 0.6, 200 +# Δt, Nt = 0.6, 200 # Δt, Nt = 0.05, 400 +Δt ,Nt = 0.3, 200 T = timebasisshiftedlagrange(Δt, Nt, 2) δ = timebasisdelta(Δt, Nt) V = X ⊗ T W = Y ⊗ δ # width, delay, scaling = 8.0, 12.0, 1.0 -duration = 20 * Δt +duration = 20 * Δt * 2 delay = 1.5 * duration amplitude = 1.0 gaussian = derive(creategaussian(duration, delay, amplitude)) diff --git a/examples/dpie.jl b/examples/dpie.jl index 7724d4a8..89c1a251 100644 --- a/examples/dpie.jl +++ b/examples/dpie.jl @@ -13,10 +13,12 @@ Y = lagrangec0d1(Γ) x = refspace(X) y = refspace(Y) charts = [chart(Γ,c) for c in cells(Γ)] -qd = quaddata(s, x, x, charts, charts) + +qs = BEAST.defaultquadstrat(s, x, x) +qd = quaddata(s, x, x, charts, charts, qs) m1,m2 = 10,11 -ql = quadrule(s, x, x, m1, charts[m1], m2, charts[m2], qd) -@show @which quadrule(s, x, x, m1, charts[m1], m2, charts[m2], qd) +ql = quadrule(s, x, x, m1, charts[m1], m2, charts[m2], qd, qs) +@show @which quadrule(s, x, x, m1, charts[m1], m2, charts[m2], qd, qs) @show typeof(ql) BEAST.momintegrals!(s, x, x, charts[10], charts[20], zeros(ComplexF64,3,3), ql) @@ -43,11 +45,11 @@ u = solve(Eq) uj = u[1:numfunctions(X)] up = u[numfunctions(X)+1:end] -#xrange = range(-2.0, stop=2.0, length=40) -xrange = [0.0] -yrange = [0.0] +xrange = range(-2.0, stop=2.0, length=40) +# xrange = [0.0] +# yrange = [0.0] zrange = range(-2.0, stop=2.0, length=40) -pts = [point(x,y,z) for x ∈ xrange, y ∈ yrange, z ∈ zrange] +pts = [point(x,0,z) for x ∈ xrange, z ∈ zrange] A1 = BEAST.DPVectorialTerm(1.0, 1.0*im) A2 = BEAST.DPScalarTerm(1.0, 1.0*im) diff --git a/examples/ex_fem.jl b/examples/ex_fem.jl index f43505bb..73722b77 100644 --- a/examples/ex_fem.jl +++ b/examples/ex_fem.jl @@ -70,8 +70,8 @@ G = boundary(tetrs) Z = BEAST.ttrace(curl(X), G) fcr, geo = facecurrents(u, Z) -import PlotlyJS -PlotlyJS.plot(patch(geo, norm.(fcr))) +import Plotly +Plotly.plot(patch(geo, norm.(fcr))) const CSM = CompScienceMeshes hemi = submesh(tet -> cartesian(CSM.center(chart(tetrs,tet)))[3] < 0, tetrs) @@ -82,5 +82,4 @@ Xhemi = BEAST.restrict(X, hemi) tXhemi = BEAST.ttrace(Xhemi, bnd_hemi) fcr, geo = facecurrents(u, tXhemi) -import Plotly Plotly.plot(patch(geo, norm.(fcr))) diff --git a/examples/ex_restrict_space.jl b/examples/ex_restrict_space.jl index 08e0d192..80182d70 100644 --- a/examples/ex_restrict_space.jl +++ b/examples/ex_restrict_space.jl @@ -2,7 +2,7 @@ using CompScienceMeshes using BEAST sphere = CompScienceMeshes.tetmeshsphere(1.0, 0.35) -hemi = submesh(tet -> cartesian(center(chart(sphere,tet)))[3] < 0, sphere) +hemi = submesh(tet -> cartesian(CompScienceMeshes.center(chart(sphere,tet)))[3] < 0, sphere) X = BEAST.nedelecd3d(sphere) Y = BEAST.restrict(X, hemi) diff --git a/examples/helmholtz3d_neumann.jl b/examples/helmholtz3d_neumann.jl index 2acbd8f1..719a655a 100644 --- a/examples/helmholtz3d_neumann.jl +++ b/examples/helmholtz3d_neumann.jl @@ -8,7 +8,7 @@ Pkg.activate(@__DIR__) X = lagrangec0d1(Γ) @show numfunctions(X) -κ = 1.0; γ = im*κ +κ = 2π; γ = im*κ a = -1Helmholtz3D.hypersingular(gamma=γ) b = Helmholtz3D.doublelayer(gamma=γ) - 0.5Identity() diff --git a/examples/mfie_bdm.jl b/examples/mfie_bdm.jl index 4040a81e..388d2d70 100644 --- a/examples/mfie_bdm.jl +++ b/examples/mfie_bdm.jl @@ -1,17 +1,21 @@ using CompScienceMeshes, BEAST Γ = readmesh(joinpath(dirname(@__FILE__),"sphere2.in")) -X, Y = raviartthomas(Γ), buffachristiansen(Γ) +# X, Y = raviartthomas(Γ), buffachristiansen(Γ) +X = brezzidouglasmarini(Γ) +Y = brezzidouglasmarini(Γ) +# X = raviartthomas(Γ) +# Y = raviartthomas(Γ) ϵ, μ, ω = 1.0, 1.0, 1.0; κ = ω * √(ϵ*μ) NK, Id = BEAST.DoubleLayerRotatedMW3D(im*κ), Identity() E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) H = -1/(im*μ*ω)*curl(E) -h = (n × H) × n +h = n × H @hilbertspace j @hilbertspace m -mfie = @discretise (K+0.5N)[m,j] == h[m] j∈X m∈Y +mfie = @discretise (NK-0.5Id)[m,j] == h[m] j∈X m∈Y u = gmres(mfie) include("utils/postproc.jl") diff --git a/examples/nitsche.jl b/examples/nitsche.jl index 980e9545..df99562c 100644 --- a/examples/nitsche.jl +++ b/examples/nitsche.jl @@ -11,12 +11,24 @@ width, height = 1.0, 0.5 γ = meshsegment(width, width, 3) κ = 1.0 -S, T, I = SingleLayerTrace(κ), MWSingleLayer3D(κ), Identity() +S, T, I = SingleLayerTrace(κ*im), MWSingleLayer3D(κ), Identity() St = transpose(S) E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) e = (n×E)×n -X1, X2, X3 = raviartthomas(Γ1, γ), raviartthomas(Γ2, γ), raviartthomas(Γ3, γ) +in_interior1 = CompScienceMeshes.interior_tpredicate(Γ1) +in_interior2 = CompScienceMeshes.interior_tpredicate(Γ2) +in_interior3 = CompScienceMeshes.interior_tpredicate(Γ3) + +on_junction = CompScienceMeshes.overlap_gpredicate(γ) +edges1 = skeleton(e -> in_interior1(e) || on_junction(chart(Γ1,e)), Γ1,1) +edges2 = skeleton(e -> in_interior2(e) || on_junction(chart(Γ2,e)), Γ2,1) +edges3 = skeleton(e -> in_interior3(e) || on_junction(chart(Γ3,e)), Γ3,1) + +X1 = raviartthomas(Γ1, edges1) +X2 = raviartthomas(Γ2, edges2) +X3 = raviartthomas(Γ3, edges3) +# X1, X2, X3 = raviartthomas(Γ1, γ), raviartthomas(Γ2, γ), raviartthomas(Γ3, γ) X = X1 × X2 × X3 @hilbertspace j diff --git a/examples/scomplex_efie.jl b/examples/scomplex_efie.jl index f945a7ab..d35e5a3c 100644 --- a/examples/scomplex_efie.jl +++ b/examples/scomplex_efie.jl @@ -22,8 +22,8 @@ for (e1,e2) in zip(E1.faces, E2.edges) q = getindex.(Ref(E2.nodes), e2) @assert q[1][1] == e1[1] @assert q[2][1] == e1[2] - ctr1 = cartesian(center(chart(E1, e1))) - ctr2 = cartesian(center(chart(E2, e2))) + ctr1 = cartesian(CompScienceMeshes.center(chart(E1, e1))) + ctr2 = cartesian(CompScienceMeshes.center(chart(E2, e2))) if norm(ctr1-ctr2) > 1e-8 @show ctr1 @show ctr2 diff --git a/examples/tdefie_neumann.jl b/examples/tdefie_neumann.jl index 66bf0662..3746417c 100644 --- a/examples/tdefie_neumann.jl +++ b/examples/tdefie_neumann.jl @@ -43,7 +43,7 @@ using Plotly Xefie, Δω, ω0 = fouriertransform(xefie, Δt, 0.0, 2) -ω = collect(ω0 + (0:Nt-1)*Δω) +ω = collect(ω0 .+ (0:Nt-1)*Δω) _, i1 = findmin(abs.(ω.-sol)) ω1 = ω[i1] diff --git a/src/BEAST.jl b/src/BEAST.jl index aa3c13f9..a13d7e11 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -147,6 +147,10 @@ include("bases/stagedtimestep.jl") include("bases/timebasis.jl") include("bases/tensorbasis.jl") +include("quadrature/quadstrats.jl") +include("quadrature/double_quadrature.jl") +include("quadrature/singularity_extraction.jl") + include("excitation.jl") include("operator.jl") include("localop.jl") @@ -157,9 +161,6 @@ include("quaddata.jl") include("postproc.jl") include("postproc/segcurrents.jl") -include("quadrature/double_quadrature.jl") -include("quadrature/singularity_extraction.jl") - include("timedomain/tdintegralop.jl") include("timedomain/tdexcitation.jl") include("timedomain/motlu.jl") diff --git a/src/bases/basis.jl b/src/bases/basis.jl index a92474d8..9a6c4618 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -57,6 +57,10 @@ mutable struct DirectProductSpace{T} <: AbstractSpace factors::Vector{Space{T}} end +# defaultquadstrat(op, tfs::DirectProductSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs[1], bfs[1]) +# defaultquadstrat(op, tfs, bfs::DirectProductSpace) = defaultquadstrat(op, tfs, bfs[1]) +# defaultquadstrat(op, tfs::DirectProductSpace, bfs) = defaultquadstrat(op, tfs[1], bfs) + export cross, × cross(a::Space{T}, b::Space{T}) where {T} = DirectProductSpace(Space{T}[a,b]) diff --git a/src/bases/bcspace.jl b/src/bases/bcspace.jl index 88fa0e49..7efff2d0 100644 --- a/src/bases/bcspace.jl +++ b/src/bases/bcspace.jl @@ -372,7 +372,7 @@ end using LinearAlgebra function buildhalfbc2(patch, port, dirichlet, prt_fluxes) - println() + # println() edges = skeleton(patch,1) verts = skeleton(patch,0) @@ -383,8 +383,8 @@ function buildhalfbc2(patch, port, dirichlet, prt_fluxes) reverse(edge) in cells(bndry) && return true return false end - @show numcells(patch) - @show numcells(dirbnd) + # @show numcells(patch) + # @show numcells(dirbnd) @assert numcells(dirbnd) ≤ 4 bnd_dirbnd = boundary(dirbnd) @@ -393,25 +393,25 @@ function buildhalfbc2(patch, port, dirichlet, prt_fluxes) node in cells(bnd_dirbnd) && return false return true end - @show numcells(int_nodes_dirbnd) + # @show numcells(int_nodes_dirbnd) @assert numcells(int_nodes_dirbnd) ≤ 2 int_pred = interior_tpredicate(patch) num_edges_on_port = 0 num_edges_on_dirc = 0 - @show numcells(edges) + # @show numcells(edges) - for edge in cells(edges) - println(edge) - end - println() - for edge in cells(dirbnd) - println(edge) - end - println() - for edge in cells(port) - println(edge) - end + # for edge in cells(edges) + # println(edge) + # end + # println() + # for edge in cells(dirbnd) + # println(edge) + # end + # println() + # for edge in cells(port) + # println(edge) + # end int_edges = submesh(edges) do edge (edge in cells(port)) && (num_edges_on_port+=1 ; return false) @@ -421,13 +421,13 @@ function buildhalfbc2(patch, port, dirichlet, prt_fluxes) (!int_pred(edge)) && return false return true end - println() - for edge in cells(int_edges) - println(edge) - end - @show numcells(int_edges) - @show num_edges_on_port - @show num_edges_on_dirc + # println() + # for edge in cells(int_edges) + # println(edge) + # end + # @show numcells(int_edges) + # @show num_edges_on_port + # @show num_edges_on_dirc bnd_verts = skeleton(bndry,0) prt_verts = skeleton(port,0) @@ -442,7 +442,7 @@ function buildhalfbc2(patch, port, dirichlet, prt_fluxes) node in cells(prt_verts) && return false return true end - @show numcells(int_verts) + # @show numcells(int_verts) RT_int = raviartthomas(patch, cellpairs(patch, int_edges)) RT_prt = raviartthomas(patch, cellpairs(patch, port)) @@ -455,16 +455,17 @@ function buildhalfbc2(patch, port, dirichlet, prt_fluxes) Q = assemble(Id, divergence(RT_int), divergence(RT_prt)) d = -Q * prt_fluxes - @show numfunctions(L0_int) + # @show numfunctions(L0_int) @assert numfunctions(L0_int) in [1,2] C = assemble(Id, curl(L0_int), RT_int) curl_L0_int = curl(L0_int) c = real(assemble(Id, curl_L0_int, RT_prt)) * prt_fluxes - x1 = pinv(D) * d - N = nullspace(D) - @show size(N) - @show size(C) + mD = Matrix(D) + x1 = pinv(mD) * d + N = nullspace(mD) + # @show size(N) + # @show size(C) p = (C*N) \ (c - C*x1) x = x1 + N*p @@ -495,27 +496,27 @@ function buffachristiansen2(Faces::CompScienceMeshes.AbstractMesh) pos = Vector{vertextype(Faces)}(undef, numcells(Edges)) dirichlet = boundary(faces) for (E,Edge) in enumerate(cells(Edges)) - @show Edge + # @show Edge bfs[E] = Vector{Shape{T}}() pos[E] = cartesian(center(chart(Edges,Edge))) # port_vertex_idx = numvertices(Faces) + E port_vertex_idx = argmin(norm.(vertices(faces) .- Ref(pos[E]))) # pos[E] = vertices(faces)[port_vertex_idx] - @show carttobary(chart(Edges,Edge),pos[E]) - @show pos[E] - @show (Faces.vertices[Edge[1]]+Faces.vertices[Edge[2]])/2 + # @show carttobary(chart(Edges,Edge),pos[E]) + # @show pos[E] + # @show (Faces.vertices[Edge[1]]+Faces.vertices[Edge[2]])/2 # Build the plus-patch ptch_vert_idx = Edge[1] ptch_face_idcs = [i for (i,face) in enumerate(cells(faces)) if ptch_vert_idx in face] patch = Mesh(vertices(faces), cells(faces)[ptch_face_idcs]) patch_bnd = boundary(patch) - @show numcells(patch_bnd) + # @show numcells(patch_bnd) port = Mesh(vertices(faces), filter(c->port_vertex_idx in c, cells(patch_bnd))) - @show numcells(patch) - @show numcells(port) + # @show numcells(patch) + # @show numcells(port) # @assert numcells(patch) >= 6 @assert numcells(port) == 2 @@ -600,9 +601,9 @@ function buffachristiansen3(Faces::CompScienceMeshes.AbstractMesh) patch_bnd = boundary(patch1) port = Mesh(vertices(faces), filter(c->port_vertex_idx in c, cells(patch_bnd))) patch = CompScienceMeshes.union(patch1, patch2) - @show numcells(patch_bnd) - @show numcells(patch) - @show numcells(port) + # @show numcells(patch_bnd) + # @show numcells(patch) + # @show numcells(port) # @assert numcells(skeleton(patch,1))+2 == numcells(skeleton(patch1,1)) + numcells(skeleton(patch2,1)) @assert numcells(patch) >= 6 diff --git a/src/helmholtz2d/helmholtzop.jl b/src/helmholtz2d/helmholtzop.jl index bc3fd942..f61631bb 100644 --- a/src/helmholtz2d/helmholtzop.jl +++ b/src/helmholtz2d/helmholtzop.jl @@ -29,16 +29,19 @@ function testfunc1() print("test function!") end -function quaddata(op::HelmholtzOperator2D, g::LagrangeRefSpace, f::LagrangeRefSpace, tels, bels) +defaultquadstrat(op::HelmholtzOperator2D, tfs, bfs) = DoubleNumWiltonSauterQStrat(4,3,4,3,4,4,4,4) - tqd = quadpoints(g, tels, (4,)) - bqd = quadpoints(f, bels, (3,)) +function quaddata(op::HelmholtzOperator2D, g::LagrangeRefSpace, f::LagrangeRefSpace, tels, bels, + qs::DoubleNumWiltonSauterQStrat) + + tqd = quadpoints(g, tels, (qs.outer_rule_far,)) + bqd = quadpoints(f, bels, (qs.inner_rule_far,)) return (tpoints=tqd, bpoints=bqd) end - -function quadrule(op::HelmholtzOperator2D, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd) +function quadrule(op::HelmholtzOperator2D, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd, + qs::DoubleNumWiltonSauterQStrat) DoubleQuadRule( qd.tpoints[1,i], diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index 0da0df32..2a423299 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -50,8 +50,11 @@ struct HH3DDoubleLayerTransposed{T,K} <: Helmholtz3DOp gamma::K end +defaultquadstrat(::Helmholtz3DOp, ::LagrangeRefSpace, ::LagrangeRefSpace) = DoubleNumWiltonSauterQStrat(2,3,2,3,4,4,4,4) + function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, - trial_refspace::LagrangeRefSpace, test_elements, trial_elements) + trial_refspace::LagrangeRefSpace, test_elements, trial_elements, + qs::DoubleNumWiltonSauterQStrat) test_eval(x) = test_refspace(x, Val{:withcurl}) trial_eval(x) = trial_refspace(x, Val{:withcurl}) @@ -63,17 +66,20 @@ function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, # test_qp = quadpoints(test_eval, test_elements, (6,)) # bssi_qp = quadpoints(trial_eval, trial_elements, (7,)) - test_qp = quadpoints(test_eval, test_elements, (2,)) - bsis_qp = quadpoints(trial_eval, trial_elements, (3,)) + test_qp = quadpoints(test_eval, test_elements, (qs.outer_rule_far,)) + bsis_qp = quadpoints(trial_eval, trial_elements, (qs.inner_rule_far,)) return test_qp, bsis_qp end +defaultquadstrat(::Helmholtz3DOp, ::subReferenceSpace, ::subReferenceSpace) = DoubleNumWiltonSauterQStrat(4,7,4,7,4,4,4,4) + function quaddata(op::Helmholtz3DOp, test_refspace::subReferenceSpace, - trial_refspace::subReferenceSpace, test_elements, trial_elements) + trial_refspace::subReferenceSpace, test_elements, trial_elements, + qs::DoubleNumWiltonSauterQStrat) - test_qp = quadpoints(test_refspace, test_elements, (4,)) - bsis_qp = quadpoints(trial_refspace, trial_elements, (7,)) + test_qp = quadpoints(test_refspace, test_elements, (qs.outer_rule_far,)) + bsis_qp = quadpoints(trial_refspace, trial_elements, (qs.inner_rule_far,)) return test_qp, bsis_qp end @@ -83,7 +89,8 @@ end function quadrule(op::HH3DSingleLayerFDBIO, test_refspace::LagrangeRefSpace{T,0} where T, trial_refspace::LagrangeRefSpace{T,0} where T, - i, test_element, j, trial_element, quadrature_data) + i, test_element, j, trial_element, quadrature_data, + qs::DoubleNumWiltonSauterQStrat) tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 for t in test_element.vertices @@ -107,7 +114,8 @@ end function quadrule(op::HH3DSingleLayerFDBIO, test_refspace::subReferenceSpace, - trial_refspace::subReferenceSpace, i, test_element, j, trial_element, quadrature_data) + trial_refspace::subReferenceSpace, i, test_element, j, trial_element, quadrature_data, + qs::DoubleNumWiltonSauterQStrat) # tol, hits = 1e-10, 0 # for t in getelementVertices(test_element) @@ -140,7 +148,8 @@ end function quadrule(op::Helmholtz3DOp, test_refspace::RefSpace, trial_refspace::RefSpace, - i, test_element, j, trial_element, quadrature_data) + i, test_element, j, trial_element, quadrature_data, + qs::DoubleNumWiltonSauterQStrat) test_quadpoints = quadrature_data[1] trial_quadpoints = quadrature_data[2] @@ -152,7 +161,8 @@ end function quadrule(op::Helmholtz3DOp, test_refspace::RTRefSpace, trial_refspace::RTRefSpace, - i, test_element, j, trial_element, quadrature_data) + i, test_element, j, trial_element, quadrature_data, + qs::DoubleNumWiltonSauterQStrat) test_quadpoints = quadrature_data[1] trial_quadpoints = quadrature_data[2] diff --git a/src/helmholtz3d/nitsche.jl b/src/helmholtz3d/nitsche.jl index 13740939..78fa8d5d 100644 --- a/src/helmholtz3d/nitsche.jl +++ b/src/helmholtz3d/nitsche.jl @@ -4,19 +4,23 @@ mutable struct NitscheHH3{T} <: MaxwellOperator3D gamma::T end +defaultquadstrat(::NitscheHH3, ::LagrangeRefSpace, ::LagrangeRefSpace) = DoubleNumWiltonSauterQStrat(10,8,10,8,3,3,3,3) + function quaddata(operator::NitscheHH3, localtestbasis::LagrangeRefSpace, localtrialbasis::LagrangeRefSpace, - testelements, trialelements) + testelements, trialelements, qs::DoubleNumWiltonSauterQStrat) - tqd = quadpoints(localtestbasis, testelements, (10,)) - bqd = quadpoints(x -> localtrialbasis(x, Val{:withcurl}), trialelements, (8,)) + tqd = quadpoints(localtestbasis, testelements, (qs.outer_rule_far,)) + bqd = quadpoints(x -> localtrialbasis(x, Val{:withcurl}), trialelements, (qs.inner_rule_far,)) #return QuadData(tqd, bqd) return (tpoints=tqd, bpoints=bqd) end -function quadrule(op::NitscheHH3, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd) +function quadrule(op::NitscheHH3, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd, + qs::DoubleNumWiltonSauterQStrat) + DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j] diff --git a/src/helmholtz3d/timedomain/tdhh3dops.jl b/src/helmholtz3d/timedomain/tdhh3dops.jl index 50945758..17cf8e0a 100644 --- a/src/helmholtz3d/timedomain/tdhh3dops.jl +++ b/src/helmholtz3d/timedomain/tdhh3dops.jl @@ -31,10 +31,12 @@ end HH3DDoubleLayerTDBIO(;speed_of_light) = HH3DDoubleLayerTDBIO(speed_of_light,one(speed_of_light),0) +defaultquadstrat(::HH3DTDBIO, tfs, bfs) = nothing + # See: ?BEAST.quaddata for help function quaddata(operator::HH3DTDBIO, test_local_space, trial_local_space, time_local_space, - test_element, trial_element, time_element) + test_element, trial_element, time_element, quadstrat::Nothing) dmax = numfunctions(time_local_space)-1 bn = binomial.((0:dmax),(0:dmax)') @@ -52,7 +54,7 @@ end function quadrule(operator::HH3DTDBIO, test_local_space, trial_local_space, time_local_space, p, test_element, q, trial_element, r, time_element, - quad_data) + quad_data, quadstrat::Nothing) # WiltonInts84Strat(quad_data[1,p]) qd = quad_data diff --git a/src/identityop.jl b/src/identityop.jl index bb6b5981..6be16dca 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -20,63 +20,79 @@ function _alloc_workspace(qd, g, f, tels, bels) A = Vector{typeof(a)}(undef,length(qd)) end -const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace} -function quaddata(op::LocalOperator, g::LinearRefSpaceTriangle, f::LinearRefSpaceTriangle, tels, bels) - u, w = trgauss(6) +# defaultquadstrat(op::LocalOperator, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bfs)) +defaultquadstrat(op::LocalOperator, tfs::Space, bfs::DirectProductSpace) = defaultquadstrat(op, tfs, bfs.factors[1]) +defaultquadstrat(op::LocalOperator, tfs::DirectProductSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs.factors[1], bfs) +defaultquadstrat(op::LocalOperator, tfs::DirectProductSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs.factors[1], bfs.factors[1]) + +const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace, BDMRefSpace} +defaultquadstrat(::LocalOperator, ::LinearRefSpaceTriangle, ::LinearRefSpaceTriangle) = SingleNumQStrat(6) +function quaddata(op::LocalOperator, g::LinearRefSpaceTriangle, f::LinearRefSpaceTriangle, tels, bels, + qs::SingleNumQStrat) + + u, w = trgauss(qs.quad_rule) qd = [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] A = _alloc_workspace(qd, g, f, tels, bels) return qd, A end -function quaddata(op::LocalOperator, g::subReferenceSpace, f::subReferenceSpace, tels, bels) - u, w = trgauss(6) +defaultquadstrat(::LocalOperator, ::subReferenceSpace, ::subReferenceSpace) = SingleNumQStrat(6) +function quaddata(op::LocalOperator, g::subReferenceSpace, f::subReferenceSpace, tels, bels, + qs::SingleNumQStrat) + + u, w = trgauss(qs.quad_rule) qd = [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] A = _alloc_workspace(qd, g, f, tels, bels) return qd, A end const LinearRefSpaceTetr = Union{NDLCCRefSpace, NDLCDRefSpace} -function quaddata(op::LocalOperator, g::LinearRefSpaceTetr, f::LinearRefSpaceTetr, tels, bels) +defaultquadstrat(::LocalOperator, ::LinearRefSpaceTetr, ::LinearRefSpaceTetr) = SingleNumQStrat(3) +function quaddata(op::LocalOperator, g::LinearRefSpaceTetr, f::LinearRefSpaceTetr, tels, bels, qs::SingleNumQStrat) + o, x, y, z = CompScienceMeshes.euclidianbasis(3) reftet = simplex(x,y,z,o) - qps = quadpoints(reftet, 3) + qps = quadpoints(reftet, qs.quad_rule) qd = [(w, parametric(p)) for (p,w) in qps] A = _alloc_workspace(qd, g, f, tels, bels) return qd, A end +defaultquadstrat(::LocalOperator, ::LagrangeRefSpace{T,D1,2}, ::LagrangeRefSpace{T,D2,2}) where {T,D1,D2} = SingleNumQStrat(6) function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,2} where {T,Deg}, - f::LagrangeRefSpace, tels::Vector, bels::Vector) + f::LagrangeRefSpace, tels::Vector, bels::Vector, qs::SingleNumQStrat) - u, w = legendre(6, 0.0, 1.0) + u, w = legendre(qs.quad_rule, 0.0, 1.0) qd = [(w[i],u[i]) for i in eachindex(w)] A = _alloc_workspace(qd, g, f, tels, bels) return qd, A end +defaultquadstrat(::LocalOperator, ::LagrangeRefSpace{T,D1,3}, ::LagrangeRefSpace{T,D2,3}) where {T,D1,D2} = SingleNumQStrat(6) function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,3} where {T,Deg}, - f::LagrangeRefSpace, tels::Vector, bels::Vector) + f::LagrangeRefSpace, tels::Vector, bels::Vector, qs::SingleNumQStrat) - u, w = trgauss(6) + u, w = trgauss(qs.quad_rule) qd = [(w[i], SVector(u[1,i], u[2,i])) for i in 1:length(w)] A = _alloc_workspace(qd, g, f, tels, bels) return qd, A end +defaultquadstrat(::LocalOperator, ::LagrangeRefSpace{T,D1,4}, ::LagrangeRefSpace{T,D2,4}) where {T,D1,D2} = SingleNumQStrat(6) function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,4} where {T,Deg}, - f::LagrangeRefSpace, tels::Vector, bels::Vector) + f::LagrangeRefSpace, tels::Vector, bels::Vector, qs::SingleNumQStrat) o, x, y, z = CompScienceMeshes.euclidianbasis(3) reftet = simplex(x,y,z,o) - qps = quadpoints(reftet, 6) + qps = quadpoints(reftet, qs.quad_rule) qd = [(w, parametric(p)) for (p,w) in qps] A = _alloc_workspace(qd, g, f, tels, bels) return qd, A end -function quadrule(op::LocalOperator, ψ::RefSpace, ϕ::RefSpace, τ, (qd,A)) +function quadrule(op::LocalOperator, ψ::RefSpace, ϕ::RefSpace, τ, (qd,A), qs::SingleNumQStrat) for i in eachindex(qd) q = qd[i] w, p = q[1], neighborhood(τ,q[2]) diff --git a/src/integralop.jl b/src/integralop.jl index e414b233..916ac3fb 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -66,7 +66,7 @@ elements(sp::Space) = elements(geometry(sp)) Computes the matrix of operator biop wrt the finite element spaces tfs and bfs """ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(biop, tfs, bfs)) test_elements, tad = assemblydata(tfs) bsis_elements, bad = assemblydata(bfs) @@ -74,20 +74,20 @@ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; tshapes = refspace(tfs); num_tshapes = numfunctions(tshapes) bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes) - qd = quaddata(biop, tshapes, bshapes, test_elements, bsis_elements) + qd = quaddata(biop, tshapes, bshapes, test_elements, bsis_elements, quadstrat) zlocal = zeros(scalartype(biop, tfs, bfs), 2num_tshapes, 2num_bshapes) if !CompScienceMeshes.refines(tfs.geo, bfs.geo) assemblechunk_body!(biop, tshapes, test_elements, tad, bshapes, bsis_elements, bad, - qd, zlocal, store, quadrule=quadrule) + qd, zlocal, store; quadstrat) else @info "assemblechunk for nested meshes" assemblechunk_body_nested_meshes!(biop, tshapes, test_elements, tad, bshapes, bsis_elements, bad, - qd, zlocal, store, quadrule=quadrule) + qd, zlocal, store; quadstrat) end end @@ -95,7 +95,7 @@ end function assemblechunk_body!(biop, test_shapes, test_elements, test_assembly_data, trial_shapes, trial_elements, trial_assembly_data, - qd, zlocal, store; quadrule=quadrule) + qd, zlocal, store; quadstrat) myid = Threads.threadid() myid == 1 && print("dots out of 10: ") @@ -104,8 +104,8 @@ function assemblechunk_body!(biop, for (q,bcell) in enumerate(trial_elements) fill!(zlocal, 0) - strat = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, qd) - momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, strat) + qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, qd, quadstrat) + momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule) I = length(test_assembly_data[p]) J = length(trial_assembly_data[q]) for j in 1 : J, i in 1 : I @@ -129,7 +129,7 @@ end function assemblechunk_body_nested_meshes!(biop, test_shapes, test_elements, test_assembly_data, trial_shapes, trial_elements, trial_assembly_data, - qd, zlocal, store; quadrule=quadrule) + qd, zlocal, store; quadstrat) myid = Threads.threadid() myid == 1 && print("dots out of 10: ") @@ -138,8 +138,8 @@ function assemblechunk_body_nested_meshes!(biop, for (q,bcell) in enumerate(trial_elements) fill!(zlocal, 0) - strat = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, qd) - momintegrals_nested!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, strat) + qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, qd, quadstrat) + momintegrals_nested!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule) I = length(test_assembly_data[p]) J = length(trial_assembly_data[q]) for j in 1 : J, i in 1 : I @@ -148,57 +148,58 @@ function assemblechunk_body_nested_meshes!(biop, zb = zij*b for (m,a) in test_assembly_data[p][i] store(a*zb, m, n) - end end end - - done += 1 - new_pctg = round(Int, done / todo * 100) - if new_pctg > pctg + 9 - myid == 1 && print(".") - pctg = new_pctg - end end end + end end end end + + done += 1 + new_pctg = round(Int, done / todo * 100) + if new_pctg > pctg + 9 + myid == 1 && print(".") + pctg = new_pctg + end end myid == 1 && println("") end function blockassembler(biop::IntegralOperator, tfs::Space, bfs::Space; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(biop, tfs, bfs)) test_elements, test_assembly_data, trial_elements, trial_assembly_data, - quadrature_data, zlocals = assembleblock_primer(biop, tfs, bfs, quaddata=quaddata) + quadrature_data, zlocals = assembleblock_primer(biop, tfs, bfs; quadstrat) if !CompScienceMeshes.refines(tfs.geo, bfs.geo) return (test_ids, trial_ids, store) -> begin assembleblock_body!(biop, tfs, test_ids, test_elements, test_assembly_data, bfs, trial_ids, trial_elements, trial_assembly_data, - quadrature_data, zlocals, store, quadrule=quadrule) + quadrature_data, zlocals, store; quadstrat) end else return (test_ids, trial_ids, store) -> begin assembleblock_body_nested!(biop, tfs, test_ids, test_elements, test_assembly_data, bfs, trial_ids, trial_elements, trial_assembly_data, - quadrature_data, zlocals, store, quadrule=quadrule) + quadrature_data, zlocals, store; quadstrat) end end end function assembleblock(operator::AbstractOperator, test_functions, trial_functions; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) + Z, store = allocatestorage(operator, test_functions, trial_functions) - assembleblock!(operator, test_functions, trial_functions, store, - quaddata=quaddata, quadrule=quadrule) + assembleblock!(operator, test_functions, trial_functions, store; quadstrat) + sdata(Z) end function assembleblock!(biop::IntegralOperator, tfs::Space, bfs::Space, store; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(biop, tfs, bfs)) test_elements, tad, trial_elements, bad, quadrature_data, zlocals = - assembleblock_primer(biop, tfs, bfs, quaddata=quaddata) + assembleblock_primer(biop, tfs, bfs; quadstrat) active_test_dofs = collect(1:numfunctions(tfs)) active_trial_dofs = collect(1:numfunctions(bfs)) @@ -206,11 +207,12 @@ function assembleblock!(biop::IntegralOperator, tfs::Space, bfs::Space, store; assembleblock_body!(biop, tfs, active_test_dofs, test_elements, tad, bfs, active_trial_dofs, trial_elements, bad, - quadrature_data, zlocals, store, quadrule=quadrule) + quadrature_data, zlocals, store; quadstrat) end -function assembleblock_primer(biop, tfs, bfs; quaddata=quaddata) +function assembleblock_primer(biop, tfs, bfs; + quadstrat=defaultquadstrat(biop, tfs, bfs)) test_elements, tad = assemblydata(tfs) bsis_elements, bad = assemblydata(bfs) @@ -218,7 +220,7 @@ function assembleblock_primer(biop, tfs, bfs; quaddata=quaddata) tshapes = refspace(tfs); num_tshapes = numfunctions(tshapes) bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes) - qd = quaddata(biop, tshapes, bshapes, test_elements, bsis_elements) + qd = quaddata(biop, tshapes, bshapes, test_elements, bsis_elements, quadstrat) zlocals = Matrix{scalartype(biop, tfs, bfs)}[] @@ -232,7 +234,7 @@ end function assembleblock_body!(biop::IntegralOperator, tfs, test_ids, test_elements, test_assembly_data, bfs, trial_ids, bsis_elements, trial_assembly_data, - quadrature_data, zlocals, store; quadrule=quadrule) + quadrature_data, zlocals, store; quadstrat) test_shapes = refspace(tfs) trial_shapes = refspace(bfs) @@ -259,8 +261,8 @@ function assembleblock_body!(biop::IntegralOperator, bcell = bsis_elements[q] fill!(zlocals[Threads.threadid()], 0) - strat = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data) - momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocals[Threads.threadid()], strat) + qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) + momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocals[Threads.threadid()], qrule) for j in 1 : size(zlocals[Threads.threadid()],2) for i in 1 : size(zlocals[Threads.threadid()],1) @@ -277,7 +279,7 @@ end end end end end end end function assembleblock_body_nested!(biop::IntegralOperator, tfs, test_ids, test_elements, test_assembly_data, bfs, trial_ids, bsis_elements, trial_assembly_data, - quadrature_data, zlocals, store; quadrule=quadrule) + quadrature_data, zlocals, store; quadstrat) test_shapes = refspace(tfs) trial_shapes = refspace(bfs) @@ -304,8 +306,8 @@ function assembleblock_body_nested!(biop::IntegralOperator, bcell = bsis_elements[q] fill!(zlocals[Threads.threadid()], 0) - strat = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data) - momintegrals_nested!(biop, test_shapes, trial_shapes, tcell, bcell, zlocals[Threads.threadid()], strat) + qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) + momintegrals_nested!(biop, test_shapes, trial_shapes, tcell, bcell, zlocals[Threads.threadid()], qrule) for j in 1 : size(zlocals[Threads.threadid()],2) for i in 1 : size(zlocals[Threads.threadid()],1) @@ -320,7 +322,7 @@ end end end end end end end function assemblerow!(biop::IntegralOperator, test_functions::Space, trial_functions::Space, store; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(biop, test_functions, trial_functions)) test_elements = elements(geometry(test_functions)) trial_elements, trial_assembly_data = assemblydata(trial_functions) @@ -331,7 +333,8 @@ function assemblerow!(biop::IntegralOperator, test_functions::Space, trial_funct num_test_shapes = numfunctions(test_shapes) num_trial_shapes = numfunctions(trial_shapes) - quadrature_data = quaddata(biop, test_shapes, trial_shapes, test_elements, trial_elements) + quadrature_data = quaddata(biop, test_shapes, trial_shapes, test_elements, trial_elements, + quadstrat) zlocal = zeros(scalartype(biop, test_functions, trial_functions), num_test_shapes, num_trial_shapes) @@ -341,14 +344,14 @@ function assemblerow!(biop::IntegralOperator, test_functions::Space, trial_funct assemblerow_body!(biop, test_functions, test_elements, test_shapes, trial_assembly_data, trial_elements, trial_shapes, - zlocal, quadrature_data, store, quadrule=quadrule) + zlocal, quadrature_data, store; quadstrat) end function assemblerow_body!(biop, test_functions, test_elements, test_shapes, trial_assembly_data, trial_elements, trial_shapes, - zlocal, quadrature_data, store; quadrule=quadrule) + zlocal, quadrature_data, store; quadstrat) test_function = test_functions.fns[1] for shape in test_function @@ -359,8 +362,8 @@ function assemblerow_body!(biop, for (q,bcell) in enumerate(trial_elements) fill!(zlocal, 0) - strat = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data) - momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, strat) + qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) + momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule) for j in 1:size(zlocal,2) for (n,b) in trial_assembly_data[q,j] @@ -369,7 +372,7 @@ end end end end end function assemblecol!(biop::IntegralOperator, test_functions::Space, trial_functions::Space, store; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(biop, test_functions, trial_functions)) test_elements, test_assembly_data = assemblydata(test_functions) trial_elements = elements(geometry(trial_functions)) @@ -380,7 +383,7 @@ function assemblecol!(biop::IntegralOperator, test_functions::Space, trial_funct num_test_shapes = numfunctions(test_shapes) num_trial_shapes = numfunctions(trial_shapes) - quadrature_data = quaddata(biop, test_shapes, trial_shapes, test_elements, trial_elements) + quadrature_data = quaddata(biop, test_shapes, trial_shapes, test_elements, trial_elements, quadstrat) zlocal = zeros( scalartype(biop, test_functions, trial_functions), num_test_shapes, num_trial_shapes) @@ -391,14 +394,14 @@ function assemblecol!(biop::IntegralOperator, test_functions::Space, trial_funct assemblecol_body!(biop, test_assembly_data, test_elements, test_shapes, trial_functions, trial_elements, trial_shapes, - zlocal, quadrature_data, store, quadrule=quadrule) + zlocal, quadrature_data, store; quadstrat) end function assemblecol_body!(biop, test_assembly_data, test_elements, test_shapes, trial_functions, trial_elements, trial_shapes, - zlocal, quadrature_data, store; quadrule=quadrule) + zlocal, quadrature_data, store; quadstrat) trial_function = trial_functions.fns[1] for shape in trial_function @@ -410,8 +413,8 @@ function assemblecol_body!(biop, for (p,tcell) in enumerate(test_elements) fill!(zlocal, 0) - strat = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data) - momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, strat) + qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) + momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule) for i in 1:size(zlocal,1) for (m,a) in test_assembly_data[p,i] diff --git a/src/localop.jl b/src/localop.jl index 14ef7732..dfafa62d 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -58,21 +58,22 @@ function allocatestorage(op::LocalOperator, test_functions, trial_functions, end function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, - threading::Type{Threading{:multi}}; quaddata=quaddata, quadrule=quadrule) + threading::Type{Threading{:multi}}; + quadstrat=defaultquadstrat(biop, tfs, bfs)) if geometry(tfs) == geometry(bfs) - return assemble_local_matched!(biop, tfs, bfs, store) + return assemble_local_matched!(biop, tfs, bfs, store; quadstrat) end if CompScienceMeshes.refines(geometry(tfs), geometry(bfs)) - return assemble_local_refines!(biop, tfs, bfs, store) + return assemble_local_refines!(biop, tfs, bfs, store; quadstrat) end - return assemble_local_mixed!(biop, tfs, bfs, store) + return assemble_local_mixed!(biop, tfs, bfs, store; quadstrat) end function assemble_local_matched!(biop::LocalOperator, tfs::Space, bfs::Space, store; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(biop, tfs, bfs)) tels, tad, ta2g = assemblydata(tfs) bels, bad, ba2g = assemblydata(bfs) @@ -83,14 +84,14 @@ function assemble_local_matched!(biop::LocalOperator, tfs::Space, bfs::Space, st trefs = refspace(tfs) brefs = refspace(bfs) - qd = quaddata(biop, trefs, brefs, tels, bels) + qd = quaddata(biop, trefs, brefs, tels, bels, quadstrat) locmat = zeros(scalartype(biop, trefs, brefs), numfunctions(trefs), numfunctions(brefs)) for (p,cell) in enumerate(tels) P = ta2g[p] q = bg2a[P] q == 0 && continue - qr = quadrule(biop, trefs, brefs, cell, qd) + qr = quadrule(biop, trefs, brefs, cell, qd, quadstrat) fill!(locmat, 0) cellinteractions_matched!(locmat, biop, trefs, brefs, cell, qr) @@ -101,7 +102,7 @@ end end end end function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, store; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(biop, tfs, bfs)) println("Using 'refines' algorithm for local assembly:") @@ -118,7 +119,7 @@ function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, st bg2a = zeros(Int, length(geometry(bfs))) for (i,j) in enumerate(ba2g) bg2a[j] = i end - qd = quaddata(biop, trefs, brefs, tels, bels) + qd = quaddata(biop, trefs, brefs, tels, bels, quadstrat) print("dots out of 10: ") todo, done, pctg = length(tels), 0, 0 @@ -137,7 +138,7 @@ function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, st P = restrict(brefs, bcell, cell) Q = restrict(trefs, tcell, cell) - qr = quadrule(biop, trefs, brefs, cell, qd) + qr = quadrule(biop, trefs, brefs, cell, qd, quadstrat) zlocal = cellinteractions(biop, trefs, brefs, cell, qr) zlocal = Q * zlocal * P' @@ -166,7 +167,7 @@ function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, st end function assemble_local_matched!(biop::LocalOperator, tfs::subdBasis, bfs::subdBasis, store; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(biop, tfs, bfs)) tels, tad = assemblydata(tfs) bels, bad = assemblydata(bfs) @@ -174,10 +175,10 @@ function assemble_local_matched!(biop::LocalOperator, tfs::subdBasis, bfs::subdB trefs = refspace(tfs) brefs = refspace(bfs) - qd = quaddata(biop, trefs, brefs, tels, bels) + qd = quaddata(biop, trefs, brefs, tels, bels, quadstrat) for (p,cell) in enumerate(tels) - qr = quadrule(biop, trefs, brefs, cell, qd) + qr = quadrule(biop, trefs, brefs, cell, qd, quadstrat) locmat = cellinteractions(biop, trefs, brefs, cell, qr) for i in 1 : size(locmat, 1), j in 1 : size(locmat, 2) @@ -223,7 +224,7 @@ end For use when basis and test functions are defined on different meshes """ function assemble_local_mixed!(biop::LocalOperator, tfs::Space, bfs::Space, store; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(biop, tfs, bfs)) tol = sqrt(eps(Float64)) @@ -233,7 +234,7 @@ function assemble_local_mixed!(biop::LocalOperator, tfs::Space, bfs::Space, stor tels, tad = assemblydata(tfs) bels, bad = assemblydata(bfs) - qd = quaddata(biop, trefs, brefs, tels, bels) + qd = quaddata(biop, trefs, brefs, tels, bels, quadstrat) # store the bcells in an octree tree = elementstree(bels) @@ -258,7 +259,7 @@ function assemble_local_mixed!(biop::LocalOperator, tfs::Space, bfs::Space, stor P = restrict(brefs, bcell, cell) Q = restrict(trefs, tcell, cell) - qr = quadrule(biop, trefs, brefs, cell, qd) + qr = quadrule(biop, trefs, brefs, cell, qd, quadstrat) zlocal = cellinteractions(biop, trefs, brefs, cell, qr) zlocal = Q * zlocal * P' diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index f078d549..6c65c084 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -80,16 +80,19 @@ function _legendre(n,a,b) collect(zip(x,w)) end +defaultquadstrat(op::MaxwellOperator3D, tfs, bfs) = DoubleNumWiltonSauterQStrat(2,3,6,7,5,5,4,3) + function quaddata(op::MaxwellOperator3D, test_local_space::RefSpace, trial_local_space::RefSpace, - test_charts, trial_charts) + test_charts, trial_charts, qs::DoubleNumWiltonSauterQStrat) - a, b = 0.0, 1.0 - # CommonVertex, CommonEdge, CommonFace rules + tqd = quadpoints(test_local_space, test_charts, (qs.outer_rule_far,qs.outer_rule_near)) + bqd = quadpoints(trial_local_space, trial_charts, (qs.inner_rule_far,qs.inner_rule_near)) + leg = ( + _legendre(qs.sauter_schwab_common_vert,0,1), + _legendre(qs.sauter_schwab_common_edge,0,1), + _legendre(qs.sauter_schwab_common_face,0,1),) - tqd = quadpoints(test_local_space, test_charts, (2,6)) - bqd = quadpoints(trial_local_space, trial_charts, (3,7)) - leg = (_legendre(3,a,b), _legendre(4,a,b), _legendre(5,a,b),) # High accuracy rules (use them e.g. in LF MFIE scenarios) # tqd = quadpoints(test_local_space, test_charts, (8,8)) @@ -144,13 +147,14 @@ function integrand(biop::MWDL3DGen, kerneldata, tvals, tgeo, bvals, bgeo) end -quadrule(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd) = qrss(op, g, f, i, τ, j, σ, qd) +# quadrule(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd) = qrss(op, g, f, i, τ, j, σ, qd) -function qrss(op, g, f, i, τ, j, σ, qd) - # defines coincidence of points - dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) +# function qrss(op, g, f, i, τ, j, σ, qd) +function quadrule(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd, + qs::DoubleNumWiltonSauterQStrat) hits = 0 + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) dmin2 = floatmax(eltype(eltype(τ.vertices))) for t in τ.vertices for s in σ.vertices @@ -177,6 +181,37 @@ function qrss(op, g, f, i, τ, j, σ, qd) qd.bpoints[1,j],) end +function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::BDMRefSpace, i, τ, j, σ, qd, + qs::DoubleNumWiltonSauterQStrat) + + hits = 0 + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) + dmin2 = floatmax(eltype(eltype(τ.vertices))) + for t in τ.vertices + for s in σ.vertices + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < dtol) + end + end + + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) + hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) + hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + + h2 = volume(σ) + xtol2 = 0.2 * 0.2 + k2 = abs2(op.gamma) + # max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( + # qd.tpoints[2,i], + # DoubleQuadRule( + # qd.tpoints[2,i], + # qd.bpoints[2,j],),) + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j],) +end + function qrib(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd) # defines coincidence of points diff --git a/src/maxwell/nitsche.jl b/src/maxwell/nitsche.jl index a833ee1a..d99135c9 100644 --- a/src/maxwell/nitsche.jl +++ b/src/maxwell/nitsche.jl @@ -12,14 +12,14 @@ mutable struct SingleLayerTrace{T} <: MaxwellOperator3D gamma::T end +defaultquadstrat(::SingleLayerTrace, ::LagrangeRefSpace, ::LagrangeRefSpace) = DoubleNumWiltonSauterQStrat(10,8,10,8,3,3,3,3) function quaddata(operator::SingleLayerTrace, localtestbasis::LagrangeRefSpace, localtrialbasis::LagrangeRefSpace, - testelements, - trialelements) + testelements, trialelements, qs::DoubleNumWiltonSauterQStrat) - tqd = quadpoints(localtestbasis, testelements, (10,)) - bqd = quadpoints(localtrialbasis, trialelements, (8,)) + tqd = quadpoints(localtestbasis, testelements, (qs.outer_rule_far,)) + bqd = quadpoints(localtrialbasis, trialelements, (qs.outer_rule_near,)) #return QuadData(tqd, bqd) return (tpoints=tqd, bpoints=bqd) @@ -27,7 +27,9 @@ end # Use numerical quadrature for now # Note: basis integral is over triangle, test over line -function quadrule(op::SingleLayerTrace, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd) +function quadrule(op::SingleLayerTrace, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd, + qs::DoubleNumWiltonSauterQStrat) + DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j] diff --git a/src/maxwell/nxdbllayer.jl b/src/maxwell/nxdbllayer.jl index 89ec92c8..e081d4d9 100644 --- a/src/maxwell/nxdbllayer.jl +++ b/src/maxwell/nxdbllayer.jl @@ -7,24 +7,23 @@ end LinearAlgebra.cross(::NormalVector, a::MWDoubleLayer3D) = DoubleLayerRotatedMW3D(a.gamma) +defaultquadstrat(::DoubleLayerRotatedMW3D, tfs, bfs) = DoubleNumQStrat(2,3) + function quaddata(operator::DoubleLayerRotatedMW3D, - local_test_basis::RTRefSpace, - local_trial_basis::RTRefSpace, - test_elements, trial_elements) + local_test_basis::LinearRefSpaceTriangle, local_trial_basis::LinearRefSpaceTriangle, + test_elements, trial_elements, qs::DoubleNumQStrat) - test_quad_data = quadpoints(local_test_basis, test_elements, (2,)) - trial_quad_data = quadpoints(local_trial_basis, trial_elements, (3,)) + test_quad_data = quadpoints(local_test_basis, test_elements, (qs.outer_rule,)) + trial_quad_data = quadpoints(local_trial_basis, trial_elements, (qs.inner_rule,)) return test_quad_data, trial_quad_data end function quadrule(operator::DoubleLayerRotatedMW3D, - local_test_basis::RTRefSpace, - local_trial_basis::RTRefSpace, - test_id, test_element, - trial_id, trial_element, - quad_data) + local_test_basis::LinearRefSpaceTriangle, local_trial_basis::LinearRefSpaceTriangle, + test_id, test_element, trial_id, trial_element, + quad_data, qs::DoubleNumQStrat) test_quad_rules = quad_data[1] trial_quad_rules = quad_data[2] diff --git a/src/maxwell/sauterschwabints_rt.jl b/src/maxwell/sauterschwabints_rt.jl index 1813e127..784af619 100644 --- a/src/maxwell/sauterschwabints_rt.jl +++ b/src/maxwell/sauterschwabints_rt.jl @@ -147,12 +147,13 @@ function momintegrals_nested!(op::MWOperator3D, simplex(ct, p3, e2), simplex(ct, e2, p1)] + qs = defaultquadstrat(op, test_local_space, trial_local_space) qd = quaddata(op, test_local_space, trial_local_space, - [test_chart], refined_trial_chart) + [test_chart], refined_trial_chart, qs) for (q,chart) in enumerate(refined_trial_chart) qr = quadrule(op, test_local_space, trial_local_space, - 1, test_chart, q ,chart, qd) + 1, test_chart, q ,chart, qd, qs) Q = restrict(trial_local_space, trial_chart, chart) zlocal = zero(out) diff --git a/src/maxwell/timedomain/mwtdops.jl b/src/maxwell/timedomain/mwtdops.jl index c7daad61..5a0becea 100644 --- a/src/maxwell/timedomain/mwtdops.jl +++ b/src/maxwell/timedomain/mwtdops.jl @@ -74,8 +74,10 @@ end # module TDMaxwell3D export TDMaxwell3D +defaultquadstrat(::MWSingleLayerTDIO, tfs, bfs) = nothing + function quaddata(op::MWSingleLayerTDIO, testrefs, trialrefs, timerefs, - testels, trialels, timeels) + testels, trialels, timeels, ::Nothing) dmax = numfunctions(timerefs)-1 bn = binomial.((0:dmax),(0:dmax)') @@ -88,7 +90,7 @@ end quadrule(op::MWSingleLayerTDIO, testrefs, trialrefs, timerefs, - p, testel, q, trialel, r, timeel, qd) = WiltonInts84Strat(qd[1][1,p],qd[2],qd[3]) + p, testel, q, trialel, r, timeel, qd, ::Nothing) = WiltonInts84Strat(qd[1][1,p],qd[2],qd[3]) struct TransposedStorage{F} @@ -137,7 +139,7 @@ end # end function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, store, - threading=Threading{:multi}; quaddata=quaddata, quadrule=quadrule) + threading=Threading{:multi}; quadstrat=defaultquadstrat(dl,W,V)) X, T = spatialbasis(W), temporalbasis(W) Y, U = spatialbasis(V), temporalbasis(V) @@ -160,14 +162,16 @@ function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, lo <= hi || continue Y_p = subset(Y, lo:hi) store2 = (v,m,n,k) -> store(v,lo+m-1,n,k) - assemble_chunk!(dl, Y_p ⊗ S, V, store2) + assemble_chunk!(dl, Y_p ⊗ S, V, store2; quadstrat) end # return assemble_chunk!(dl, W, V, store1) end +defaultquadstrat(::MWDoubleLayerTDIO, tfs, bfs) = nothing + function quaddata(op::MWDoubleLayerTDIO, testrefs, trialrefs, timerefs, - testels, trialels, timeels) + testels, trialels, timeels, quadstrat::Nothing) dmax = numfunctions(timerefs)-1 bn = binomial.((0:dmax),(0:dmax)') @@ -179,12 +183,15 @@ function quaddata(op::MWDoubleLayerTDIO, testrefs, trialrefs, timerefs, end quadrule(op::MWDoubleLayerTDIO, testrefs, trialrefs, timerefs, - p, testel, q, trialel, r, timeel, qd) = WiltonInts84Strat(qd[1][1,p],qd[2],qd[3]) + p, testel, q, trialel, r, timeel, qd, quadstrat::Nothing) = + WiltonInts84Strat(qd[1][1,p],qd[2],qd[3]) + +defaultquadstrat(::MWDoubleLayerTransposedTDIO, tfs, bfs) = nothing function quaddata(op::MWDoubleLayerTransposedTDIO, testrefs, trialrefs, timerefs, - testels, trialels, timeels) + testels, trialels, timeels, quadstrat::Nothing) dmax = numfunctions(timerefs)-1 bn = binomial.((0:dmax),(0:dmax)') @@ -195,7 +202,8 @@ function quaddata(op::MWDoubleLayerTransposedTDIO, end quadrule(op::MWDoubleLayerTransposedTDIO, testrefs, trialrefs, timerefs, - p, testel, q, trialel, r, timeel, qd) = WiltonInts84Strat(qd[1][1,p],qd[2],qd[3]) + p, testel, q, trialel, r, timeel, qd, quadstrat::Nothing) = + WiltonInts84Strat(qd[1][1,p],qd[2],qd[3]) function momintegrals!(z, op::MWDoubleLayerTransposedTDIO, g, f, T, τ, σ, ι, qr::WiltonInts84Strat) diff --git a/src/operator.jl b/src/operator.jl index 9a889430..ae283be2 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -19,6 +19,7 @@ mutable struct TransposedOperator <: Operator end scalartype(op::TransposedOperator) = scalartype(op.op) +defaultquadstrat(op::TransposedOperator, tfs, bfs) = defaultquadstrat(op.op, tfs, bfs) mutable struct LinearCombinationOfOperators{T} <: AbstractOperator coeffs::Vector{T} @@ -74,43 +75,42 @@ end transpose(op::TransposedOperator) = op.op transpose(op::Operator) = TransposedOperator(op) +defaultquadstrat(lc::LinearCombinationOfOperators, tfs, bfs) = + [defaultquadstrat(op,tfs,bfs) for op in lc.ops] function assemble(operator::AbstractOperator, test_functions, trial_functions; storage_policy = Val{:bandedstorage}, long_delays_policy = LongDelays{:compress}, threading = Threading{:multi}, - quaddata=quaddata, - quadrule=quadrule) - # This is a convenience function whose only job is to allocate - # the storage for the interaction matrix. Further dispatch on - # operator and space types is handled by the 4-argument version + quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) + Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) - assemble!(operator, test_functions, trial_functions, store, threading, - quaddata=quaddata, quadrule=quadrule) + assemble!(operator, test_functions, trial_functions, store, threading; quadstrat) return Z() end function assemblerow(operator::AbstractOperator, test_functions, trial_functions, storage_policy = Val{:bandedstorage}, long_delays_policy = LongDelays{:ignore}; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) - assemblerow!(operator, test_functions, trial_functions, store, - quaddata=quaddata, quadrule=quadrule) + assemblerow!(operator, test_functions, trial_functions, store; quadstrat) + Z() end function assemblecol(operator::AbstractOperator, test_functions, trial_functions, storage_policy = Val{:bandestorage}, - long_delays_policy = LongDelays{:ignore}) + long_delays_policy = LongDelays{:ignore}; + quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) - assemblecol!(operator, test_functions, trial_functions, store, - quaddata=quaddata, quadrule=quadrule) + assemblecol!(operator, test_functions, trial_functions, store; quadstrat) + Z() end @@ -155,7 +155,8 @@ end function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, - threading::Type{Threading{:multi}} = Threading{:multi}; quaddata=quaddata, quadrule=quadrule) + threading::Type{Threading{:multi}} = Threading{:multi}; + quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) @info "Multi-threaded assembly:" @@ -169,68 +170,69 @@ function assemble!(operator::Operator, test_functions::Space, trial_functions::S lo <= hi || continue test_functions_p = subset(test_functions, lo:hi) store1 = (v,m,n) -> store(v,lo+m-1,n) - assemblechunk!(operator, test_functions_p, trial_functions, store1, - quaddata=quaddata, quadrule=quadrule) + assemblechunk!(operator, test_functions_p, trial_functions, store1; quadstrat) end end function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, - threading::Type{Threading{:single}}; quaddata=quaddata, quadrule=quadrule) + threading::Type{Threading{:single}}; + quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) @info "Single-threaded assembly" - assemblechunk!(operator, test_functions, trial_functions, store, - quaddata=quaddata, quadrule=quadrule) + assemblechunk!(operator, test_functions, trial_functions, store; quadstrat) end function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, store; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(op, tfs, bfs)) store1(v,m,n) = store(v,n,m) - assemble!(op.op, bfs, tfs, store1, quaddata=quaddata, quadrule=quadrule) + assemble!(op.op, bfs, tfs, store1; quadstrat) end function assemble!(op::LinearCombinationOfOperators, tfs::AbstractSpace, bfs::AbstractSpace, - store, threading = Threading{:multi}; quaddata=quaddata, quadrule=quadrule) - for (a,A) in zip(op.coeffs, op.ops) + store, threading = Threading{:multi}; + quadstrat=defaultquadstrat(op, tfs, bfs)) + + for (a,A,qs) in zip(op.coeffs, op.ops, quadstrat) store1(v,m,n) = store(a*v,m,n) - assemble!(A, tfs, bfs, store1, quaddata=quaddata, quadrule=quadrule) + assemble!(A, tfs, bfs, store1; quadstrat=qs) end end # Support for direct product spaces function assemble!(op::Operator, tfs::DirectProductSpace, bfs::Space, store, threading = Threading{:multi}; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(op, tfs[1], bfs)) I = Int[0] for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) store1(v,m,n) = store(v,m + I[i], n) - assemble!(op, s, bfs, store1, quaddata=quaddata, quadrule=quadrule) + assemble!(op, s, bfs, store1; quadstrat) end end function assemble!(op::Operator, tfs::Space, bfs::DirectProductSpace, store, threading=Threading{:multi}; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(op, tfs, bfs[1])) J = Int[0] for s in bfs.factors push!(J, last(J) + numfunctions(s)) end for (j,s) in enumerate(bfs.factors) store1(v,m,n) = store(v,m,n + J[j]) - assemble!(op, tfs, s, store1, quaddata=quaddata, quadrule=quadrule) + assemble!(op, tfs, s, store1; quadstrat) end end function assemble!(op::Operator, tfs::DirectProductSpace, bfs::DirectProductSpace, store, threading=Threading{:multi}; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(op, tfs[1], bfs[1])) I = Int[0] for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) store1(v,m,n) = store(v,m + I[i],n) - assemble!(op, s, bfs, store1, quaddata=quaddata, quadrule=quadrule) + assemble!(op, s, bfs, store1; quadstrat) end end diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index 32c60cca..d8e97636 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -9,7 +9,16 @@ struct DoubleNumWiltonSauterQStrat{R,S} sauter_schwab_common_vert::S end -struct DoubleNum{R} +struct DoubleNumQStrat{R} outer_rule::R inner_rule::R +end + + +# defaultquadstrat(op, tfs, bfs) = DoubleNumWiltonSauterQStrat(2,3,6,7,6,5,4,3) +defaultquadstrat(op, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bfs)) +# defaultquadstrat(op, ::RefSpace, ::RefSpace) = error("No default quadstrat set.") + +struct SingleNumQStrat + quad_rule::Int end \ No newline at end of file diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 51856718..543b3044 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -8,6 +8,7 @@ mutable struct EmptyRP{T} <: RetardedPotential{T} speed_of_light::T end Base.eltype(::EmptyRP) = Int +defaultquadstrat(::EmptyRP, tfs, bfs) = nothing quaddata(op::EmptyRP, xs...) = nothing quadrule(op::EmptyRP, xs...) = nothing momintegrals!(z, op::EmptyRP, xs...) = nothing @@ -129,7 +130,7 @@ function allocatestorage(op::RetardedPotential, testST, basisST, end function assemble!(op::LinearCombinationOfOperators, tfs::SpaceTimeBasis, bfs::SpaceTimeBasis, store, - threading=Threading{:multi}; quaddata=quaddata, quadrule=quadrule) + threading=Threading{:multi}; quadstrat=defaultquadstrat(op, tfs, bfs)) for (a,A) in zip(op.coeffs, op.ops) store1(v,m,n,k) = store(a*v,m,n,k) @@ -138,7 +139,7 @@ function assemble!(op::LinearCombinationOfOperators, tfs::SpaceTimeBasis, bfs::S end function assemble!(op::RetardedPotential, testST, trialST, store, - threading=Threading{:multi}; quaddata=quaddata, quadrule=quadrule) + threading=Threading{:multi}; quadstrat=defaultquadstrat(op, testST, trialST)) Y, S = spatialbasis(testST), temporalbasis(testST) @@ -159,7 +160,7 @@ function assemble!(op::RetardedPotential, testST, trialST, store, end function assemble_chunk!(op::RetardedPotential, testST, trialST, store; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(op, testST, trialST)) myid = Threads.threadid() @@ -189,7 +190,7 @@ function assemble_chunk!(op::RetardedPotential, testST, trialST, store; V = refspace(trialspace) W = refspace(timebasisfunction) - qd = quaddata(op, U, V, W, testels, trialels, nothing) + qd = quaddata(op, U, V, W, testels, trialels, nothing, quadstrat) udim = numfunctions(U) vdim = numfunctions(V) @@ -210,7 +211,7 @@ function assemble_chunk!(op::RetardedPotential, testST, trialST, store; # compute interactions between reference shape functions fill!(z, 0) - qr = quadrule(op, U, V, W, p, τ, q, σ, r, ι, qd) + qr = quadrule(op, U, V, W, p, τ, q, σ, r, ι, qd, quadstrat) momintegrals!(z, op, U, V, W, τ, σ, ι, qr) # assemble in the global matrix diff --git a/src/timedomain/tdtimeops.jl b/src/timedomain/tdtimeops.jl index 2de1cb7b..8f73d332 100644 --- a/src/timedomain/tdtimeops.jl +++ b/src/timedomain/tdtimeops.jl @@ -37,6 +37,8 @@ mutable struct TensorOperator <: Operator temporal_factor end +BEAST.defaultquadstrat(::TensorOperator, tfs, bfs) = nothing + ⊗(A::AbstractOperator, B::AbstractOperator) = TensorOperator(A, B) function scalartype(A::TensorOperator) promote_type( @@ -175,6 +177,8 @@ derive(op::AbstractOperator) = TemporalDifferentiation(op) scalartype(op::TemporalDifferentiation) = scalartype(op.operator) Base.:*(a::Number, op::TemporalDifferentiation) = TemporalDifferentiation(a * op.operator) +defaultquadstrat(op::TemporalDifferentiation, tfs, bfs) = defaultquadstrat(op.operator, tfs, bfs) + function allocatestorage(op::TemporalDifferentiation, testfns, trialfns, storage_trait, longdelays_trait) @@ -189,7 +193,8 @@ function allocatestorage(op::TemporalDifferentiation, testfns, trialfns, return allocatestorage(op.operator, testfns, trialfns, storage_trait, longdelays_trait) end -function assemble!(operator::TemporalDifferentiation, testfns, trialfns, store, threading = Threading{:multi}) +function assemble!(operator::TemporalDifferentiation, testfns, trialfns, store, threading = Threading{:multi}; + quadstrat=defaultquadstrat(operator, testfns, trialfns)) trial_time_fns = temporalbasis(trialfns) trial_space_fns = spatialbasis(trialfns) @@ -199,7 +204,7 @@ function assemble!(operator::TemporalDifferentiation, testfns, trialfns, store, derive(trial_time_fns) ) - assemble!(operator.operator, testfns, trialfns, store, threading) + assemble!(operator.operator, testfns, trialfns, store, threading; quadstrat) end @@ -207,6 +212,8 @@ struct TemporalIntegration <: AbstractOperator operator::AbstractOperator end +defaultquadstrat(op::TemporalIntegration, tfs, bfs) = defaultquadstrat(op.operator, tfs, bfs) + integrate(op::AbstractOperator) = TemporalIntegration(op) derive(op::TemporalIntegration) = op.operator scalartype(op::TemporalIntegration) = scalartype(op.operator) @@ -227,7 +234,7 @@ function allocatestorage(op::TemporalIntegration, testfns, trialfns, end function assemble!(operator::TemporalIntegration, testfns, trialfns, store, - threading = Threading{:multi}) + threading = Threading{:multi}; quadstrat=defaultquadstrat(operator, testfns, trialfns)) trial_time_fns = temporalbasis(trialfns) trial_space_fns = spatialbasis(trialfns) @@ -237,6 +244,6 @@ function assemble!(operator::TemporalIntegration, testfns, trialfns, store, integrate(trial_time_fns) ) - assemble!(operator.operator, testfns, trialfns, store) + assemble!(operator.operator, testfns, trialfns, store; quadstrat) end diff --git a/test/test_assemblerow.jl b/test/test_assemblerow.jl index de27e4e7..1154fcf4 100644 --- a/test/test_assemblerow.jl +++ b/test/test_assemblerow.jl @@ -28,14 +28,15 @@ X1 = subset(X,I) T2 = zeros(scalartype(t,X1,X1),numfunctions(X1),numfunctions(X1)) store(v,m,n) = (T2[m,n] += v) +qs = BEAST.defaultquadstrat(t,X,X) test_elements, test_assembly_data, trial_elements, trial_assembly_data, - quadrature_data, zlocal = BEAST.assembleblock_primer(t,X,X) + quadrature_data, zlocal = BEAST.assembleblock_primer(t,X,X, quadstrat=qs) BEAST.assembleblock_body!(t, X, I, test_elements, test_assembly_data, X, I, trial_elements, trial_assembly_data, - quadrature_data, zlocal, store) + quadrature_data, zlocal, store, quadstrat=qs) T1 = assemble(t,X1,X1) @test T1 == T2 diff --git a/test/test_tdassembly.jl b/test/test_tdassembly.jl index ca6e915b..8baf9214 100644 --- a/test/test_tdassembly.jl +++ b/test/test_tdassembly.jl @@ -71,11 +71,12 @@ for r in BEAST.rings(τ1, τ2, ΔR) momintegrals!(z1, G, x1, x2, q, τ1, τ2, ι, DoubleQuadTimeDomainRule()) end -qd = quaddata(G, x1, x2, q, [τ1], [τ2], nothing) +qs = BEAST.defaultquadstrat(G,X1,X2) +qd = quaddata(G, x1, x2, q, [τ1], [τ2], nothing, qs) z2 = zeros(numfunctions(x1), numfunctions(x2), numfunctions(q)) for r in BEAST.rings(τ1, τ2, ΔR) ι = BEAST.ring(r, ΔR) - quad_rule = quadrule(G, x1, x2, q, 1, τ1, 1, τ2, r, ι, qd) + quad_rule = quadrule(G, x1, x2, q, 1, τ1, 1, τ2, r, ι, qd, qs) BEAST.momintegrals!(z2, G, x1, x2, q, τ1, τ2, ι, quad_rule) end diff --git a/test/test_tdmwdbl.jl b/test/test_tdmwdbl.jl index 2c65935e..a10ce658 100644 --- a/test/test_tdmwdbl.jl +++ b/test/test_tdmwdbl.jl @@ -51,10 +51,10 @@ test_els = BEAST.elements(G) trial_els = BEAST.elements(γ) time_els = [(0.0, 20.0)] -qdt = BEAST.quaddata(K, refspace(X), refspace(Y), refspace(T2), test_els, trial_els, nothing) -qrl = BEAST.quadrule(K, - refspace(X), refspace(Y), refspace(T2), - 1, test_els[1], 1, trial_els[1], 1, time_els[1], qdt) +qs = BEAST.defaultquadstrat(K,X,Y) +qdt = BEAST.quaddata(K, refspace(X), refspace(Y), refspace(T2), test_els, trial_els, nothing, qs) +qrl = BEAST.quadrule(K, refspace(X), refspace(Y), refspace(T2), + 1, test_els[1], 1, trial_els[1], 1, time_els[1], qdt, qs) z = zeros(Float64, 3, 3, 10) BEAST.innerintegrals!(z, K, x, refspace(X), refspace(Y), refspace(T2), test_els[1], trial_els[1], (0.0, 20.0), qrl, w) @test_broken !any(isnan.(z)) From 9918bfa7fa6f121577bb2d9f3263990fa6ec2f3c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 15 Oct 2021 12:30:24 +0200 Subject: [PATCH 077/528] quadinfo provides insight in quadstrat dispatch --- Project.toml | 1 + examples/quadstrats.jl | 48 ++++++++++++++++++++++++++++++++++++ examples/utils/showfns.jl | 34 +++++++++++++++++++++++++ src/integralop.jl | 4 +-- src/quadrature/quadstrats.jl | 22 +++++++++++++++++ 5 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 examples/quadstrats.jl create mode 100644 examples/utils/showfns.jl diff --git a/Project.toml b/Project.toml index 3a639ba2..c6c894e0 100644 --- a/Project.toml +++ b/Project.toml @@ -12,6 +12,7 @@ Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" +InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LiftedMaps = "d22a30c1-52ac-4762-a8c9-5838452405e0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" diff --git a/examples/quadstrats.jl b/examples/quadstrats.jl new file mode 100644 index 00000000..069497c7 --- /dev/null +++ b/examples/quadstrats.jl @@ -0,0 +1,48 @@ +using CompScienceMeshes +using BEAST + +Γ1 = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +Γ2 = CompScienceMeshes.translate(Γ1, [10,0,0]) + +SL = Maxwell3D.singlelayer(wavenumber=1.0) +X1 = raviartthomas(Γ1) +X2 = raviartthomas(Γ2) + +x1 = refspace(X1) +x2 = refspace(X2) + +function BEAST.quaddata(op::typeof(SL), tref::typeof(x1), bref::typeof(x2), + tels, bels, qs::BEAST.DoubleNumQStrat) + + qs = BEAST.DoubleNumWiltonSauterQStrat(qs.outer_rule, qs.inner_rule, 1, 1, 1, 1, 1, 1) + BEAST.quaddata(op, tref, bref, tels, bels, qs) +end + +function BEAST.quadrule(op::typeof(SL), tref::typeof(x1), bref::typeof(x2), + i ,τ, j, σ, qd, qs::BEAST.DoubleNumQStrat) + + return BEAST.DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j]) +end + +BEAST.quadinfo(SL,X1,X2) +BEAST.quadinfo(SL,X1,X2, quadstrat=BEAST.DoubleNumQStrat(2,3)) + +@time Z1 = assemble(SL,X1,X2); +@time Z2 = assemble(SL,X1,X2,quadstrat=BEAST.DoubleNumQStrat(2,3)); + +ba1 = blockassembler(SL,X1,X2) +ba2 = blockassembler(SL,X1,X2,quadstrat=BEAST.DoubleNumQStrat(2,3)) + +T = scalartype(SL,X1,X2) +n1 = numfunctions(X1) +n2 = numfunctions(X2) + +W1 = zeros(T,n1,n2); +W2 = zeros(T,n1,n2); + +idcs1 = collect(1:n1) +idcs2 = collect(1:n2) +@time ba1(idcs1,idcs2, (v,m,n)->(W1[m,n]+=v)) +@time ba2(idcs1,idcs2, (v,m,n)->(W2[m,n]+=v)); \ No newline at end of file diff --git a/examples/utils/showfns.jl b/examples/utils/showfns.jl new file mode 100644 index 00000000..47229415 --- /dev/null +++ b/examples/utils/showfns.jl @@ -0,0 +1,34 @@ +import Plotly +function showfn(space,i) + geo = geometry(space) + T = coordtype(geo) + X = Dict{Int,T}() + Y = Dict{Int,T}() + Z = Dict{Int,T}() + U = Dict{Int,T}() + V = Dict{Int,T}() + W = Dict{Int,T}() + for sh in space.fns[i] + chrt = chart(geo, cells(geo)[sh.cellid]) + nbd = CompScienceMeshes.center(chrt) + vals = refspace(space)(nbd) + x,y,z = cartesian(nbd) + @show vals[sh.refid].value + u,v,w = vals[sh.refid].value + # @show x, y, z + # @show u, v, w + X[sh.cellid] = x + Y[sh.cellid] = y + Z[sh.cellid] = z + U[sh.cellid] = get(U,sh.cellid,zero(T)) + sh.coeff * u + V[sh.cellid] = get(V,sh.cellid,zero(T)) + sh.coeff * v + W[sh.cellid] = get(W,sh.cellid,zero(T)) + sh.coeff * w + end + X = collect(values(X)) + Y = collect(values(Y)) + Z = collect(values(Z)) + U = collect(values(U)) + V = collect(values(V)) + W = collect(values(W)) + Plotly.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) +end \ No newline at end of file diff --git a/src/integralop.jl b/src/integralop.jl index 916ac3fb..74f9a752 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -252,8 +252,8 @@ function assembleblock_body!(biop::IntegralOperator, for m in test_ids, sh in tfs.fns[m]; push!(active_test_el_ids, sh.cellid); end for m in trial_ids, sh in bfs.fns[m]; push!(active_trial_el_ids, sh.cellid); end - active_test_el_ids = unique(sort(active_test_el_ids)) - active_trial_el_ids = unique(sort(active_trial_el_ids)) + active_test_el_ids = unique!(sort!(active_test_el_ids)) + active_trial_el_ids = unique!(sort!(active_trial_el_ids)) for p in active_test_el_ids tcell = test_elements[p] diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index d8e97636..22a0b4c4 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -1,3 +1,5 @@ +using InteractiveUtils + struct DoubleNumWiltonSauterQStrat{R,S} outer_rule_far::R inner_rule_far::R @@ -21,4 +23,24 @@ defaultquadstrat(op, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bf struct SingleNumQStrat quad_rule::Int +end + +function quadinfo(op, tfs, bfs; quadstrat=defaultquadstrat(op, tfs, bfs)) + + tels, tad = assemblydata(tfs) + bels, bad = assemblydata(bfs) + + tref = refspace(tfs) + bref = refspace(bfs) + + i, τ = 1, first(tels) + j, σ = 1, first(bels) + + @show quadstrat + println(@which BEAST.quaddata(op,tref,bref,tels,bels,quadstrat)) + + qd = quaddata(op,tref,bref,tels,bels,quadstrat) + println(@which quadrule(op,tref,bref,i,τ,j,σ,qd,quadstrat)) + + nothing end \ No newline at end of file From 2e2ce55ac61183815d86cfae2898757fa31eb104 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 18 Oct 2021 16:48:27 +0200 Subject: [PATCH 078/528] BC central node detection independent of edge orderign scheme --- src/bases/bcspace.jl | 66 +++++++++++++++++++++++++++++++++----------- src/identityop.jl | 2 +- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/bases/bcspace.jl b/src/bases/bcspace.jl index 7efff2d0..45f55e0b 100644 --- a/src/bases/bcspace.jl +++ b/src/bases/bcspace.jl @@ -87,42 +87,76 @@ isclosed(a, pred) = length(a)>2 && pred(a[end], a[1]) Construct the set of Buffa-Christiansen functions subject to mesh Γ and only enforcing zero normal components on ∂Γ ∖ γ. """ -function buffachristiansen(Γ, γ=mesh(coordtype(Γ),1,3); ibscaled=false, sort=:spacefillingcurve) +function buffachristiansen(Γ, γ=mesh(coordtype(Γ),1,3); ibscaled=false, sort=:spacefillingcurve, edges=:all) @assert CompScienceMeshes.isoriented(Γ) T = coordtype(Γ) P = vertextype(Γ) - edges = skeleton(Γ, 1; sort) + in_interior = interior_tpredicate(Γ) + on_junction = overlap_gpredicate(γ) + + # edges = skeleton(Γ, 1; sort) + + if edges == :all + edges = skeleton(Γ, 1; sort) + edges = submesh(edges) do edge + ch = chart(edges, edge) + !in_interior(edge) && !on_junction(ch) && return false + return true + end + end + @assert edges isa CompScienceMeshes.AbstractMesh + @assert dimension(edges) == 1 + fine = if ibscaled CompScienceMeshes.lineofsight_refinement(Γ) else barycentric_refinement(Γ; sort) end - in_interior = interior_tpredicate(Γ) - on_junction = overlap_gpredicate(γ) - # first pass to determine the number of functions - numfuncs = 0 - for edge in cells(edges) - ch = chart(edges, edge) - !in_interior(edge) && !on_junction(ch) && continue - numfuncs += 1 - end + # # first pass to determine the number of functions + # numfuncs = 0 + # for edge in edges + # ch = chart(edges, edge) + # !in_interior(edge) && !on_junction(ch) && continue + # numfuncs += 1 + # end vtof, vton = vertextocellmap(fine) jct_pred = overlap_gpredicate(γ) - bcs, k = Vector{Vector{Shape{T}}}(undef,numfuncs), 1 - pos = Vector{P}(undef,numfuncs) - for (i,edge) in enumerate(cells(edges)) + bcs, k = Vector{Vector{Shape{T}}}(undef,length(edges)), 1 + pos = Vector{P}(undef,length(edges)) + for (i,edge) in enumerate(edges) ch = chart(edges, edge) - !in_interior(edge) && !on_junction(ch) && continue + ln = volume(ch) + # !in_interior(edge) && !on_junction(ch) && continue # index of edge center in fine's vertexbuffer - p = numvertices(edges) + i + v = edge[1] + n = vton[v] + F = vtof[v,1:n] + supp = fine[F] + bnd = boundary(supp) + bnd_nodes = skeleton(bnd,0) + p = 0 + for node in bnd_nodes + vert1 = vertices(Γ)[edge[1]] + vert2 = vertices(Γ)[edge[2]] + vert = vertices(fine)[node[1]] + dist = norm(vert-vert1) + node[1] == edge[1] && continue + if dot(vert-vert1,vert2-vert1) ≈ dist*ln + p = node[1] + break + end + end + @assert p != 0 + + # p = numvertices(edges) + i pos[k] = vertices(fine)[p] # sanity check diff --git a/src/identityop.jl b/src/identityop.jl index 6be16dca..dc626750 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -22,7 +22,7 @@ end # defaultquadstrat(op::LocalOperator, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bfs)) defaultquadstrat(op::LocalOperator, tfs::Space, bfs::DirectProductSpace) = defaultquadstrat(op, tfs, bfs.factors[1]) -defaultquadstrat(op::LocalOperator, tfs::DirectProductSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs.factors[1], bfs) +defaultquadstrat(op::LocalOperator, tfs::DirectProductSpace, bfs::Space) = defaultquadstrat(op, tfs.factors[1], bfs) defaultquadstrat(op::LocalOperator, tfs::DirectProductSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs.factors[1], bfs.factors[1]) const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace, BDMRefSpace} From e9299141254117ba85c6030aae9090e6d0de572a Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 14 Jul 2020 14:22:28 +0200 Subject: [PATCH 079/528] adding of convops - untested --- src/operator.jl | 6 ++++ src/timedomain/convop.jl | 58 ++++++++++++++++++++++--------------- src/timedomain/tdtimeops.jl | 3 +- test/test_convop.jl | 16 ++++++++++ 4 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 test/test_convop.jl diff --git a/src/operator.jl b/src/operator.jl index ef4aa736..e522b3a5 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -32,6 +32,12 @@ function scalartype(op::LinearCombinationOfOperators{T}) where T W end +function derive(a::LinearCombinationOfOperators) + coeffs = copy(a.coeffs) + ops = [derive(op) for op in a.ops] + return LinearCombinationOfOperators(coeffs, ops) +end + function +(a::LinearCombinationOfOperators, b::Operator) LinearCombinationOfOperators( diff --git a/src/timedomain/convop.jl b/src/timedomain/convop.jl index 38d00b0d..5940b115 100644 --- a/src/timedomain/convop.jl +++ b/src/timedomain/convop.jl @@ -22,24 +22,6 @@ function Base.getindex(obj::ConvOp, m::Int, n::Int, k::Int) return obj.data[k-obj.k0[m,n]+1,m,n] end -# -# function convolve!(y, Z::ConvOp, x, j, k_start, k_stop=size(Z,3)) -# for m in axes(y,1) -# for n in axes(x,1) -# k0 = Z.k0[m,n] -# k1 = Z.k1[m,n] -# for k in max(k0,k_start):min(k1,k_stop) -# p = k - k0 + 1 -# j-k < 0 && continue -# y[m] += Z.data[p,m,n] * x[n,j-k+1] -# end -# -# X = sum(x[n,1:j-k1]) -# y[m] += Z.tail[m,n] * X -# end -# end -# end - function convolve!(y, Z::ConvOp, x, X, j, k_start, k_stop=size(Z,3)) # @info "The corrrect convolve!" @@ -60,11 +42,6 @@ function convolve!(y, Z::ConvOp, x, X, j, k_start, k_stop=size(Z,3)) end -# function convolve(Z::ConvOp, x, j, k_start) -# y = zeros(eltype(Z), size(Z)[1]) -# convolve!(y, Z, x, j, k_start, size(Z)[3]) -# return y -# end function polyeig(Z::ConvOp) kmax = maximum(Z.k1) M = size(Z,1) @@ -79,4 +56,37 @@ function polyeig(Z::ConvOp) Q[1:M,M+1:2M,kmax+1] .= Z[:,:,kmax+1] return eigvals(companion(Q)), Q # return Q -end \ No newline at end of file +end + + +function Base.:+(a::ConvOp, b::ConvOp) + + @assert size(a.data) == size(b.data) + M,N = size(a.data) + T = promote_type(eltype(a.data), eltype(b.data)) + + k0 = min.(a.k0, b.k0) + k1 = max.(a.k1, b.k1) + + bandwidth = maximum(k1 - k0) + 1 + data = zeros(T, bandwidth, M, N) + + k1max = maximum(k1) + tail = zeros(T,M,N) + for m in 1:M, n in 1:N + tail[m,n] = a[m,n,k1max+1] + b[m,n,k1max+1] + end + + for m in 1:M + for n in 1:N + for k in k0[m,n]:k1[m,n] + data[k,m,n] = a[m,n,k] + b[m,n,k] + end + end + end + + lgt = max(a.length, b.length) + return ConvOp(data, tail, k0, k1, lgt) +end + + diff --git a/src/timedomain/tdtimeops.jl b/src/timedomain/tdtimeops.jl index e8061197..7ebb8fd9 100644 --- a/src/timedomain/tdtimeops.jl +++ b/src/timedomain/tdtimeops.jl @@ -213,7 +213,7 @@ mutable struct TemporalDifferentiation <: Operator operator end -derive(op::Operator) = TemporalDifferentiation(op) +derive(op::AbstractOperator) = TemporalDifferentiation(op) scalartype(op::TemporalDifferentiation) = scalartype(op.operator) Base.:*(a::Number, op::TemporalDifferentiation) = TemporalDifferentiation(a * op.operator) @@ -236,6 +236,7 @@ struct TemporalIntegration <: AbstractOperator end integrate(op::AbstractOperator) = TemporalIntegration(op) +derive(op::TemporalIntegration) = op.operator scalartype(op::TemporalIntegration) = scalartype(op.operator) Base.:*(a::Number, op::TemporalIntegration) = TemporalIntegration(a * op.operator) diff --git a/test/test_convop.jl b/test/test_convop.jl new file mode 100644 index 00000000..2daea9f6 --- /dev/null +++ b/test/test_convop.jl @@ -0,0 +1,16 @@ +using Test +using BEAST +using CompScienceMeshes + +Γ = meshrectangle(1.0, 1.0, 0.5, 3) +X = raviartthomas(Γ) +SL = derive(TDMaxwell3D.singlelayer(speedoflight=1.0)) + +Δt, Nt = 1.01, 10 +δ = timebasisdelta(Δt,Nt) +h = timebasisc0d1(Δt,Nt) + +SLh = (SL, X⊗δ, X⊗h) +A = assemble(SLh...) + +@show size(A) \ No newline at end of file From e0da52bf183412afa9118bf07c7d9e911de6dae8 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 21 Sep 2020 14:52:26 +0200 Subject: [PATCH 080/528] hvcat for MatrixConvolution --- .gitignore | 1 + examples/Project.toml | 5 ++++ examples/stgalerkin.jl | 1 + src/maxwell/timedomain/mwtdexc.jl | 8 +++++ src/timedomain/tdintegralop.jl | 8 +++-- src/utils/matrixconv.jl | 49 +++++++++++++++++++++++++++++++ test/runtests.jl | 1 + test/test_matrixconv.jl | 12 ++++++++ 8 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 examples/Project.toml create mode 100644 test/test_matrixconv.jl diff --git a/.gitignore b/.gitignore index 21e1a1bc..d29bd28c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,6 @@ docs/build/ docs/site/ temp/ +dev/ profile/ Manifest.toml diff --git a/examples/Project.toml b/examples/Project.toml new file mode 100644 index 00000000..6c64e382 --- /dev/null +++ b/examples/Project.toml @@ -0,0 +1,5 @@ +[deps] +BEAST = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" +CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" +Plotly = "58dd65bb-95f3-509e-9936-c39a10fdeae7" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" diff --git a/examples/stgalerkin.jl b/examples/stgalerkin.jl index 90b9ea87..aeb95319 100644 --- a/examples/stgalerkin.jl +++ b/examples/stgalerkin.jl @@ -5,6 +5,7 @@ o, x, y, z = euclidianbasis(3) # Γ = meshsphere(D, Δx) Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = meshsphere(1.0, 0.45) X = raviartthomas(Γ) #Δt, Nt = 0.08, 400 Δt, Nt = 0.6, 200 diff --git a/src/maxwell/timedomain/mwtdexc.jl b/src/maxwell/timedomain/mwtdexc.jl index 43c6f4f1..2a30075b 100644 --- a/src/maxwell/timedomain/mwtdexc.jl +++ b/src/maxwell/timedomain/mwtdexc.jl @@ -45,3 +45,11 @@ function integrate(f::BEAST.PlaneWaveMWTD) polarization = f.polarization, speedoflight = f.speedoflight) end + +function differentiate(f::BEAST.PlaneWaveMWTD) + planewave( + signature = derive(f.amplitude), + direction = f.direction, + polarization = f.polarisation, + speedoflight = f.speedoflight) +end diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 1347a5d6..883c1dd7 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -90,7 +90,7 @@ function allocatestorage(op::RetardedPotential, testST, basisST, Nt = numfunctions(temporalbasis(basisST)) tbf = convolve(temporalbasis(testST), temporalbasis(basisST)) - has_tail = all(tbf.polys[end].data .== 0) + has_tail = !all(tbf.polys[end].data .== 0) M = numfunctions(tfs) N = numfunctions(bfs) @@ -112,8 +112,10 @@ function allocatestorage(op::RetardedPotential, testST, basisST, bandwidth = maximum(K1 .- K0 .+ 1) data = zeros(T, bandwidth, M, N) - tail = zeros(T, M, N) - Z = ConvOp(data, K0, K1, tail, Nt) + tail = zeros(T, M, N) + # kmax = maximum(K1) + len = has_tail ? Nt : maximum(K1) + Z = ConvOp(data, K0, K1, tail, len) function store1(v,m,n,k) if Z.k0[m,n] ≤ k ≤ Z.k1[m,n] diff --git a/src/utils/matrixconv.jl b/src/utils/matrixconv.jl index e2ee84fa..c197ba62 100644 --- a/src/utils/matrixconv.jl +++ b/src/utils/matrixconv.jl @@ -77,3 +77,52 @@ function convolve(Z::Array,x,j,k0) end convolve(x::MatrixConvolution, y::Matrix, i, j) = convolve(x.arr, y, i, j) + + +function Base.hvcat((M,N)::Tuple{Int,Int}, as::MatrixConvolution...) + kmax = maximum(size(a,3) for a in as) + + @assert length(as) == M*N + + li = LinearIndices((1:M,1:N)) + for m in 1:M + a = as[li[m,1]] + M1 = size(a,1) + for n in 2:N + a = as[li[m,n]] + @assert size(a,1) == M1 + end + end + + for n in 1:N + a = as[li[1,n]] + N1 = size(a,2) + for m in 2:M + a = as[li[m,n]] + @assert size(a,2) == N1 + end + end + + Ms = [size(as[li[i,1]],1) for i in 1:M] + Ns = [size(as[li[1,j]],2) for j in 1:N] + + cMs = pushfirst!(cumsum(Ms),0) + cNs = pushfirst!(cumsum(Ns),0) + T = promote_type(eltype.(as)...) + data = zeros(T, last(cMs), last(cNs), kmax) + + @show size(data) + @show eltype(data) + + for m in 1:M + I = cMs[m]+1 : cMs[m+1] + for n in 1:N + J = cNs[n]+1 : cNs[n+1] + a = as[li[m,n]] + K = 1:size(a,3) + data[I,J,K] .= a + end + end + + return MatrixConvolution(data) +end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 52527ef5..a896eb5e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -58,6 +58,7 @@ include("test_tdassembly.jl") include("test_tdhhdbl.jl") include("test_tdmwdbl.jl") include("test_compressed_storage.jl") +include("test_matrixconv.jl") include("test_tdop_scaling.jl") include("test_tdrhs_scaling.jl") diff --git a/test/test_matrixconv.jl b/test/test_matrixconv.jl new file mode 100644 index 00000000..2bca7317 --- /dev/null +++ b/test/test_matrixconv.jl @@ -0,0 +1,12 @@ +using Test + +using BEAST + +A11 = BEAST.MatrixConvolution(rand(2,3,10)) +A21 = BEAST.MatrixConvolution(rand(4,3,8)) +A12 = BEAST.MatrixConvolution(rand(2,5,12)) +A22 = BEAST.MatrixConvolution(rand(4,5,14)) + +A = BEAST.hvcat((2,2), A11, A21, A12, A22) + +@test A[3,5,6] == A22[1,2,6] \ No newline at end of file From 9602e0b11924dabb333ed027e5deb7f71dcf8357 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 24 Sep 2020 16:45:12 +0200 Subject: [PATCH 081/528] Allocate storage for differentiated operators --- examples/tdefie_nodot.jl | 1 - examples/tdpmchwt.jl | 4 ++++ src/maxwell/timedomain/mwtdexc.jl | 2 +- src/timedomain/tdtimeops.jl | 16 ++++++++++++++++ src/utils/matrixconv.jl | 1 + 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/examples/tdefie_nodot.jl b/examples/tdefie_nodot.jl index 5327c7c6..a296df27 100644 --- a/examples/tdefie_nodot.jl +++ b/examples/tdefie_nodot.jl @@ -2,7 +2,6 @@ using CompScienceMeshes, BEAST # Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -@show numcells(Γ) X = raviartthomas(Γ) Δt, Nt = 0.6, 200 diff --git a/examples/tdpmchwt.jl b/examples/tdpmchwt.jl index 6fde3cd7..326ec946 100644 --- a/examples/tdpmchwt.jl +++ b/examples/tdpmchwt.jl @@ -1,3 +1,7 @@ +using Pkg +Pkg.activate(@__DIR__) +Pkg.instantiate() + using CompScienceMeshes, BEAST using LinearAlgebra diff --git a/src/maxwell/timedomain/mwtdexc.jl b/src/maxwell/timedomain/mwtdexc.jl index 2a30075b..b2f651f4 100644 --- a/src/maxwell/timedomain/mwtdexc.jl +++ b/src/maxwell/timedomain/mwtdexc.jl @@ -42,7 +42,7 @@ function integrate(f::BEAST.PlaneWaveMWTD) planewave( signature = integrate(f.amplitude), direction = f.direction, - polarization = f.polarization, + polarization = f.polarisation, speedoflight = f.speedoflight) end diff --git a/src/timedomain/tdtimeops.jl b/src/timedomain/tdtimeops.jl index 7ebb8fd9..8e8673fd 100644 --- a/src/timedomain/tdtimeops.jl +++ b/src/timedomain/tdtimeops.jl @@ -217,6 +217,22 @@ derive(op::AbstractOperator) = TemporalDifferentiation(op) scalartype(op::TemporalDifferentiation) = scalartype(op.operator) Base.:*(a::Number, op::TemporalDifferentiation) = TemporalDifferentiation(a * op.operator) +function allocatestorage(op::TemporalDifferentiation, testfns, trialfns, + storage_trait, longdelays_trait) + + trial_time_fns = temporalbasis(trialfns) + trial_space_fns = spatialbasis(trialfns) + + trialfns = SpaceTimeBasis( + trial_space_fns, + derive(trial_time_fns) + ) + + Z, store = allocatestorage(op.operator, testfns, trialfns, + storage_trait, longdelays_trait) + return Z, store +end + function assemble!(operator::TemporalDifferentiation, testfns, trialfns, store) trial_time_fns = temporalbasis(trialfns) diff --git a/src/utils/matrixconv.jl b/src/utils/matrixconv.jl index c197ba62..4b95f4d5 100644 --- a/src/utils/matrixconv.jl +++ b/src/utils/matrixconv.jl @@ -64,6 +64,7 @@ end Base.:*(a::Number, x::MatrixConvolution) = MatrixConvolution(a * x.arr) Base.:*(x::MatrixConvolution, a::Number) = MatrixConvolution(x.arr * a) Base.:/(x::MatrixConvolution, a::Number) = MatrixConvolution(x.arr / a) +Base.:-(x::MatrixConvolution) = (-1) * x function convolve(Z::Array,x,j,k0) M,N,K = size(Z) From 5fa88d721212bb946b20008a3d7f2ada3eb06d40 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 30 Sep 2020 18:29:31 +0200 Subject: [PATCH 082/528] hvcat fixed. dbl works with nd functions --- examples/tdefie.jl | 4 +-- examples/tdmfie.jl | 4 +-- examples/tdmfie_sym.jl | 43 +++++++++++++++++++++++++++++++ src/identityop.jl | 2 +- src/maxwell/timedomain/mwtdops.jl | 23 +++++++++++------ src/utils/matrixconv.jl | 16 ++++++------ 6 files changed, 71 insertions(+), 21 deletions(-) create mode 100644 examples/tdmfie_sym.jl diff --git a/examples/tdefie.jl b/examples/tdefie.jl index bc606076..3a243b58 100644 --- a/examples/tdefie.jl +++ b/examples/tdefie.jl @@ -5,14 +5,14 @@ using CompScienceMeshes, BEAST X = raviartthomas(Γ) -Δt, Nt = 0.6, 200 +Δt, Nt = 0.3, 200 T = timebasisshiftedlagrange(Δt, Nt, 3) U = timebasisdelta(Δt, Nt) V = X ⊗ T W = X ⊗ U -duration = 20 * Δt +duration = 20 * Δt * 2 delay = 1.5 * duration amplitude = 1.0 gaussian = creategaussian(duration, delay, amplitude) diff --git a/examples/tdmfie.jl b/examples/tdmfie.jl index 4c57d4ae..ed3dd7dc 100644 --- a/examples/tdmfie.jl +++ b/examples/tdmfie.jl @@ -5,14 +5,14 @@ using CompScienceMeshes, BEAST X = raviartthomas(Γ) Y = buffachristiansen(Γ) -Δt, Nt = 0.6, 200 +Δt, Nt = 0.3, 200 T = timebasisshiftedlagrange(Δt, Nt, 2) δ = timebasisdelta(Δt, Nt) V = X ⊗ T W = Y ⊗ δ -duration = 20 * Δt +duration = 20 * Δt * 2 delay = 1.5 * duration amplitude = 1.0 gaussian = creategaussian(duration, delay, amplitude) diff --git a/examples/tdmfie_sym.jl b/examples/tdmfie_sym.jl new file mode 100644 index 00000000..8605223d --- /dev/null +++ b/examples/tdmfie_sym.jl @@ -0,0 +1,43 @@ +using CompScienceMeshes, BEAST +Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +# Γ = meshsphere(1.0, 0.4) + +X = raviartthomas(Γ) +Y = BEAST.nedelec(Γ) + +Δt, Nt = 0.3, 200 +T = timebasisshiftedlagrange(Δt, Nt, 2) +δ = timebasisdelta(Δt, Nt) + +V = X ⊗ T +W = Y ⊗ δ + +duration = 20 * Δt * 2 +delay = 1.5 * duration +amplitude = 1.0 +gaussian = creategaussian(duration, delay, amplitude) + +direction, polarisation = ẑ, x̂ +E = BEAST.planewave(polarisation, direction, gaussian, 1.0) +H = direction × E + +DL = TDMaxwell3D.doublelayer(speedoflight=1.0) +I = Identity() +N = NCross() + +@hilbertspace k +@hilbertspace j +mfie = @discretise (0.5(N⊗I) + 1.0DL)[k,j] == -1.0H[k] k∈W j∈V + +# Kyx = assemble(DL, W, V) +# Nyx = assemble(N⊗I, W, V) + +xmfie_sym = solve(mfie) +# xmfie_sym = xmfie + +Xmfie_sym, Δω, ω0 = fouriertransform(xmfie_sym, Δt, 0.0, 2) +ω = collect(ω0 .+ (0:Nt-1)*Δω) +_, i1 = findmin(abs.(ω .- 1.0)) + +ω1 = ω[i1] +um = Xmfie_sym[:,i1] / fouriertransform(gaussian)(ω1) diff --git a/src/identityop.jl b/src/identityop.jl index 03cc06d3..f1bafe83 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -23,7 +23,7 @@ function quaddata(op::LocalOperator, g::NDRefSpace, f::NDRefSpace, tels, bels) return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] end -function quaddata(op::LocalOperator, g::BDMRefSpace, f::BDMRefSpace, tels, bels) +function quaddata(op::LocalOperator, g::NDRefSpace, f::RTRefSpace, tels, bels) u, w = trgauss(6) return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] end diff --git a/src/maxwell/timedomain/mwtdops.jl b/src/maxwell/timedomain/mwtdops.jl index 6450cb81..64c2f778 100644 --- a/src/maxwell/timedomain/mwtdops.jl +++ b/src/maxwell/timedomain/mwtdops.jl @@ -82,8 +82,8 @@ function quaddata(op::MWSingleLayerTDIO, testrefs, trialrefs, timerefs, V = eltype(testels[1].vertices) ws = WiltonInts84.workspace(V) + # quadpoints(testrefs, testels, (3,)), bn, ws quadpoints(testrefs, testels, (3,)), bn, ws - end @@ -173,6 +173,7 @@ function quaddata(op::MWDoubleLayerTDIO, testrefs, trialrefs, timerefs, V = eltype(testels[1].vertices) ws = WiltonInts84.workspace(V) + # quadpoints(testrefs, testels, (3,)), bn, ws quadpoints(testrefs, testels, (3,)), bn, ws end @@ -354,21 +355,27 @@ function innerintegrals!(z, op::MWDoubleLayerTDIO, αg = 1 / volume(τ) / 2 αf = 1 / volume(σ) / 2 αG = 1 / 4 / π - α = αg * αf * αG * op.weight * dx + α = αG * op.weight * dx # * αg * αf ds = op.num_diffs - @inline function tmRoR(d, iGG) + @inline function tmGRoR(d, iGG) sgn = isodd(d) ? -1 : 1 r = sgn * iGG[d+1] end + Ux = U(p) + Vx = αf * @SVector[(x-σ[1]), (x-σ[2]), (x-σ[3])] + for i in 1 : numfunctions(U) - a = τ[i] - g = (x-a) + # a = τ[i] + # g = αg * (x-τ[i]) + g = Ux[i].value for j in 1 : numfunctions(V) - b = σ[j] - f = (x-b) + # b = σ[j] + # f = αf * (x-σ[j]) + # f = Vx[j].value + f = Vx[j] fxg = f × g for k in 1 : numfunctions(W) d = k-1 @@ -379,7 +386,7 @@ function innerintegrals!(z, op::MWDoubleLayerTDIO, q *= (d-p) end # @assert q == 1 - z[i,j,k] += -α * q * ( fxg ⋅ tmRoR(d-ds, ∫∇G) ) / sol^(d-ds) + z[i,j,k] += -α * q * ( fxg ⋅ tmGRoR(d-ds, ∫∇G) ) / sol^(d-ds) end end end diff --git a/src/utils/matrixconv.jl b/src/utils/matrixconv.jl index 4b95f4d5..60648943 100644 --- a/src/utils/matrixconv.jl +++ b/src/utils/matrixconv.jl @@ -85,27 +85,27 @@ function Base.hvcat((M,N)::Tuple{Int,Int}, as::MatrixConvolution...) @assert length(as) == M*N - li = LinearIndices((1:M,1:N)) + li = LinearIndices((1:N,1:M)) for m in 1:M - a = as[li[m,1]] + a = as[li[1,m]] M1 = size(a,1) for n in 2:N - a = as[li[m,n]] + a = as[li[n,m]] @assert size(a,1) == M1 end end for n in 1:N - a = as[li[1,n]] + a = as[li[n,1]] N1 = size(a,2) for m in 2:M - a = as[li[m,n]] + a = as[li[n,m]] @assert size(a,2) == N1 end end - Ms = [size(as[li[i,1]],1) for i in 1:M] - Ns = [size(as[li[1,j]],2) for j in 1:N] + Ms = [size(as[li[1,i]],1) for i in 1:M] + Ns = [size(as[li[j,1]],2) for j in 1:N] cMs = pushfirst!(cumsum(Ms),0) cNs = pushfirst!(cumsum(Ns),0) @@ -119,7 +119,7 @@ function Base.hvcat((M,N)::Tuple{Int,Int}, as::MatrixConvolution...) I = cMs[m]+1 : cMs[m+1] for n in 1:N J = cNs[n]+1 : cNs[n+1] - a = as[li[m,n]] + a = as[li[n,m]] K = 1:size(a,3) data[I,J,K] .= a end From af6c04c2f9f78974127315ac32f670c8e245d4ad Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 30 Sep 2020 21:22:40 +0200 Subject: [PATCH 083/528] fixed broken MatrixConv test (the test itself was broken) --- test/test_matrixconv.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_matrixconv.jl b/test/test_matrixconv.jl index 2bca7317..7cb9530a 100644 --- a/test/test_matrixconv.jl +++ b/test/test_matrixconv.jl @@ -7,6 +7,6 @@ A21 = BEAST.MatrixConvolution(rand(4,3,8)) A12 = BEAST.MatrixConvolution(rand(2,5,12)) A22 = BEAST.MatrixConvolution(rand(4,5,14)) -A = BEAST.hvcat((2,2), A11, A21, A12, A22) +A = BEAST.hvcat((2,2), A11, A12, A21, A22) @test A[3,5,6] == A22[1,2,6] \ No newline at end of file From c73eb66764ce670b37061e1b0d3530aeea63ed03 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 22 Oct 2020 14:35:01 +0200 Subject: [PATCH 084/528] assemble haas switch to disable multithreading --- src/localop.jl | 3 +- src/maxwell/timedomain/mwtdops.jl | 3 +- src/operator.jl | 52 +++++++++++----------------- src/timedomain/tdintegralop.jl | 7 ++-- src/timedomain/tdtimeops.jl | 56 ++++--------------------------- 5 files changed, 34 insertions(+), 87 deletions(-) diff --git a/src/localop.jl b/src/localop.jl index bf39126c..7b27cb72 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -10,7 +10,8 @@ end abstract type LocalOperator <: Operator end -function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store) +function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, + threading::Type{Threading{:multi}}) if geometry(tfs) == geometry(bfs) return assemble_local_matched!(biop, tfs, bfs, store) diff --git a/src/maxwell/timedomain/mwtdops.jl b/src/maxwell/timedomain/mwtdops.jl index 64c2f778..54063d03 100644 --- a/src/maxwell/timedomain/mwtdops.jl +++ b/src/maxwell/timedomain/mwtdops.jl @@ -136,7 +136,8 @@ function allocatestorage(op::MWDoubleLayerTDIO, testST, basisST, return Z, store1 end -function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, store) +function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, store, + threading=Threading{:multi}) X, T = spatialbasis(W), temporalbasis(W) Y, U = spatialbasis(V), temporalbasis(V) diff --git a/src/operator.jl b/src/operator.jl index e522b3a5..0b8aa5e1 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -1,6 +1,7 @@ using .LinearSpace struct LongDelays{T} end +struct Threading{T} end import Base: transpose, +, -, * @@ -73,15 +74,16 @@ transpose(op::TransposedOperator) = op.op transpose(op::Operator) = TransposedOperator(op) -function assemble(operator::AbstractOperator, test_functions, trial_functions, +function assemble(operator::AbstractOperator, test_functions, trial_functions; storage_policy = Val{:bandedstorage}, - long_delays_policy = LongDelays{:compress}) + long_delays_policy = LongDelays{:compress}, + threading = Threading{:multi}) # This is a convenience function whose only job is to allocate # the storage for the interaction matrix. Further dispatch on # operator and space types is handled by the 4-argument version Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) - assemble!(operator, test_functions, trial_functions, store) + assemble!(operator, test_functions, trial_functions, store, threading) sdata(Z) end @@ -108,8 +110,6 @@ end function allocatestorage(operator::AbstractOperator, test_functions, trial_functions, storage_trait, longdelays_trait) - # ::Type{Val{:bandedstorage}}, - # ::Type{LongDelays{:ignore}}) T = promote_type( scalartype(operator) , @@ -148,7 +148,10 @@ function allocatestorage(operator::LinearCombinationOfOperators, end -function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store) +function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, + threading::Type{Threading{:multi}} = Threading{:multi}) + + @info "Multi-threaded assembly:" P = Threads.nthreads() numchunks = P @@ -165,31 +168,14 @@ function assemble!(operator::Operator, test_functions::Space, trial_functions::S end +function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, + threading::Type{Threading{:single}}) + + @info "Single-threaded assembly" + + assemblechunk!(operator, test_functions, trial_functions, store) +end -# function assemble_st!(operator::Operator, test_functions::Space, trial_functions::Space, store) -# -# # This method should only be called for `atomic` discrete operators, this means -# # in particular that the spaces of test and trial functions are fully conforming -# # to the Space concept and that the operator/space combinations conform the -# # concept implicitly defined by the assemble! function in itegralop.jl -# # (no more transposes or repositioning in a larger system for example) -# -# @warn "Parallel assembly support disabled." -# -# P = procs() -# -# @assert length(P) == 1 -# -# if length(P) > 1; P = P[2:end]; end -# numchunks = length(P) -# @assert numchunks >= 1 -# splits = [round(Int,s) for s in linspace(0, numfunctions(test_functions), numchunks+1)] -# -# T = typeof(test_functions) -# S = eltype(test_functions.fns) -# -# assemblechunk!(operator, test_functions, trial_functions, store) -# end function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, store) @@ -208,7 +194,7 @@ end # Support for direct product spaces -function assemble!(op::Operator, tfs::DirectProductSpace, bfs::Space, store) +function assemble!(op::Operator, tfs::DirectProductSpace, bfs::Space, store, threading = Threading{:multi}) I = Int[0] for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) @@ -218,7 +204,7 @@ function assemble!(op::Operator, tfs::DirectProductSpace, bfs::Space, store) end -function assemble!(op::Operator, tfs::Space, bfs::DirectProductSpace, store) +function assemble!(op::Operator, tfs::Space, bfs::DirectProductSpace, store, threading=Threading{:multi}) J = Int[0] for s in bfs.factors push!(J, last(J) + numfunctions(s)) end for (j,s) in enumerate(bfs.factors) @@ -227,7 +213,7 @@ function assemble!(op::Operator, tfs::Space, bfs::DirectProductSpace, store) end end -function assemble!(op::Operator, tfs::DirectProductSpace, bfs::DirectProductSpace, store) +function assemble!(op::Operator, tfs::DirectProductSpace, bfs::DirectProductSpace, store, threading=Threading{:multi}) I = Int[0] for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 883c1dd7..12660ad3 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -128,14 +128,17 @@ function allocatestorage(op::RetardedPotential, testST, basisST, return Z, store1 end -function assemble!(op::LinearCombinationOfOperators, tfs::SpaceTimeBasis, bfs::SpaceTimeBasis, store) +function assemble!(op::LinearCombinationOfOperators, tfs::SpaceTimeBasis, bfs::SpaceTimeBasis, store, + threading=Threading{:multi}) + for (a,A) in zip(op.coeffs, op.ops) store1(v,m,n,k) = store(a*v,m,n,k) assemble!(A, tfs, bfs, store1) end end -function assemble!(op::RetardedPotential, testST, trialST, store) +function assemble!(op::RetardedPotential, testST, trialST, store, + threading=Threading{:multi}) Y, S = spatialbasis(testST), temporalbasis(testST) diff --git a/src/timedomain/tdtimeops.jl b/src/timedomain/tdtimeops.jl index 8e8673fd..a31f7f8e 100644 --- a/src/timedomain/tdtimeops.jl +++ b/src/timedomain/tdtimeops.jl @@ -45,31 +45,6 @@ function scalartype(A::TensorOperator) end -# function assemble(operator::TensorOperator, testfns, trialfns) -# -# test_spatial_basis = spatialbasis(testfns) -# trial_spatial_basis = spatialbasis(trialfns) -# -# M = numfunctions(test_spatial_basis) -# N = numfunctions(trial_spatial_basis) -# -# tbf = convolve( -# temporalbasis(testfns), -# temporalbasis(trialfns)) -# -# kmax = numintervals(tbf) - 1 -# k0 = fill(1, (M,N)) -# k1 = fill(kmax, (M,N)) -# -# T = promote_type(scalartype(operator), scalartype(testfns), scalartype(trialfns)) -# data = zeros(T, M, N, kmax) -# Z = SparseND.Banded3D(k0, k1, data) -# -# store(v, m, n, k) = (Z[m,n,k] += v) -# assemble!(operator, testfns, trialfns, store) -# return Z -# -# end function allocatestorage(op::TensorOperator, test_functions, trial_functions, ::Type{Val{:bandedstorage}}, ::Type{LongDelays{:ignore}},) @@ -81,28 +56,10 @@ function allocatestorage(op::TensorOperator, test_functions, trial_functions, temporalbasis(test_functions), temporalbasis(trial_functions)) - # has_zero_tail = all(time_basis_function.polys[end].data .== 0) - # @show has_zero_tail - - # if has_zero_tail - # Numintervals includes the semi-infinite interval stretching to +Inf - # K = numintervals(time_basis_function)-1 - # else - # speedoflight = 1.0 - # @warn "Assuming speed of light to be equal to 1!" - # Δt = timestep(time_basis_function) - # ct, hs = boundingbox(geometry(spatialbasis(trial_functions)).vertices) - # diam = 2 * sqrt(3) * hs - # K = ceil(Int, (numintervals(time_basis_function)-1) + diam/speedoflight/Δt)+1 - # end - # @assert K > 0 - space_operator = op.spatial_factor A = assemble(space_operator, spatialbasis(test_functions), spatialbasis(trial_functions)) - # K0 = Int.(A .!= 0) K0 = ones(M,N) - # K0 = ones(Int,M,N) bandwidth = numintervals(time_basis_function) - 1 data = zeros(scalartype(op), bandwidth, M, N) maxk1 = bandwidth @@ -172,7 +129,8 @@ end # return MatrixConvolution(Z), (v,m,n,k)->(Z[m,n,k] += v) # end -function assemble!(operator::TensorOperator, testfns, trialfns, store) +function assemble!(operator::TensorOperator, testfns, trialfns, store, + threading = Threading{:multi}) space_operator = operator.spatial_factor time_operator = operator.temporal_factor @@ -233,7 +191,7 @@ function allocatestorage(op::TemporalDifferentiation, testfns, trialfns, return Z, store end -function assemble!(operator::TemporalDifferentiation, testfns, trialfns, store) +function assemble!(operator::TemporalDifferentiation, testfns, trialfns, store, threading = Threading{:multi}) trial_time_fns = temporalbasis(trialfns) trial_space_fns = spatialbasis(trialfns) @@ -243,7 +201,7 @@ function assemble!(operator::TemporalDifferentiation, testfns, trialfns, store) derive(trial_time_fns) ) - assemble!(operator.operator, testfns, trialfns, store) + assemble!(operator.operator, testfns, trialfns, store, threading) end @@ -258,8 +216,6 @@ Base.:*(a::Number, op::TemporalIntegration) = TemporalIntegration(a * op.operato function allocatestorage(op::TemporalIntegration, testfns, trialfns, storage_trait, longdelays_trait) - # ::Type{Val{:bandedstorage}}, - # ::Type{LongDelays{:ignore}}) trial_time_fns = temporalbasis(trialfns) trial_space_fns = spatialbasis(trialfns) @@ -270,11 +226,11 @@ function allocatestorage(op::TemporalIntegration, testfns, trialfns, ) Z, store = allocatestorage(op.operator, testfns, trialfns, storage_trait, longdelays_trait) - @show size(Z) return Z, store end -function assemble!(operator::TemporalIntegration, testfns, trialfns, store) +function assemble!(operator::TemporalIntegration, testfns, trialfns, store, + threading = Threading{:multi}) trial_time_fns = temporalbasis(trialfns) trial_space_fns = spatialbasis(trialfns) From e236fb38729be692aac4ff39a005fe53b7cba746 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 27 Oct 2020 12:44:01 +0100 Subject: [PATCH 085/528] Linear combinations support threading flag --- src/operator.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/operator.jl b/src/operator.jl index 0b8aa5e1..f7d339a8 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -185,7 +185,8 @@ function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, store) end -function assemble!(op::LinearCombinationOfOperators, tfs::AbstractSpace, bfs::AbstractSpace, store) +function assemble!(op::LinearCombinationOfOperators, tfs::AbstractSpace, bfs::AbstractSpace, + store, threading = Threading{:multi}) for (a,A) in zip(op.coeffs, op.ops) store1(v,m,n) = store(a*v,m,n) assemble!(A, tfs, bfs, store1) From dacd0fdb24b733a13e36974261708edff5325d29 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 27 Oct 2020 16:29:23 +0100 Subject: [PATCH 086/528] dbl potential implemented; sign errors from pmchwt --- examples/pmchwt.jl | 26 +++++++++++++++++++++----- src/maxwell/nearfield.jl | 33 +++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 9bee1956..14110089 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -1,7 +1,8 @@ using CompScienceMeshes, BEAST using LinearAlgebra -Γ = meshcuboid(1.0, 1.0, 1.0, 0.15) +# Γ = meshcuboid(1.0, 1.0, 1.0, 0.15) +Γ = meshsphere(1.0, 0.10) X = raviartthomas(Γ) κ, η = 1.0, 1.0 @@ -26,8 +27,8 @@ e, h = (n × E) × n, (n × H) × n α, α′ = 1/η, 1/η′ pmchwt = @discretise( - (η*T+η′*T′)[k,j] + (K+K′)[k,m] - - (K+K′)[l,j] + (α*T+α′*T′)[l,m] == h[k] + e[l], + (η*T+η′*T′)[k,j] - (K+K′)[k,m] + + (K+K′)[l,j] + (α*T+α′*T′)[l,m] == e[k] - h[l], j∈X, m∈X, k∈X, l∈X) u = solve(pmchwt) @@ -48,7 +49,22 @@ using Plots plot(xlabel="theta") plot!(Θ,norm.(ff),label="far field") -import PlotlyJS +import Plotly using LinearAlgebra fcrj, _ = facecurrents(uj,X) -PlotlyJS.plot(patch(Γ, norm.(fcrj))) +fcrm, _ = facecurrents(um,X) +Plotly.plot(patch(Γ, norm.(fcrm))) + +Z = range(-2,2,length=200) +nfpoints = [point(0,0,z) for z in Z] +nfm_in = potential(BEAST.MWDoubleLayerField3D(wavenumber=κ′), nfpoints, um, X) +nfj_in = potential(BEAST.MWSingleLayerField3D(wavenumber=κ′), nfpoints, uj, X) +nf_in = -nfm_in + η′ * nfj_in + +nfm_ex = potential(BEAST.MWDoubleLayerField3D(wavenumber=κ), nfpoints, um, X) +nfj_ex = potential(BEAST.MWSingleLayerField3D(wavenumber=κ), nfpoints, uj, X) +nf_ex = nfm_ex - η * nfj_ex + +plot() +plot!(Z,real.(getindex.(nf_in,1))) +plot!(Z,real.(getindex.(nf_ex + E.(nfpoints),1))) \ No newline at end of file diff --git a/src/maxwell/nearfield.jl b/src/maxwell/nearfield.jl index c8f1c259..5d9fcabc 100644 --- a/src/maxwell/nearfield.jl +++ b/src/maxwell/nearfield.jl @@ -4,18 +4,23 @@ mutable struct MWSingleLayerField3D{K} wavenumber::K end +mutable struct MWDoubleLayerField3D{K} + wavenumber::K +end + """ MWSingleLayerField3D(wavenumber = error()) Create the single layer near field operator, for use with `potential`. """ MWSingleLayerField3D(;wavenumber = error("Missing arg: `wavenumber`")) = MWSingleLayerField3D(wavenumber) +MWDoubleLayerField3D(;wavenumber = error("Missing arg: `wavenumber`")) = MWDoubleLayerField3D(wavenumber) -quaddata(op::MWSingleLayerField3D,rs,els) = quadpoints(rs,els,(2,)) -quadrule(op::MWSingleLayerField3D,refspace,p,y,q,el,qdata) = qdata[1,q] - -function kernelvals(op::MWSingleLayerField3D,y,p) +const MWField3D = Union{MWSingleLayerField3D,MWDoubleLayerField3D} +quaddata(op::MWField3D,rs,els) = quadpoints(rs,els,(2,)) +quadrule(op::MWField3D,refspace,p,y,q,el,qdata) = qdata[1,q] +function kernelvals(op::MWField3D,y,p) k = op.wavenumber r = y - cartesian(p) @@ -26,18 +31,26 @@ function kernelvals(op::MWSingleLayerField3D,y,p) green = expn / (4pi*R) gradgreen = -(im*k + 1/R) * green / R * r - KernelValsMaxwell3D(im*k, r, R, green, gradgreen) + krn = (trans=r, dist=R, green=green, gradgreen=gradgreen) end function integrand(op::MWSingleLayerField3D, krn, y, fp, p) - j = fp[1] - ρ = fp[2] + γ = im * op.wavenumber + + j = fp.value + ρ = fp.divergence - #κ = krn. - γ = krn.gamma - G = krn.green + G = krn.green ∇G = krn.gradgreen -γ*G*j + ∇G*ρ/γ end + +function integrand(op::MWDoubleLayerField3D, krn, y, fp, p) + + j = fp.value + ∇G = krn.gradgreen + + ∇G × j +end From a6b737fa753b6d4148acda87ed15bb54a35e1f2c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 27 Oct 2020 17:51:59 +0100 Subject: [PATCH 087/528] hf example for pmchwt --- examples/pmchwt.jl | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 14110089..8f402e97 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -2,11 +2,12 @@ using CompScienceMeshes, BEAST using LinearAlgebra # Γ = meshcuboid(1.0, 1.0, 1.0, 0.15) -Γ = meshsphere(1.0, 0.10) +Γ = meshsphere(1.0, 0.075) X = raviartthomas(Γ) +@show numfunctions(X) -κ, η = 1.0, 1.0 -κ′, η′ = 1.4κ, η/1.4 +κ, η = 6.0, 1.0 +κ′, η′ = 2.4κ, η/2.4 T = Maxwell3D.singlelayer(wavenumber=κ) T′ = Maxwell3D.singlelayer(wavenumber=κ′) @@ -55,8 +56,9 @@ fcrj, _ = facecurrents(uj,X) fcrm, _ = facecurrents(um,X) Plotly.plot(patch(Γ, norm.(fcrm))) -Z = range(-2,2,length=200) -nfpoints = [point(0,0,z) for z in Z] +Z = range(-2,2,length=100) +Y = range(-2,2,length=100) +nfpoints = [point(0,y,z) for z in Z, y in Y] nfm_in = potential(BEAST.MWDoubleLayerField3D(wavenumber=κ′), nfpoints, um, X) nfj_in = potential(BEAST.MWSingleLayerField3D(wavenumber=κ′), nfpoints, uj, X) nf_in = -nfm_in + η′ * nfj_in @@ -65,6 +67,11 @@ nfm_ex = potential(BEAST.MWDoubleLayerField3D(wavenumber=κ), nfpoints, um, X) nfj_ex = potential(BEAST.MWSingleLayerField3D(wavenumber=κ), nfpoints, uj, X) nf_ex = nfm_ex - η * nfj_ex +nf_in = reshape(nf_in, size(nfpoints)) +nf_ex = reshape(nf_ex, size(nfpoints)) +nf = nf_in + nf_ex + E.(nfpoints) + +contour(real.(getindex.(nf,1))) + plot() -plot!(Z,real.(getindex.(nf_in,1))) -plot!(Z,real.(getindex.(nf_ex + E.(nfpoints),1))) \ No newline at end of file +plot(real.(getindex.(nf[:,51],1))) From 2e1b21ab8357561ea6383e249d04a6c238027b3b Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 4 Nov 2020 16:20:37 +0100 Subject: [PATCH 088/528] reduced allocation during FEM assembly --- src/identityop.jl | 156 +++++++++++++++++++++++++++++----------------- 1 file changed, 98 insertions(+), 58 deletions(-) diff --git a/src/identityop.jl b/src/identityop.jl index f1bafe83..33ce1c6e 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -1,37 +1,34 @@ struct Identity <: LocalOperator end -kernelvals(biop::Identity, x) = Nothing +kernelvals(biop::Identity, x) = nothing integrand(op::Identity, kernel, x, g, f) = dot(f[1], g[1]) -# scalartype(op::Identity) = Union{} -scalartype(op::Identity) = Float64 +scalartype(op::Identity) = Union{} +# scalartype(op::Identity) = Float64 struct NCross <: LocalOperator end -kernelvals(op::NCross, mp) = Nothing() +kernelvals(op::NCross, mp) = nothing integrand(op::NCross, kernel, x, g, f) = dot(g[1], normal(x) × f[1]) scalartype(op::NCross) = Union{} -function quaddata(op::LocalOperator, g::RTRefSpace, f::RTRefSpace, tels, bels) - u, w = trgauss(6) - return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] -end -function quaddata(op::LocalOperator, g::NDRefSpace, f::NDRefSpace, tels, bels) - u, w = trgauss(6) - return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] -end +# function quaddata(op::LocalOperator, g::RTRefSpace, f::RTRefSpace, tels, bels) +# u, w = trgauss(6) +# return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] +# end -function quaddata(op::LocalOperator, g::NDRefSpace, f::RTRefSpace, tels, bels) - u, w = trgauss(6) - return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] -end +# function quaddata(op::LocalOperator, g::NDRefSpace, f::NDRefSpace, tels, bels) +# u, w = trgauss(6) +# return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] +# end + +# function quaddata(op::LocalOperator, g::NDRefSpace, f::RTRefSpace, tels, bels) +# u, w = trgauss(6) +# return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] +# end -function quaddata(op::LocalOperator, g::subReferenceSpace, f::subReferenceSpace, tels, bels) - u, w = trgauss(6) - return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] -end # function quaddata(op::LocalOperator, g::Nd4DRefSpace, f::Nd4DRefSpace, tels, bels) # o, x, y, z = CompScienceMeshes.euclidianbasis(3) @@ -39,63 +36,106 @@ end # qps = quadpoints(reftet, rul) # [(w, parametric(p)) for (p,w) in qps] # end +# function quaddata(op::LocalOperator, g::NDLCCRefSpace, f::NDLCCRefSpace, tels, bels) +# o, x, y, z = CompScienceMeshes.euclidianbasis(3) +# reftet = simplex(x,y,z,o) +# qps = quadpoints(reftet, 6) +# [(w, parametric(p)) for (p,w) in qps] +# end + +# function quaddata(op::LocalOperator, g::NDLCDRefSpace, f::NDLCDRefSpace, tels, bels) +# o, x, y, z = CompScienceMeshes.euclidianbasis(3) +# reftet = simplex(x,y,z,o) +# qps = quadpoints(reftet, 6) +# [(w, parametric(p)) for (p,w) in qps] +# end + +# function quaddata(op::LocalOperator, g::NDLCDRefSpace, f::NDLCCRefSpace, tels, bels) +# o, x, y, z = CompScienceMeshes.euclidianbasis(3) +# reftet = simplex(x,y,z,o) +# qps = quadpoints(reftet, 6) +# [(w, parametric(p)) for (p,w) in qps] +# end + + +# quaddata(op::LocalOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, +# tels::Vector, bels::Vector) = quaddata(op, g, f, tels, bels, Val{dimension(tels[1])}) + +# function quaddata(op::LocalOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, +# tels::Vector, bels::Vector, dim::Type{Val{1}}) + +# u, w = legendre(6, 0.0, 1.0) +# [(w[i],u[i]) for i in eachindex(w)] +# end + +# function quaddata(op::LocalOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, +# tels::Vector, bels::Vector, dim::Type{Val{2}}) + +# u, w = trgauss(6) +# [(w[i], SVector(u[1,i], u[2,i])) for i in 1:length(w)] +# end -function quaddata(op::LocalOperator, g::NDLCCRefSpace, f::NDLCCRefSpace, tels, bels) - o, x, y, z = CompScienceMeshes.euclidianbasis(3) - reftet = simplex(x,y,z,o) - qps = quadpoints(reftet, 6) - [(w, parametric(p)) for (p,w) in qps] +function _alloc_workspace(qd, g, f, tels, bels) + q = qd[1] + τ = tels[1] + w, p = q[1], neighborhood(τ,q[2]) + a = (w, p, g(p), f(p)) + A = Vector{typeof(a)}(undef,length(qd)) end -function quaddata(op::LocalOperator, g::NDLCDRefSpace, f::NDLCDRefSpace, tels, bels) - o, x, y, z = CompScienceMeshes.euclidianbasis(3) - reftet = simplex(x,y,z,o) - qps = quadpoints(reftet, 6) - [(w, parametric(p)) for (p,w) in qps] +const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace} +function quaddata(op::LocalOperator, g::LinearRefSpaceTriangle, f::LinearRefSpaceTriangle, tels, bels) + u, w = trgauss(6) + qd = [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] + A = _alloc_workspace(qd, g, f, tels, bels) + + return qd, A end -function quaddata(op::LocalOperator, g::NDLCDRefSpace, f::NDLCCRefSpace, tels, bels) - o, x, y, z = CompScienceMeshes.euclidianbasis(3) - reftet = simplex(x,y,z,o) - qps = quadpoints(reftet, 6) - [(w, parametric(p)) for (p,w) in qps] +function quaddata(op::LocalOperator, g::subReferenceSpace, f::subReferenceSpace, tels, bels) + u, w = trgauss(6) + qd = [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] + A = _alloc_workspace(qd, g, f, tels, bels) + return qd, A end -function quaddata(op::LocalOperator, g::BDM3DRefSpace, f::BDM3DRefSpace, tels, bels) - o, x, y, z, = CompScienceMeshes.euclidianbasis(3) +const LinearRefSpaceTetr = Union{NDLCCRefSpace, NDLCDRefSpace} +function quaddata(op::LocalOperator, g::LinearRefSpaceTetr, f::LinearRefSpaceTetr, tels, bels) + o, x, y, z = CompScienceMeshes.euclidianbasis(3) reftet = simplex(x,y,z,o) - qps = quadpoints(reftet, 6) - [(w, parametric(p)) for (p,w) in qps] + qps = quadpoints(reftet, 3) + qd = [(w, parametric(p)) for (p,w) in qps] + A = _alloc_workspace(qd, g, f, tels, bels) + return qd, A end - -quaddata(op::LocalOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, - tels::Vector, bels::Vector) = quaddata(op, g, f, tels, bels, Val{dimension(tels[1])}) - -function quaddata(op::LocalOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, - tels::Vector, bels::Vector, dim::Type{Val{1}}) - +function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,2} where {T,Deg}, + f::LagrangeRefSpace, tels::Vector, bels::Vector) + u, w = legendre(6, 0.0, 1.0) - [(w[i],u[i]) for i in eachindex(w)] + qd = [(w[i],u[i]) for i in eachindex(w)] + A = _alloc_workspace(qd, g, f, tels, bels) + return qd, A end - -function quaddata(op::LocalOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, - tels::Vector, bels::Vector, dim::Type{Val{2}}) +function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,3} where {T,Deg}, + f::LagrangeRefSpace, tels::Vector, bels::Vector) u, w = trgauss(6) - [(w[i], SVector(u[1,i], u[2,i])) for i in 1:length(w)] + qd = [(w[i], SVector(u[1,i], u[2,i])) for i in 1:length(w)] + A = _alloc_workspace(qd, g, f, tels, bels) + return qd, A end -function quadrule(op::LocalOperator, ψ::RefSpace, ϕ::RefSpace, τ, qd) - q = qd[1] - w, p = q[1], neighborhood(τ,q[2]) - a = (w, p, ψ(p), ϕ(p)) - A = Vector{typeof(a)}(undef,length(qd)) - A[1] = a - for i in 2:length(qd) +function quadrule(op::LocalOperator, ψ::RefSpace, ϕ::RefSpace, τ, (qd,A)) + # q = qd[1] + # w, p = q[1], neighborhood(τ,q[2]) + # a = (w, p, ψ(p), ϕ(p)) + # A = Vector{typeof(a)}(undef,length(qd)) + # A[1] = a + for i in eachindex(A) q = qd[i] w, p = q[1], neighborhood(τ,q[2]) A[i] = (w, p, ψ(p), ϕ(p)) From f86c0a136bc560494a670ae8bccc60ba725e6f77 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 10 Nov 2020 13:02:37 +0100 Subject: [PATCH 089/528] quaddata specialised for (localop, LagOnTet) combo --- src/identityop.jl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/identityop.jl b/src/identityop.jl index 33ce1c6e..2f5b4145 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -128,6 +128,17 @@ function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,3} where {T,Deg}, return qd, A end +function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,4} where {T,Deg}, + f::LagrangeRefSpace, tels::Vector, bels::Vector) + + o, x, y, z = CompScienceMeshes.euclidianbasis(3) + reftet = simplex(x,y,z,o) + qps = quadpoints(reftet, 6) + qd = [(w, parametric(p)) for (p,w) in qps] + A = _alloc_workspace(qd, g, f, tels, bels) + return qd, A +end + function quadrule(op::LocalOperator, ψ::RefSpace, ϕ::RefSpace, τ, (qd,A)) # q = qd[1] From ad82473155e0265c1b4e0761f2e2b9f30a17bee4 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 11 Nov 2020 10:48:02 +0100 Subject: [PATCH 090/528] Local operators use sparse storage now. Assemble! api changed. --- src/identityop.jl | 71 +------------------------------ src/localop.jl | 60 +++++++++++++------------- src/maxwell/timedomain/mwtdops.jl | 2 +- src/operator.jl | 16 +++---- src/timedomain/tdintegralop.jl | 6 +-- src/timedomain/tdtimeops.jl | 9 ++-- test/test_compressed_storage.jl | 35 ++++++++------- test/test_local_assembly.jl | 12 ++++-- test/test_subd_basis.jl | 2 +- test/test_tdhhdbl.jl | 3 +- test/test_tdmwdbl.jl | 6 ++- 11 files changed, 79 insertions(+), 143 deletions(-) diff --git a/src/identityop.jl b/src/identityop.jl index 2f5b4145..bb6b5981 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -4,7 +4,6 @@ end kernelvals(biop::Identity, x) = nothing integrand(op::Identity, kernel, x, g, f) = dot(f[1], g[1]) scalartype(op::Identity) = Union{} -# scalartype(op::Identity) = Float64 struct NCross <: LocalOperator end @@ -13,69 +12,6 @@ kernelvals(op::NCross, mp) = nothing integrand(op::NCross, kernel, x, g, f) = dot(g[1], normal(x) × f[1]) scalartype(op::NCross) = Union{} - -# function quaddata(op::LocalOperator, g::RTRefSpace, f::RTRefSpace, tels, bels) -# u, w = trgauss(6) -# return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] -# end - -# function quaddata(op::LocalOperator, g::NDRefSpace, f::NDRefSpace, tels, bels) -# u, w = trgauss(6) -# return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] -# end - -# function quaddata(op::LocalOperator, g::NDRefSpace, f::RTRefSpace, tels, bels) -# u, w = trgauss(6) -# return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] -# end - - -# function quaddata(op::LocalOperator, g::Nd4DRefSpace, f::Nd4DRefSpace, tels, bels) -# o, x, y, z = CompScienceMeshes.euclidianbasis(3) -# reftet = simplex(o,x,y,z) -# qps = quadpoints(reftet, rul) -# [(w, parametric(p)) for (p,w) in qps] -# end -# function quaddata(op::LocalOperator, g::NDLCCRefSpace, f::NDLCCRefSpace, tels, bels) -# o, x, y, z = CompScienceMeshes.euclidianbasis(3) -# reftet = simplex(x,y,z,o) -# qps = quadpoints(reftet, 6) -# [(w, parametric(p)) for (p,w) in qps] -# end - -# function quaddata(op::LocalOperator, g::NDLCDRefSpace, f::NDLCDRefSpace, tels, bels) -# o, x, y, z = CompScienceMeshes.euclidianbasis(3) -# reftet = simplex(x,y,z,o) -# qps = quadpoints(reftet, 6) -# [(w, parametric(p)) for (p,w) in qps] -# end - -# function quaddata(op::LocalOperator, g::NDLCDRefSpace, f::NDLCCRefSpace, tels, bels) -# o, x, y, z = CompScienceMeshes.euclidianbasis(3) -# reftet = simplex(x,y,z,o) -# qps = quadpoints(reftet, 6) -# [(w, parametric(p)) for (p,w) in qps] -# end - - -# quaddata(op::LocalOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, -# tels::Vector, bels::Vector) = quaddata(op, g, f, tels, bels, Val{dimension(tels[1])}) - -# function quaddata(op::LocalOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, -# tels::Vector, bels::Vector, dim::Type{Val{1}}) - -# u, w = legendre(6, 0.0, 1.0) -# [(w[i],u[i]) for i in eachindex(w)] -# end - -# function quaddata(op::LocalOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, -# tels::Vector, bels::Vector, dim::Type{Val{2}}) - -# u, w = trgauss(6) -# [(w[i], SVector(u[1,i], u[2,i])) for i in 1:length(w)] -# end - - function _alloc_workspace(qd, g, f, tels, bels) q = qd[1] τ = tels[1] @@ -141,12 +77,7 @@ end function quadrule(op::LocalOperator, ψ::RefSpace, ϕ::RefSpace, τ, (qd,A)) - # q = qd[1] - # w, p = q[1], neighborhood(τ,q[2]) - # a = (w, p, ψ(p), ϕ(p)) - # A = Vector{typeof(a)}(undef,length(qd)) - # A[1] = a - for i in eachindex(A) + for i in eachindex(qd) q = qd[i] w, p = q[1], neighborhood(τ,q[2]) A[i] = (w, p, ψ(p), ϕ(p)) diff --git a/src/localop.jl b/src/localop.jl index 7b27cb72..1d49c8da 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -2,14 +2,38 @@ using CollisionDetection -mutable struct SingleQuadStrategy{T} - coords::Vector{T} - weights::Vector{T} -end +# mutable struct SingleQuadStrategy{T} +# coords::Vector{T} +# weights::Vector{T} +# end abstract type LocalOperator <: Operator end +function allocatestorage(op::LocalOperator, test_functions, trial_functions, + storage_trait, longdelays_trait) + + T = scalartype(op, test_functions, trial_functions) + + M = Int[] + N = Int[] + V = T[] + + function store(v,m,n) + push!(M,m) + push!(N,n) + push!(V,v) + end + + function freeze() + nrows = numfunctions(test_functions) + ncols = numfunctions(trial_functions) + return sparse(M,N,V, nrows, ncols) + end + + return freeze, store +end + function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, threading::Type{Threading{:multi}}) @@ -21,12 +45,6 @@ function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, return assemble_local_refines!(biop, tfs, bfs, store) end - # tels, tad = assemblydata(tfs) - # bels, bad = assemblydata(bfs) - # - # length(tels) != numcells(geometry(tfs)) && return assemble_local_mixed!(biop, tfs, bfs, store) - # length(bels) != numcells(geometry(bfs)) && return assemble_local_mixed!(biop, tfs, bfs, store) - return assemble_local_mixed!(biop, tfs, bfs, store) end @@ -60,7 +78,6 @@ function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, st println("Using 'refines' algorithm for local assembly:") - # tol = sqrt(eps(Float64)) tgeo = geometry(tfs) bgeo = geometry(bfs) @assert CompScienceMeshes.refines(tgeo, bgeo) @@ -76,9 +93,6 @@ function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, st qd = quaddata(biop, trefs, brefs, tels, bels) - # store the bcells in an octree - # tree = elementstree(bels) - print("dots out of 10: ") todo, done, pctg = length(tels), 0, 0 for (p,tcell) in enumerate(tels) @@ -87,19 +101,11 @@ function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, st Q = CompScienceMeshes.parent(tgeo, P) q = bg2a[Q] - # tc, ts = boundingbox(tcell.vertices) - # pred = (c,s) -> boxesoverlap(c,s,tc,ts) - - # for box in boxes(tree, pred) - # for q in box bcell = bels[q] @assert overlap(tcell, bcell) - # if overlap(tcell, bcell) - isct = intersection(tcell, bcell) for cell in isct - # volume(cell) < tol && continue P = restrict(brefs, bcell, cell) Q = restrict(trefs, tcell, cell) @@ -119,9 +125,6 @@ function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, st end # next refshape on test side end # next cell in intersection - # end # if overlap - # end # next cell in the basis geometry - # end # next box in the octree done += 1 new_pctg = round(Int, done / todo * 100) @@ -258,9 +261,7 @@ end function cellinteractions(biop, trefs, brefs, cell, qr) - # num_tshs = numfunctions(trefs) num_tshs = length(qr[1][3]) - # num_bshs = numfunctions(brefs) num_bshs = length(qr[1][4]) zlocal = zeros(Float64, num_tshs, num_bshs) @@ -268,14 +269,11 @@ function cellinteractions(biop, trefs, brefs, cell, qr) w, mp, tvals, bvals = q[1], q[2], q[3], q[4] j = w * jacobian(mp) - kernel = kernelvals(biop, mp) - # for m in 1 : num_tshs for m in 1 : length(tvals) - tval = tvals[m] - # for n in 1 : num_bshs + for n in 1 : length(bvals) bval = bvals[n] diff --git a/src/maxwell/timedomain/mwtdops.jl b/src/maxwell/timedomain/mwtdops.jl index 54063d03..359f6fc8 100644 --- a/src/maxwell/timedomain/mwtdops.jl +++ b/src/maxwell/timedomain/mwtdops.jl @@ -133,7 +133,7 @@ function allocatestorage(op::MWDoubleLayerTDIO, testST, basisST, data = zeros(eltype(op), bandwidth, M, N) Z = SparseND.Banded3D(K0, data, maxk1) store1(v,m,n,k) = (Z[m,n,k] += v) - return Z, store1 + return ()->Z, store1 end function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, store, diff --git a/src/operator.jl b/src/operator.jl index f7d339a8..45c483fa 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -84,7 +84,7 @@ function assemble(operator::AbstractOperator, test_functions, trial_functions; Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) assemble!(operator, test_functions, trial_functions, store, threading) - sdata(Z) + return Z() end function assemblerow(operator::AbstractOperator, test_functions, trial_functions, @@ -94,7 +94,7 @@ function assemblerow(operator::AbstractOperator, test_functions, trial_functions Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) assemblerow!(operator, test_functions, trial_functions, store) - sdata(Z) + Z() end function assemblecol(operator::AbstractOperator, test_functions, trial_functions, @@ -104,7 +104,7 @@ function assemblecol(operator::AbstractOperator, test_functions, trial_functions Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) assemblecol!(operator, test_functions, trial_functions, store) - sdata(Z) + Z() end function allocatestorage(operator::AbstractOperator, test_functions, trial_functions, @@ -116,13 +116,13 @@ function allocatestorage(operator::AbstractOperator, test_functions, trial_funct scalartype(test_functions) , scalartype(trial_functions), ) - Z = SharedArray{T}( - numfunctions(test_functions) , + Z = Matrix{T}(undef, + numfunctions(test_functions), numfunctions(trial_functions), ) fill!(Z, 0) store(v,m,n) = (Z[m,n] += v) - return Z, store + return ()->Z, store end @@ -132,7 +132,7 @@ function allocatestorage(operator::LinearCombinationOfOperators, long_delays_policy::Type{LongDelays{:ignore}}) # TODO: remove this ugly, ugly patch - Z, store = allocatestorage(operator.ops[end], test_functions, trial_functions, + return allocatestorage(operator.ops[end], test_functions, trial_functions, storage_policy, long_delays_policy) end @@ -143,7 +143,7 @@ function allocatestorage(operator::LinearCombinationOfOperators, long_delays_policy::Type{LongDelays{L}}) where {L,S} # TODO: remove this ugly, ugly patch - Z, store = allocatestorage(operator.ops[end], test_functions, trial_functions, + return allocatestorage(operator.ops[end], test_functions, trial_functions, storage_policy, long_delays_policy) end diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 12660ad3..efa52f96 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -38,7 +38,7 @@ function allocatestorage(op::RetardedPotential, testST, basisST, kmax = maximum(K1); Z = zeros(eltype(op), M, N, kmax) store1(v,m,n,k) = (Z[m,n,k] += v) - return MatrixConvolution(Z), store1 + return ()->MatrixConvolution(Z), store1 end @@ -70,7 +70,7 @@ function allocatestorage(op::RetardedPotential, testST, basisST, data = zeros(eltype(op), bandwidth, M, N) Z = SparseND.Banded3D(K0, data, maxk1) store1(v,m,n,k) = (Z[m,n,k] += v) - return Z, store1 + return ()->Z, store1 end struct Storage{T} end @@ -125,7 +125,7 @@ function allocatestorage(op::RetardedPotential, testST, basisST, end end - return Z, store1 + return ()->Z, store1 end function assemble!(op::LinearCombinationOfOperators, tfs::SpaceTimeBasis, bfs::SpaceTimeBasis, store, diff --git a/src/timedomain/tdtimeops.jl b/src/timedomain/tdtimeops.jl index a31f7f8e..aaa276be 100644 --- a/src/timedomain/tdtimeops.jl +++ b/src/timedomain/tdtimeops.jl @@ -64,7 +64,7 @@ function allocatestorage(op::TensorOperator, test_functions, trial_functions, data = zeros(scalartype(op), bandwidth, M, N) maxk1 = bandwidth Z = SparseND.Banded3D(K0, data, maxk1) - return Z, (v,m,n,k)->(Z[m,n,k] += v) + return ()->Z, (v,m,n,k)->(Z[m,n,k] += v) end @@ -186,9 +186,7 @@ function allocatestorage(op::TemporalDifferentiation, testfns, trialfns, derive(trial_time_fns) ) - Z, store = allocatestorage(op.operator, testfns, trialfns, - storage_trait, longdelays_trait) - return Z, store + return allocatestorage(op.operator, testfns, trialfns, storage_trait, longdelays_trait) end function assemble!(operator::TemporalDifferentiation, testfns, trialfns, store, threading = Threading{:multi}) @@ -225,8 +223,7 @@ function allocatestorage(op::TemporalIntegration, testfns, trialfns, integrate(trial_time_fns) ) - Z, store = allocatestorage(op.operator, testfns, trialfns, storage_trait, longdelays_trait) - return Z, store + return allocatestorage(op.operator, testfns, trialfns, storage_trait, longdelays_trait) end function assemble!(operator::TemporalIntegration, testfns, trialfns, store, diff --git a/test/test_compressed_storage.jl b/test/test_compressed_storage.jl index 3a5d28b8..b5993e5a 100644 --- a/test/test_compressed_storage.jl +++ b/test/test_compressed_storage.jl @@ -36,27 +36,27 @@ iT2 = integrate(T2) X = raviartthomas(Γ) Y = buffachristiansen(Γ, ibscaled=false) -Z1, store1 = BEAST.allocatestorage(SL0, X⊗δ, X⊗T1, +fr1, store1 = BEAST.allocatestorage(SL0, X⊗δ, X⊗T1, BEAST.Val{:bandedstorage}, BEAST.LongDelays{:compress}) -Z2, store2 = BEAST.allocatestorage(SL0, X⊗δ, X⊗T1, +fr2, store2 = BEAST.allocatestorage(SL0, X⊗δ, X⊗T1, BEAST.Val{:bandedstorage}, BEAST.LongDelays{:ignore}) -BEAST.assemble!(SL0, X⊗δ, X⊗T1, store1) -BEAST.assemble!(SL0, X⊗δ, X⊗T1, store2) +BEAST.assemble!(SL0, X⊗δ, X⊗T1, store1); Z1 = fr1() +BEAST.assemble!(SL0, X⊗δ, X⊗T1, store2); Z2 = fr2() @test norm(Z1-Z2,Inf) < 1e-12 -Z3, store7 = BEAST.allocatestorage(SL1, X⊗δ, X⊗T2, +fr3, store7 = BEAST.allocatestorage(SL1, X⊗δ, X⊗T2, BEAST.Val{:bandedstorage}, BEAST.LongDelays{:compress}) -Z4, store8 = BEAST.allocatestorage(SL1, X⊗δ, X⊗T2, +fr4, store8 = BEAST.allocatestorage(SL1, X⊗δ, X⊗T2, BEAST.Val{:bandedstorage}, BEAST.LongDelays{:ignore}) -BEAST.assemble!(SL1, X⊗δ, X⊗T2, store7) -BEAST.assemble!(SL1, X⊗δ, X⊗T2, store8) +BEAST.assemble!(SL1, X⊗δ, X⊗T2, store7); Z3 = fr3() +BEAST.assemble!(SL1, X⊗δ, X⊗T2, store8); Z4 = fr4() @@ -97,27 +97,30 @@ X2 = raviartthomas(Γ2) Y2 = raviartthomas(Γ2) DLh = (DL0, X2⊗δ, X2⊗iT2) -Z9, store9 = BEAST.allocatestorage(DLh..., +fr9, store9 = BEAST.allocatestorage(DLh..., BEAST.Val{:bandedstorage}, BEAST.LongDelays{:compress}) -Z10, store10 = BEAST.allocatestorage(DLh..., +fr10, store10 = BEAST.allocatestorage(DLh..., BEAST.Val{:bandedstorage}, BEAST.LongDelays{:ignore}) @time BEAST.assemble!(DLh..., store9) @time BEAST.assemble!(DLh..., store10) +Z9 = fr9() +Z10 = fr10() + @test norm(Z9-Z10,Inf) < 1e-12 iDLh = (integrate(DL0), Y2⊗δ, X2⊗T1) -Z11, store11 = BEAST.allocatestorage(iDLh..., BEAST.Val{:densestorage}, BEAST.LongDelays{:ignore}) -Z12, store12 = BEAST.allocatestorage(iDLh..., BEAST.Val{:bandedstorage}, BEAST.LongDelays{:compress}) -Z13, store13 = BEAST.allocatestorage(iDLh..., BEAST.Val{:bandedstorage}, BEAST.LongDelays{:ignore}) +fr11, store11 = BEAST.allocatestorage(iDLh..., BEAST.Val{:densestorage}, BEAST.LongDelays{:ignore}) +fr12, store12 = BEAST.allocatestorage(iDLh..., BEAST.Val{:bandedstorage}, BEAST.LongDelays{:compress}) +fr13, store13 = BEAST.allocatestorage(iDLh..., BEAST.Val{:bandedstorage}, BEAST.LongDelays{:ignore}) -BEAST.assemble!(iDLh..., store11) -BEAST.assemble!(iDLh..., store12) -BEAST.assemble!(iDLh..., store13) +BEAST.assemble!(iDLh..., store11); Z11 = fr11() +BEAST.assemble!(iDLh..., store12); Z12 = fr12() +BEAST.assemble!(iDLh..., store13); Z13 = fr13() @test norm(Z11-Z12,Inf) < 1e-8 @test norm(Z11-Z13,Inf) < 1e-8 \ No newline at end of file diff --git a/test/test_local_assembly.jl b/test/test_local_assembly.jl index 4c4689e7..a99ca345 100644 --- a/test/test_local_assembly.jl +++ b/test/test_local_assembly.jl @@ -21,11 +21,13 @@ Lx = BEAST.lagrangecxd0(m) @test numfunctions(Lx) == 8 Id = BEAST.Identity() -Q1, st1 = BEAST.allocatestorage(Id, L0, Lx, Val{:bandedstorage}, BEAST.LongDelays{:ignore}) +fr1, st1 = BEAST.allocatestorage(Id, L0, Lx, Val{:bandedstorage}, BEAST.LongDelays{:ignore}) BEAST.assemble_local_matched!(Id, L0, Lx, st1) +Q1 = fr1() -Q2, st2 = BEAST.allocatestorage(Id, L0, Lx, Val{:bandedstorage}, BEAST.LongDelays{:ignore}) +fr2, st2 = BEAST.allocatestorage(Id, L0, Lx, Val{:bandedstorage}, BEAST.LongDelays{:ignore}) BEAST.assemble_local_mixed!(Id, L0, Lx, st2) +Q2 = fr2() @test isapprox(Q1, Q2, atol=1e-8) @@ -33,9 +35,11 @@ BEAST.assemble_local_mixed!(Id, L0, Lx, st2) RT = raviartthomas(m) BC = buffachristiansen(m) -Q1, st1 = BEAST.allocatestorage(Id, BC, RT, Val{:bandedstorage}, BEAST.LongDelays{:ignore}) +fr1, st1 = BEAST.allocatestorage(Id, BC, RT, Val{:bandedstorage}, BEAST.LongDelays{:ignore}) BEAST.assemble_local_refines!(Id, BC, RT, st1) +Q1 = fr1() -Q2, st2 = BEAST.allocatestorage(Id, BC, RT, Val{:bandedstorage}, BEAST.LongDelays{:ignore}) +fr2, st2 = BEAST.allocatestorage(Id, BC, RT, Val{:bandedstorage}, BEAST.LongDelays{:ignore}) BEAST.assemble_local_mixed!(Id, BC, RT, st2) +Q2 = fr2() @test isapprox(Q1, Q2, atol=1e-8) diff --git a/test/test_subd_basis.jl b/test/test_subd_basis.jl index df3938cf..ef54ffa2 100644 --- a/test/test_subd_basis.jl +++ b/test/test_subd_basis.jl @@ -32,6 +32,6 @@ identityop = Identity() singlelayer = Helmholtz3D.singlelayer(gamma=1.0) I = assemble(identityop, X, X) #S = assemble(singlelayer, X, X) -ncd = cond(I) +ncd = cond(Matrix(I)) @test ncd ≈ 64.50401358713235 rtol=1e-8 diff --git a/test/test_tdhhdbl.jl b/test/test_tdhhdbl.jl index 615cb721..2103d4e1 100644 --- a/test/test_tdhhdbl.jl +++ b/test/test_tdhhdbl.jl @@ -30,8 +30,9 @@ T = timebasiscxd0(Δt, Nt) V = X ⊗ T W = Y ⊗ T -Z, store2 = BEAST.allocatestorage(K, V, V, Val{:densestorage}, BEAST.LongDelays{:ignore}) +fr2, store2 = BEAST.allocatestorage(K, V, V, Val{:densestorage}, BEAST.LongDelays{:ignore}) BEAST.assemble!(K, W, V, store2) +Z = fr2() @test all(==(0), Z[:,:,1]) # import WiltonInts84 diff --git a/test/test_tdmwdbl.jl b/test/test_tdmwdbl.jl index 4ce15611..2c65935e 100644 --- a/test/test_tdmwdbl.jl +++ b/test/test_tdmwdbl.jl @@ -24,18 +24,20 @@ T2 = timebasisshiftedlagrange(Δt, Nt, 2) V = X ⊗ T W = Y ⊗ T -Z, store1 = BEAST.allocatestorage(K, W, V, +fr1, store1 = BEAST.allocatestorage(K, W, V, Val{:densestorage}, BEAST.LongDelays{:ignore}) BEAST.assemble!(K, W, V, store1) +Z = fr1() @test all(==(0), Z[:,:,1]) W = X⊗δ V = Y⊗T2 K = TDMaxwell3D.doublelayer(speedoflight=1.0, numdiffs=1) -Z2, store2 = BEAST.allocatestorage(K, W, V, Val{:densestorage}, BEAST.LongDelays{:ignore}) +fr2, store2 = BEAST.allocatestorage(K, W, V, Val{:densestorage}, BEAST.LongDelays{:ignore}) BEAST.assemble!(K, W, V, store2) +Z2 = fr2() @test all(==(0), Z2[:,:,1]) γ = geometry(Y) From 6ff30f27fe34dbe72c5b5dc811eea6d749a03bd1 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 11 Nov 2020 13:25:56 +0100 Subject: [PATCH 091/528] taking ntrace uses only relevant subgeometry --- src/bases/trace.jl | 40 ++++++++++++++++++++++++---------------- test/test_nitsche.jl | 2 +- test/test_trace.jl | 2 +- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/bases/trace.jl b/src/bases/trace.jl index 1fc49495..eea5765f 100644 --- a/src/bases/trace.jl +++ b/src/bases/trace.jl @@ -42,44 +42,52 @@ end function ntrace(X::Space, γ) - x = refspace(X) - on_target = overlap_gpredicate(γ) + # on_target = overlap_gpredicate(γ) # ad = assemblydata(X) + x = refspace(X) E, ad = assemblydata(X) + igeo = geometry(X) + @assert dimension(γ) == dimension(igeo)-1 + # Γ = geo + # Dγ = dimension(γ) + # Σ = skeleton(Γ,Dγ) - geo = geometry(X) - Γ = geo - Dγ = dimension(γ) - Σ = skeleton(Γ,Dγ) + ogeo = boundary(igeo) + on_target = overlap_gpredicate(γ) + ogeo = submesh(ogeo) do face + on_target(chart(ogeo,face)) + end - D = copy(transpose(connectivity(Σ, Γ, abs))) + D = copy(transpose(connectivity(ogeo, igeo, abs))) rows, vals = rowvals(D), nonzeros(D) T = scalartype(X) - fns = [Shape{T}[] for i in 1:numfunctions(X)] + S = Shape{T} + fns = [Vector{S}() for i in 1:numfunctions(X)] for (p,el) in enumerate(E) for (q,fc) in enumerate(faces(el)) - on_target(fc) || continue - Q = ntrace(x,el,q,fc) - # print(Q) - @assert norm(Q,Inf) != 0 - # find the global index in Σ of the q-th face of the p-element + # print(Q) + # @assert norm(Q,Inf) != 0 + r = 0 for k in nzrange(D,p) vals[k] == q && (r = rows[k]; break) end @assert r != 0 + + fc1 = chart(ogeo, cells(ogeo)[r]) + Q = ntrace(x, el, q, fc1) + for i in 1:size(Q,1) for j in 1:size(Q,2) for (m,a) in ad[p,j] # j == q && println("bingo",j,q) v = a*Q[i,j] - @assert a != 0 - v == 0 && continue + isapprox(v,0,atol=sqrt(eps(T))) && continue push!(fns[m], Shape(r, i, v)) end end @@ -89,7 +97,7 @@ function ntrace(X::Space, γ) end - ntrace(X, Σ, fns) + ntrace(X, ogeo, fns) end diff --git a/test/test_nitsche.jl b/test/test_nitsche.jl index bdc0c774..d748d22a 100644 --- a/test/test_nitsche.jl +++ b/test/test_nitsche.jl @@ -91,7 +91,7 @@ end for _f in Y.fns @test length(_f) == 1 @test 0 < _f[1].cellid < 4 - seg = chart(Σ, Σ.faces[_f[1].cellid]) + seg = chart(Σ, cells(Σ)[_f[1].cellid]) @test _f[1].refid == 1 @test _f[1].coeff ≈ (1 / volume(seg)) end diff --git a/test/test_trace.jl b/test/test_trace.jl index 18144ffa..43d65b11 100644 --- a/test/test_trace.jl +++ b/test/test_trace.jl @@ -21,7 +21,7 @@ for i in eachindex(rwg.fns) length(nt.fns[i]) == 1 || continue global n += 1 c = nt.fns[i][1].cellid - edge = chart(Σ, Σ.faces[c]) + edge = chart(Σ, cells(Σ)[c]) @test on_bnd(edge) @test nt.fns[i][1].refid == 1 @test nt.fns[i][1].coeff == 2.0 From a17956d6683cb63876932c05cc3d05d02db5502a Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 20 Nov 2020 17:03:12 +0100 Subject: [PATCH 092/528] banded and dense storage implemented for local ops --- src/localop.jl | 14 +++++++++++++- test/runtests.jl | 1 + test/test_local_storage.jl | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 test/test_local_storage.jl diff --git a/src/localop.jl b/src/localop.jl index 1d49c8da..ce1a5542 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -11,7 +11,7 @@ abstract type LocalOperator <: Operator end function allocatestorage(op::LocalOperator, test_functions, trial_functions, - storage_trait, longdelays_trait) + storage_trait::Type{Val{:bandedstorage}}, longdelays_trait) T = scalartype(op, test_functions, trial_functions) @@ -34,6 +34,18 @@ function allocatestorage(op::LocalOperator, test_functions, trial_functions, return freeze, store end +function allocatestorage(op::LocalOperator, test_functions, trial_functions, + storage_trait::Type{Val{:densestorage}}, longdelays_trait) + + T = scalartype(op, test_functions, trial_functions) + + Z = zeros(T, numfunctions(test_functions), numfunctions(trial_functions)) + store(v,m,n) = (Z[m,n] += v) + freeze() = Z + + return freeze, store +end + function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, threading::Type{Threading{:multi}}) diff --git a/test/runtests.jl b/test/runtests.jl index a896eb5e..db56b6e1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -39,6 +39,7 @@ include("test_mult.jl") include("test_gram.jl") include("test_vector_gram.jl") +include("test_local_storage.jl") include("test_embedding.jl") include("test_laminated.jl") diff --git a/test/test_local_storage.jl b/test/test_local_storage.jl new file mode 100644 index 00000000..9331923a --- /dev/null +++ b/test/test_local_storage.jl @@ -0,0 +1,14 @@ +using Test +using BEAST +using CompScienceMeshes + +fn = joinpath(@__DIR__, "assets/sphere5.in") +m = readmesh(fn) + +Id = BEAST.Identity() +X = BEAST.raviartthomas(m) + +Z1 = assemble(Id, X, X, storage_policy=Val{:densestorage}) +Z2 = assemble(Id, X, X, storage_policy=Val{:bandedstorage}) + +@test Z1 ≈ Z2 atol=1e-8 \ No newline at end of file From 98953cf0c15eb3b7a425013bf0b4bfdb5054d0de Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 25 Nov 2020 10:23:34 +0100 Subject: [PATCH 093/528] localop assembly can use dict based storage --- Project.toml | 1 + src/BEAST.jl | 1 + src/localop.jl | 15 +++++++++++++++ test/test_local_storage.jl | 9 ++++++++- 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 85b31c5b..9019bada 100644 --- a/Project.toml +++ b/Project.toml @@ -16,6 +16,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +SparseMatrixDicts = "5cb6c4b0-9b79-11e8-24c9-f9621d252589" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" diff --git a/src/BEAST.jl b/src/BEAST.jl index 97f18211..1d1270f4 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -6,6 +6,7 @@ using LinearAlgebra using SharedArrays using SparseArrays using BlockArrays +using SparseMatrixDicts using SauterSchwabQuadrature using FastGaussQuadrature diff --git a/src/localop.jl b/src/localop.jl index ce1a5542..1047e93f 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -34,6 +34,21 @@ function allocatestorage(op::LocalOperator, test_functions, trial_functions, return freeze, store end +function allocatestorage(op::LocalOperator, testfunctions, trialfunctions, + storage_trait::Type{Val{:sparsedicts}}, longdelays_trait) + + T = scalartype(op, testfunctions, trialfunctions) + + m = numfunctions(testfunctions) + n = numfunctions(trialfunctions) + Z = SparseMatrixDict{T,Int}(m,n) + + store(v,m,n) = (Z[m,n] += v) + freeze() = SparseArrays.SparseMatrixCSC(Z) + + return freeze, store +end + function allocatestorage(op::LocalOperator, test_functions, trial_functions, storage_trait::Type{Val{:densestorage}}, longdelays_trait) diff --git a/test/test_local_storage.jl b/test/test_local_storage.jl index 9331923a..980baab3 100644 --- a/test/test_local_storage.jl +++ b/test/test_local_storage.jl @@ -1,6 +1,7 @@ using Test using BEAST using CompScienceMeshes +using SparseArrays fn = joinpath(@__DIR__, "assets/sphere5.in") m = readmesh(fn) @@ -10,5 +11,11 @@ X = BEAST.raviartthomas(m) Z1 = assemble(Id, X, X, storage_policy=Val{:densestorage}) Z2 = assemble(Id, X, X, storage_policy=Val{:bandedstorage}) +Z3 = assemble(Id, X, X, storage_policy=Val{:sparsedicts}) -@test Z1 ≈ Z2 atol=1e-8 \ No newline at end of file +@test Z1 isa DenseMatrix +@test Z2 isa SparseMatrixCSC +@test Z3 isa SparseMatrixCSC + +@test Z1 ≈ Z2 atol=1e-8 +@test Z1 ≈ Z3 atol=1e-8 \ No newline at end of file From a6e5b6ae2b35e66d310cda42641ec674392c1c11 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 17 Dec 2020 11:26:51 +0000 Subject: [PATCH 094/528] localop workspace allocated once at the top --- src/localop.jl | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/localop.jl b/src/localop.jl index 1047e93f..1b9a62e5 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -87,13 +87,15 @@ function assemble_local_matched!(biop::LocalOperator, tfs::Space, bfs::Space, st brefs = refspace(bfs) qd = quaddata(biop, trefs, brefs, tels, bels) + locmat = zeros(scalartype(biop, trefs, brefs), numfunctions(trefs), numfunctions(brefs)) for (p,cell) in enumerate(tels) P = ta2g[p] q = bg2a[P] q == 0 && continue qr = quadrule(biop, trefs, brefs, cell, qd) - locmat = cellinteractions(biop, trefs, brefs, cell, qr) + fill!(locmat, 0) + cellinteractions_matched!(locmat, biop, trefs, brefs, cell, qr) for i in 1 : size(locmat, 1), j in 1 : size(locmat, 2) for (m,a) in tad[p,i], (n,b) in bad[q,j] @@ -286,6 +288,33 @@ function assemble_local_mixed!(biop::LocalOperator, tfs::Space, bfs::Space, stor println("") end + +function cellinteractions_matched!(zlocal, biop, trefs, brefs, cell, qr) + + num_tshs = length(qr[1][3]) + num_bshs = length(qr[1][4]) + + # zlocal = zeros(Float64, num_tshs, num_bshs) + for q in qr + + w, mp, tvals, bvals = q[1], q[2], q[3], q[4] + j = w * jacobian(mp) + kernel = kernelvals(biop, mp) + + for n in 1 : num_bshs + bval = bvals[n] + for m in 1 : num_tshs + tval = tvals[m] + + igd = integrand(biop, kernel, mp, tval, bval) + zlocal[m,n] += j * igd + end + end + end + + return zlocal +end + function cellinteractions(biop, trefs, brefs, cell, qr) num_tshs = length(qr[1][3]) @@ -298,10 +327,10 @@ function cellinteractions(biop, trefs, brefs, cell, qr) j = w * jacobian(mp) kernel = kernelvals(biop, mp) - for m in 1 : length(tvals) + for m in 1 : num_tshs tval = tvals[m] - for n in 1 : length(bvals) + for n in 1 : num_bshs bval = bvals[n] igd = integrand(biop, kernel, mp, tval, bval) From d603b2dac4107c815c356d7e1436ea7dee6aef25 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sun, 31 Jan 2021 18:14:05 +0100 Subject: [PATCH 095/528] Fixed bug in rhs assembly --- examples/pmchwt.jl | 82 +++++++++++++++++++++++++++++++++---------- examples/tdefie.jl | 23 +++++++----- src/bases/rtspace.jl | 2 +- src/bases/trace.jl | 24 ++----------- src/integralop.jl | 7 ++-- src/operator.jl | 1 + src/postproc.jl | 2 +- src/solvers/solver.jl | 2 +- 8 files changed, 87 insertions(+), 56 deletions(-) diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 8f402e97..71d30605 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -1,14 +1,20 @@ using CompScienceMeshes, BEAST using LinearAlgebra -# Γ = meshcuboid(1.0, 1.0, 1.0, 0.15) -Γ = meshsphere(1.0, 0.075) +Γ = meshcuboid(0.5, 1.0, 1.0, 0.075) +CompScienceMeshes.translate!(Γ, point(-0.25, -0.5, -0.5)) +# Γ = meshsphere(1.0, 0.075) X = raviartthomas(Γ) @show numfunctions(X) κ, η = 6.0, 1.0 κ′, η′ = 2.4κ, η/2.4 +# κ = 1.0 +# η = 1.0 +# κ′ = 1.5*κ +# η′ = η/1.5 + T = Maxwell3D.singlelayer(wavenumber=κ) T′ = Maxwell3D.singlelayer(wavenumber=κ′) K = Maxwell3D.doublelayer(wavenumber=κ) @@ -29,7 +35,7 @@ e, h = (n × E) × n, (n × H) × n α, α′ = 1/η, 1/η′ pmchwt = @discretise( (η*T+η′*T′)[k,j] - (K+K′)[k,m] + - (K+K′)[l,j] + (α*T+α′*T′)[l,m] == e[k] - h[l], + (K+K′)[l,j] + (α*T+α′*T′)[l,m] == -e[k] - h[l], j∈X, m∈X, k∈X, l∈X) u = solve(pmchwt) @@ -41,10 +47,10 @@ um = u[nX+1:end] Θ, Φ = range(0.0,stop=2π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] -# Don't forgt the far field comprises two contributions +# Don't forget the far field comprises two contributions ffm = potential(MWFarField3D(κ*im), ffpoints, um, X) ffj = potential(MWFarField3D(κ*im), ffpoints, uj, X) -ff = η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) +ff = η*im*κ*ffj + im*κ*cross.(ffpoints, ffj) using Plots plot(xlabel="theta") @@ -54,24 +60,62 @@ import Plotly using LinearAlgebra fcrj, _ = facecurrents(uj,X) fcrm, _ = facecurrents(um,X) +Plotly.plot(patch(Γ, norm.(fcrj))) Plotly.plot(patch(Γ, norm.(fcrm))) -Z = range(-2,2,length=100) -Y = range(-2,2,length=100) + +function nearfield(um,uj,Xm,Xj,κ,η,points, + Einc=(x->point(0,0,0)), + Hinc=(x->point(0,0,0))) + + K = BEAST.MWDoubleLayerField3D(wavenumber=κ) + T = BEAST.MWSingleLayerField3D(wavenumber=κ) + + Em = potential(K, points, um, Xm) + Ej = potential(T, points, uj, Xj) + E = -Em + η * Ej + Einc.(points) + + Hm = potential(T, points, um, Xm) + Hj = potential(K, points, uj, Xj) + H = 1/η*Hm + Hj + Hinc.(points) + + return E, H +end + +Z = range(-1,1,length=100) +Y = range(-1,1,length=100) nfpoints = [point(0,y,z) for z in Z, y in Y] -nfm_in = potential(BEAST.MWDoubleLayerField3D(wavenumber=κ′), nfpoints, um, X) -nfj_in = potential(BEAST.MWSingleLayerField3D(wavenumber=κ′), nfpoints, uj, X) -nf_in = -nfm_in + η′ * nfj_in -nfm_ex = potential(BEAST.MWDoubleLayerField3D(wavenumber=κ), nfpoints, um, X) -nfj_ex = potential(BEAST.MWSingleLayerField3D(wavenumber=κ), nfpoints, uj, X) -nf_ex = nfm_ex - η * nfj_ex +# nfm_in = potential(BEAST.MWDoubleLayerField3D(wavenumber=κ′), nfpoints, um, X) +# nfj_in = potential(BEAST.MWSingleLayerField3D(wavenumber=κ′), nfpoints, uj, X) +# E_in = nfm_in - η′ * nfj_in + +# nfm_ex = potential(BEAST.MWDoubleLayerField3D(wavenumber=κ), nfpoints, um, X) +# nfj_ex = potential(BEAST.MWSingleLayerField3D(wavenumber=κ), nfpoints, uj, X) +# E_ex = -nfm_ex + η * nfj_ex + E.(nfpoints) + +E_ex, H_ex = nearfield(um,uj,X,X,κ,η,nfpoints,E,H) +E_in, H_in = nearfield(-um,-uj,X,X,κ′,η′,nfpoints) + +E_tot = E_in + E_ex +H_tot = H_in + H_ex + +contour(real.(getindex.(E_tot,1))) +heatmap(Z, Y, real.(getindex.(E_tot,1))) +plot(real.(getindex.(E_tot[:,51],1))) + + -nf_in = reshape(nf_in, size(nfpoints)) -nf_ex = reshape(nf_ex, size(nfpoints)) -nf = nf_in + nf_ex + E.(nfpoints) +# Compute also the near magnetic field +# Hm_ex = 1/η*potential(BEAST.MWSingleLayerField3D(wavenumber=κ), nfpoints, um, X) +# Hj_ex = potential(BEAST.MWDoubleLayerField3D(wavenumber=κ), nfpoints, uj, X) +# H_ex = Hm_ex + Hj_ex + H.(nfpoints) -contour(real.(getindex.(nf,1))) +# Hm_in = -1/η′*potential(BEAST.MWSingleLayerField3D(wavenumber=κ′), nfpoints, um, X) +# Hj_in = -potential(BEAST.MWDoubleLayerField3D(wavenumber=κ′), nfpoints, uj, X) +# H_in = Hm_in + Hj_in +# Hnf = H_ex + H_in -plot() -plot(real.(getindex.(nf[:,51],1))) +contour(real.(getindex.(H_tot,2))) +heatmap(Z, Y, real.(getindex.(H_tot,2))) +plot!(real.(getindex.(H_tot[:,51],2))) \ No newline at end of file diff --git a/examples/tdefie.jl b/examples/tdefie.jl index 3a243b58..37228e5c 100644 --- a/examples/tdefie.jl +++ b/examples/tdefie.jl @@ -1,10 +1,8 @@ -using CompScienceMeshes, BEAST +using CompScienceMeshes, BEAST, LinearAlgebra Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) - X = raviartthomas(Γ) - Δt, Nt = 0.3, 200 T = timebasisshiftedlagrange(Δt, Nt, 3) U = timebasisdelta(Δt, Nt) @@ -16,20 +14,27 @@ duration = 20 * Δt * 2 delay = 1.5 * duration amplitude = 1.0 gaussian = creategaussian(duration, delay, amplitude) - direction, polarisation = ẑ, x̂ -E = BEAST.planewave(polarisation, direction, derive(gaussian), 1.0) +E = planewave(polarisation, direction, derive(gaussian), 1.0) +@hilbertspace j +@hilbertspace j′ SL = TDMaxwell3D.singlelayer(speedoflight=1.0, numdiffs=1) +tdefie = @discretise SL[j′,j] == -1.0E[j′] j∈V j′∈W +xefie = solve(tdefie) + +import Plots +Plots.plot(xefie[1,:]) + +import Plotly +fcr, geo = facecurrents(xefie[:,60], X) +Plotly.plot(patch(geo, norm.(fcr))) + -@hilbertspace j -@hilbertspace j′ -tdefie = @discretise SL[j′,j] == -1.0E[j′] j∈V j′∈W -xefie = solve(tdefie) Xefie, Δω, ω0 = fouriertransform(xefie, Δt, 0.0, 2) ω = collect(ω0 .+ (0:Nt-1)*Δω) diff --git a/src/bases/rtspace.jl b/src/bases/rtspace.jl index 0b8345bb..5ab29623 100644 --- a/src/bases/rtspace.jl +++ b/src/bases/rtspace.jl @@ -57,7 +57,7 @@ function raviartthomas(mesh, cellpairs::Array{Int,2}) e1 = cellpairs[2,i] functions[i] = [ Shape(c1, abs(e1), +1.0)] - positions[i] = cartesian(center(chart(mesh, mesh.faces[c1]))) + positions[i] = cartesian(center(chart(mesh, Cells[c1]))) end end diff --git a/src/bases/trace.jl b/src/bases/trace.jl index eea5765f..a7cd8308 100644 --- a/src/bases/trace.jl +++ b/src/bases/trace.jl @@ -17,28 +17,7 @@ function strace(X::DirectProductSpace{T}, γ) where T return DirectProductSpace(x) end -function faces(c) - [ - simplex(c[2], c[3]), - simplex(c[3], c[1]), - simplex(c[1], c[2]), - ] -end -function faces(c::CompScienceMeshes.Simplex{3,3,0,4,Float64}) - # [ - # simplex(c[2], c[3], c[4]), - # simplex(c[3], c[1], c[4]), - # simplex(c[1], c[2], c[4]), - # simplex(c[2], c[1], c[3]) - # ] - [ - simplex(c[4],c[3],c[2]), - simplex(c[1],c[3],c[4]), - simplex(c[1],c[4],c[2]), - simplex(c[1],c[2],c[3]) - ] -end function ntrace(X::Space, γ) @@ -228,7 +207,8 @@ function ttrace(X::Space, γ) on_target(chart(ogeo, face)) end - D = copy(transpose(connectivity(ogeo, igeo, abs))) + # D = copy(transpose(connectivity(ogeo, igeo, abs))) + D = connectivity(igeo, ogeo, abs) rows, vals = rowvals(D), nonzeros(D) T = scalartype(X) diff --git a/src/integralop.jl b/src/integralop.jl index 8f666e7d..73ea92ce 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -130,7 +130,8 @@ function assemblechunk_body_nested_meshes!(biop, trial_shapes, trial_elements, trial_assembly_data, qd, zlocal, store) - print("dots out of 10: ") + myid = Threads.threadid() + myid == 1 && print("dots out of 10: ") todo, done, pctg = length(test_elements), 0, 0 for (p,tcell) in enumerate(test_elements) for (q,bcell) in enumerate(trial_elements) @@ -148,10 +149,10 @@ function assemblechunk_body_nested_meshes!(biop, done += 1 new_pctg = round(Int, done / todo * 100) if new_pctg > pctg + 9 - print(".") + myid == 1 && print(".") pctg = new_pctg end end - println("") + myid == 1 && println("") end diff --git a/src/operator.jl b/src/operator.jl index 45c483fa..da500a12 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -61,6 +61,7 @@ end *(a::Number, b::Operator) = LinearCombinationOfOperators([a], [b]) *(a::Number, b::LinearCombinationOfOperators) = LinearCombinationOfOperators(a * b.coeffs, b.ops) -(a::AbstractOperator, b::AbstractOperator) = a + (-1.0) * b +-(a::AbstractOperator) = (-1.0) * a function +(a::Operator, b::Operator) LinearCombinationOfOperators( diff --git a/src/postproc.jl b/src/postproc.jl index b4923a39..3e09edc5 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -127,7 +127,7 @@ end function potential(op, points, coeffs, basis) T = SVector{3,ComplexF64} - ff = zeros(T, length(points)) + ff = zeros(T, size(points)) store(v,m,n) = (ff[m] += v*coeffs[n]) potential!(store, op, points, basis) return ff diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index c34c1abb..351459c9 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -81,7 +81,7 @@ function assemble(lform::LinForm, test_space_dict) end b = assemble(a, X) - B[I[m] : I[m+1]-1] = b + B[I[m] : I[m+1]-1] = α * b end return B From cb3d9d23ada3e3e76fdc0c45566c149592eabafc Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 2 Feb 2021 20:47:03 +0100 Subject: [PATCH 096/528] Bumped version --- .github/workflows/CompatHelpeer.yml | 17 +++++++++++++++++ .github/workflows/TagBot.yml | 9 +++++++-- Project.toml | 20 ++++++++++---------- src/bases/local/rtlocal.jl | 12 ++++++++---- src/maxwell/mwops.jl | 6 ++++-- src/solvers/itsolver.jl | 2 +- test/REQUIRE.old | 1 - 7 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/CompatHelpeer.yml delete mode 100644 test/REQUIRE.old diff --git a/.github/workflows/CompatHelpeer.yml b/.github/workflows/CompatHelpeer.yml new file mode 100644 index 00000000..27a1a10d --- /dev/null +++ b/.github/workflows/CompatHelpeer.yml @@ -0,0 +1,17 @@ +name: CompatHelper +on: + schedule: + - cron: '00 00 * * *' + workflow_dispatch: +jobs: + CompatHelper: + runs-on: ubuntu-latest + steps: + - name: Pkg.add("CompatHelper") + run: julia -e 'using Pkg; Pkg.add("CompatHelper")' + - name: CompatHelper.main() + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMPATHELPER_PRIV: ${{ secrets.COMPATHELPER_PRIV }} # optional + run: julia -e 'using CompatHelper; CompatHelper.main()' + \ No newline at end of file diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index d77d3a0c..35600114 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -1,11 +1,16 @@ name: TagBot on: - schedule: - - cron: 0 * * * * + issue_comment: + types: + - created + workflow_dispatch: jobs: TagBot: + if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' runs-on: ubuntu-latest steps: - uses: JuliaRegistries/TagBot@v1 with: token: ${{ secrets.GITHUB_TOKEN }} + ssh: ${{ secrets.DOCUMENTER_KEY }} + diff --git a/Project.toml b/Project.toml index 9019bada..e55dbd13 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.2.0" +version = "1.2.1" [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" @@ -22,18 +22,18 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] -BlockArrays = "^0.10, ^0.11, ^0.12" +BlockArrays = "^0.10, ^0.11, ^0.12, ^0.13, ^0.14" CollisionDetection = "^0.1" -Combinatorics = "^0.7" -CompScienceMeshes = "^0.2.5" -Compat = "^2" +Combinatorics = "^0.7, ^1" +CompScienceMeshes = "^0.2.8" +Compat = "^2, ^3" FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" -IterativeSolvers = "^0.7, ^0.8" -SauterSchwabQuadrature = "^2.1" -SpecialFunctions = "^0.7, ^0.8" -StaticArrays = "^0.8.3, ^0.9, ^0.10, ^0.11, ^0.12" -WiltonInts84 = "^0.2.0" +IterativeSolvers = "^0.9" +SauterSchwabQuadrature = "^2.1.1" +SpecialFunctions = "^0.7, ^0.8, ^0.9, ^0.10, ^1" +StaticArrays = "^0.8.3, ^0.9, ^0.10, ^0.11, ^0.12, ^1" +WiltonInts84 = "^0.2" julia = "^1" [extras] diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index 19800334..52b89e07 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -13,12 +13,16 @@ function (ϕ::RTRefSpace)(mp) tu = tangents(mp,1) tv = tangents(mp,2) - d = 2/j + inv_j = 1/j + d = 2 * inv_j + + u_tu = u*tu + v_tv = v*tv return SVector(( - (value=(tu*(u-1) + tv*v )/j, divergence=d), - (value=(tu*u + tv*(v-1))/j, divergence=d), - (value=(tu*u + tv*v )/j, divergence=d) + (value=(tu*(u-1) + v_tv )*inv_j, divergence=d), + (value=(u_tu + tv*(v-1))*inv_j, divergence=d), + (value=(u_tu + v_tv )*inv_j, divergence=d) )) end diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 5b0bd818..742f185d 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -17,9 +17,11 @@ function kernelvals(biop::MaxwellOperator3D, p, q) R = norm(r) γR = γ*R + inv_R = 1/R + expn = exp(-γR) - green = expn / (4pi*R) - gradgreen = -(γ + 1/R) * green / R * r + green = expn * inv_R / (4pi) + gradgreen = -(γ + inv_R) * green * inv_R * r KernelValsMaxwell3D(γ, r, R, green, gradgreen) end diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index 2550b806..77f2ecbb 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -29,7 +29,7 @@ operator(solver::GMRESSolver) = solver.linear_operator function solve(solver::GMRESSolver, b) op = operator(solver) x, ch = IterativeSolvers.gmres(op, b, log=true, maxiter=solver.maxiter, - restart=solver.restart, tol=solver.tol) + restart=solver.restart, reltol=solver.tol) return x, ch end diff --git a/test/REQUIRE.old b/test/REQUIRE.old deleted file mode 100644 index 8b137891..00000000 --- a/test/REQUIRE.old +++ /dev/null @@ -1 +0,0 @@ - From b50c8f790f3ded877575475cb32fbb73926720c1 Mon Sep 17 00:00:00 2001 From: Bernd Hofmann Date: Sun, 31 Jan 2021 18:18:23 +0100 Subject: [PATCH 097/528] Fix a wrong index in MWSL3DIntegrand --- src/maxwell/sauterschwabints_rt.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/maxwell/sauterschwabints_rt.jl b/src/maxwell/sauterschwabints_rt.jl index edb2b7bd..7095570f 100644 --- a/src/maxwell/sauterschwabints_rt.jl +++ b/src/maxwell/sauterschwabints_rt.jl @@ -39,7 +39,7 @@ function (igd::MWSL3DIntegrand)(u,v) dot(f[1].value,G[2]) + f[1].divergence*H[2], dot(f[2].value,G[2]) + f[2].divergence*H[2], dot(f[3].value,G[2]) + f[3].divergence*H[2], - dot(f[1].value,G[3]) + f[2].divergence*H[3], + dot(f[1].value,G[3]) + f[1].divergence*H[3], dot(f[2].value,G[3]) + f[2].divergence*H[3], dot(f[3].value,G[3]) + f[3].divergence*H[3])) end From d415a193f56059216a2c17392fcddab1c2e50675 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 2 Feb 2021 21:08:32 +0100 Subject: [PATCH 098/528] Drop support for julia <1.3 --- .travis.yml | 4 ++-- Project.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 47f137e3..c3be022e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,8 @@ os: - osx - windows julia: - - 1.0 - - 1.4 + - 1.3 + - 1.5 notifications: email: false after_success: diff --git a/Project.toml b/Project.toml index e55dbd13..fe3f79f7 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.2.1" +version = "1.3.0" [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" @@ -34,7 +34,7 @@ SauterSchwabQuadrature = "^2.1.1" SpecialFunctions = "^0.7, ^0.8, ^0.9, ^0.10, ^1" StaticArrays = "^0.8.3, ^0.9, ^0.10, ^0.11, ^0.12, ^1" WiltonInts84 = "^0.2" -julia = "^1" +julia = "^1.3" [extras] DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" From a7e46de24c014493ba65f735b5cdc8fafb8b9c32 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Feb 2021 00:23:52 +0000 Subject: [PATCH 099/528] CompatHelper: bump compat for "SauterSchwabQuadrature" to "2.1" --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index fe3f79f7..73641f26 100644 --- a/Project.toml +++ b/Project.toml @@ -30,7 +30,7 @@ Compat = "^2, ^3" FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" IterativeSolvers = "^0.9" -SauterSchwabQuadrature = "^2.1.1" +SauterSchwabQuadrature = "^2.1.1, 2.1" SpecialFunctions = "^0.7, ^0.8, ^0.9, ^0.10, ^1" StaticArrays = "^0.8.3, ^0.9, ^0.10, ^0.11, ^0.12, ^1" WiltonInts84 = "^0.2" From 346cd250228d5b889be9f68d3d16c32af46e7ea0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Feb 2021 00:23:44 +0000 Subject: [PATCH 100/528] CompatHelper: add new compat entry for "SparseMatrixDicts" at version "0.2" --- Project.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 73641f26..4e007e12 100644 --- a/Project.toml +++ b/Project.toml @@ -30,7 +30,8 @@ Compat = "^2, ^3" FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" IterativeSolvers = "^0.9" -SauterSchwabQuadrature = "^2.1.1, 2.1" +SauterSchwabQuadrature = "^2.1.1" +SparseMatrixDicts = "0.2" SpecialFunctions = "^0.7, ^0.8, ^0.9, ^0.10, ^1" StaticArrays = "^0.8.3, ^0.9, ^0.10, ^0.11, ^0.12, ^1" WiltonInts84 = "^0.2" From f7e8e9cd771c7c05bdd6b294efa9216dfe7df44d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Feb 2021 00:24:03 +0000 Subject: [PATCH 101/528] CompatHelper: bump compat for "CompScienceMeshes" to "0.2" --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 4e007e12..8ce3a1de 100644 --- a/Project.toml +++ b/Project.toml @@ -25,7 +25,7 @@ WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" BlockArrays = "^0.10, ^0.11, ^0.12, ^0.13, ^0.14" CollisionDetection = "^0.1" Combinatorics = "^0.7, ^1" -CompScienceMeshes = "^0.2.8" +CompScienceMeshes = "^0.2.8, 0.2" Compat = "^2, ^3" FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" From bc80b71069975b3465d0dd91ac0a31b43de91abd Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 3 Feb 2021 14:37:24 +0100 Subject: [PATCH 102/528] minor optimisations in qrss and rtlocal --- .../workflows/{CompatHelpeer.yml => CompatHelper.yml} | 0 src/bases/local/rtlocal.jl | 4 ++-- src/maxwell/mwops.jl | 9 ++++----- 3 files changed, 6 insertions(+), 7 deletions(-) rename .github/workflows/{CompatHelpeer.yml => CompatHelper.yml} (100%) diff --git a/.github/workflows/CompatHelpeer.yml b/.github/workflows/CompatHelper.yml similarity index 100% rename from .github/workflows/CompatHelpeer.yml rename to .github/workflows/CompatHelper.yml diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index 52b89e07..a54ea44e 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -20,8 +20,8 @@ function (ϕ::RTRefSpace)(mp) v_tv = v*tv return SVector(( - (value=(tu*(u-1) + v_tv )*inv_j, divergence=d), - (value=(u_tu + tv*(v-1))*inv_j, divergence=d), + (value=(u_tu-tu + v_tv )*inv_j, divergence=d), + (value=(u_tu + v_tv-tv)*inv_j, divergence=d), (value=(u_tu + v_tv )*inv_j, divergence=d) )) end diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 742f185d..d0f98eed 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -169,11 +169,10 @@ function qrss(op, g, f, i, τ, j, σ, qd) hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - h = sqrt(volume(σ)) - dmin = sqrt(dmin2) - xtol = 0.2 - k = norm(op.gamma) - max(dmin*k, dmin/4h) < xtol && return WiltonSEStrategy( + h2 = volume(σ) + xtol2 = 0.2 * 0.2 + k2 = abs2(op.gamma) + max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSEStrategy( qd.tpoints[2,i], DoubleQuadStrategy( qd.tpoints[2,i], From a65900ca735d6fec450ed27420f79b98b8de843c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 3 Feb 2021 14:57:32 +0100 Subject: [PATCH 103/528] tightened CSM compat entry --- Project.toml | 2 +- src/maxwell/mwops.jl | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Project.toml b/Project.toml index 8ce3a1de..4e007e12 100644 --- a/Project.toml +++ b/Project.toml @@ -25,7 +25,7 @@ WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" BlockArrays = "^0.10, ^0.11, ^0.12, ^0.13, ^0.14" CollisionDetection = "^0.1" Combinatorics = "^0.7, ^1" -CompScienceMeshes = "^0.2.8, 0.2" +CompScienceMeshes = "^0.2.8" Compat = "^2, ^3" FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index d0f98eed..904bf1f8 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -158,10 +158,7 @@ function qrss(op, g, f, i, τ, j, σ, qd) for s in σ.vertices d2 = LinearAlgebra.norm_sqr(t-s) dmin2 = min(dmin2, d2) - if d2 < dtol - hits +=1 - break - end + hits += (d2 < dtol) end end From 5f89b5a0384f7e2dd6bce9636f4dc8d2aaffab13 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 3 Feb 2021 15:17:19 +0100 Subject: [PATCH 104/528] opt: double loop reordering and minimise cplx ops --- src/maxwell/mwops.jl | 5 +++-- src/quadrature/double_quadrature.jl | 18 ++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 904bf1f8..b35f37bc 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -10,6 +10,7 @@ struct KernelValsMaxwell3D{T,U,P,Q} gradgreen::Q end +const inv_4pi = 1/(4pi) function kernelvals(biop::MaxwellOperator3D, p, q) γ = biop.gamma @@ -20,7 +21,7 @@ function kernelvals(biop::MaxwellOperator3D, p, q) inv_R = 1/R expn = exp(-γR) - green = expn * inv_R / (4pi) + green = expn * inv_R * inv_4pi gradgreen = -(γ + inv_R) * green * inv_R * r KernelValsMaxwell3D(γ, r, R, green, gradgreen) @@ -142,7 +143,7 @@ function integrand(biop::MWDL3DGen, kerneldata, tvals, tgeo, bvals, bgeo) g = tvals[1] f = bvals[1] ∇G = kerneldata.gradgreen - g ⋅ (∇G × f) + (f × g) ⋅ ∇G end diff --git a/src/quadrature/double_quadrature.jl b/src/quadrature/double_quadrature.jl index d7232327..03da1cf6 100644 --- a/src/quadrature/double_quadrature.jl +++ b/src/quadrature/double_quadrature.jl @@ -17,28 +17,26 @@ function momintegrals!(biop, tshs, bshs, tcell, bcell, z, strat::DoubleQuadStrat womps = strat.outer_quad_points wimps = strat.inner_quad_points - M, N = size(z) - + for womp in womps tgeo = womp.point tvals = womp.value + M = length(tvals) jx = womp.weight - + for wimp in wimps bgeo = wimp.point bvals = wimp.value + N = length(bvals) jy = wimp.weight j = jx * jy kernel = kernelvals(biop, tgeo, bgeo) - #for m in 1 : M - for m in 1 : length(tvals) - tval = tvals[m] - #for n in 1 : N - for n in 1 : length(bvals) - bval = bvals[n] - + for n in 1 : N + bval = bvals[n] + for m in 1 : M + tval = tvals[m] igd = integrand(biop, kernel, tval, tgeo, bval, bgeo) z[m,n] += j * igd end From 0f06f0ec89921ecdc09f5bc202de3cb145880e72 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 17 Feb 2021 17:32:57 +0100 Subject: [PATCH 105/528] Retired Banded3D --- examples/dot_tdmfie.jl | 2 +- examples/tdefie_neumann.jl | 18 ++- src/maxwell/timedomain/mwtdops.jl | 74 +++++----- src/operator.jl | 16 +- src/timedomain/convop.jl | 5 + src/timedomain/tdintegralop.jl | 60 ++++---- src/timedomain/tdtimeops.jl | 34 ++--- src/utils/sparsend.jl | 234 +++++++++++++++--------------- test/runtests.jl | 2 +- test/test_compressed_storage.jl | 12 +- 10 files changed, 232 insertions(+), 225 deletions(-) diff --git a/examples/dot_tdmfie.jl b/examples/dot_tdmfie.jl index 46005cd5..380a81ac 100644 --- a/examples/dot_tdmfie.jl +++ b/examples/dot_tdmfie.jl @@ -33,7 +33,7 @@ K = MWDoubleLayerTDIO(1.0, 1.0, 1) N = BEAST.TemporalDifferentiation(NCross()⊗Identity()) M = 0.5*N + 1.0*K -Z_mfie = assemble(M, W, V, Val{:bandedstorage}) +Z_mfie = assemble(M, W, V, storage_policy = Val{:bandedstorage}) b_mfie = assemble(H, W) dot_xmfie = marchonintime(inv(Z_mfie[:,:,1]), Z_mfie, b_mfie, Nt) diff --git a/examples/tdefie_neumann.jl b/examples/tdefie_neumann.jl index 0146fa2d..66bf0662 100644 --- a/examples/tdefie_neumann.jl +++ b/examples/tdefie_neumann.jl @@ -11,7 +11,7 @@ D, Δx = 1.0, 0.1 Γ = meshrectangle(D,D,Δx,3) #γ = boundary(Γ) γ1 = meshsegment(D,D,3) -γ2 = translate(γ1, point(0,D,0)) +γ2 = CompScienceMeshes.translate(γ1, point(0,D,0)) #X = raviartthomas(Γ,weld(γ1,γ2)) X = rt_ports(Γ,(γ1,γ2)) @@ -38,24 +38,26 @@ T = MWSingleLayerTDIO(sol,-1/sol,-sol,2,0) tdefie = @discretise T[j′,j] == -1E[j′] j∈V j′∈W xefie = solve(tdefie) -using PlotlyJS -include(Pkg.dir("CompScienceMeshes","examples","matlab_patches.jl")) +using Plotly +# include(Pkg.dir("CompScienceMeshes","examples","matlab_patches.jl")) Xefie, Δω, ω0 = fouriertransform(xefie, Δt, 0.0, 2) ω = collect(ω0 + (0:Nt-1)*Δω) -_, i1 = findmin(abs.(ω-1.0*sol)) +_, i1 = findmin(abs.(ω.-sol)) ω1 = ω[i1] ue = Xefie[:,i1] / fouriertransform(gaussian)(ω1) +using LinearAlgebra fcre, geo = facecurrents(ue, X) t2 = patch(geo, real.(norm.(fcre))) -#PlotlyJS.plot(t2) +Plotly.plot(t2) fcr_td, geo = facecurrents(xefie[:,end], X) -fcr_ch, geo = facecurrents(xefie[:,end], divergence(X)) +# fcr_ch, geo = facecurrents(xefie[:,end], divergence(X)) -patch(Γ,real.(norm.(fcr_td))) -jmatlab_quiver(Γ, fcr_td) +t3 = patch(Γ,real.(norm.(fcr_td))) +Plotly.plot(t3) +# jmatlab_quiver(Γ, fcr_td) diff --git a/src/maxwell/timedomain/mwtdops.jl b/src/maxwell/timedomain/mwtdops.jl index 359f6fc8..b0139e2b 100644 --- a/src/maxwell/timedomain/mwtdops.jl +++ b/src/maxwell/timedomain/mwtdops.jl @@ -98,43 +98,43 @@ end @inline (f::TransposedStorage)(v,m,n,k) = f.store(v,n,m,k) -function allocatestorage(op::MWDoubleLayerTDIO, testST, basisST, - ::Type{Val{:bandedstorage}}, - ::Type{LongDelays{:ignore}}) - - # tfs = spatialbasis(testST) - # bfs = spatialbasis(basisST) - X, T = spatialbasis(testST), temporalbasis(testST) - Y, U = spatialbasis(basisST), temporalbasis(basisST) - - if CompScienceMeshes.refines(geometry(Y), geometry(X)) - testST = Y⊗T - basisST = X⊗U - end - - M = numfunctions(X) - N = numfunctions(Y) - - K0 = fill(typemax(Int), M, N) - K1 = zeros(Int, M, N) - - function store(v,m,n,k) - K0[m,n] = min(K0[m,n],k) - K1[m,n] = max(K1[m,n],k) - end - - aux = EmptyRP(op.speed_of_light) - print("Allocating memory for convolution operator: ") - assemble!(aux, testST, basisST, store) - println("\nAllocated memory for convolution operator.") - - maxk1 = maximum(K1) - bandwidth = maximum(K1 .- K0 .+ 1) - data = zeros(eltype(op), bandwidth, M, N) - Z = SparseND.Banded3D(K0, data, maxk1) - store1(v,m,n,k) = (Z[m,n,k] += v) - return ()->Z, store1 -end +# function allocatestorage(op::MWDoubleLayerTDIO, testST, basisST, +# ::Type{Val{:bandedstorage}}, +# ::Type{LongDelays{:ignore}}) + +# # tfs = spatialbasis(testST) +# # bfs = spatialbasis(basisST) +# X, T = spatialbasis(testST), temporalbasis(testST) +# Y, U = spatialbasis(basisST), temporalbasis(basisST) + +# if CompScienceMeshes.refines(geometry(Y), geometry(X)) +# testST = Y⊗T +# basisST = X⊗U +# end + +# M = numfunctions(X) +# N = numfunctions(Y) + +# K0 = fill(typemax(Int), M, N) +# K1 = zeros(Int, M, N) + +# function store(v,m,n,k) +# K0[m,n] = min(K0[m,n],k) +# K1[m,n] = max(K1[m,n],k) +# end + +# aux = EmptyRP(op.speed_of_light) +# print("Allocating memory for convolution operator: ") +# assemble!(aux, testST, basisST, store) +# println("\nAllocated memory for convolution operator.") + +# maxk1 = maximum(K1) +# bandwidth = maximum(K1 .- K0 .+ 1) +# data = zeros(eltype(op), bandwidth, M, N) +# Z = SparseND.Banded3D(K0, data, maxk1) +# store1(v,m,n,k) = (Z[m,n,k] += v) +# return ()->Z, store1 +# end function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, store, threading=Threading{:multi}) diff --git a/src/operator.jl b/src/operator.jl index da500a12..4d487489 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -40,7 +40,7 @@ function derive(a::LinearCombinationOfOperators) end -function +(a::LinearCombinationOfOperators, b::Operator) +function +(a::LinearCombinationOfOperators, b::AbstractOperator) LinearCombinationOfOperators( [a.coeffs;[1.0]], [a.ops;[b]] @@ -54,16 +54,16 @@ function +(a::LinearCombinationOfOperators, b::LinearCombinationOfOperators) ) end -+(a::Operator, b::LinearCombinationOfOperators) = b + a -+(a::Operator, b::Number) = a + (b * Identity()) -+(a::Number, b::Operator) = b + a ++(a::AbstractOperator, b::LinearCombinationOfOperators) = b + a ++(a::AbstractOperator, b::Number) = a + (b * Identity()) ++(a::Number, b::AbstractOperator) = b + a -*(a::Number, b::Operator) = LinearCombinationOfOperators([a], [b]) +*(a::Number, b::AbstractOperator) = LinearCombinationOfOperators([a], [b]) *(a::Number, b::LinearCombinationOfOperators) = LinearCombinationOfOperators(a * b.coeffs, b.ops) -(a::AbstractOperator, b::AbstractOperator) = a + (-1.0) * b -(a::AbstractOperator) = (-1.0) * a -function +(a::Operator, b::Operator) +function +(a::AbstractOperator, b::AbstractOperator) LinearCombinationOfOperators( [1.0, 1.0], [a, b] @@ -108,8 +108,8 @@ function assemblecol(operator::AbstractOperator, test_functions, trial_functions Z() end -function allocatestorage(operator::AbstractOperator, test_functions, trial_functions, - storage_trait, +function allocatestorage(operator::Operator, test_functions, trial_functions, + storage_trait::Type{Val{:bandedstorage}}, longdelays_trait) T = promote_type( diff --git a/src/timedomain/convop.jl b/src/timedomain/convop.jl index 5940b115..9a32e1d2 100644 --- a/src/timedomain/convop.jl +++ b/src/timedomain/convop.jl @@ -59,6 +59,11 @@ function polyeig(Z::ConvOp) end +function polyeig(Z) + return eigvals(companion(Z)) +end + + function Base.:+(a::ConvOp, b::ConvOp) @assert size(a.data) == size(b.data) diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index efa52f96..eaef6d95 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -42,36 +42,36 @@ function allocatestorage(op::RetardedPotential, testST, basisST, end -function allocatestorage(op::RetardedPotential, testST, basisST, - ::Type{Val{:bandedstorage}}, - ::Type{LongDelays{:ignore}}) - - tfs = spatialbasis(testST) - bfs = spatialbasis(basisST) - - M = numfunctions(tfs) - N = numfunctions(bfs) - - K0 = fill(typemax(Int), M, N) - K1 = zeros(Int, M, N) - - function store(v,m,n,k) - K0[m,n] = min(K0[m,n],k) - K1[m,n] = max(K1[m,n],k) - end - - aux = EmptyRP(op.speed_of_light) - print("Allocating memory for convolution operator: ") - assemble!(aux, testST, basisST, store) - println("\nAllocated memory for convolution operator.") - - maxk1 = maximum(K1) - bandwidth = maximum(K1 .- K0 .+ 1) - data = zeros(eltype(op), bandwidth, M, N) - Z = SparseND.Banded3D(K0, data, maxk1) - store1(v,m,n,k) = (Z[m,n,k] += v) - return ()->Z, store1 -end +# function allocatestorage(op::RetardedPotential, testST, basisST, +# ::Type{Val{:bandedstorage}}, +# ::Type{LongDelays{:ignore}}) + +# tfs = spatialbasis(testST) +# bfs = spatialbasis(basisST) + +# M = numfunctions(tfs) +# N = numfunctions(bfs) + +# K0 = fill(typemax(Int), M, N) +# K1 = zeros(Int, M, N) + +# function store(v,m,n,k) +# K0[m,n] = min(K0[m,n],k) +# K1[m,n] = max(K1[m,n],k) +# end + +# aux = EmptyRP(op.speed_of_light) +# print("Allocating memory for convolution operator: ") +# assemble!(aux, testST, basisST, store) +# println("\nAllocated memory for convolution operator.") + +# maxk1 = maximum(K1) +# bandwidth = maximum(K1 .- K0 .+ 1) +# data = zeros(eltype(op), bandwidth, M, N) +# Z = SparseND.Banded3D(K0, data, maxk1) +# store1(v,m,n,k) = (Z[m,n,k] += v) +# return ()->Z, store1 +# end struct Storage{T} end diff --git a/src/timedomain/tdtimeops.jl b/src/timedomain/tdtimeops.jl index aaa276be..7407490e 100644 --- a/src/timedomain/tdtimeops.jl +++ b/src/timedomain/tdtimeops.jl @@ -46,26 +46,26 @@ end -function allocatestorage(op::TensorOperator, test_functions, trial_functions, - ::Type{Val{:bandedstorage}}, ::Type{LongDelays{:ignore}},) +# function allocatestorage(op::TensorOperator, test_functions, trial_functions, +# ::Type{Val{:bandedstorage}}, ::Type{LongDelays{:ignore}},) - M = numfunctions(spatialbasis(test_functions)) - N = numfunctions(spatialbasis(trial_functions)) +# M = numfunctions(spatialbasis(test_functions)) +# N = numfunctions(spatialbasis(trial_functions)) - time_basis_function = BEAST.convolve( - temporalbasis(test_functions), - temporalbasis(trial_functions)) +# time_basis_function = BEAST.convolve( +# temporalbasis(test_functions), +# temporalbasis(trial_functions)) - space_operator = op.spatial_factor - A = assemble(space_operator, spatialbasis(test_functions), spatialbasis(trial_functions)) +# space_operator = op.spatial_factor +# A = assemble(space_operator, spatialbasis(test_functions), spatialbasis(trial_functions)) - K0 = ones(M,N) - bandwidth = numintervals(time_basis_function) - 1 - data = zeros(scalartype(op), bandwidth, M, N) - maxk1 = bandwidth - Z = SparseND.Banded3D(K0, data, maxk1) - return ()->Z, (v,m,n,k)->(Z[m,n,k] += v) -end +# K0 = ones(M,N) +# bandwidth = numintervals(time_basis_function) - 1 +# data = zeros(scalartype(op), bandwidth, M, N) +# maxk1 = bandwidth +# Z = SparseND.Banded3D(K0, data, maxk1) +# return ()->Z, (v,m,n,k)->(Z[m,n,k] += v) +# end function allocatestorage(op::TensorOperator, test_functions, trial_functions, @@ -167,7 +167,7 @@ function assemble!(operator::TensorOperator, testfns, trialfns, store, end -mutable struct TemporalDifferentiation <: Operator +mutable struct TemporalDifferentiation <: AbstractOperator operator end diff --git a/src/utils/sparsend.jl b/src/utils/sparsend.jl index 2177c18d..82b0c15b 100644 --- a/src/utils/sparsend.jl +++ b/src/utils/sparsend.jl @@ -1,122 +1,122 @@ module SparseND -import BEAST - -struct Banded3D{T} <: AbstractArray{T,3} - k0::Array{Int,2} - data::Array{T,3} - maxk0::Int - maxk1::Int - function Banded3D{T}(k0,data,maxk1) where T - @assert size(k0) == size(data)[2:3] - return new(k0,data,maximum(k0),maxk1) - end -end - -Banded3D(k0,data::Array{T},maxk1) where {T} = Banded3D{T}(k0,data,maxk1) - -bandwidth(A::Banded3D) = size(A.data,1) - -import Base: size, getindex, setindex! - -# size(A::Banded3D) = size(A.data) -size(A::Banded3D) = tuple(size(A.k0)..., A.maxk1) - -function getindex(A::Banded3D, m::Int, n::Int, k::Int) - k0 = A.k0[m,n] - k0 == 0 && return zero(eltype(A)) - l = k - k0 + 1 - l < 1 && return zero(eltype(A)) - l > bandwidth(A) && return zero(eltype(A)) - A.data[l,m,n] -end - -function setindex!(A::Banded3D, v, m, n, k) - k0 = A.k0[m,n] - @assert k0 != 0 "Failed: $v, $m, $n, $k" - @assert A.k0[m,n] <= k <= A.k0[m,n] + bandwidth(A) - 1 - A.data[k-A.k0[m,n]+1,m,n] = v -end - - -function Base.:+(A::Banded3D{T}, B::Banded3D{T}) where {T} - - M = size(A,1) - N = size(A,2) - - @assert M == size(B,1) - @assert N == size(B,2) - - Abw = size(A.data,1) - Bbw = size(B.data,1) - - # keep track of empty columns - Az = findall(A.k0 .== 0) - Bz = findall(B.k0 .== 0) - - K0 = min.(A.k0, B.k0) - K0[Az] = B.k0[Az] - K0[Bz] = A.k0[Bz] - - AK1 = A.k0 .+ (Abw - 1); AK1[Az] .= -1 - BK1 = B.k0 .+ (Bbw - 1); BK1[Bz] .= -1 - K1 = max.(AK1, BK1) - - bw = maximum(K1 - K0 .+ 1) - @assert bw > 0 - @assert bw >= Abw - @assert bw >= Bbw - data = zeros(T, bw, M, N) - for m in axes(A.data,2) - for n in axes(A.data,3) - k0 = K0[m,n] - @assert k0 != 0 - - Ak0 = A.k0[m,n] - if Ak0 != 0 - for Al in 1:Abw - k = Ak0 + Al - 1 - l = k - k0 + 1 - data[l,m,n] += A.data[Al,m,n] - end - end - - Bk0 = B.k0[m,n] - if Bk0 != 0 - for Bl in 1:Bbw - k = Bk0 + Bl - 1 - l = k - k0 + 1 - l < 1 && @show m, n, k0, k, Bk0, Bl - d = data[l,m,n] - data[l,m,n] = d + B.data[Bl,m,n] - end - end - end - end - - Banded3D(K0, data, max(A.maxk1, B.maxk1)) -end - -function BEAST.convolve(Z::SparseND.Banded3D,x,j,k_start) - T = promote_type(eltype(Z), eltype(x)) - M,N,L = size(Z) - K = size(Z.data,1) - @assert M == size(x,1) - y = zeros(T,M) - for n in 1:N - for m in 1:M - k0 = Z.k0[m,n] # k0 is 1-based - l0 = max(1, k_start - k0 + 1) - l1 = min(K, j - k0 + 1) - for l in l0 : l1 - k = k0 + l - 1 - # j - k + 1 < 1 && break - y[m] += Z.data[l,m,n] * x[n,j - k + 1] - end - end - end - return y -end +# import BEAST + +# struct Banded3D{T} <: AbstractArray{T,3} +# k0::Array{Int,2} +# data::Array{T,3} +# maxk0::Int +# maxk1::Int +# function Banded3D{T}(k0,data,maxk1) where T +# @assert size(k0) == size(data)[2:3] +# return new(k0,data,maximum(k0),maxk1) +# end +# end + +# Banded3D(k0,data::Array{T},maxk1) where {T} = Banded3D{T}(k0,data,maxk1) + +# bandwidth(A::Banded3D) = size(A.data,1) + +# import Base: size, getindex, setindex! + +# # size(A::Banded3D) = size(A.data) +# size(A::Banded3D) = tuple(size(A.k0)..., A.maxk1) + +# function getindex(A::Banded3D, m::Int, n::Int, k::Int) +# k0 = A.k0[m,n] +# k0 == 0 && return zero(eltype(A)) +# l = k - k0 + 1 +# l < 1 && return zero(eltype(A)) +# l > bandwidth(A) && return zero(eltype(A)) +# A.data[l,m,n] +# end + +# function setindex!(A::Banded3D, v, m, n, k) +# k0 = A.k0[m,n] +# @assert k0 != 0 "Failed: $v, $m, $n, $k" +# @assert A.k0[m,n] <= k <= A.k0[m,n] + bandwidth(A) - 1 +# A.data[k-A.k0[m,n]+1,m,n] = v +# end + + +# function Base.:+(A::Banded3D{T}, B::Banded3D{T}) where {T} + +# M = size(A,1) +# N = size(A,2) + +# @assert M == size(B,1) +# @assert N == size(B,2) + +# Abw = size(A.data,1) +# Bbw = size(B.data,1) + +# # keep track of empty columns +# Az = findall(A.k0 .== 0) +# Bz = findall(B.k0 .== 0) + +# K0 = min.(A.k0, B.k0) +# K0[Az] = B.k0[Az] +# K0[Bz] = A.k0[Bz] + +# AK1 = A.k0 .+ (Abw - 1); AK1[Az] .= -1 +# BK1 = B.k0 .+ (Bbw - 1); BK1[Bz] .= -1 +# K1 = max.(AK1, BK1) + +# bw = maximum(K1 - K0 .+ 1) +# @assert bw > 0 +# @assert bw >= Abw +# @assert bw >= Bbw +# data = zeros(T, bw, M, N) +# for m in axes(A.data,2) +# for n in axes(A.data,3) +# k0 = K0[m,n] +# @assert k0 != 0 + +# Ak0 = A.k0[m,n] +# if Ak0 != 0 +# for Al in 1:Abw +# k = Ak0 + Al - 1 +# l = k - k0 + 1 +# data[l,m,n] += A.data[Al,m,n] +# end +# end + +# Bk0 = B.k0[m,n] +# if Bk0 != 0 +# for Bl in 1:Bbw +# k = Bk0 + Bl - 1 +# l = k - k0 + 1 +# l < 1 && @show m, n, k0, k, Bk0, Bl +# d = data[l,m,n] +# data[l,m,n] = d + B.data[Bl,m,n] +# end +# end +# end +# end + +# Banded3D(K0, data, max(A.maxk1, B.maxk1)) +# end + +# function BEAST.convolve(Z::SparseND.Banded3D,x,j,k_start) +# T = promote_type(eltype(Z), eltype(x)) +# M,N,L = size(Z) +# K = size(Z.data,1) +# @assert M == size(x,1) +# y = zeros(T,M) +# for n in 1:N +# for m in 1:M +# k0 = Z.k0[m,n] # k0 is 1-based +# l0 = max(1, k_start - k0 + 1) +# l1 = min(K, j - k0 + 1) +# for l in l0 : l1 +# k = k0 + l - 1 +# # j - k + 1 < 1 && break +# y[m] += Z.data[l,m,n] * x[n,j - k + 1] +# end +# end +# end +# return y +# end struct MatrixOfConvolutions{T} <: AbstractArray{Vector{T},2} diff --git a/test/runtests.jl b/test/runtests.jl index db56b6e1..6bb6afe2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,7 +14,7 @@ import BEAST include("test_fourier.jl") include("test_specials.jl") -include("test_sparsend.jl") +# include("test_sparsend.jl") include("test_basis.jl") include("test_directproduct.jl") diff --git a/test/test_compressed_storage.jl b/test/test_compressed_storage.jl index b5993e5a..e20d393c 100644 --- a/test/test_compressed_storage.jl +++ b/test/test_compressed_storage.jl @@ -40,7 +40,7 @@ fr1, store1 = BEAST.allocatestorage(SL0, X⊗δ, X⊗T1, BEAST.Val{:bandedstorage}, BEAST.LongDelays{:compress}) fr2, store2 = BEAST.allocatestorage(SL0, X⊗δ, X⊗T1, - BEAST.Val{:bandedstorage}, + BEAST.Val{:densestorage}, BEAST.LongDelays{:ignore}) BEAST.assemble!(SL0, X⊗δ, X⊗T1, store1); Z1 = fr1() @@ -52,7 +52,7 @@ fr3, store7 = BEAST.allocatestorage(SL1, X⊗δ, X⊗T2, BEAST.Val{:bandedstorage}, BEAST.LongDelays{:compress}) fr4, store8 = BEAST.allocatestorage(SL1, X⊗δ, X⊗T2, - BEAST.Val{:bandedstorage}, + BEAST.Val{:densestorage}, BEAST.LongDelays{:ignore}) BEAST.assemble!(SL1, X⊗δ, X⊗T2, store7); Z3 = fr3() @@ -101,7 +101,7 @@ fr9, store9 = BEAST.allocatestorage(DLh..., BEAST.Val{:bandedstorage}, BEAST.LongDelays{:compress}) fr10, store10 = BEAST.allocatestorage(DLh..., - BEAST.Val{:bandedstorage}, + BEAST.Val{:densestorage}, BEAST.LongDelays{:ignore}) @time BEAST.assemble!(DLh..., store9) @@ -115,12 +115,12 @@ Z10 = fr10() iDLh = (integrate(DL0), Y2⊗δ, X2⊗T1) fr11, store11 = BEAST.allocatestorage(iDLh..., BEAST.Val{:densestorage}, BEAST.LongDelays{:ignore}) fr12, store12 = BEAST.allocatestorage(iDLh..., BEAST.Val{:bandedstorage}, BEAST.LongDelays{:compress}) -fr13, store13 = BEAST.allocatestorage(iDLh..., BEAST.Val{:bandedstorage}, BEAST.LongDelays{:ignore}) +# fr13, store13 = BEAST.allocatestorage(iDLh..., BEAST.Val{:bandedstorage}, BEAST.LongDelays{:ignore}) BEAST.assemble!(iDLh..., store11); Z11 = fr11() BEAST.assemble!(iDLh..., store12); Z12 = fr12() -BEAST.assemble!(iDLh..., store13); Z13 = fr13() +# BEAST.assemble!(iDLh..., store13); Z13 = fr13() @test norm(Z11-Z12,Inf) < 1e-8 -@test norm(Z11-Z13,Inf) < 1e-8 \ No newline at end of file +# @test norm(Z11-Z13,Inf) < 1e-8 \ No newline at end of file From 68f9d8b924c6d87488353f164ad4ce42233e4c47 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 1 Mar 2021 14:52:00 +0000 Subject: [PATCH 106/528] FD assembly uses BlockArrays that can be index by placeholders vectors --- Project.toml | 2 ++ examples/pmchwt.jl | 49 ++++++----------------------------- src/BEAST.jl | 2 ++ src/operator.jl | 5 ++-- src/solvers/solver.jl | 41 ++++++++++++----------------- src/timedomain/motlu.jl | 2 -- src/timedomain/tdtimeops.jl | 2 +- src/utils/mixedblockarrays.jl | 24 +++++++++++++++++ src/utils/variational.jl | 10 +++++++ 9 files changed, 66 insertions(+), 71 deletions(-) create mode 100644 src/utils/mixedblockarrays.jl diff --git a/Project.toml b/Project.toml index 4e007e12..dde12786 100644 --- a/Project.toml +++ b/Project.toml @@ -11,6 +11,7 @@ Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" +FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" @@ -29,6 +30,7 @@ CompScienceMeshes = "^0.2.8" Compat = "^2, ^3" FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" +FillArrays = "0.11" IterativeSolvers = "^0.9" SauterSchwabQuadrature = "^2.1.1" SparseMatrixDicts = "0.2" diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 71d30605..177eb81b 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -10,21 +10,12 @@ X = raviartthomas(Γ) κ, η = 6.0, 1.0 κ′, η′ = 2.4κ, η/2.4 -# κ = 1.0 -# η = 1.0 -# κ′ = 1.5*κ -# η′ = η/1.5 - T = Maxwell3D.singlelayer(wavenumber=κ) T′ = Maxwell3D.singlelayer(wavenumber=κ′) K = Maxwell3D.doublelayer(wavenumber=κ) K′ = Maxwell3D.doublelayer(wavenumber=κ′) -# d = normalize(x̂+ŷ+ẑ) -d = ẑ -# p = normalize(d × ẑ) -p = x̂ -E = Maxwell3D.planewave(direction=d, polarization=p, wavenumber=κ) +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) H = -1/(im*κ*η)*curl(E) e, h = (n × E) × n, (n × H) × n @@ -40,16 +31,12 @@ pmchwt = @discretise( u = solve(pmchwt) -nX = numfunctions(X) -uj = u[1:nX] -um = u[nX+1:end] - Θ, Φ = range(0.0,stop=2π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] # Don't forget the far field comprises two contributions -ffm = potential(MWFarField3D(κ*im), ffpoints, um, X) -ffj = potential(MWFarField3D(κ*im), ffpoints, uj, X) +ffm = potential(MWFarField3D(κ*im), ffpoints, u[m], X) +ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], X) ff = η*im*κ*ffj + im*κ*cross.(ffpoints, ffj) using Plots @@ -58,8 +45,8 @@ plot!(Θ,norm.(ff),label="far field") import Plotly using LinearAlgebra -fcrj, _ = facecurrents(uj,X) -fcrm, _ = facecurrents(um,X) +fcrj, _ = facecurrents(u[j],X) +fcrm, _ = facecurrents(u[m],X) Plotly.plot(patch(Γ, norm.(fcrj))) Plotly.plot(patch(Γ, norm.(fcrm))) @@ -86,16 +73,8 @@ Z = range(-1,1,length=100) Y = range(-1,1,length=100) nfpoints = [point(0,y,z) for z in Z, y in Y] -# nfm_in = potential(BEAST.MWDoubleLayerField3D(wavenumber=κ′), nfpoints, um, X) -# nfj_in = potential(BEAST.MWSingleLayerField3D(wavenumber=κ′), nfpoints, uj, X) -# E_in = nfm_in - η′ * nfj_in - -# nfm_ex = potential(BEAST.MWDoubleLayerField3D(wavenumber=κ), nfpoints, um, X) -# nfj_ex = potential(BEAST.MWSingleLayerField3D(wavenumber=κ), nfpoints, uj, X) -# E_ex = -nfm_ex + η * nfj_ex + E.(nfpoints) - -E_ex, H_ex = nearfield(um,uj,X,X,κ,η,nfpoints,E,H) -E_in, H_in = nearfield(-um,-uj,X,X,κ′,η′,nfpoints) +E_ex, H_ex = nearfield(u[m],u[j],X,X,κ,η,nfpoints,E,H) +E_in, H_in = nearfield(-u[m],-u[j],X,X,κ′,η′,nfpoints) E_tot = E_in + E_ex H_tot = H_in + H_ex @@ -104,18 +83,6 @@ contour(real.(getindex.(E_tot,1))) heatmap(Z, Y, real.(getindex.(E_tot,1))) plot(real.(getindex.(E_tot[:,51],1))) - - -# Compute also the near magnetic field -# Hm_ex = 1/η*potential(BEAST.MWSingleLayerField3D(wavenumber=κ), nfpoints, um, X) -# Hj_ex = potential(BEAST.MWDoubleLayerField3D(wavenumber=κ), nfpoints, uj, X) -# H_ex = Hm_ex + Hj_ex + H.(nfpoints) - -# Hm_in = -1/η′*potential(BEAST.MWSingleLayerField3D(wavenumber=κ′), nfpoints, um, X) -# Hj_in = -potential(BEAST.MWDoubleLayerField3D(wavenumber=κ′), nfpoints, uj, X) -# H_in = Hm_in + Hj_in -# Hnf = H_ex + H_in - contour(real.(getindex.(H_tot,2))) heatmap(Z, Y, real.(getindex.(H_tot,2))) -plot!(real.(getindex.(H_tot[:,51],2))) \ No newline at end of file +plot(real.(getindex.(H_tot[:,51],2))) \ No newline at end of file diff --git a/src/BEAST.jl b/src/BEAST.jl index 1d1270f4..2ca78ade 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -5,6 +5,7 @@ using LinearAlgebra # using Pkg using SharedArrays using SparseArrays +using FillArrays using BlockArrays using SparseMatrixDicts @@ -107,6 +108,7 @@ using SparseArrays function convolve end include("utils/polynomial.jl") +include("utils/mixedblockarrays.jl") include("utils/sparsend.jl") include("utils/specialfns.jl") include("utils/combinatorics.jl") diff --git a/src/operator.jl b/src/operator.jl index 4d487489..d34d0a51 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -108,9 +108,8 @@ function assemblecol(operator::AbstractOperator, test_functions, trial_functions Z() end -function allocatestorage(operator::Operator, test_functions, trial_functions, - storage_trait::Type{Val{:bandedstorage}}, - longdelays_trait) +function allocatestorage(operator::AbstractOperator, test_functions, trial_functions, + storage_trait, longdelays_trait) T = promote_type( scalartype(operator) , diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 351459c9..5bc0e05f 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -59,12 +59,16 @@ function assemble(lform::LinForm, test_space_dict) terms = lform.terms T = ComplexF64 - I = Int[1] + # I = Int[1] + blocksizes1 = Int[] for p in 1:length(lform.test_space) X = test_space_dict[p] - push!(I, last(I) + numfunctions(X)) + # push!(I, last(I) + numfunctions(X)) + push!(blocksizes1, numfunctions(X)) end - B = zeros(T, last(I)-1) + + Z = zeros(T, sum(blocksizes1)) + B = PseudoBlockArray{T}(Z, blocksizes1) for t in terms @@ -81,7 +85,8 @@ function assemble(lform::LinForm, test_space_dict) end b = assemble(a, X) - B[I[m] : I[m+1]-1] = α * b + # B[I[m] : I[m+1]-1] = α * b + B[Block(m)] = α * b end return B @@ -130,23 +135,21 @@ function assemble(bilform::BilForm, test_space_dict, trial_space_dict) lhterms = bilform.terms T = ComplexF64 # TDOD: Fix this - # determine the offsets of the different blocks in the sys matrix - I = Int[1] - J = Int[1] - + blocksizes1 = Int[] for p in 1:length(bilform.test_space) X = test_space_dict[p] - push!(I, last(I) + numfunctions(X)) + push!(blocksizes1, numfunctions(X)) end + blocksizes2 = Int[] for q in 1:length(bilform.trial_space) Y = trial_space_dict[q] - push!(J, last(J) + numfunctions(Y)) + push!(blocksizes2, numfunctions(Y)) end # allocate the memory for the matrices - Z = zeros(T, last(I)-1, last(J)-1) - + A = zeros(T, sum(blocksizes1), sum(blocksizes2)) + Z = PseudoBlockArray{T}(A, blocksizes1, blocksizes2) # For each block, compute the interaction matrix for t in lhterms @@ -165,11 +168,8 @@ function assemble(bilform::BilForm, test_space_dict, trial_space_dict) y = op[end](op[1:end-1]..., y) end - r = I[m] : (I[m+1] - 1) - c = J[n] : (J[n+1] - 1) - z = assemble(a, x, y) - Z[r,c] += α * z + Z[Block(m,n)] += α * z end return Z @@ -192,17 +192,12 @@ function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict) I = [numfunctions(spatialbasis(test_space_dict[i])) for i in 1:length(bilform.test_space)] J = [numfunctions(spatialbasis(trial_space_dict[i])) for i in 1:length(bilform.trial_space)] - # @show I - # @show J - BT = SparseND.MatrixOfConvolutions{T} Z = BlockArray(undef_blocks, BT, I, J) # For each block, compute the interaction matrix for t in lhterms - # α = t.coeff - # @show t.coeff @show (t.coeff,t.kernel) a = t.coeff * t.kernel @@ -221,9 +216,7 @@ function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict) z = assemble(a, x, y) @warn "variation formulations where combinations of test and trial space recur multiple times are not supported!" Z[Block(m,n)] = SparseND.MatrixOfConvolutions(z) - # Z[r,c] += α * z + end - # - # return Z return Z end diff --git a/src/timedomain/motlu.jl b/src/timedomain/motlu.jl index 91bb8292..f318b536 100644 --- a/src/timedomain/motlu.jl +++ b/src/timedomain/motlu.jl @@ -55,8 +55,6 @@ function marchonintime(iZ0, Z::ConvOp, B, Nt) x end -using BlockArrays - function convolve(Z::BlockArray, x, i, j_start) # ax1 = axes(Z,1) ax2 = axes(Z,2) diff --git a/src/timedomain/tdtimeops.jl b/src/timedomain/tdtimeops.jl index 7407490e..2de1cb7b 100644 --- a/src/timedomain/tdtimeops.jl +++ b/src/timedomain/tdtimeops.jl @@ -213,7 +213,7 @@ scalartype(op::TemporalIntegration) = scalartype(op.operator) Base.:*(a::Number, op::TemporalIntegration) = TemporalIntegration(a * op.operator) function allocatestorage(op::TemporalIntegration, testfns, trialfns, - storage_trait, longdelays_trait) + storage_trait::Type{Val{S}}, longdelays_trait) where {S} trial_time_fns = temporalbasis(trialfns) trial_space_fns = spatialbasis(trialfns) diff --git a/src/utils/mixedblockarrays.jl b/src/utils/mixedblockarrays.jl new file mode 100644 index 00000000..4f218286 --- /dev/null +++ b/src/utils/mixedblockarrays.jl @@ -0,0 +1,24 @@ +struct ZeroBlockInitializer end +const zero_blocks = ZeroBlockInitializer() + +import Base.Cartesian: @nloops, @ntuple +import BlockArrays: BlockArray + +""" +Initialise a BlockArray where each block can have a different type. + + BlockArray{T}(zero_blocks, blocksize...) +""" +@generated function BlockArray{T}(::ZeroBlockInitializer, blocksizes::Vararg{AbstractVector{Int},N}) where {T,N} + return quote + B = BlockArray{T,N,Array{AbstractArray{T,N},N}}(undef_blocks, blocksizes...) + axs = axes(B) + @nloops $N block dim->blockaxes(B,dim) begin + block_indices = @ntuple $N block + indices = getindex.(axs, block_indices) + block_size = length.(indices) + B[block_indices...] = Zeros{T}(block_size...) + end + return B + end +end diff --git a/src/utils/variational.jl b/src/utils/variational.jl index d7def632..9888ea2f 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -1,5 +1,7 @@ module Variational +using BlockArrays + # import Base: start, done, next export transposecalls! @@ -104,6 +106,14 @@ mutable struct HilbertVector opstack end +Base.Int(hv::HilbertVector) = hv.idx + +Base.getindex(A::AbstractBlockArray, p::HilbertVector, q::HilbertVector) = A[Block(Int(p),Int(q))] +Base.getindex(u::AbstractBlockArray, p::HilbertVector) = u[Block(Int(p))] + +Base.setindex!(A::AbstractBlockArray, v, p::HilbertVector, q::HilbertVector) = setindex!(A, v, Block(Int(p),Int(q))) +Base.setindex!(A::AbstractBlockArray, v, p::HilbertVector) = setindex!(A, v, Block(Int(p))) + mutable struct LinForm test_space terms From 4c68ad8d49bcd3dab0c810577db9102d2331bffe Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 2 Mar 2021 11:59:06 +0000 Subject: [PATCH 107/528] Use convop as storage for MatrixOfConvolutions --- examples/tdefie_nodot.jl | 4 -- src/BEAST.jl | 2 +- src/solvers/lusolver.jl | 6 +- src/timedomain/convop.jl | 1 - src/timedomain/motlu.jl | 2 +- src/utils/sparsend.jl | 127 ++------------------------------------- 6 files changed, 10 insertions(+), 132 deletions(-) diff --git a/examples/tdefie_nodot.jl b/examples/tdefie_nodot.jl index a296df27..e23cd5c9 100644 --- a/examples/tdefie_nodot.jl +++ b/examples/tdefie_nodot.jl @@ -25,10 +25,6 @@ SL = TDMaxwell3D.singlelayer(speedoflight=1.0) @hilbertspace j′ efie_nodot = @discretise SL[j′,j] == E[j′] j∈V j′∈W xefie_nodot = solve(efie_nodot) -# xefie_nodot = solve(@discretise( -# SL[j′,j] == E[j′], -# j∈V, j′∈W)) - Xefie, Δω, ω0 = fouriertransform(xefie_nodot, Δt, 0.0, 2) ω = collect(ω0 .+ (0:Nt-1)*Δω) diff --git a/src/BEAST.jl b/src/BEAST.jl index 2ca78ade..46e380a8 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -109,6 +109,7 @@ function convolve end include("utils/polynomial.jl") include("utils/mixedblockarrays.jl") +include("timedomain/convop.jl") include("utils/sparsend.jl") include("utils/specialfns.jl") include("utils/combinatorics.jl") @@ -161,7 +162,6 @@ include("postproc/segcurrents.jl") include("quadrature/double_quadrature.jl") include("quadrature/singularity_extraction.jl") -include("timedomain/convop.jl") include("timedomain/tdintegralop.jl") include("timedomain/tdexcitation.jl") include("timedomain/motlu.jl") diff --git a/src/solvers/lusolver.jl b/src/solvers/lusolver.jl index acde7735..fa040055 100644 --- a/src/solvers/lusolver.jl +++ b/src/solvers/lusolver.jl @@ -43,9 +43,6 @@ end function timeslice(A::BlockArray, k) - # I = [blocksize(A, (1,i))[1] for i in 1:nblocks(A,1)] - # J = [blocksize(A, (j,1))[2] for j in 1:nblocks(A,2)] - I = blocklengths(axes(A,1)) J = blocklengths(axes(A,2)) @@ -56,7 +53,8 @@ function timeslice(A::BlockArray, k) for i in 1:blocksize(A,1) for j in 1:blocksize(A,2) isassigned(A.blocks, i, j) || continue - S[Block(i,j)] = A[Block(i,j)].banded[:,:,k] + # S[Block(i,j)] = A[Block(i,j)].banded[:,:,k] + S[Block(i,j)] = A[Block(i,j)].convop[:,:,k] end end diff --git a/src/timedomain/convop.jl b/src/timedomain/convop.jl index 9a32e1d2..25ae7740 100644 --- a/src/timedomain/convop.jl +++ b/src/timedomain/convop.jl @@ -24,7 +24,6 @@ end function convolve!(y, Z::ConvOp, x, X, j, k_start, k_stop=size(Z,3)) - # @info "The corrrect convolve!" for n in axes(x,1) for m in axes(y,1) k0 = Z.k0[m,n] diff --git a/src/timedomain/motlu.jl b/src/timedomain/motlu.jl index f318b536..cb26ce52 100644 --- a/src/timedomain/motlu.jl +++ b/src/timedomain/motlu.jl @@ -87,7 +87,7 @@ function convolve!(y,Z::BlockArray, x, csx, i, j_start, j_stop) xJ = view(x, ax2[J], :) csxJ = view(csx, ax2[J], :) try - ZIJ = Z[I,J].banded + ZIJ = Z[I,J].convop # y[I] .+= convolve(ZIJ, xJ, i, j_start) yI = view(y, ax1[I]) convolve!(yI, ZIJ, xJ, csxJ, i, j_start, j_stop) diff --git a/src/utils/sparsend.jl b/src/utils/sparsend.jl index 82b0c15b..191ff5f3 100644 --- a/src/utils/sparsend.jl +++ b/src/utils/sparsend.jl @@ -1,134 +1,19 @@ module SparseND -# import BEAST - -# struct Banded3D{T} <: AbstractArray{T,3} -# k0::Array{Int,2} -# data::Array{T,3} -# maxk0::Int -# maxk1::Int -# function Banded3D{T}(k0,data,maxk1) where T -# @assert size(k0) == size(data)[2:3] -# return new(k0,data,maximum(k0),maxk1) -# end -# end - -# Banded3D(k0,data::Array{T},maxk1) where {T} = Banded3D{T}(k0,data,maxk1) - -# bandwidth(A::Banded3D) = size(A.data,1) - -# import Base: size, getindex, setindex! - -# # size(A::Banded3D) = size(A.data) -# size(A::Banded3D) = tuple(size(A.k0)..., A.maxk1) - -# function getindex(A::Banded3D, m::Int, n::Int, k::Int) -# k0 = A.k0[m,n] -# k0 == 0 && return zero(eltype(A)) -# l = k - k0 + 1 -# l < 1 && return zero(eltype(A)) -# l > bandwidth(A) && return zero(eltype(A)) -# A.data[l,m,n] -# end - -# function setindex!(A::Banded3D, v, m, n, k) -# k0 = A.k0[m,n] -# @assert k0 != 0 "Failed: $v, $m, $n, $k" -# @assert A.k0[m,n] <= k <= A.k0[m,n] + bandwidth(A) - 1 -# A.data[k-A.k0[m,n]+1,m,n] = v -# end - - -# function Base.:+(A::Banded3D{T}, B::Banded3D{T}) where {T} - -# M = size(A,1) -# N = size(A,2) - -# @assert M == size(B,1) -# @assert N == size(B,2) - -# Abw = size(A.data,1) -# Bbw = size(B.data,1) - -# # keep track of empty columns -# Az = findall(A.k0 .== 0) -# Bz = findall(B.k0 .== 0) - -# K0 = min.(A.k0, B.k0) -# K0[Az] = B.k0[Az] -# K0[Bz] = A.k0[Bz] - -# AK1 = A.k0 .+ (Abw - 1); AK1[Az] .= -1 -# BK1 = B.k0 .+ (Bbw - 1); BK1[Bz] .= -1 -# K1 = max.(AK1, BK1) - -# bw = maximum(K1 - K0 .+ 1) -# @assert bw > 0 -# @assert bw >= Abw -# @assert bw >= Bbw -# data = zeros(T, bw, M, N) -# for m in axes(A.data,2) -# for n in axes(A.data,3) -# k0 = K0[m,n] -# @assert k0 != 0 - -# Ak0 = A.k0[m,n] -# if Ak0 != 0 -# for Al in 1:Abw -# k = Ak0 + Al - 1 -# l = k - k0 + 1 -# data[l,m,n] += A.data[Al,m,n] -# end -# end - -# Bk0 = B.k0[m,n] -# if Bk0 != 0 -# for Bl in 1:Bbw -# k = Bk0 + Bl - 1 -# l = k - k0 + 1 -# l < 1 && @show m, n, k0, k, Bk0, Bl -# d = data[l,m,n] -# data[l,m,n] = d + B.data[Bl,m,n] -# end -# end -# end -# end - -# Banded3D(K0, data, max(A.maxk1, B.maxk1)) -# end - -# function BEAST.convolve(Z::SparseND.Banded3D,x,j,k_start) -# T = promote_type(eltype(Z), eltype(x)) -# M,N,L = size(Z) -# K = size(Z.data,1) -# @assert M == size(x,1) -# y = zeros(T,M) -# for n in 1:N -# for m in 1:M -# k0 = Z.k0[m,n] # k0 is 1-based -# l0 = max(1, k_start - k0 + 1) -# l1 = min(K, j - k0 + 1) -# for l in l0 : l1 -# k = k0 + l - 1 -# # j - k + 1 < 1 && break -# y[m] += Z.data[l,m,n] * x[n,j - k + 1] -# end -# end -# end -# return y -# end - +import ...BEAST struct MatrixOfConvolutions{T} <: AbstractArray{Vector{T},2} - banded::AbstractArray{T,3} + # banded::AbstractArray{T,3} + convop::BEAST.ConvOp{T} end function Base.eltype(x::MatrixOfConvolutions{T}) where {T} Vector{T} end -Base.size(x::MatrixOfConvolutions) = size(x.banded)[1:2] +Base.size(x::MatrixOfConvolutions) = size(x.convop)[1:2] function Base.getindex(x::MatrixOfConvolutions, m, n) - return x.banded[m,n,:] + # return x.banded[m,n,:] + return x.convop[m,n,:] end From f607242bd4b2a862daf12ab50e7267198fca5288 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 1 Apr 2021 10:29:48 +0100 Subject: [PATCH 108/528] TD assembly of systems uses BlockArrays --- examples/tdpmchwt.jl | 6 +++--- src/solvers/lusolver.jl | 3 ++- src/solvers/solver.jl | 6 ++++-- src/timedomain/motlu.jl | 12 ++---------- src/utils/mixedblockarrays.jl | 17 ++++++++++++++++- src/utils/sparsend.jl | 6 +++++- 6 files changed, 32 insertions(+), 18 deletions(-) diff --git a/examples/tdpmchwt.jl b/examples/tdpmchwt.jl index 326ec946..004228a2 100644 --- a/examples/tdpmchwt.jl +++ b/examples/tdpmchwt.jl @@ -1,6 +1,6 @@ -using Pkg -Pkg.activate(@__DIR__) -Pkg.instantiate() +# using Pkg +# Pkg.activate(@__DIR__) +# Pkg.instantiate() using CompScienceMeshes, BEAST using LinearAlgebra diff --git a/src/solvers/lusolver.jl b/src/solvers/lusolver.jl index fa040055..c5de7991 100644 --- a/src/solvers/lusolver.jl +++ b/src/solvers/lusolver.jl @@ -53,7 +53,8 @@ function timeslice(A::BlockArray, k) for i in 1:blocksize(A,1) for j in 1:blocksize(A,2) isassigned(A.blocks, i, j) || continue - # S[Block(i,j)] = A[Block(i,j)].banded[:,:,k] + A[Block(i,j)] isa Zeros && continue + A[Block(i,j)] isa Fill && continue S[Block(i,j)] = A[Block(i,j)].convop[:,:,k] end end diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 5bc0e05f..4ab8d3c4 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -192,8 +192,10 @@ function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict) I = [numfunctions(spatialbasis(test_space_dict[i])) for i in 1:length(bilform.test_space)] J = [numfunctions(spatialbasis(trial_space_dict[i])) for i in 1:length(bilform.trial_space)] - BT = SparseND.MatrixOfConvolutions{T} - Z = BlockArray(undef_blocks, BT, I, J) +# BT = SparseND.MatrixOfConvolutions{T} +# Z = BlockArray(undef_blocks, BT, I, J) + + Z = BlockArray{Vector{T}}(zero_blocks, I, J) # For each block, compute the interaction matrix for t in lhterms diff --git a/src/timedomain/motlu.jl b/src/timedomain/motlu.jl index cb26ce52..4c65653d 100644 --- a/src/timedomain/motlu.jl +++ b/src/timedomain/motlu.jl @@ -80,21 +80,13 @@ function convolve!(y,Z::BlockArray, x, csx, i, j_start, j_stop) ax1 = axes(Z,1) ax2 = axes(Z,2) T = eltype(eltype(Z)) - # y = PseudoBlockVector{T}(undef,blocklengths(axes(Z,1))) fill!(y,0) for I in blockaxes(Z,1) for J in blockaxes(Z,2) xJ = view(x, ax2[J], :) csxJ = view(csx, ax2[J], :) - try - ZIJ = Z[I,J].convop - # y[I] .+= convolve(ZIJ, xJ, i, j_start) - yI = view(y, ax1[I]) - convolve!(yI, ZIJ, xJ, csxJ, i, j_start, j_stop) - catch - @info "Skipping unassigned block." - continue - end + yI = view(y, ax1[I]) + convolve!(yI, Z[I,J], xJ, csxJ, i, j_start, j_stop) end end return y diff --git a/src/utils/mixedblockarrays.jl b/src/utils/mixedblockarrays.jl index 4f218286..cccdcea8 100644 --- a/src/utils/mixedblockarrays.jl +++ b/src/utils/mixedblockarrays.jl @@ -2,7 +2,8 @@ struct ZeroBlockInitializer end const zero_blocks = ZeroBlockInitializer() import Base.Cartesian: @nloops, @ntuple -import BlockArrays: BlockArray +import BlockArrays: BlockArray, undef_blocks, blockaxes +import FillArrays: Zeros, Fill """ Initialise a BlockArray where each block can have a different type. @@ -22,3 +23,17 @@ Initialise a BlockArray where each block can have a different type. return B end end + +@generated function BlockArray{T}(::ZeroBlockInitializer, blocksizes::Vararg{AbstractVector{Int},N}) where {T <: Array,N} + return quote + B = BlockArray{T,N,Array{AbstractArray{T,N},N}}(undef_blocks, blocksizes...) + axs = axes(B) + @nloops $N block dim->blockaxes(B,dim) begin + block_indices = @ntuple $N block + indices = getindex.(axs, block_indices) + block_size = length.(indices) + B[block_indices...] = Fill{T}(T(), block_size...) + end + return B + end +end diff --git a/src/utils/sparsend.jl b/src/utils/sparsend.jl index 191ff5f3..ae2ee957 100644 --- a/src/utils/sparsend.jl +++ b/src/utils/sparsend.jl @@ -1,6 +1,7 @@ module SparseND import ...BEAST +import FillArrays struct MatrixOfConvolutions{T} <: AbstractArray{Vector{T},2} # banded::AbstractArray{T,3} @@ -12,10 +13,13 @@ function Base.eltype(x::MatrixOfConvolutions{T}) where {T} end Base.size(x::MatrixOfConvolutions) = size(x.convop)[1:2] function Base.getindex(x::MatrixOfConvolutions, m, n) - # return x.banded[m,n,:] return x.convop[m,n,:] end +BEAST.convolve!(y, Z::MatrixOfConvolutions, x, csx, i, k_start, k_stop) = BEAST.convolve!(y, Z.convop, x, csx, i, k_start, k_stop) +BEAST.convolve!(y, Z::FillArrays.Zeros, x, csx, i, k_start, k_stop) = nothing +BEAST.convolve!(y, Z::FillArrays.Fill, x, csx, i, k_start, k_stop) = nothing + struct SpaceTimeData{T} <: AbstractArray{Vector{T},1} data::Array{T,2} From 7fe71f070f01f136c778b7e3e9abe45478eb014b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 12 Mar 2021 00:13:06 +0000 Subject: [PATCH 109/528] CompatHelper: bump compat for "BlockArrays" to "0.15" --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index dde12786..1fc2059e 100644 --- a/Project.toml +++ b/Project.toml @@ -23,7 +23,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] -BlockArrays = "^0.10, ^0.11, ^0.12, ^0.13, ^0.14" +BlockArrays = "^0.10, ^0.11, ^0.12, ^0.13, ^0.14, 0.15" CollisionDetection = "^0.1" Combinatorics = "^0.7, ^1" CompScienceMeshes = "^0.2.8" From bfb513c1f817e4bc02da06b335d43df6a76c4b1e Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 26 Apr 2021 16:15:27 +0200 Subject: [PATCH 110/528] left and right hand of varform can be constructed separately --- src/solvers/solver.jl | 58 ++++++++++++++++++++++++++++++++++++++++ src/utils/variational.jl | 2 +- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 4ab8d3c4..248b7085 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -8,6 +8,61 @@ mutable struct DiscreteEquation test_space_dict # dictionary mapping indices into test space to FE spaces end +struct DiscreteBilform + bilform + trial_space_dict # dictionary mapping indices into trial space to FE spaces + test_space_dict # dictionary mapping indices into test space to FE spaces +end + +struct DiscreteLinform + linform + test_space_dict +end + + +function discretise(bf::BilForm, space_mappings::Pair...) + trial_space_dict = Dict() + test_space_dict = Dict() + for sm in space_mappings + + found = false + sm.first.space == bf.trial_space && (dict = trial_space_dict; found = true) + sm.first.space == bf.test_space && (dict = test_space_dict; found = true) + @assert found "Vector $(sm.first) neither in test nor in trial space" + + @assert !haskey(dict, sm.first.idx) "multiple mappings for $(sm.first)" + dict[sm.first.idx] = sm.second + end + + # check that all symbols where mapped + for p in eachindex(bf.trial_space) @assert haskey(trial_space_dict,p) end + for p in eachindex(bf.test_space) @assert haskey(test_space_dict, p) end + + DiscreteBilform(bf, trial_space_dict, test_space_dict) +end + + +function discretise(lf::LinForm, space_mappings::Pair...) + # trial_space_dict = Dict() + test_space_dict = Dict() + for sm in space_mappings + + found = false + # sm.first.space == bf.trial_space && (dict = trial_space_dict; found = true) + sm.first.space == lf.test_space && (dict = test_space_dict; found = true) + @assert found "Vector $(sm.first) not found in test space" + + @assert !haskey(dict, sm.first.idx) "multiple mappings for $(sm.first)" + dict[sm.first.idx] = sm.second + end + + # check that all symbols where mapped + # for p in eachindex(bf.trial_space) @assert haskey(trial_space_dict,p) end + for p in eachindex(lf.test_space) @assert haskey(test_space_dict, p) end + + DiscreteLinform(lf, test_space_dict) +end + function discretise(eq, space_mappings::Pair...) trial_space_dict = Dict() @@ -54,6 +109,9 @@ end sysmatrix(eq::DiscreteEquation) = assemble(eq.equation.lhs, eq.test_space_dict, eq.trial_space_dict) rhs(eq::DiscreteEquation) = assemble(eq.equation.rhs, eq.test_space_dict) +assemble(dbf::DiscreteBilform) = assemble(dbf.bilform, dbf.test_space_dict, dbf.trial_space_dict) +assemble(dlf::DiscreteLinform) = assemble(dlf.linform, dlf.test_space_dict) + function assemble(lform::LinForm, test_space_dict) terms = lform.terms diff --git a/src/utils/variational.jl b/src/utils/variational.jl index 9888ea2f..eb16ebce 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -179,7 +179,7 @@ macro hilbertspace(syms...) $vars = $rhs end for (i,s) in enumerate(syms) - push!(xp.args, :(global $s = $vars[$i])) + push!(xp.args, :($(esc(s)) = $vars[$i])) end xp From 5f84fa40051a49d13b3c677d423f9b81d1e7781c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 30 Apr 2021 13:23:09 +0200 Subject: [PATCH 111/528] Arrays of hilbertspace generators can be declared --- examples/stgalerkin.jl | 3 + src/integralop.jl | 61 +------------------- src/maxwell/sauterschwabints_rt.jl | 5 +- src/utils/variational.jl | 93 +++++++++++++++++++++++------- 4 files changed, 81 insertions(+), 81 deletions(-) diff --git a/examples/stgalerkin.jl b/examples/stgalerkin.jl index aeb95319..d4079493 100644 --- a/examples/stgalerkin.jl +++ b/examples/stgalerkin.jl @@ -12,6 +12,9 @@ X = raviartthomas(Γ) T = timebasisc0d1(Δt, Nt) U = timebasiscxd0(Δt, Nt) +# T = timebasisshiftedlagrange(Δt, Nt, 2) +# U = timebasisdelta(Δt, Nt) + V = X ⊗ T W = X ⊗ U diff --git a/src/integralop.jl b/src/integralop.jl index 73ea92ce..2553f85c 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -82,6 +82,7 @@ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store) bshapes, bsis_elements, bad, qd, zlocal, store) else + @info "assemblechunk for nested meshes" assemblechunk_body_nested_meshes!(biop, tshapes, test_elements, tad, bshapes, bsis_elements, bad, @@ -354,63 +355,3 @@ end end end end end - -# mutable struct SauterSchwabStrategy -# hits::Int64 -# end -# function momintegrals!(biop, tshs::subReferenceSpace, bshs::subReferenceSpace, tcell, bcell, z, strat::SauterSchwabStrategy) -# -# A = biop.alpha -# k = biop.gamma -# -# M, N = size(z) -# -# hits = strat.hits -# print(hits) -# acc = 3 -# -# if hits == 0 -# ssm = PositiveDistance(acc) -# elseif hits == 1 -# ssm = CommonVertex(acc) -# elseif hits == 2 -# ssm = CommonEdge(acc) -# elseif hits == 3 -# ssm = CommonFace(acc) -# else -# error("hits can not exceed 3") -# end -# M= tcell.N -# N= bcell.N -# print("M = $(M) and N = $(N) \n") -# z = Array{Complex{Float64},2}(M,N) -# for i = 1:M -# for j = 1:N -# # print("i = $(i) and j = $(j) \n") -# function integrand(u,v) -# upt = neighborhood(tcell,u) -# vpt = neighborhood(bcell,v) -# tshape = shapefuns(upt) -# bshape = shapefuns(vpt) -# y = cartesian(upt) -# x = cartesian(vpt) -# kernel = A * exp(-complex(0,1)*k*norm(x-y))/(4.0*π*norm(x-y)) -# # kernel = kernelvals(biop, upt, vpt) -# ujac = jacobian(upt) -# vjac = jacobian(vpt) -# return tshape[i]*bshape[j]*kernel* ujac * vjac -# # return kernel* ujac * vjac -# end -# z[i,j] = (sauterschwab_parameterized(tcell,bcell,integrand,ssm)) -# end -# end -# -# return z -# end - - - -# mutable struct QuadData{WPV1,WPV2} -# tpoints::Matrix{Vector{WPV1}} -# bpoints::Matrix{Vector{WPV2}} -# end diff --git a/src/maxwell/sauterschwabints_rt.jl b/src/maxwell/sauterschwabints_rt.jl index 7095570f..1813e127 100644 --- a/src/maxwell/sauterschwabints_rt.jl +++ b/src/maxwell/sauterschwabints_rt.jl @@ -131,11 +131,14 @@ function momintegrals_nested!(op::MWOperator3D, # 1. Refine the trial_chart p1, p2, p3 = trial_chart.vertices - e1 = cartesian(neighborhood(trial_chart, (0,1/2))) + # TODO: generalise this to include more general refinements + e1 = cartesian(neighborhood(trial_chart, (0,1/2))) e2 = cartesian(neighborhood(trial_chart, (1/2,0))) e3 = cartesian(neighborhood(trial_chart, (1/2,1/2))) + ct = cartesian(center(trial_chart)) + refined_trial_chart = [ simplex(ct, p1, e3), simplex(ct, e3, p2), diff --git a/src/utils/variational.jl b/src/utils/variational.jl index eb16ebce..e10aebb7 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -155,36 +155,89 @@ Build an equation from a left hand and right hand side -""" - hilbert_space(type, g1, g2, ...) +# """ +# hilbert_space(type, g1, g2, ...) -Returns generators defining a Hilbert space of field `type` -""" -hilbertspace(vars::Symbol...) = [HilbertVector(i, [vars...], []) for i in 1:length(vars)] +# Returns generators defining a Hilbert space of field `type` +# """ +# hilbertspace(vars::Symbol...) = [HilbertVector(i, [vars...], []) for i in 1:length(vars)] +function genspace(syms...) -macro hilbertspace(syms...) - + space = Vector{Symbol}() + lengths = Int[] + starts = Int[] + stops = Int[] for sym in syms - @assert isa(sym, Symbol) "@hilbertspace takes a list of Symbols" - end - rhs = :(hilbertspace()) - for sym in syms - push!(rhs.args, QuoteNode(sym)) + if sym isa Symbol + push!(space, sym) + push!(lengths,1) + push!(starts,1) + push!(stops,1) + elseif sym isa Expr && sym.head == :ref + base = sym.args[1] + start = sym.args[2].args[2] + stop = sym.args[2].args[3] + for k in start:stop + sym = Symbol(base,k) + push!(space, sym) + end + push!(lengths,stop-start+1) + push!(starts,start) + push!(stops,stop) + end end + + return space, starts, stops +end - vars = gensym() - xp = quote - $vars = $rhs - end - for (i,s) in enumerate(syms) - push!(xp.args, :($(esc(s)) = $vars[$i])) - end +macro hilbertspace(syms...) + + space, starts, stops = genspace(syms...) + + ex = quote end + k = 1 + for (s, (start,stop)) in enumerate(zip(starts,stops)) + + + len = stop-start+1 + if len == 1 + sym = syms[s] + push!(ex.args, :($(esc(sym)) = HilbertVector($k,$space,[]))) + k += 1 + else + sym = syms[s].args[1] + push!(ex.args, :($(esc(sym)) = [HilbertVector(i,$space,[]) for i in $k:$(k+len-1)])) + k += len + end - xp + end + return ex end +# macro hilbertspace(syms...) + +# for sym in syms +# @assert isa(sym, Symbol) "@hilbertspace takes a list of Symbols" +# end + +# rhs = :(hilbertspace()) +# for sym in syms +# push!(rhs.args, QuoteNode(sym)) +# end + +# vars = gensym() +# xp = quote +# $vars = $rhs +# end +# for (i,s) in enumerate(syms) +# push!(xp.args, :($(esc(s)) = $vars[$i])) +# end + +# xp +# end + """ call(u::HilbertVector, f, params...) From b98e23d751c332a233c0ebfffd254ca2378391f6 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 2 Jun 2021 10:21:45 +0200 Subject: [PATCH 112/528] GMRESSolver isa LinearMap --- Project.toml | 4 +++- src/BEAST.jl | 2 +- src/solvers/itsolver.jl | 19 ++++++++++++++----- src/timedomain/convop.jl | 2 +- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Project.toml b/Project.toml index 1fc2059e..b36f3353 100644 --- a/Project.toml +++ b/Project.toml @@ -14,6 +14,7 @@ FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" @@ -32,7 +33,8 @@ FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" FillArrays = "0.11" IterativeSolvers = "^0.9" -SauterSchwabQuadrature = "^2.1.1" +LinearMaps = "^3.3" +SauterSchwabQuadrature = "^2.1.1, 2.1" SparseMatrixDicts = "0.2" SpecialFunctions = "^0.7, ^0.8, ^0.9, ^0.10, ^1" StaticArrays = "^0.8.3, ^0.9, ^0.10, ^0.11, ^0.12, ^1" diff --git a/src/BEAST.jl b/src/BEAST.jl index 46e380a8..9aaa9d63 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -2,7 +2,6 @@ module BEAST using Distributed using LinearAlgebra -# using Pkg using SharedArrays using SparseArrays using FillArrays @@ -11,6 +10,7 @@ using SparseMatrixDicts using SauterSchwabQuadrature using FastGaussQuadrature +using LinearMaps import LinearAlgebra: cross, dot import LinearAlgebra: ×, ⋅ diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index 77f2ecbb..2d98c303 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -3,11 +3,11 @@ import IterativeSolvers -struct GMRESSolver{L,R} +struct GMRESSolver{L,T} <: LinearMap{T} linear_operator::L maxiter::Int restart::Int - tol::R + tol::T end @@ -34,11 +34,20 @@ function solve(solver::GMRESSolver, b) end -function Base.:*(solver::GMRESSolver, b) - x, ch = solve(solver, b) +# function Base.:*(solver::GMRESSolver, b) +# x, ch = solve(solver, b) +# println("Number of iterations: ", ch.iters) +# ch.isconverged || error("Iterative solver did not converge.") +# return x +# end + +Base.size(solver::GMRESSolver) = reverse(size(solver.linear_operator)) + +function LinearAlgebra.mul!(y::AbstractVecOrMat, solver::GMRESSolver, x::AbstractVector) + temp, ch = solve(solver, x) println("Number of iterations: ", ch.iters) ch.isconverged || error("Iterative solver did not converge.") - return x + y .= temp end diff --git a/src/timedomain/convop.jl b/src/timedomain/convop.jl index 25ae7740..57a613e0 100644 --- a/src/timedomain/convop.jl +++ b/src/timedomain/convop.jl @@ -6,7 +6,7 @@ struct ConvOp{T} <: AbstractArray{T,3} length::Int end -function Base.size(obj) +function Base.size(obj::ConvOp) return (size(obj.data)[2:3]...,obj.length) end From 5cc375eceac5594990c226ae8370ffa6c8dc299d Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Thu, 6 May 2021 23:29:17 +0200 Subject: [PATCH 113/528] Added electric and magnetic dipole excitation This adds an electric and magnetic dipole excitation. --- src/BEAST.jl | 2 + src/maxwell/mwexc.jl | 167 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) diff --git a/src/BEAST.jl b/src/BEAST.jl index 9aaa9d63..14ebcb7f 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -56,6 +56,8 @@ export MWFarField3D export MWSingleLayer3D, MWHyperSingular, MWWeaklySingular export MWDoubleLayer3D export PlaneWaveMW +export electricdipole, ElectricDipole +export magneticdipole, MagneticDipole export TangTraceMW, CrossTraceMW export curl export MWSingleLayerField3D diff --git a/src/maxwell/mwexc.jl b/src/maxwell/mwexc.jl index cb22aa47..6f58119a 100644 --- a/src/maxwell/mwexc.jl +++ b/src/maxwell/mwexc.jl @@ -43,6 +43,172 @@ end *(a::Number, e::PlaneWaveMW) = PlaneWaveMW(e.direction, e.polarisation, e.wavenumber, a*e.amplitude) +abstract type Dipole end + +mutable struct ElectricDipole{T,P} <: Dipole + location::P + orientation::P + wavenumber::T + ε::T + μ::T +end + +function ElectricDipole(l,o,k,ε,μ) + T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) + P = similar_type(typeof(l), T) + ElectricDipole{T,P}(l,o,k,ε,μ) +end + +mutable struct curlElectricDipole{T,P} <: Dipole + location::P + orientation::P + wavenumber::T + ε::T + μ::T +end + +function curlElectricDipole(l,o,k,ε,μ) + T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) + P = similar_type(typeof(l), T) + curlElectricDipole{T,P}(l,o,k,ε,μ) +end + +mutable struct MagneticDipole{T,P} <: Dipole + location::P + orientation::P + wavenumber::T + ε::T + μ::T +end + +function MagneticDipole(l,o,k,ε,μ) + T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) + P = similar_type(typeof(l), T) + MagneticDipole{T,P}(l,o,k,ε,μ) +end + +mutable struct curlMagneticDipole{T,P} <: Dipole + location::P + orientation::P + wavenumber::T + ε::T + μ::T +end + +function curlMagneticDipole(l,o,k,ε,μ) + T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) + P = similar_type(typeof(l), T) + curlMagneticDipole{T,P}(l,o,k,ε,μ) +end + +""" + electricdipole(;location, orientation, wavenumber, permittivity, permeability) + +Create an electric dipole solution to Maxwell's equations representing the electric +field part. Implementation is based on (9.18) of Jackson's “Classical electrodynamics”, +with the notable difference that the ``\exp(ikr)`` is used. +""" +electricdipole(; + location = error("missing arguement `location`"), + orientation = error("missing arguement `orientation`"), + wavenumber = error("missing arguement `wavenumber`"), + permittivity = error("missing arguement `permittivity`"), + permeability = error("missing arguement `permeability`")) = + ElectricDipole(location, orientation, wavenumber, permittivity, permeability) + +function (hd::ElectricDipole)(x; isfarfield=false) + k = hd.wavenumber + x_0 = hd.location + p = hd.orientation + r = norm(x-x_0) + n = (x - x_0)/r + η = sqrt(hd.μ/hd.ε) + if isfarfield + # postfactor (4*π*im)/k to be consistent with BEAST far field computation + # and, of course, omitted exp(-im*k*r)/r factor in (9.19) + return η*k^2/(4*π*sqrt(hd.ε*hd.μ))*cross(cross(n,p),n)*(4*π*im)/k + else + return (1/(4*π*hd.ε))*exp(-im*k*r)*(k^2/r*cross(cross(n,p),n) + (1/r^3 + im*k/r^2)*(3*n*dot(n,p) - p)) + end +end + +function (hd::curlElectricDipole)(x; isfarfield=false) + k = hd.wavenumber + x_0 = hd.location + p = hd.orientation + r = norm(x-x_0) + n = (x - x_0)/r + c = 1/sqrt(hd.ε*hd.μ) + if isfarfield + # prefactor (-im*hd.μ*c*k), because this is the curl + # postfactor (4*π*im)/k to be consistent with BEAST far field computation + return (-im*hd.μ*c*k)*k^2/(4*π*sqrt(hd.ε*hd.μ))*cross(n,p)*(4*π*im)/k + else + #return (k^2/(4*π*sqrt(hd.ε*hd.μ)))*cross(n,p)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) + return -im*hd.μ*c^2*(k^3)/(4*π)*cross(n,p)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) + end +end + +function curl(ehd::ElectricDipole) + return curlElectricDipole(ehd.location,ehd.orientation,ehd.wavenumber,ehd.ε,ehd.μ) +end + +""" + magneticdipole(;location, orientation, wavenumber, permittivity, permeability) + +Create a magnetic dipole solution to Maxwell's equations representing the electric +field part. Implementation is based on (9.36) of Jackson's “Classical electrodynamics”, +with the notable difference that the ``\exp(ikr)`` is used. +""" +magneticdipole(; + location = error("missing arguement `location`"), + orientation = error("missing arguement `orientation`"), + wavenumber = error("missing arguement `wavenumber`"), + permittivity = error("missing arguement `permittivity`"), + permeability = error("missing arguement `permeability`")) = + MagneticDipole(location, orientation, wavenumber, permittivity, permeability) + +function (hd::MagneticDipole)(x; isfarfield=false) + k = hd.wavenumber + x_0 = hd.location + m = hd.orientation + r = norm(x-x_0) + n = (x - x_0)/r + η = sqrt(hd.μ/hd.ε) + if isfarfield + # Jackson (9.36) without exp(-im*k*r)/r, r → ∞ and with (im*4*π)/k to match BEAST's farfield + return -η/(4*π)*k^2*cross(n,m)*(im*4*π)/k + else + # Jackson (9.36) + return -η/(4*π)*k^2*cross(n,m)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) + end +end + +function (hd::curlMagneticDipole)(x; isfarfield=false) + k = hd.wavenumber + x_0 = hd.location + m = hd.orientation + r = norm(x-x_0) + n = (x - x_0)/r + c = 1/sqrt(hd.ε*hd.μ) + if isfarfield + # Jackson (9.35) without exp(-im*k*r)/r, r → ∞ and with (im*4*π)/k to match BEAST's farfield + return -im*hd.μ*c*k/(4π)*(k^2*cross(cross(n,m),n))*(im*4*π)/k + else + # Jackson (9.35) + return -im*hd.μ*c*k/(4π)*exp(-im*k*r)*(k^2*cross(cross(n,m),n)/r + (3*n*dot(n,m)-m)*(1/r^3 + im*k/r^2)) + end +end + +function curl(ehd::MagneticDipole) + return curlMagneticDipole(ehd.location,ehd.orientation,ehd.wavenumber,ehd.ε,ehd.μ) +end + +*(a::Number, e::ElectricDipole) = ElectricDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) +*(a::Number, e::curlElectricDipole) = curlElectricDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) +*(a::Number, e::MagneticDipole) = MagneticDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) +*(a::Number, e::curlMagneticDipole) = curlMagneticDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) + mutable struct CrossTraceMW{F} <: Functional field::F end @@ -53,6 +219,7 @@ end cross(::NormalVector, p::Function) = CrossTraceMW(p) cross(::NormalVector, p::PlaneWaveMW) = CrossTraceMW(p) +cross(::NormalVector, p::Dipole) = CrossTraceMW(p) cross(t::CrossTraceMW, ::NormalVector) = TangTraceMW(t.field) function (ϕ::CrossTraceMW)(p) From 4563a6abff5fd3d962ce1189b5c21848485e3850 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Fri, 7 May 2021 20:37:30 +0200 Subject: [PATCH 114/528] Test for dipole excitation The test is an example of using an dipole as a manufactured solution, i.e., we have an analytical solution for arbitrary geometries. --- test/runtests.jl | 2 + test/test_dipole.jl | 106 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 test/test_dipole.jl diff --git a/test/runtests.jl b/test/runtests.jl index 6bb6afe2..7bab9663 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -46,6 +46,8 @@ include("test_laminated.jl") include("test_assemblerow.jl") include("test_local_assembly.jl") +include("test_dipole.jl") + include("test_wiltonints.jl") include("test_sauterschwabints.jl") include("test_ss_nested_meshes.jl") diff --git a/test/test_dipole.jl b/test/test_dipole.jl new file mode 100644 index 00000000..b58f6dd3 --- /dev/null +++ b/test/test_dipole.jl @@ -0,0 +1,106 @@ +using Test + +using CompScienceMeshes +using BEAST +using StaticArrays +using LinearAlgebra + +c = 3e8 +μ = 4*π*1e-7 +ε = 1/(μ*c^2) +f = 1e8 +λ = c/f +k = 2*π/λ +ω = k*c +η = sqrt(μ/ε) + +a = 1 +Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.3) +Γ = translate(Γ_orig,SVector(-a/2,-a/2,-a/2)) + +Φ, Θ = [0.0], range(0,stop=π,length=100) +pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] + +E = electricdipole(location=SVector(0,0,0), + orientation=1e-9.*SVector(0.5,0.5,0), + wavenumber=k, + permittivity=ε, + permeability=μ) + +n = BEAST.NormalVector() + +𝒆 = (n × E) × n +H = (-1/(im*μ*ω))*curl(E) +𝒉 = (n × H) × n + +𝓣 = Maxwell3D.singlelayer(wavenumber=k) +𝓝 = BEAST.NCross() +𝓚 = Maxwell3D.doublelayer(wavenumber=k) + +X = raviartthomas(Γ) +Y = buffachristiansen(Γ) + +T = Matrix(assemble(𝓣,X,X)) +e = Vector(assemble(𝒆,X)) +j_EFIE = T\e + +nf_E_EFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_EFIE, X) +nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_EFIE, X) ./ η +ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X) + +@test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 +@test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 +@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.001 + +K_bc = Matrix(assemble(𝓚,Y,X)) +G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) +h_bc = η*Vector(assemble(𝒉,Y)) +M_bc = -0.5*G_nxbc_rt + K_bc +j_BCMFIE = M_bc\h_bc + +nf_E_BCMFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) +nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) ./ η +ff_E_BCMFIE = potential(MWFarField3D(wavenumber=k), pts, j_BCMFIE, X) + +@test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 +@test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 +@test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 + +E = magneticdipole(location=SVector(0,0,0), + orientation=1e-9.*SVector(0.5,0.5,0), + wavenumber=k, + permittivity=ε, + permeability=μ) + +𝒆 = (n × E) × n +H = (-1/(im*μ*ω))*curl(E) +𝒉 = (n × H) × n + +X = raviartthomas(Γ) +Y = buffachristiansen(Γ) + +T = Matrix(assemble(𝓣,X,X)) +e = Vector(assemble(𝒆,X)) +j_EFIE = T\e + +nf_E_EFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_EFIE, X) +nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_EFIE, X) ./ η +ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X) + +@test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 +@test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 +@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 + +K_bc = Matrix(assemble(𝓚,Y,X)) +G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) +h_bc = η*Vector(assemble(𝒉,Y)) +M_bc = -0.5*G_nxbc_rt + K_bc +j_BCMFIE = M_bc\h_bc + +nf_E_BCMFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) +nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) ./ η +ff_E_BCMFIE = potential(MWFarField3D(wavenumber=k), pts, j_BCMFIE, X) + +@test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 +@test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 +@test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 \ No newline at end of file From 2125c149447dd6e0d95e368359c7053061d96627 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Mon, 31 May 2021 17:23:12 +0200 Subject: [PATCH 115/528] Removed physical units from dipole definition Instead of distinguishing between an electric and a magnetic dipole there exists now a single data type DipoleMW (and curlDipoleMW) This reduces the code duplication and resembles more closely the PlaneWaveMW type The test "test_dipole.jl" shows how to obtain an electric and a magnetic dipole corresponding to the formulas in Jackson's Classical Electrodynamics --- src/BEAST.jl | 3 +- src/maxwell/mwexc.jl | 151 +++++++++---------------------------------- test/test_dipole.jl | 29 ++++----- 3 files changed, 46 insertions(+), 137 deletions(-) diff --git a/src/BEAST.jl b/src/BEAST.jl index 14ebcb7f..aa598b09 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -56,8 +56,7 @@ export MWFarField3D export MWSingleLayer3D, MWHyperSingular, MWWeaklySingular export MWDoubleLayer3D export PlaneWaveMW -export electricdipole, ElectricDipole -export magneticdipole, MagneticDipole +export dipolemw3d, DipoleMW export TangTraceMW, CrossTraceMW export curl export MWSingleLayerField3D diff --git a/src/maxwell/mwexc.jl b/src/maxwell/mwexc.jl index 6f58119a..e6e7bda9 100644 --- a/src/maxwell/mwexc.jl +++ b/src/maxwell/mwexc.jl @@ -45,169 +45,80 @@ end abstract type Dipole end -mutable struct ElectricDipole{T,P} <: Dipole +mutable struct DipoleMW{T,P} <: Dipole location::P orientation::P wavenumber::T - ε::T - μ::T end -function ElectricDipole(l,o,k,ε,μ) - T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) +function DipoleMW(l,o,k) + T = promote_type(eltype(l), eltype(o), typeof(k)) P = similar_type(typeof(l), T) - ElectricDipole{T,P}(l,o,k,ε,μ) + DipoleMW{T,P}(l,o,k) end -mutable struct curlElectricDipole{T,P} <: Dipole +mutable struct curlDipoleMW{T,P} <: Dipole location::P orientation::P wavenumber::T - ε::T - μ::T end -function curlElectricDipole(l,o,k,ε,μ) - T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) +function curlDipoleMW(l,o,k) + T = promote_type(eltype(l), eltype(o), typeof(k)) P = similar_type(typeof(l), T) - curlElectricDipole{T,P}(l,o,k,ε,μ) -end - -mutable struct MagneticDipole{T,P} <: Dipole - location::P - orientation::P - wavenumber::T - ε::T - μ::T -end - -function MagneticDipole(l,o,k,ε,μ) - T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) - P = similar_type(typeof(l), T) - MagneticDipole{T,P}(l,o,k,ε,μ) -end - -mutable struct curlMagneticDipole{T,P} <: Dipole - location::P - orientation::P - wavenumber::T - ε::T - μ::T -end - -function curlMagneticDipole(l,o,k,ε,μ) - T = promote_type(eltype(l), eltype(o), typeof(k), typeof(ε), typeof(μ)) - P = similar_type(typeof(l), T) - curlMagneticDipole{T,P}(l,o,k,ε,μ) + curlDipoleMW{T,P}(l,o,k) end """ - electricdipole(;location, orientation, wavenumber, permittivity, permeability) + dipolemw3d(;location, orientation, wavenumber) Create an electric dipole solution to Maxwell's equations representing the electric field part. Implementation is based on (9.18) of Jackson's “Classical electrodynamics”, with the notable difference that the ``\exp(ikr)`` is used. """ -electricdipole(; +dipolemw3d(; location = error("missing arguement `location`"), orientation = error("missing arguement `orientation`"), wavenumber = error("missing arguement `wavenumber`"), - permittivity = error("missing arguement `permittivity`"), - permeability = error("missing arguement `permeability`")) = - ElectricDipole(location, orientation, wavenumber, permittivity, permeability) - -function (hd::ElectricDipole)(x; isfarfield=false) - k = hd.wavenumber - x_0 = hd.location - p = hd.orientation + ) = DipoleMW(location, orientation, wavenumber) + +function (d::DipoleMW)(x; isfarfield=false) + k = d.wavenumber + x_0 = d.location + p = d.orientation r = norm(x-x_0) - n = (x - x_0)/r - η = sqrt(hd.μ/hd.ε) + n = (x - x_0)/r if isfarfield # postfactor (4*π*im)/k to be consistent with BEAST far field computation # and, of course, omitted exp(-im*k*r)/r factor in (9.19) - return η*k^2/(4*π*sqrt(hd.ε*hd.μ))*cross(cross(n,p),n)*(4*π*im)/k + # of Jackson's Classical Electrodynamics + return k^2/(4*π)*cross(cross(n,p),n)*(4*π*im)/k else - return (1/(4*π*hd.ε))*exp(-im*k*r)*(k^2/r*cross(cross(n,p),n) + (1/r^3 + im*k/r^2)*(3*n*dot(n,p) - p)) + return 1/(4*π)*exp(-im*k*r)*(k^2/r*cross(cross(n,p),n) + + (1/r^3 + im*k/r^2)*(3*n*dot(n,p) - p)) end end -function (hd::curlElectricDipole)(x; isfarfield=false) - k = hd.wavenumber - x_0 = hd.location - p = hd.orientation +function (d::curlDipoleMW)(x; isfarfield=false) + k = d.wavenumber + x_0 = d.location + p = d.orientation r = norm(x-x_0) n = (x - x_0)/r - c = 1/sqrt(hd.ε*hd.μ) if isfarfield - # prefactor (-im*hd.μ*c*k), because this is the curl # postfactor (4*π*im)/k to be consistent with BEAST far field computation - return (-im*hd.μ*c*k)*k^2/(4*π*sqrt(hd.ε*hd.μ))*cross(n,p)*(4*π*im)/k - else - #return (k^2/(4*π*sqrt(hd.ε*hd.μ)))*cross(n,p)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) - return -im*hd.μ*c^2*(k^3)/(4*π)*cross(n,p)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) - end -end - -function curl(ehd::ElectricDipole) - return curlElectricDipole(ehd.location,ehd.orientation,ehd.wavenumber,ehd.ε,ehd.μ) -end - -""" - magneticdipole(;location, orientation, wavenumber, permittivity, permeability) - -Create a magnetic dipole solution to Maxwell's equations representing the electric -field part. Implementation is based on (9.36) of Jackson's “Classical electrodynamics”, -with the notable difference that the ``\exp(ikr)`` is used. -""" -magneticdipole(; - location = error("missing arguement `location`"), - orientation = error("missing arguement `orientation`"), - wavenumber = error("missing arguement `wavenumber`"), - permittivity = error("missing arguement `permittivity`"), - permeability = error("missing arguement `permeability`")) = - MagneticDipole(location, orientation, wavenumber, permittivity, permeability) - -function (hd::MagneticDipole)(x; isfarfield=false) - k = hd.wavenumber - x_0 = hd.location - m = hd.orientation - r = norm(x-x_0) - n = (x - x_0)/r - η = sqrt(hd.μ/hd.ε) - if isfarfield - # Jackson (9.36) without exp(-im*k*r)/r, r → ∞ and with (im*4*π)/k to match BEAST's farfield - return -η/(4*π)*k^2*cross(n,m)*(im*4*π)/k - else - # Jackson (9.36) - return -η/(4*π)*k^2*cross(n,m)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) - end -end - -function (hd::curlMagneticDipole)(x; isfarfield=false) - k = hd.wavenumber - x_0 = hd.location - m = hd.orientation - r = norm(x-x_0) - n = (x - x_0)/r - c = 1/sqrt(hd.ε*hd.μ) - if isfarfield - # Jackson (9.35) without exp(-im*k*r)/r, r → ∞ and with (im*4*π)/k to match BEAST's farfield - return -im*hd.μ*c*k/(4π)*(k^2*cross(cross(n,m),n))*(im*4*π)/k + return (-im*k)*k^2/(4*π)*cross(n,p)*(4*π*im)/k else - # Jackson (9.35) - return -im*hd.μ*c*k/(4π)*exp(-im*k*r)*(k^2*cross(cross(n,m),n)/r + (3*n*dot(n,m)-m)*(1/r^3 + im*k/r^2)) + return -im*(k^3)/(4*π)*cross(n,p)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) end end -function curl(ehd::MagneticDipole) - return curlMagneticDipole(ehd.location,ehd.orientation,ehd.wavenumber,ehd.ε,ehd.μ) +function curl(d::DipoleMW) + return curlDipoleMW(d.location, d.orientation, d.wavenumber) end -*(a::Number, e::ElectricDipole) = ElectricDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) -*(a::Number, e::curlElectricDipole) = curlElectricDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) -*(a::Number, e::MagneticDipole) = MagneticDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) -*(a::Number, e::curlMagneticDipole) = curlMagneticDipole(e.location, a .* e.orientation, e.wavenumber,e.ε,e.μ) +*(a::Number, d::DipoleMW) = DipoleMW(d.location, a .* d.orientation, d.wavenumber) +*(a::Number, d::curlDipoleMW) = curlDipoleMW(d.location, a .* d.orientation, d.wavenumber) mutable struct CrossTraceMW{F} <: Functional field::F diff --git a/test/test_dipole.jl b/test/test_dipole.jl index b58f6dd3..4e2641a1 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -8,24 +8,25 @@ using LinearAlgebra c = 3e8 μ = 4*π*1e-7 ε = 1/(μ*c^2) -f = 1e8 +f = 5e7 λ = c/f k = 2*π/λ ω = k*c η = sqrt(μ/ε) a = 1 -Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.3) +Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.2) Γ = translate(Γ_orig,SVector(-a/2,-a/2,-a/2)) Φ, Θ = [0.0], range(0,stop=π,length=100) pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] -E = electricdipole(location=SVector(0,0,0), - orientation=1e-9.*SVector(0.5,0.5,0), - wavenumber=k, - permittivity=ε, - permeability=μ) +# This is an electric dipole +# The pre-factor (1/ε) is used to resemble +# (9.18) in Jackson's Classical Electrodynamics +E = (1/ε) * dipolemw3d(location=SVector(0,0,0), + orientation=1e-9.*SVector(0.5,0.5,0), + wavenumber=k) n = BEAST.NormalVector() @@ -50,7 +51,7 @@ ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X) @test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 @test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 -@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.001 +@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 K_bc = Matrix(assemble(𝓚,Y,X)) G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) @@ -66,15 +67,13 @@ ff_E_BCMFIE = potential(MWFarField3D(wavenumber=k), pts, j_BCMFIE, X) @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 -E = magneticdipole(location=SVector(0,0,0), - orientation=1e-9.*SVector(0.5,0.5,0), - wavenumber=k, - permittivity=ε, - permeability=μ) +H = dipolemw3d(location=SVector(0,0,0), + orientation=1e-9.*SVector(0.5,0.5,0), + wavenumber=k) -𝒆 = (n × E) × n -H = (-1/(im*μ*ω))*curl(E) 𝒉 = (n × H) × n +E = (1/(im*ε*ω))*curl(H) +𝒆 = (n × E) × n X = raviartthomas(Γ) Y = buffachristiansen(Γ) From 383ed56ef0a31815cab40ce7963ca1d3cfca0f67 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 11 Jun 2021 15:39:02 +0200 Subject: [PATCH 116/528] facecurrents fixed for scalar values FEM spaces --- examples/helmholtz3d_neumann.jl | 10 ++++++++-- src/postproc.jl | 6 ++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/helmholtz3d_neumann.jl b/examples/helmholtz3d_neumann.jl index 9d14db8a..fe6fc58f 100644 --- a/examples/helmholtz3d_neumann.jl +++ b/examples/helmholtz3d_neumann.jl @@ -1,6 +1,9 @@ using CompScienceMeshes, BEAST +using LinearAlgebra, Pkg -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Pkg.activate(@__DIR__) +# Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) X = lagrangec0d1(Γ) @show numfunctions(X) @@ -26,7 +29,10 @@ fcr1, geo1 = facecurrents(x1, X) fcr2, geo2 = facecurrents(x2, X) using Plots -using LinearAlgebra plot(title="Comparse 1st and 2nd kind eqs.") plot!(norm.(fcr1),c=:blue,label="1st") scatter!(norm.(fcr2),c=:red,label="2nd") + +import Plotly +Plotly.plot(patch(Γ, norm.(fcr1))) +Plotly.plot(patch(Γ, norm.(fcr2))) \ No newline at end of file diff --git a/src/postproc.jl b/src/postproc.jl index 3e09edc5..0ee1907a 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -20,8 +20,10 @@ function facecurrents(coeffs, basis) # U = D+1 U = 3 - # TODO: express relative to input types - PT = SVector{U, T} + # TODO: remove ugliness + vals = refs(center(first(cells))) + PT = typeof(first(coeffs)*vals[1][1]) + fcr = zeros(PT, numcells(mesh)) for (t,cell) in enumerate(cells) From c568c2cbae74b40b4469d9c02f85953ea60a9077 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 20 Jun 2021 00:22:55 +0000 Subject: [PATCH 117/528] CompatHelper: bump compat for "BlockArrays" to "0.16" --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index b36f3353..555b3aa6 100644 --- a/Project.toml +++ b/Project.toml @@ -24,7 +24,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] -BlockArrays = "^0.10, ^0.11, ^0.12, ^0.13, ^0.14, 0.15" +BlockArrays = "^0.10, ^0.11, ^0.12, ^0.13, ^0.14, 0.15, 0.16" CollisionDetection = "^0.1" Combinatorics = "^0.7, ^1" CompScienceMeshes = "^0.2.8" From c2d25a9a6dce661975e33b08e35a97e540eda9fb Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 7 Jul 2021 17:05:03 +0200 Subject: [PATCH 118/528] iterative solvers log convergence --- examples/mthelmholtz.jl | 26 -------------------- examples/mtjunction.jl | 54 ++++++++++++++++++++++++++++++++++++++--- src/solvers/itsolver.jl | 2 +- 3 files changed, 51 insertions(+), 31 deletions(-) diff --git a/examples/mthelmholtz.jl b/examples/mthelmholtz.jl index 17e2b26b..cbd30b06 100644 --- a/examples/mthelmholtz.jl +++ b/examples/mthelmholtz.jl @@ -73,30 +73,4 @@ using Plots plot() scatter!(w1) scatter!(w2) -# u = gmres(mtefie) -# -# offset = 1; u12 = u[offset:offset+numfunctions(X12)-1] -# offset += numfunctions(X12); u23 = u[offset:offset+numfunctions(X23)-1] -# offset += numfunctions(X23); u31 = u[offset:offset+numfunctions(X31)-1] -# -# fcr12, _ = facecurrents(u12, X12) -# fcr23, _ = facecurrents(u23, X23) -# fcr31, _ = facecurrents(u31, X31) - -# import PlotlyJS -using LinearAlgebra -# p1 = PlotlyJS.Plot(patch(G12, norm.(fcr12))) -# p2 = PlotlyJS.Plot(patch(G23, norm.(fcr23))) -# p3 = PlotlyJS.Plot(patch(G31, norm.(fcr31))) - -# PlotlyJS.plot([p1, p2, p3]) - - - - -Sxx = BEAST.sysmatrix(mtefie) - -Syy = assemble(SL, Y, Y) -iNxy = inv(Nxy) -# gmres() diff --git a/examples/mtjunction.jl b/examples/mtjunction.jl index ea35748a..1b207ee2 100644 --- a/examples/mtjunction.jl +++ b/examples/mtjunction.jl @@ -43,7 +43,7 @@ mtefie = @discretise( # fcr23, _ = facecurrents(u23, X23) # fcr31, _ = facecurrents(u31, X31) -import PlotlyJS +import Plotly using LinearAlgebra # p1 = PlotlyJS.Plot(patch(G12, norm.(fcr12))) # p2 = PlotlyJS.Plot(patch(G23, norm.(fcr23))) @@ -76,10 +76,11 @@ Nxy = blkdiagm(N1,N2,N3) Syy = assemble(SL, Y, Y) iNxy = inv(Nxy) -cond(Sxx) +# cond(Matrix(Sxx)) ex = assemble(e,X) -Q = transpose(iNxy) * Syy * iNxy * Sxx; -R = transpose(iNxy) * Syy * iNxy * ex; +P = transpose(iNxy) * Syy * iNxy +Q = P * Sxx; +R = P * ex; u1, ch1 = solve(BEAST.GMRESSolver(Sxx),ex) @@ -88,3 +89,48 @@ u2, ch2 = solve(BEAST.GMRESSolver(Q),R) @show ch1.iters @show ch2.iters # gmres() + +ns = [ + 0, + numfunctions(X12), + numfunctions(X23), + numfunctions(X31)] + +cns = cumsum(ns) +u12 = u1[cns[1]+1:cns[2]] +u23 = u1[cns[2]+1:cns[3]] +u31 = u1[cns[3]+1:cns[4]] + +fcr1, geo1 = facecurrents(u12, X12) +fcr2, geo2 = facecurrents(u23, X23) +fcr3, geo3 = facecurrents(u31, X31) + +p1 = patch(geo1, norm.(fcr1)) +p2 = patch(geo2, norm.(fcr2)) +p3 = patch(geo3, norm.(fcr3)) + +Plotly.plot([p1,p2,p3]) + +G123 = weld(G1,G2,G3) +X123 = raviartthomas(G123) + +stefie = @discretise( + SL[k,j] == e[k], + j ∈ X123, k ∈ X123) + +Sst = assemble(SL, X123, X123) +bst = assemble(e, X123) +ust, chst = solve(BEAST.GMRESSolver(Sst),bst) + +fcrst, geost = facecurrents(ust, X123) +Plotly.plot(patch(geost, norm.(fcrst))) + + +Φ, Θ = [0.0], range(0,stop=π,length=100) +pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] + +ffd_mt = potential(MWFarField3D(wavenumber=κ), pts, u1, X) +ffd_st = potential(MWFarField3D(wavenumber=κ), pts, ust, X123) + +plot(norm.(ffd_mt)) +scatter!(norm.(ffd_st)) \ No newline at end of file diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index 2d98c303..c96ec31b 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -29,7 +29,7 @@ operator(solver::GMRESSolver) = solver.linear_operator function solve(solver::GMRESSolver, b) op = operator(solver) x, ch = IterativeSolvers.gmres(op, b, log=true, maxiter=solver.maxiter, - restart=solver.restart, reltol=solver.tol) + restart=solver.restart, reltol=solver.tol, verbose=true) return x, ch end From 1eb67e178f1251054eb6bc14addd8cb7579f4e42 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 8 Jul 2021 14:25:56 +0200 Subject: [PATCH 119/528] utils to lift LinearMaps added --- src/BEAST.jl | 1 + src/utils/liftmap.jl | 17 +++++++++++++++++ src/utils/variational.jl | 3 +++ 3 files changed, 21 insertions(+) create mode 100644 src/utils/liftmap.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index aa598b09..e0480206 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -117,6 +117,7 @@ include("utils/combinatorics.jl") include("utils/linearspace.jl") include("utils/matrixconv.jl") include("utils/polyeig.jl") +include("utils/liftmap.jl") include("bases/basis.jl") include("bases/lincomb.jl") diff --git a/src/utils/liftmap.jl b/src/utils/liftmap.jl new file mode 100644 index 00000000..cf196918 --- /dev/null +++ b/src/utils/liftmap.jl @@ -0,0 +1,17 @@ +# Utils to lift LinearMaps to LinearMaps acting on larger vector spaces +import LinearMaps +import LinearAlgebra + +struct LiftedMap{T,TI,TJ} <: LinearMap{T} + A::LinearMap{T} + I::TI + J::TJ +end + +function LinearAlgebra.mul!(y::AbstractVector, L::LiftedMap, x::AbstractVector, α::Number, β::Number) + + yI = view(y, L.I) + xJ = view(x, L.J) + AIJ = L.A + LinearAlgebra.mul!(yI, AIJ, xJ, α, β) +end \ No newline at end of file diff --git a/src/utils/variational.jl b/src/utils/variational.jl index e10aebb7..6818067d 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -114,6 +114,9 @@ Base.getindex(u::AbstractBlockArray, p::HilbertVector) = u[Block(Int(p))] Base.setindex!(A::AbstractBlockArray, v, p::HilbertVector, q::HilbertVector) = setindex!(A, v, Block(Int(p),Int(q))) Base.setindex!(A::AbstractBlockArray, v, p::HilbertVector) = setindex!(A, v, Block(Int(p))) +Base.view(A::AbstractBlockArray, p::HilbertVector, q::HilbertVector) = view(A, Block(Int(p), Int(q))) +Base.view(A::AbstractBlockArray, p::HilbertVector) = view(A, Block(Int(p))) + mutable struct LinForm test_space terms From b073de73351dbc10f5d310729fc0cd9e50ea7272 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Wed, 18 Aug 2021 00:22:46 +0000 Subject: [PATCH 120/528] CompatHelper: bump compat for FillArrays to 0.12, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 555b3aa6..f7f51650 100644 --- a/Project.toml +++ b/Project.toml @@ -31,7 +31,7 @@ CompScienceMeshes = "^0.2.8" Compat = "^2, ^3" FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" -FillArrays = "0.11" +FillArrays = "0.11, 0.12" IterativeSolvers = "^0.9" LinearMaps = "^3.3" SauterSchwabQuadrature = "^2.1.1, 2.1" From c8f5f4f7c413c903168610a659c5d4b16c305178 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Thu, 26 Aug 2021 13:36:58 +0200 Subject: [PATCH 121/528] Fix of the far field computation of Maxwell dipole The previous formula assumed that the dipole is located in the origin. - Now the phase-term is correctly included - Also the n x ( ) x n operation is performed stripping away the radial component. --- src/maxwell/mwexc.jl | 20 ++++++++++++-------- test/test_dipole.jl | 9 +++++---- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/maxwell/mwexc.jl b/src/maxwell/mwexc.jl index e6e7bda9..048d8b78 100644 --- a/src/maxwell/mwexc.jl +++ b/src/maxwell/mwexc.jl @@ -86,14 +86,16 @@ function (d::DipoleMW)(x; isfarfield=false) k = d.wavenumber x_0 = d.location p = d.orientation - r = norm(x-x_0) - n = (x - x_0)/r if isfarfield # postfactor (4*π*im)/k to be consistent with BEAST far field computation - # and, of course, omitted exp(-im*k*r)/r factor in (9.19) - # of Jackson's Classical Electrodynamics - return k^2/(4*π)*cross(cross(n,p),n)*(4*π*im)/k + # and, of course, adapted phase factor exp(im*k*dot(n,x_0)) with + # respect to (9.19) of Jackson's Classical Electrodynamics + r = norm(x) + n = x/r + return cross(cross(n,1/(4*π)*(k^2*cross(cross(n,p),n))*exp(im*k*dot(n,x_0))*(4*π*im)/k),n) else + r = norm(x-x_0) + n = (x - x_0)/r return 1/(4*π)*exp(-im*k*r)*(k^2/r*cross(cross(n,p),n) + (1/r^3 + im*k/r^2)*(3*n*dot(n,p) - p)) end @@ -103,12 +105,14 @@ function (d::curlDipoleMW)(x; isfarfield=false) k = d.wavenumber x_0 = d.location p = d.orientation - r = norm(x-x_0) - n = (x - x_0)/r if isfarfield # postfactor (4*π*im)/k to be consistent with BEAST far field computation - return (-im*k)*k^2/(4*π)*cross(n,p)*(4*π*im)/k + r = norm(x) + n = x/r + return (-im*k)*k^2/(4*π)*cross(n,p)*(4*π*im)/k*exp(im*k*dot(n,x_0)) else + r = norm(x-x_0) + n = (x - x_0)/r return -im*(k^3)/(4*π)*cross(n,p)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) end end diff --git a/test/test_dipole.jl b/test/test_dipole.jl index 4e2641a1..0742a0f7 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -15,7 +15,7 @@ k = 2*π/λ η = sqrt(μ/ε) a = 1 -Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.2) +Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.1) Γ = translate(Γ_orig,SVector(-a/2,-a/2,-a/2)) Φ, Θ = [0.0], range(0,stop=π,length=100) @@ -24,7 +24,7 @@ pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in # This is an electric dipole # The pre-factor (1/ε) is used to resemble # (9.18) in Jackson's Classical Electrodynamics -E = (1/ε) * dipolemw3d(location=SVector(0,0,0), +E = (1/ε) * dipolemw3d(location=SVector(0.4,0.2,0), orientation=1e-9.*SVector(0.5,0.5,0), wavenumber=k) @@ -51,7 +51,7 @@ ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X) @test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 @test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 -@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 +@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.001 K_bc = Matrix(assemble(𝓚,Y,X)) G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) @@ -67,7 +67,7 @@ ff_E_BCMFIE = potential(MWFarField3D(wavenumber=k), pts, j_BCMFIE, X) @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 -H = dipolemw3d(location=SVector(0,0,0), +H = dipolemw3d(location=SVector(0.0,0.0,0.3), orientation=1e-9.*SVector(0.5,0.5,0), wavenumber=k) @@ -100,6 +100,7 @@ nf_E_BCMFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) ./ η ff_E_BCMFIE = potential(MWFarField3D(wavenumber=k), pts, j_BCMFIE, X) +@test norm(j_BCMFIE - j_EFIE)/norm(j_EFIE) ≈ 0 atol=0.02 @test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 \ No newline at end of file From 79b12a68e466271ec069dc0e1250930405039d6d Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 26 Aug 2021 16:01:01 +0200 Subject: [PATCH 122/528] migrate to GH actions for CI --- .github/workflows/CI.yml | 68 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/workflows/CI.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 00000000..20dde54e --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,68 @@ +name: CI +on: + pull_request: + branches: + - master + push: + branches: + - master + tags: '*' +jobs: + test: + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1.3' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'. + - '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia. + - 'nightly' + os: + - ubuntu-latest + arch: + - x64 + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: actions/cache@v1 + env: + cache-name: cache-artifacts + with: + path: ~/.julia/artifacts + key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }} + restore-keys: | + ${{ runner.os }}-test-${{ env.cache-name }}- + ${{ runner.os }}-test- + ${{ runner.os }}- + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v1 + with: + file: lcov.info + docs: + name: Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@v1 + with: + version: '1' + - run: | + julia --project=docs -e ' + using Pkg + Pkg.develop(PackageSpec(path=pwd())) + Pkg.instantiate()' + - run: | + julia --project=docs -e ' + using Documenter: doctest + using BEAST + doctest(BEAST)' # change MYPACKAGE to the name of your package + - run: julia --project=docs docs/make.jl + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} \ No newline at end of file From 3e17e57b36920f78209870852a043c600b7410dd Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 27 Aug 2021 14:36:29 +0200 Subject: [PATCH 123/528] narrow CSM compat to 0.3 onwards --- Project.toml | 2 +- docs/Project.toml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 docs/Project.toml diff --git a/Project.toml b/Project.toml index f7f51650..b592bdc4 100644 --- a/Project.toml +++ b/Project.toml @@ -27,7 +27,7 @@ WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" BlockArrays = "^0.10, ^0.11, ^0.12, ^0.13, ^0.14, 0.15, 0.16" CollisionDetection = "^0.1" Combinatorics = "^0.7, ^1" -CompScienceMeshes = "^0.2.8" +CompScienceMeshes = "^0.3" Compat = "^2, ^3" FFTW = "^0.2.3, ^1" FastGaussQuadrature = "^0.3, ^0.4" diff --git a/docs/Project.toml b/docs/Project.toml new file mode 100644 index 00000000..6a233f0c --- /dev/null +++ b/docs/Project.toml @@ -0,0 +1,3 @@ +[deps] +BEAST = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" From 3ec3540d0922e6e74fd2dc4189ee08055106bddd Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 27 Aug 2021 15:15:15 +0200 Subject: [PATCH 124/528] removed travis config file --- .travis.yml | 13 ------------- TODO.md | 5 ----- 2 files changed, 18 deletions(-) delete mode 100644 .travis.yml delete mode 100644 TODO.md diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c3be022e..00000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -# Documentation: http://docs.travis-ci.com/user/languages/julia/ -language: julia -os: - - linux - - osx - - windows -julia: - - 1.3 - - 1.5 -notifications: - email: false -after_success: - - julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())' diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 686ee4fd..00000000 --- a/TODO.md +++ /dev/null @@ -1,5 +0,0 @@ -[x] Create wrapper type around Array{T,3} to make it behave like a convolution operator - -[] Properly implement the convolution of two piecewise polynomial time basis functions - -[] assemble and allocatestorage for tensor operators need to be more robust From 125f50c11cbfc4d79b8f08c91040263a8318aaee Mon Sep 17 00:00:00 2001 From: krcools Date: Fri, 27 Aug 2021 14:50:26 +0200 Subject: [PATCH 125/528] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dcb060af..37ad8863 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Boundary Element Analysis and Simulation Toolkit -[![Build Status](https://travis-ci.org/krcools/BEAST.jl.svg?branch=master)](https://travis-ci.org/krcools/BEAST.jl) +[![Build status](https://github.com/krcools/BEAST.jl/workflows/CI/badge.svg)](https://github.com/krcools/BEAST.jl/actions) [![codecov.io](http://codecov.io/github/krcools/BEAST.jl/coverage.svg?branch=master)](http://codecov.io/github/krcools/BEAST.jl?branch=master) [![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://krcools.github.io/BEAST.jl/latest/) [![DOI](https://zenodo.org/badge/87720391.svg)](https://zenodo.org/badge/latestdoi/87720391) From 0235b3077d93c9c18e0295a50948a6b831586c26 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 3 Sep 2021 17:13:56 +0200 Subject: [PATCH 126/528] Added Zero linear map to use as stub --- src/solvers/solver.jl | 25 +++++++++++++++++++++++++ src/utils/zeromap.jl | 13 +++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/utils/zeromap.jl diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 248b7085..80361214 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -234,6 +234,31 @@ function assemble(bilform::BilForm, test_space_dict, trial_space_dict) end +# function assemble(bilform::BilForm, test_space_dict, trial_space_dict) + +# @assert !isempty(terms) +# Z = ZeroMap{Float32} +# for term in bilform.terms + +# x = test_space_dict[term.test_id] +# for op in reverse(term.test_ops) +# x = op[end](op[1:end-1]..., x) +# end + +# y = trial_space_dict[term.trial_id] +# for op in reverse(t.trial_ops) +# y = op[end](op[1:end-1]..., y) +# end + +# end + + + + + +# end + + function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict) diff --git a/src/utils/zeromap.jl b/src/utils/zeromap.jl new file mode 100644 index 00000000..e312cb74 --- /dev/null +++ b/src/utils/zeromap.jl @@ -0,0 +1,13 @@ +import LinearMaps + +struct ZeroMap{T} <: LinearMap{T} + num_rows::Int + num_cols::Int +end + +Base.size(A::ZeroMap) = (A.num_rows, A.num_cols,) + +function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector, α::Number, β::Number) + y .= β * y + # LinearAlgebra.mul!(yI, AIJ, xJ, α, β) +end \ No newline at end of file From 61db32fb9ee5418cb1e5bef5abd831d48af2ce41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cedric=20M=C3=BCnger?= Date: Mon, 21 Sep 2020 14:04:57 +0200 Subject: [PATCH 127/528] 2d/3d BDM elements --- src/identityop.jl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/identityop.jl b/src/identityop.jl index bb6b5981..2eddd130 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -29,6 +29,11 @@ function quaddata(op::LocalOperator, g::LinearRefSpaceTriangle, f::LinearRefSpac return qd, A end +function quaddata(op::LocalOperator, g::BDMRefSpace, f::BDMRefSpace, tels, bels) + u, w = trgauss(6) + return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] +end + function quaddata(op::LocalOperator, g::subReferenceSpace, f::subReferenceSpace, tels, bels) u, w = trgauss(6) qd = [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] @@ -55,9 +60,17 @@ function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,2} where {T,Deg}, return qd, A end +function quaddata(op::LocalOperator, g::BDM3DRefSpace, f::BDM3DRefSpace, tels, bels) + o, x, y, z, = CompScienceMeshes.euclidianbasis(3) + reftet = simplex(x,y,z,o) + qps = quadpoints(reftet, 6) + [(w, parametric(p)) for (p,w) in qps] +end + function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,3} where {T,Deg}, f::LagrangeRefSpace, tels::Vector, bels::Vector) + u, w = trgauss(6) qd = [(w[i], SVector(u[1,i], u[2,i])) for i in 1:length(w)] A = _alloc_workspace(qd, g, f, tels, bels) From 19f531154e207caf5e377010d67aaf3099cce5b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cedric=20M=C3=BCnger?= Date: Mon, 6 Sep 2021 16:13:36 +0200 Subject: [PATCH 128/528] update main file --- src/BEAST.jl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/BEAST.jl b/src/BEAST.jl index e0480206..658ea321 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -9,8 +9,13 @@ using BlockArrays using SparseMatrixDicts using SauterSchwabQuadrature +using SauterSchwab3D using FastGaussQuadrature +<<<<<<< HEAD using LinearMaps +======= +using ShunnHamQuadrature +>>>>>>> 90b92be (update main file) import LinearAlgebra: cross, dot import LinearAlgebra: ×, ⋅ @@ -28,6 +33,11 @@ export restrict export raviartthomas, raowiltonglisson, positions export brezzidouglasmarini export brezzidouglasmarini3d +<<<<<<< HEAD +======= +export nedelecd3d +export nedelecc3d +>>>>>>> 90b92be (update main file) export portcells, rt_ports, getindex_rtg, subset export StagedTimeStep export subdsurface,subdBasis,assemblydata,refspace @@ -40,6 +50,7 @@ export timebasisshiftedlagrange export TimeBasisDeltaShifted export ntrace export strace +export ttrace export SingleLayer export DoubleLayer export DoubleLayerTransposed @@ -63,6 +74,9 @@ export MWSingleLayerField3D export SingleLayerTrace export DoubleLayerRotatedMW3D export MWSingleLayerPotential3D + +export VIEOperator + export gmres export @hilbertspace, @varform, @discretise export solve @@ -186,6 +200,16 @@ include("maxwell/spotential.jl") include("maxwell/maxwell.jl") include("maxwell/sourcefield.jl") +<<<<<<< HEAD +======= +#suport for Volume Integral equation +include("volumeintegral/vie.jl") +include("volumeintegral/vieexc.jl") +include("volumeintegral/vieops.jl") +include("volumeintegral/farfield.jl") +include("volumeintegral/sauterschwab_ints.jl") + +>>>>>>> 90b92be (update main file) # Support for the Helmholtz equation include("helmholtz2d/helmholtzop.jl") From e1530fcf20b12400f486712171044e2ffab9ee14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cedric=20M=C3=BCnger?= Date: Wed, 3 Nov 2021 11:25:58 +0100 Subject: [PATCH 129/528] Volume integral equation --- examples/dvie.jl | 57 +++++ examples/evie.jl | 55 +++++ examples/pmchwt.jl | 52 ++++- src/BEAST.jl | 23 +- src/maxwell/sauterschwabints_bdm.jl | 2 +- src/maxwell/sauterschwabints_rt.jl | 2 +- src/volumeintegral/farfield.jl | 35 +++ src/volumeintegral/sauterschwab_ints.jl | 170 ++++++++++++++ src/volumeintegral/vie.jl | 215 ++++++++++++++++++ src/volumeintegral/vieexc.jl | 42 ++++ src/volumeintegral/vieops.jl | 281 ++++++++++++++++++++++++ 11 files changed, 906 insertions(+), 28 deletions(-) create mode 100644 examples/dvie.jl create mode 100644 examples/evie.jl create mode 100644 src/volumeintegral/farfield.jl create mode 100644 src/volumeintegral/sauterschwab_ints.jl create mode 100644 src/volumeintegral/vie.jl create mode 100644 src/volumeintegral/vieexc.jl create mode 100644 src/volumeintegral/vieops.jl diff --git a/examples/dvie.jl b/examples/dvie.jl new file mode 100644 index 00000000..05ffa0db --- /dev/null +++ b/examples/dvie.jl @@ -0,0 +1,57 @@ +using CompScienceMeshes, BEAST +using LinearAlgebra +using Profile +using StaticArrays + +function tau(x::SVector{U,T}) where {U,T} + 1.0-1.0/4.0 +end + +ntrc = X->ntrace(X,y) + +T = tetmeshsphere(1.0,0.25) +X = nedelecd3d(T) +y = boundary(T) +@show numfunctions(X) + +ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) +ϵ_r =4.0 +χ = tau +K, I, B = VIE.singlelayer(wavenumber=κ, tau=χ), Identity(), VIE.boundary(wavenumber=κ, tau=χ) +E = VIE.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +H = -1/(im*κ*η)*curl(E) + +@hilbertspace j +@hilbertspace k +α = 1.0/ϵ_r +eq = @varform α*I[j,k]-K[j,k]-B[ntrc(j),k] == E[j] + +dbvie = @discretise eq j∈X k∈X +u = solve(dbvie) + +#postprocessing +Φ, Θ = [0.0], range(0,stop=2π,length=100) +pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] +ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), pts, u, X) +ff = im*κ*ffd + +using Plots + +#Farfield +plot(xlabel="theta") +plot!(Θ, norm.(ff), label="far field", title="D-VIE") + +#NearField +Z = range(-1,1,length=100) +Y = range(-1,1,length=100) +nfpoints = [point(0,y,z) for y in Y, z in Z] + +Enear = BEAST.grideval(nfpoints,α.*u,X) +Enear = reshape(Enear,100,100) + +contour(real.(getindex.(Enear,1))) +heatmap(Z, Y, real.(getindex.(Enear,1)), clim=(0.0,1.0)) + + + + diff --git a/examples/evie.jl b/examples/evie.jl new file mode 100644 index 00000000..e2bc80b8 --- /dev/null +++ b/examples/evie.jl @@ -0,0 +1,55 @@ +using CompScienceMeshes, BEAST +using LinearAlgebra +using Profile +using StaticArrays + +function tau(x::SVector{U,T}) where {U,T} + 4.0-1.0 +end + +ttrc = X->ttrace(X,y) + +T= tetmeshsphere(1.0,0.25) +X = nedelecc3d(T) +y = boundary(T) +@show numfunctions(X) + +ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) +ϵ_r = 4.0 +χ = tau +K, I, B = VIE.singlelayer2(wavenumber=κ, tau=χ), Identity(), VIE.boundary2(wavenumber=κ, tau=χ) +E = VIE.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +H = -1/(im*κ*η)*curl(E) + +@hilbertspace j +@hilbertspace k +α = ϵ_r +eq = @varform α*I[j,k]-K[j,k]+B[ttrc(j),k] == E[j] + +evie = @discretise eq j∈X k∈X +u = solve(evie) + +#postprocessing +Φ, Θ = [0.0], range(0,stop=2π,length=100) +pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ ] +ffe = potential(VIE.farfield(wavenumber=κ, tau=χ), pts, u, X) +ff = ffe + +using Plots + +#Farfield +plot(xlabel="theta") +plot!(Θ, norm.(ff), label="far field", title="E-VIE") + + +#Nearfield +Z = range(-1,1,length=100) +Y = range(-1,1,length=100) +nfpoints = [point(0,y,z) for y in Y, z in Z] + +Enear = BEAST.grideval(nfpoints,u,X) +Enear = reshape(Enear,100,100) + +contour(real.(getindex.(Enear,1))) +heatmap(Z, Y, real.(getindex.(Enear,1)), clim=(0.0,1.0)) + diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 177eb81b..d19e159d 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -1,14 +1,18 @@ using CompScienceMeshes, BEAST using LinearAlgebra -Γ = meshcuboid(0.5, 1.0, 1.0, 0.075) -CompScienceMeshes.translate!(Γ, point(-0.25, -0.5, -0.5)) +#T = tetmeshcuboid(1.0,1.0,1.0,0.1) +T = tetmeshsphere(1.0,0.25) +X = nedelecc3d(T) +Γ = boundary(T) +#Γ = meshcuboid(0.5, 1.0, 1.0, 0.075) +#CompScienceMeshes.translate!(Γ, point(-0.25, -0.5, -0.5)) # Γ = meshsphere(1.0, 0.075) X = raviartthomas(Γ) @show numfunctions(X) -κ, η = 6.0, 1.0 -κ′, η′ = 2.4κ, η/2.4 +κ, η = 1.0, 1.0 +κ′, η′ = 2.0κ, η/2.0 T = Maxwell3D.singlelayer(wavenumber=κ) T′ = Maxwell3D.singlelayer(wavenumber=κ′) @@ -37,11 +41,11 @@ ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ # Don't forget the far field comprises two contributions ffm = potential(MWFarField3D(κ*im), ffpoints, u[m], X) ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], X) -ff = η*im*κ*ffj + im*κ*cross.(ffpoints, ffj) +ff = η*im*κ*ffm + im*κ*cross.(ffpoints, ffj) using Plots plot(xlabel="theta") -plot!(Θ,norm.(ff),label="far field") +plot!(Θ,norm.(ff),label="far field",title="PMCHWT") import Plotly using LinearAlgebra @@ -69,9 +73,13 @@ function nearfield(um,uj,Xm,Xj,κ,η,points, return E, H end +#Z = range(-0.1,1.1,length=100) +#Y = range(-0.1,1.1,length=100) +#nfpoints = [point(0.5,y,z) for y in Y, z in Z] + Z = range(-1,1,length=100) Y = range(-1,1,length=100) -nfpoints = [point(0,y,z) for z in Z, y in Y] +nfpoints = [point(0,y,z) for y in Y, z in Z] E_ex, H_ex = nearfield(u[m],u[j],X,X,κ,η,nfpoints,E,H) E_in, H_in = nearfield(-u[m],-u[j],X,X,κ′,η′,nfpoints) @@ -80,9 +88,33 @@ E_tot = E_in + E_ex H_tot = H_in + H_ex contour(real.(getindex.(E_tot,1))) -heatmap(Z, Y, real.(getindex.(E_tot,1))) -plot(real.(getindex.(E_tot[:,51],1))) +heatmap(Z, Y, real.(getindex.(E_tot,1)), clim=(0.0,1.0)) +#plot(real.(getindex.(E_tot[:],1))) + +#= +nfpoints = [point(0.0,0.0,z) for z in Z] +E_ex, H_ex = nearfield(u[m],u[j],X,X,κ,η,nfpoints,E,H) +E_in, H_in = nearfield(-u[m],-u[j],X,X,κ′,η′,nfpoints) + +E_tot = E_in + E_ex +H_tot = H_in + H_ex + +plot(real.(getindex.(E_tot[:],1))) + +=# +#= contour(real.(getindex.(H_tot,2))) heatmap(Z, Y, real.(getindex.(H_tot,2))) -plot(real.(getindex.(H_tot[:,51],2))) \ No newline at end of file +#plot(real.(getindex.(H_tot[:,51],2))) + +nfpoints = [point(0,0,z) for z in Z] + +E_ex, H_ex = nearfield(u[m],u[j],X,X,κ,η,nfpoints,E,H) +E_in, H_in = nearfield(-u[m],-u[j],X,X,κ′,η′,nfpoints) + +E_tot = E_in + E_ex +H_tot = H_in + H_ex + +plot(real.(getindex.(E_tot[:],1))) +=# diff --git a/src/BEAST.jl b/src/BEAST.jl index 658ea321..241d50f8 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -11,11 +11,8 @@ using SparseMatrixDicts using SauterSchwabQuadrature using SauterSchwab3D using FastGaussQuadrature -<<<<<<< HEAD using LinearMaps -======= using ShunnHamQuadrature ->>>>>>> 90b92be (update main file) import LinearAlgebra: cross, dot import LinearAlgebra: ×, ⋅ @@ -33,11 +30,8 @@ export restrict export raviartthomas, raowiltonglisson, positions export brezzidouglasmarini export brezzidouglasmarini3d -<<<<<<< HEAD -======= export nedelecd3d export nedelecc3d ->>>>>>> 90b92be (update main file) export portcells, rt_ports, getindex_rtg, subset export StagedTimeStep export subdsurface,subdBasis,assemblydata,refspace @@ -200,8 +194,13 @@ include("maxwell/spotential.jl") include("maxwell/maxwell.jl") include("maxwell/sourcefield.jl") -<<<<<<< HEAD -======= +# Support for the Helmholtz equatio +include("helmholtz2d/helmholtzop.jl") + +include("helmholtz3d/hh3dexc.jl") +include("helmholtz3d/hh3dops.jl") +include("helmholtz3d/nitsche.jl") + #suport for Volume Integral equation include("volumeintegral/vie.jl") include("volumeintegral/vieexc.jl") @@ -209,14 +208,6 @@ include("volumeintegral/vieops.jl") include("volumeintegral/farfield.jl") include("volumeintegral/sauterschwab_ints.jl") ->>>>>>> 90b92be (update main file) -# Support for the Helmholtz equation -include("helmholtz2d/helmholtzop.jl") - -include("helmholtz3d/hh3dexc.jl") -include("helmholtz3d/hh3dops.jl") -include("helmholtz3d/nitsche.jl") - include("decoupled/dpops.jl") include("decoupled/potentials.jl") diff --git a/src/maxwell/sauterschwabints_bdm.jl b/src/maxwell/sauterschwabints_bdm.jl index 3b89272f..49034734 100644 --- a/src/maxwell/sauterschwabints_bdm.jl +++ b/src/maxwell/sauterschwabints_bdm.jl @@ -52,7 +52,7 @@ function momintegrals!(op::MWSingleLayer3D, igd = MWSL3DIntegrand2(test_triangular_element, trial_triangular_element, op, test_local_space, trial_local_space) - G = sauterschwab_parameterized(igd, strat) + G = SauterSchwabQuadrature.Sautersauterschwab_parameterized(igd, strat) A = levicivita(K) == 1 ? @SVector[1,2] : @SVector[2,1] B = levicivita(L) == 1 ? @SVector[1,2] : @SVector[2,1] diff --git a/src/maxwell/sauterschwabints_rt.jl b/src/maxwell/sauterschwabints_rt.jl index 1813e127..df9dd254 100644 --- a/src/maxwell/sauterschwabints_rt.jl +++ b/src/maxwell/sauterschwabints_rt.jl @@ -117,7 +117,7 @@ function momintegrals!(op::MWOperator3D, # op, test_local_space, trial_local_space) igd = kernel_in_bary(op, test_local_space, trial_local_space, test_triangular_element, trial_triangular_element) - G = sauterschwab_parameterized(igd, strat) + G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) for j ∈ 1:3, i ∈ 1:3 out[i,j] += G[K[i],L[j]] end diff --git a/src/volumeintegral/farfield.jl b/src/volumeintegral/farfield.jl new file mode 100644 index 00000000..5b17b7d9 --- /dev/null +++ b/src/volumeintegral/farfield.jl @@ -0,0 +1,35 @@ +""" +Operator to compute the far field of a current distribution. In particular, given the current distribution ``j`` this operator allows for the computation of + +```math +A j = n × ∫_Ω j e^{γ ̂x ⋅ y} dy +``` + +where ``̂x`` is the unit vector in the direction of observation. Note that the assembly routing expects the observation directions to be normalised by the caller. +""" +struct VIEFarField3D{K,P} + gamma::K + tau::P +end + +function VIEFarField3D(;wavenumber=error("wavenumber is a required argument"), tau=nothing) + gamma = nothing + + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = wavenumber*im + end + + tau == nothing && (tau = x->1.0) + + VIEFarField3D(gamma, tau) +end + +quaddata(op::VIEFarField3D,rs,els) = quadpoints(rs,els,(3,)) +quadrule(op::VIEFarField3D,refspace,p,y,q,el,qdata) = qdata[1,q] + +kernelvals(op::VIEFarField3D,y,p) = exp(op.gamma*dot(y,cartesian(p))) +function integrand(op::VIEFarField3D,krn,y,f,p) + (y × (op.tau(cartesian(p)) * krn * f[1])) × y +end diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl new file mode 100644 index 00000000..fe39aec4 --- /dev/null +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -0,0 +1,170 @@ +#TODO: rename to reorder_dof and make it depending on RefSpace +function reorder_dof(space::NDLCDRefSpace,I) + n = length(I) + K = zeros(MVector{4,Int64}) + for i in 1:n + for j in 1:n + if I[j] == i + K[i] = j + break + end + end + end + + return SVector(K),SVector{4,Int64}(1,1,1,1) +end + + +function reorder_dof(space::NDLCCRefSpace, I ) + + n = length(I) + J = zeros(MVector{4,Int64}) + for i in 1:n + for j in 1:n + if I[j] == i + J[i] = j + break + end + end + end + + edges = collect(combinations(J,2)) + ref_edges = collect(combinations(SVector{4,Int64}(1,2,3,4),2)) + n = length(edges) + K = zeros(MVector{6,Int64}) + O = zeros(MVector{6,Int64}) + + for i in 1:n + for j in 1:n + if edges[i] == ref_edges[j] + K[i] = j + O[i] = 1 + break + elseif reverse(edges[i]) == ref_edges[j] + K[i] = j + O[i] = -1 + break + end + end + end + + return SVector(K),SVector(O) +end + +function reorder_dof(space::RTRefSpace,I) + n = length(I) + K = zeros(MVector{3,Int64}) + for i in 1:n + for j in 1:n + if I[j] == i + K[i] = j + break + end + end + end + + return SVector(K),SVector{3,Int64}(1,1,1) +end + +function reorder_dof(space::LagrangeRefSpace{Float64,0,3,1},I) + + return SVector{1,Int64}(1),SVector{1,Int64}(1) +end + + +function momintegrals!(op::VIEOperator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) + + #Find permutation of vertices to match location of singularity to SauterSchwab + J, I= SauterSchwab3D.reorder(strat.sing) + + #Get permutation and rel. orientatio of DoFs + K,O1 = reorder_dof(test_local_space, I) + L,O2 = reorder_dof(trial_local_space, J) + #Apply permuation to elements + + if length(I) == 4 + test_tetrahedron_element = simplex( + test_tetrahedron_element.vertices[I[1]], + test_tetrahedron_element.vertices[I[2]], + test_tetrahedron_element.vertices[I[3]], + test_tetrahedron_element.vertices[I[4]]) + elseif length(I) == 3 + test_tetrahedron_element = simplex( + test_tetrahedron_element.vertices[I[1]], + test_tetrahedron_element.vertices[I[2]], + test_tetrahedron_element.vertices[I[3]]) + end + + #test_tetrahedron_element = simplex(test_tetrahedron_element.vertices[I]...) + + if length(J) == 4 + trial_tetrahedron_element = simplex( + trial_tetrahedron_element.vertices[J[1]], + trial_tetrahedron_element.vertices[J[2]], + trial_tetrahedron_element.vertices[J[3]], + trial_tetrahedron_element.vertices[J[4]]) + elseif length(J) == 3 + trial_tetrahedron_element = simplex( + trial_tetrahedron_element.vertices[J[1]], + trial_tetrahedron_element.vertices[J[2]], + trial_tetrahedron_element.vertices[J[3]]) + end + + #trial_tetrahedron_element = simplex(trial_tetrahedron_element.vertices[J]...) + + #Define integral (returns a function that only needs barycentric coordinates) + igd = VIEIntegrand(test_tetrahedron_element, trial_tetrahedron_element, + op, test_local_space, trial_local_space) + + #Evaluate integral + Q = SauterSchwab3D.sauterschwab_parameterized(igd, strat) + + #Undo permuation on DoFs + for j in 1 : length(L) + for i in 1 : length(K) + out[i,j] += Q[K[i],L[j]]*O1[i]*O2[j] + end + end + nothing +end + +function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::DoubleQuadStrategy) + + # memory allocation here is a result from the type instability on strat + # which is on purpose, i.e. the momintegrals! method is chosen based + # on dynamic polymorphism. + womps = strat.outer_quad_points + wimps = strat.inner_quad_points + + M, N = size(z) + + for womp in womps + tgeo = womp.point + tvals = womp.value + jx = womp.weight + + for wimp in wimps + bgeo = wimp.point + bvals = wimp.value + jy = wimp.weight + + j = jx * jy + kernel = kernelvals(biop, tgeo, bgeo) + + + igd = integrand(biop, kernel, tvals, tgeo, bvals, bgeo) + + for m in 1 : length(tvals) + + for n in 1 : length(bvals) + + z[m,n] += j * igd[m,n] + end + end + end + end + + return z +end diff --git a/src/volumeintegral/vie.jl b/src/volumeintegral/vie.jl new file mode 100644 index 00000000..58f52e06 --- /dev/null +++ b/src/volumeintegral/vie.jl @@ -0,0 +1,215 @@ +module VIE + using ..BEAST + Mod = BEAST + + """ + singlelayer(;gamma, alpha, beta, tau) + singlelayer(;wavenumber, alpha, beta, tau) + + Bilinear form given by: + + ```math + α ∬_{Ω×Ω} j(x)⋅τ(y)⋅k(y) G_{γ}(x,y) + β ∬_{Ω×Ω} div j(x) τ(y)⋅k(y)⋅grad G_{γ}(x,y) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast dyadic + """ + + function singlelayer(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = wavenumber*wavenumber) + beta == nothing && (beta = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VIESingleLayer(gamma, alpha, beta, tau) + end + + + function singlelayer2(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + beta == nothing && (beta = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VIESingleLayer2(gamma, alpha, beta, tau) + end + + """ + boundary(;gamma, alpha, tau) + boundary(;wavenumber, alpha, tau) + + Bilinear form given by: + + ```math + α ∬_{Ω×Ω} T_n j(x) grad G_{γ}(x,y)⋅τ(y)⋅k(y) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast dyadic + """ + + function boundary(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VIEBoundary(gamma, alpha, tau) + end + + function boundary2(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VIEBoundary2(gamma, alpha, tau) + end + + """ + doublelayer(;gamma, alpha, beta, tau) + doublelayer(;wavenumber, alpha, beta, tau) + + Bilinear form given by: + + ```math + α ∬_{Ω×Ω} j(x) ⋅ grad G_{γ}(x,y) × τ(y)⋅k(y) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast dyadic + """ + + function doublelayer(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VIEDoubleLayer(gamma, alpha, tau) + end + + planewave(; + direction = error("missing arguement `direction`"), + polarization = error("missing arguement `polarization`"), + wavenumber = error("missing arguement `wavenumber`"), + amplitude = one(real(typeof(wavenumber)))) = + Mod.PlaneWaveVIE(direction, polarization, wavenumber, amplitude) + + farfield(; + wavenumber = error("missing argument: `wavenumber`"), + tau=error("missing argument: `tau`")) = + Mod.VIEFarField3D(wavenumber=wavenumber, tau= tau) + +end \ No newline at end of file diff --git a/src/volumeintegral/vieexc.jl b/src/volumeintegral/vieexc.jl new file mode 100644 index 00000000..d1f5e92b --- /dev/null +++ b/src/volumeintegral/vieexc.jl @@ -0,0 +1,42 @@ +mutable struct PlaneWaveVIE{T,P} <: Functional + direction::P + polarisation::P + wavenumber::T + amplitude::T + end + + function PlaneWaveVIE(d,p,k,a = 1) + T = promote_type(eltype(d), eltype(p), typeof(k), typeof(a)) + P = similar_type(typeof(d), T) + PlaneWaveVIE{T,P}(d,p,k,a) +end + +planewavevie(; + direction = error("missing arguement `direction`"), + polarization = error("missing arguement `polarization`"), + wavenumber = error("missing arguement `wavenumber`"), + amplitude = 1, + ) = PlaneWaveVIE(direction, polarization, wavenumber, amplitude) + +function (e::PlaneWaveVIE)(p) + k = e.wavenumber + d = e.direction + u = e.polarisation + a = e.amplitude + x = cartesian(p) + a * exp(-im * k * dot(d, x)) * u + end + + function curl(field::PlaneWaveVIE) + k = field.wavenumber + d = field.direction + u = field.polarisation + a = field.amplitude + v = d × u + b = -a * im * k + PlaneWaveVIE(d, v, k, b) + end + +*(a::Number, e::PlaneWaveVIE) = PlaneWaveVIE(e.direction, e.polarisation, e.wavenumber, a*e.amplitude) + +integrand(::PlaneWaveVIE, test_vals, field_val) = test_vals[1] ⋅ field_val diff --git a/src/volumeintegral/vieops.jl b/src/volumeintegral/vieops.jl new file mode 100644 index 00000000..c212f349 --- /dev/null +++ b/src/volumeintegral/vieops.jl @@ -0,0 +1,281 @@ +abstract type VIEOperator <: IntegralOperator end +abstract type VolumeOperator <: VIEOperator end +abstract type BoundaryOperator <: VIEOperator end + +struct KernelValsVIE{T,U,P,Q,K} + gamma::U + vect::P + dist::T + green::U + gradgreen::Q + tau::K +end + +function kernelvals(viop::VIEOperator, p ,q) + Y = viop.gamma; + r = cartesian(p)-cartesian(q) + R = norm(r) + yR = Y*R + + expn = exp(-yR) + green = expn / (4*pi*R) + gradgreen = - (Y +1/R)*green/R*r + + + tau = viop.tau(cartesian(q)) + + KernelValsVIE(Y,r,R, green, gradgreen,tau) +end + +struct VIESingleLayer{T,U,P} <: VolumeOperator + gamma::T + α::U + β::U + tau::P +end + +struct VIEBoundary{T,U,P} <: BoundaryOperator + gamma::T + α::U + tau::P +end + +struct VIESingleLayer2{T,U,P} <: VolumeOperator + gamma::T + α::U + β::U + tau::P +end + +struct VIEBoundary2{T,U,P} <: BoundaryOperator + gamma::T + α::U + tau::P +end + + +struct VIEDoubleLayer{T,U,P} <: VolumeOperator + gamma::T + α::U + tau::P +end + +scalartype(op::VIEOperator) = typeof(op.gamma) + +export VIE + +struct VIEIntegrand{S,T,O,K,L} + test_tetrahedron_element::S + trial_tetrahedron_element::T + op::O + test_local_space::K + trial_local_space::L +end + + +function (igd::VIEIntegrand)(u,v) + + #mesh points + tgeo = neighborhood(igd.test_tetrahedron_element,v) + bgeo = neighborhood(igd.trial_tetrahedron_element,u) + + #kernel values + kerneldata = kernelvals(igd.op,tgeo,bgeo) + + #values & grad/div/curl of local shape functions + tval = igd.test_local_space(tgeo) + bval = igd.trial_local_space(bgeo) + + #jacobian + j = jacobian(tgeo) * jacobian(bgeo) + + integrand(igd.op, kerneldata,tval,tgeo,bval,tgeo) * j +end + + +function integrand(viop::VIESingleLayer, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:4] + fy = @SVector[bvals[i].value for i in 1:4] + + dgx = @SVector[tvals[i].divergence for i in 1:4] + dfy = @SVector[bvals[i].divergence for i in 1:4] + + G = kerneldata.green + gradG = kerneldata.gradgreen + + Ty = kerneldata.tau + + α = viop.α + β = viop.β + + @SMatrix[α * dot(gx[i],Ty*fy[j]) * G - β * dot(dgx[i] * (Ty * fy[j]), gradG) for i in 1:4, j in 1:4] +end + + +function integrand(viop::VIESingleLayer2, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:6] + fy = @SVector[bvals[i].value for i in 1:6] + + dgx = @SVector[tvals[i].curl for i in 1:6] + dfy = @SVector[bvals[i].curl for i in 1:6] + + G = kerneldata.green + gradG = kerneldata.gradgreen + + Ty = kerneldata.tau + + α = viop.α + β = viop.β + + + @SMatrix[dot(dgx[i] , cross(gradG, Ty*fy[j])) for i in 1:6, j in 1:6] +end + +function integrand(viop::VIEBoundary, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:1] + fy = @SVector[bvals[i].value for i in 1:4] + + gradG = kerneldata.gradgreen + + Ty = kerneldata.tau + + α = viop.α + + @SMatrix[α * gx[i] * dot(Ty * fy[j], gradG) for i in 1:1, j in 1:4] +end + +function integrand(viop::VIEBoundary2, kerneldata, tvals, tgeo, bvals, bgeo) + + + gx = @SVector[tvals[i].value for i in 1:3] + fy = @SVector[bvals[i].value for i in 1:6] + + + gradG = kerneldata.gradgreen + + Ty = kerneldata.tau + + α = viop.α + + @SMatrix[α * dot(gx[i] , cross(gradG, Ty*fy[j])) for i in 1:3, j in 1:6] + +end + +function integrand(viop::VIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) + gx = tvals[1] + fy = bvals[1] + + gradG = kerneldata.gradgreen + + Ty = kerneldata.tau + + α = viop.α + + t = α * dot(gx, cross(gradG, Ty*fy)) +end + + +function quaddata(op::VIEOperator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_charts, trial_charts) + + #The combinations of rules (6,7) and (5,7 are) BAAAADDDD + # they result in many near singularity evaluations with any + # resemblence of accuracy going down the drain! Simply don't! + # (same for (5,7) btw...). + t_qp = quadpoints(test_local_space, test_charts, (3,)) + b_qp = quadpoints(trial_local_space, trial_charts, (3,)) + + a, b = 0.0, 1.0 + sing_qp = (SauterSchwab3D._legendre(3,a,b), SauterSchwab3D._shunnham2D(3), SauterSchwab3D._shunnham3D(3), SauterSchwab3D._shunnham4D(3),) + + + return (tpoints=t_qp, bpoints=b_qp, sing_qp=sing_qp) +end + +quadrule(op::VolumeOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd) = qr_volume(op, g, f, i, τ, j, σ, qd) + + +function qr_volume(op::VolumeOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd) + + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) + + hits = 0 + idx_t = Int64[] + idx_s = Int64[] + sizehint!(idx_t,4) + sizehint!(idx_s,4) + dmin2 = floatmax(eltype(eltype(τ.vertices))) + D = dimension(τ)+dimension(σ) + for (i,t) in enumerate(τ.vertices) + for (j,s) in enumerate(σ.vertices) + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + if d2 < dtol + push!(idx_t,i) + push!(idx_s,j) + hits +=1 + break + end + end + end + + #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) + + + hits == 4 && return SauterSchwab3D.CommonVolume6D_S(SauterSchwab3D.Singularity6DVolume(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[4])) + hits == 3 && return SauterSchwab3D.CommonFace6D_S(SauterSchwab3D.Singularity6DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) + hits == 2 && return SauterSchwab3D.CommonEdge6D_S(SauterSchwab3D.Singularity6DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3],qd.sing_qp[4])) + hits == 1 && return SauterSchwab3D.CommonVertex6D_S(SauterSchwab3D.Singularity6DPoint(idx_t,idx_s),qd.sing_qp[3]) + + + + return DoubleQuadStrategy( + qd[1][1,i], + qd[2][1,j]) + +end + +quadrule(op::BoundaryOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd) = qr_boundary(op, g, f, i, τ, j, σ, qd) + +function qr_boundary(op::BoundaryOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd) + + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) + + hits = 0 + idx_t = Int64[] + idx_s = Int64[] + sizehint!(idx_t,4) + sizehint!(idx_s,4) + dmin2 = floatmax(eltype(eltype(τ.vertices))) + D = dimension(τ)+dimension(σ) + for (i,t) in enumerate(τ.vertices) + for (j,s) in enumerate(σ.vertices) + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + if d2 < dtol + push!(idx_t,i) + push!(idx_s,j) + hits +=1 + break + end + end + end + + #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) + + + hits == 3 && return SauterSchwab3D.CommonFace5D_S(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) + hits == 2 && return SauterSchwab3D.CommonEdge5D_S(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) + hits == 1 && return SauterSchwab3D.CommonVertex5D_S(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),(qd.sing_qp[3],qd.sing_qp[2])) + + + return DoubleQuadStrategy( + qd[1][1,i], + qd[2][1,j]) + +end + From d4794fa9fddd227722c079819861aba52481d290 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 8 Nov 2021 11:52:53 +0100 Subject: [PATCH 130/528] fixed hh3d_dirichlet --- Project.toml | 8 +- examples/betterbasis.jl | 85 -------------------- examples/builddual.jl | 129 ------------------------------ examples/helmholtz3d_dirichlet.jl | 50 ++++++------ examples/mthelmholtz.jl | 89 --------------------- examples/mtjunction.jl | 125 ----------------------------- src/quadrature/quadstrats.jl | 15 +++- 7 files changed, 43 insertions(+), 458 deletions(-) delete mode 100644 examples/betterbasis.jl delete mode 100644 examples/builddual.jl delete mode 100644 examples/mthelmholtz.jl delete mode 100644 examples/mtjunction.jl diff --git a/Project.toml b/Project.toml index c6c894e0..811ff52f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.3.0" +version = "1.3.1" [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" @@ -29,14 +29,14 @@ WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" CollisionDetection = "0.1" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.3.1" +CompScienceMeshes = "0.3.2" Compat = "2, 3" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4" FillArrays = "0.11, 0.12" IterativeSolvers = "0.9" -LiftedMaps = "0.2.1" -LinearMaps = "3.3" +LiftedMaps = "0.2.2" +LinearMaps = "3.5" SauterSchwabQuadrature = "2.1.1, 2.1" SparseMatrixDicts = "0.2" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1" diff --git a/examples/betterbasis.jl b/examples/betterbasis.jl deleted file mode 100644 index 78fac978..00000000 --- a/examples/betterbasis.jl +++ /dev/null @@ -1,85 +0,0 @@ -using CompScienceMeshes, BEAST -Base.getindex(m::CompScienceMeshes.AbstractMesh, i::Int) = cells(m)[i] -cellvertices(m::CompScienceMeshes.AbstractMesh, i::Int) = vertices(m)[cells(m)[i]] - -m = meshsphere(1.0, 0.35) -# m = meshcuboid(1.0, 1.0, 0.2, 0.2) - -X = raviartthomas(m) -Y = buffachristiansen(m, ibscaled=true) - -Id = BEAST.Identity() -Nx = BEAST.NCross() - -E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=1.0) -G = -assemble(Id, Y, Y) - -b = assemble((n×E)×n, Y) -x = G \ b - -# x = zeros(Float64,numfunctions(Y)) -# x[20] = 1 -fcr, geo = facecurrents(x, Y) - -import PlotlyJS -using LinearAlgebra -PlotlyJS.plot(patch(geo.mesh, norm.(fcr))) -# PlotlyJS.plot(patch(geo.mesh, rand(numcells(geo)))) - -edges = skeleton(m,1) -verts = skeleton(m,0) - -vertex_list = [v[1] for v in cells(verts)] -Q = lagrangec0d1(m, vertex_list, Val{3}) -Z = curl(Q) - -Gzy = assemble(Id, Z, Y) - -# vtoc, nc = vertextocellmap(edges) -D = connectivity(verts, edges) -@assert size(D) == (numcells(edges), numcells(verts)) - -los = geometry(Y) -# Choose a vertex: -for n_vert in 1:numcells(verts) - @assert cellvertices(verts, n_vert)[1] ≈ Q.pos[n_vert] -# find the connected edges - # n_edges = vtoc[n_vert,1:nc[n_vert]] - using SparseArrays - rows = rowvals(D) - vals = nonzeros(D) - for k in nzrange(D,n_vert) - n_edge = rows[k] - cht = chart(edges, edges[n_edge]) - ctr = cartesian(center(cht)) - vt1 = cht.vertices[1] - lvt = los.mesh.vertices[numvertices(m)+n_edge] - pos = Y.pos[n_edge] - @assert norm(lvt - pos) ≤ 1e-8 - @assert norm(cross(pos-vt1, ctr-vt1)) ≤ 1e-8 - end -end - -n_vert = 13 -x = [float(i == n_vert) for i in 1:numfunctions(Q)] -fcr1, geo1 = facecurrents(x, Q) -colors = getindex.(fcr1,1) -PlotlyJS.plot(patch(m, colors)) - -divY = divergence(Y) -fcr2, geo2 = facecurrents(rand(numfunctions(divY)), divY) -colors = getindex.(fcr2,1) -PlotlyJS.plot(patch(geo2.mesh, colors)) - -for n_fn in 1:numfunctions(divY) - n_fn = 21 - charges = zeros(numcells(los)) - for shape in divY.fns[n_fn] - f = shape.cellid - ch = chart(los, los[f]) - charges[f] += shape.coeff #* volume(ch) - end - nzs = (charges[(charges .≈ 0) .== false]) - xtr = extrema(nzs) - @assert all( Vector{Bool}(nzs .≈ xtr[1]) .| Vector{Bool}(nzs .≈ xtr[2])) -end diff --git a/examples/builddual.jl b/examples/builddual.jl deleted file mode 100644 index 4d965626..00000000 --- a/examples/builddual.jl +++ /dev/null @@ -1,129 +0,0 @@ -using CompScienceMeshes -using BEAST - -Faces = meshsphere(1.0, 0.35) -Edges = skeleton(Faces,1) - -faces = barycentric_refinement(Faces) -edges = skeleton(faces,1) -# verts = skeleton(faces,0) - -E = 1 -Edge = cells(Edges)[1] - -port_idx = numvertices(Faces) + E -ptch_idx = Edge[1] - -# patch = Mesh(vertices(faces), filter(c -> ptch_idx in c, cells(faces))) -patch_idcs = Int[] -for (i,face) in enumerate(cells(faces)) - if ptch_idx in face - push!(patch_idcs, i) - end -end -patch = Mesh(vertices(faces), cells(faces)[patch_idcs]) - -port = Mesh(vertices(edges), filter(c -> port_idx in c, cells(boundary(patch)))) - -@show numcells(patch) -@show numcells(port) - -# D, C, d, c, d0, d1, -RT_int, RT_prt, x_int, x_prt = BEAST.buildhalfbc2(patch, port, nothing) - -BF = BEAST.Shape{Float64}[] -for (m,bf) in enumerate(RT_int.fns) - for sh in bf - cellid = patch_idcs[sh.cellid] - BEAST.add!(BF,cellid, sh.refid, sh.coeff * x_int[m]) - end -end - -for (m,bf) in enumerate(RT_prt.fns) - for sh in bf - cellid = patch_idcs[sh.cellid] - BEAST.add!(BF,cellid, sh.refid, sh.coeff * x_prt[m]) - end -end - - -# RT_prt = raviartthomas(patch, cellpairs(patch, port)) -# Lx = lagrangecxd0(patch) -# @assert numfunctions(Lx) == numcells(patch) -# -# Id = BEAST.Identity() -# div_RT_prt = divergence(RT_prt) -# Z, store = BEAST.allocatestorage(Id, Lx, div_RT_prt, Val{:bandedstorage}, BEAST.LongDelays{:ignore}) -# BEAST.assemble_local_mixed!(Id, Lx, div_RT_prt, store) - -bcs3 = BEAST.buffachristiansen3(Faces) -bcs2 = BEAST.buffachristiansen2(Faces) -bcs1 = BEAST.buffachristiansen(Faces) -rts = BEAST.raviartthomas(Faces) - -G1 = assemble(BEAST.NCross(), bcs1, rts) -Q1 = assemble(BEAST.Identity(), divergence(bcs1), divergence(bcs1)) - -G2 = assemble(BEAST.NCross(), bcs2, rts) -Q2 = assemble(BEAST.Identity(), divergence(bcs2), divergence(bcs2)) - -G3 = assemble(BEAST.NCross(), bcs3, rts) -Q3 = assemble(BEAST.Identity(), divergence(bcs3), divergence(bcs3)) - -using LinearAlgebra -@show cond(G3) - -using LinearAlgebra -@assert (numcells(Faces) - 1) == (size(Q,1) - rank(Q)) -@assert cond(G) < 3.5 - -function compress!(space) - T = scalartype(space) - for (i,fn) in pairs(space.fns) - shapes = Dict{Tuple{Int,Int},T}() - for shape in fn - v = get(shapes, (shape.cellid, shape.refid), zero(T)) - shapes[(shape.cellid, shape.refid)] = v + shape.coeff - # set!(shapes, (shape.cellid, shape.refid), v + shape.coeff) - end - space.fns[i] = [BEAST.Shape(k[1], k[2], v) for (k,v) in shapes] - end -end - -getch(geo,i) = chart(geo, cells(geo)[i]) - - -import PlotlyJS -function showfn(space,i) - geo = geometry(space) - T = coordtype(geo) - X = Dict{Int,T}() - Y = Dict{Int,T}() - Z = Dict{Int,T}() - U = Dict{Int,T}() - V = Dict{Int,T}() - W = Dict{Int,T}() - for sh in space.fns[i] - chrt = chart(geo, cells(geo)[sh.cellid]) - nbd = center(chrt) - vals = refspace(space)(nbd) - x,y,z = cartesian(nbd) - @show vals[sh.refid].value - u,v,w = vals[sh.refid].value - # @show x, y, z - # @show u, v, w - X[sh.cellid] = x - Y[sh.cellid] = y - Z[sh.cellid] = z - U[sh.cellid] = get(U,sh.cellid,zero(T)) + sh.coeff * u - V[sh.cellid] = get(V,sh.cellid,zero(T)) + sh.coeff * v - W[sh.cellid] = get(W,sh.cellid,zero(T)) + sh.coeff * w - end - X = collect(values(X)) - Y = collect(values(Y)) - Z = collect(values(Z)) - U = collect(values(U)) - V = collect(values(V)) - W = collect(values(W)) - PlotlyJS.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) -end diff --git a/examples/helmholtz3d_dirichlet.jl b/examples/helmholtz3d_dirichlet.jl index f77300db..d5a8db0e 100644 --- a/examples/helmholtz3d_dirichlet.jl +++ b/examples/helmholtz3d_dirichlet.jl @@ -32,28 +32,30 @@ fcr1, geo1 = facecurrents(x1, X) fcr2, geo2 = facecurrents(x2, X) # include(Pkg.dir("CompScienceMeshes","examples","plotlyjs_patches.jl")) -patch(geo1, real.(norm.(fcr1))) -patch(geo2, real.(norm.(fcr2))) - using LinearAlgebra -using Test - -## test the results -Z = assemble(a,X,X); -m1, m2 = 1, numfunctions(X) -chm, chn = chart(Γ,cells(Γ)[m1]), chart(Γ,cells(Γ)[m2]) -ctm, ctn = CompScienceMeshes.center(chm), CompScienceMeshes.center(chn) -R = norm(cartesian(ctm)-cartesian(ctn)) -G = exp(-im*κ*R)/(4π*R) -Wmn = volume(chm) * volume(chn) * G -@show abs(Wmn-Z[m1,m2]) / abs(Z[m1,m2]) -@test abs(Wmn-Z[m1,m2]) / abs(Z[m1,m2]) < 2.0e-3 - -r = assemble(f,X) -m1 = 1 -chm = chart(Γ,cells(Γ)[m1]) -ctm = CompScienceMeshes.center(chm) -sm = volume(chm) * f(ctm) -r[m1] -@show abs(sm - r[m1]) / abs(r[m1]) -@test abs(sm - r[m1]) / abs(r[m1]) < 1e-3 +using Plotly +pt1 = Plotly.plot(patch(geo1, real.(norm.(fcr1)))); +pt2 = Plotly.plot(patch(geo2, real.(norm.(fcr2)))); +display([pt1 pt2]) + +# using Test + +# ## test the results +# Z = assemble(a,X,X); +# m1, m2 = 1, numfunctions(X) +# chm, chn = chart(Γ,cells(Γ)[m1]), chart(Γ,cells(Γ)[m2]) +# ctm, ctn = CompScienceMeshes.center(chm), CompScienceMeshes.center(chn) +# R = norm(cartesian(ctm)-cartesian(ctn)) +# G = exp(-im*κ*R)/(4π*R) +# Wmn = volume(chm) * volume(chn) * G +# @show abs(Wmn-Z[m1,m2]) / abs(Z[m1,m2]) +# @test abs(Wmn-Z[m1,m2]) / abs(Z[m1,m2]) < 2.0e-3 + +# r = assemble(f,X) +# m1 = 1 +# chm = chart(Γ,cells(Γ)[m1]) +# ctm = CompScienceMeshes.center(chm) +# sm = volume(chm) * f(ctm) +# r[m1] +# @show abs(sm - r[m1]) / abs(r[m1]) +# @test abs(sm - r[m1]) / abs(r[m1]) < 1e-3 diff --git a/examples/mthelmholtz.jl b/examples/mthelmholtz.jl deleted file mode 100644 index 553295bc..00000000 --- a/examples/mthelmholtz.jl +++ /dev/null @@ -1,89 +0,0 @@ -using CompScienceMeshes, BEAST - -function blkdiagm(blks...) - m = sum(size(b,1) for b in blks) - n = sum(size(b,2) for b in blks) - A = zeros(eltype(first(blks)), m, n) - o1 = 1 - o2 = 1 - for b in blks - m1 = size(b,1) - n1 = size(b,2) - A[o1:o1+m1-1,o2:o2+n1-1] .= b - o1 += m1 - o2 += n1 - end - A -end - -width, height, h = 1.0, 0.5, 0.05 -G1 = meshrectangle(width, height, h) -G2 = CompScienceMeshes.rotate(G1, 0.5π * x̂) -G3 = CompScienceMeshes.rotate(G1, 1.0π * x̂) - -G12 = weld(G1,-G2) -G23 = weld(G2,-G3) -G31 = weld(G3,-G1) - -X12 = lagrangec0d1(G12) -X23 = lagrangec0d1(G23) -X31 = lagrangec0d1(G31) - -Y12 = duallagrangecxd0(G12) -Y23 = duallagrangecxd0(G23) -Y31 = duallagrangecxd0(G31) - -κ = 1.0 -SL = Helmholtz3D.singlelayer(wavenumber=κ) -HS = Helmholtz3D.hypersingular(gamma=κ*im) -N = Identity() - -X = X12 × X23 × X31 -Y = Y12 × Y23 × Y31 - -E = Helmholtz3D.planewave(direction=ẑ, wavenumber=κ) -e = strace(E, G12) -ex = assemble(e, X) - -Sxx = assemble(SL, X, X) -Syy = assemble(HS, Y, Y) - -N1 = Matrix(assemble(Identity(), X12, Y12)) -N2 = Matrix(assemble(Identity(), X23, Y23)) -N3 = Matrix(assemble(Identity(), X31, Y31)) -Dyx = blkdiagm(inv(N1),inv(N2),inv(N3)) - -Q = transpose(Dyx) * Syy * Dyx * Sxx -R = transpose(Dyx) * Syy * Dyx * ex - -u1, ch1 = solve(BEAST.GMRESSolver(Sxx),ex) -u2, ch2 = solve(BEAST.GMRESSolver(Q),R) - -@show ch1.iters -@show ch2.iters - -rad, ϕ = 10, π/2 -thetas = range(0,π,length=200) -pts = [rad*point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in thetas] - -DLp = BEAST.HH3DDoubleLayerNear(wavenumber=κ) -near1 = BEAST.potential(DLp, pts, u1, X, type=ComplexF64) -near2 = BEAST.potential(DLp, pts, u2, X, type=ComplexF64) -inc = E.(pts) - -using Plots -plot(thetas, abs.(vec(near1))) -scatter!(thetas, abs.(vec(near2))) - -using LinearAlgebra -cond(Sxx) -cond(Q) - -w1 = eigvals(Sxx) -w2 = eigvals(Q) - -using Plots -plot() -scatter!(w1) -scatter!(w2) - diff --git a/examples/mtjunction.jl b/examples/mtjunction.jl deleted file mode 100644 index 88644bb4..00000000 --- a/examples/mtjunction.jl +++ /dev/null @@ -1,125 +0,0 @@ -using CompScienceMeshes, BEAST - -width, height, h = 1.0, 0.5, 0.05 -G1 = meshrectangle(width, height, h) -G2 = CompScienceMeshes.rotate(G1, 1.0π * x̂) -G3 = CompScienceMeshes.rotate(G1, 1.5π * x̂) - -G12 = weld(G1,-G2) -G23 = weld(G2,-G3) -G31 = weld(G3,-G1) - -G21 = weld(G2,-G1) - -X12 = raviartthomas(G12, sort=:none) -X23 = raviartthomas(G23, sort=:none) -X31 = raviartthomas(G31, sort=:none) - -X21 = raviartthomas(G21, sort=:none) - -Y12 = buffachristiansen(G12, sort=:none) -Y23 = buffachristiansen(G23, sort=:none) -Y31 = buffachristiansen(G31, sort=:none) - -Y21 = buffachristiansen(G21) - -κ = 1.0 -SL = Maxwell3D.singlelayer(wavenumber=κ) -N = NCross() - -X = X12 × X23 × X31 -Y = Y12 × Y23 × Y31 - -# X = X12 × X21 -# Y = Y12 × Y21 - -E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) -e = (n × E) × n - -@hilbertspace j -@hilbertspace k -mtefie = @discretise( - SL[k,j] == e[k], - j ∈ X, k ∈ X) - -# u = gmres(mtefie) -# -# offset = 1; u12 = u[offset:offset+numfunctions(X12)-1] -# offset += numfunctions(X12); u23 = u[offset:offset+numfunctions(X23)-1] -# offset += numfunctions(X23); u31 = u[offset:offset+numfunctions(X31)-1] -# -# fcr12, _ = facecurrents(u12, X12) -# fcr23, _ = facecurrents(u23, X23) -# fcr31, _ = facecurrents(u31, X31) - -import Plotly -using LinearAlgebra - -function blkdiagm(blks...) - m = sum(size(b,1) for b in blks) - n = sum(size(b,2) for b in blks) - A = zeros(eltype(first(blks)), m, n) - o1 = 1 - o2 = 1 - for b in blks - m1 = size(b,1) - n1 = size(b,2) - A[o1:o1+m1-1,o2:o2+n1-1] .= b - o1 += m1 - o2 += n1 - end - A -end - -Sxx = BEAST.sysmatrix(mtefie) -N1 = assemble(N, X12, Y12) -N2 = assemble(N, X23, Y23) -N3 = assemble(N, X31, Y31) -Nxy = blkdiagm(N1,N2,N3) -Syy = assemble(SL, Y, Y) -iNxy = blkdiagm(inv(Matrix(N1)), inv(Matrix(N2)), inv(Matrix(N3))) - -# N21 = assemble(N, X21, Y21) -# Nxy = blkdiagm(N1, N21) -# iNxy = blkdiagm(inv(Matrix(N1)), inv(Matrix(N21))) - -ex = assemble(e,X) -P = transpose(iNxy) * Syy * iNxy -Q = P * Sxx; -R = P * ex; - - -u1, ch1 = solve(BEAST.GMRESSolver(Sxx), ex) -u2, ch2 = solve(BEAST.GMRESSolver(Q), R) - -@show ch1.iters -@show ch2.iters - -G123 = weld(G1,G2,G3) -X123 = raviartthomas(G123) - -stefie = @discretise( - SL[k,j] == e[k], - j ∈ X123, k ∈ X123) - -Sst = assemble(SL, X123, X123) -bst = assemble(e, X123) -ust, chst = solve(BEAST.GMRESSolver(Sst),bst) - -fcrst, geost = facecurrents(ust, X123) -Plotly.plot(patch(geost, norm.(fcrst))) - - -Φ, Θ = [0.0], range(0,stop=π,length=100) -pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] - -ffd_mt = potential(MWFarField3D(wavenumber=κ), pts, u1, X) -ffd_mt_pc = potential(MWFarField3D(wavenumber=κ), pts, u2, X) -ffd_err = potential(MWFarField3D(wavenumber=κ), pts, u1-u2, X) -ffd_st = potential(MWFarField3D(wavenumber=κ), pts, ust, X123) - -using Plots -plot(norm.(ffd_mt)) -scatter!(norm.(ffd_mt_pc)) -scatter!(norm.(ffd_st)) -scatter!(norm.(ffd_err)) \ No newline at end of file diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index 22a0b4c4..7eb033f1 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -17,9 +17,20 @@ struct DoubleNumQStrat{R} end -# defaultquadstrat(op, tfs, bfs) = DoubleNumWiltonSauterQStrat(2,3,6,7,6,5,4,3) defaultquadstrat(op, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bfs)) -# defaultquadstrat(op, ::RefSpace, ::RefSpace) = error("No default quadstrat set.") +# macro defaultquadstrat(dop, body) +# @assert dop.head == :tuple +# @assert length(dop.args) == 3 +# op = dop.args[1] +# tfs = dop.args[2] +# bfs = dop.args[3] +# ex = quote +# function BEAST.defaultquadstrat(::typeof($op), ::typeof($tfs), ::typeof($bfs)) +# $body +# end +# end +# return esc(ex) +# end struct SingleNumQStrat quad_rule::Int From 31c81fb2b5235000bffbef42f8ec519f1f51d843 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 8 Nov 2021 13:10:33 +0100 Subject: [PATCH 131/528] require SSQ 2.1.3 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 811ff52f..5c75bdda 100644 --- a/Project.toml +++ b/Project.toml @@ -37,7 +37,7 @@ FillArrays = "0.11, 0.12" IterativeSolvers = "0.9" LiftedMaps = "0.2.2" LinearMaps = "3.5" -SauterSchwabQuadrature = "2.1.1, 2.1" +SauterSchwabQuadrature = "2.1.3" SparseMatrixDicts = "0.2" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" From 0f93cbe5cd183b523f262846dd68eb3cb5f9b9e4 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 8 Nov 2021 13:10:49 +0100 Subject: [PATCH 132/528] delete sandbox from examples --- examples/sandbox.jl | 41 ----------------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 examples/sandbox.jl diff --git a/examples/sandbox.jl b/examples/sandbox.jl deleted file mode 100644 index d1f63f40..00000000 --- a/examples/sandbox.jl +++ /dev/null @@ -1,41 +0,0 @@ -using CompScienceMeshes -using BEAST - -m = meshsphere(1.0, 0.45) -# m = CompScienceMeshes.rotate(m, rand(3)) -X = raviartthomas(m) - -Y = buffachristiansen(m) -# Y = X -Y = BEAST.subset(Y,1:135) -geo = geometry(Y) - -els, ad = BEAST.assemblydata(Y) -Base.length(it::BEAST.ADIterator) = something(findfirst(x->x[1]<1, it.ad.data[:,it.r,it.c]), size(it.ad.data,1)+1)-1 - -Ad = [Int[] for c in 1:length(els)] -for c in 1:length(els) - for r in 1:3 - length(ad[c,r]) == 0 && continue - push!(Ad[c], maximum(m for (m,a) in ad[c,r])) - end -end -top = maximum.(Ad) -bot = minimum.(Ad) - -using Plots - -plot() -plot!(top) -plot!(bot) - -import PlotlyJS - -t1 = patch(Mesh(geo.mesh.vertices, geo.mesh.faces[1:150]), rand(150)) -P = [p[i] for p in Y.pos, i in 1:3] -t2 = PlotlyJS.scatter3d(x=P[1:38,1], y=P[1:38,2], z=P[1:38,3]) -t3 = PlotlyJS.scatter3d(x=P[264:264,1], y=P[264:264,2], z=P[264:264,3]) -t4 = PlotlyJS.scatter3d(x=P[1:180,1], y=P[1:180,2], z=P[1:180,3]) -PlotlyJS.plot(t4) - -PlotlyJS.plot([t1,t2,t3]) From a6e195df7dc5268ddbf7a2a0fe57d96d8bd5c66c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 8 Nov 2021 13:36:44 +0100 Subject: [PATCH 133/528] Bump Project.toml version to 1.4.0 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 5c75bdda..0362094e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.3.1" +version = "1.4.0" [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" From 01a32c1ff69669ef878318c01af9433a86e89c63 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Mon, 15 Nov 2021 15:16:02 +0100 Subject: [PATCH 134/528] Merge upstream 2 --- .github/workflows/CI.yml | 4 -- Project.toml | 26 ------------ examples/helmholtz3d_neumann.jl | 7 ---- examples/pmchwt.jl | 70 -------------------------------- src/BEAST.jl | 13 ------ src/identityop.jl | 71 --------------------------------- src/integralop.jl | 4 -- src/localop.jl | 27 ------------- src/maxwell/mwops.jl | 4 -- src/postproc.jl | 5 --- src/solvers/itsolver.jl | 19 --------- src/solvers/solver.jl | 29 -------------- src/timedomain/tdtimeops.jl | 15 ------- 13 files changed, 294 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 5bb52d4d..fd462381 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -15,11 +15,7 @@ jobs: fail-fast: false matrix: version: -<<<<<<< HEAD - - '1.3' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'. -======= - '1.6' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'. ->>>>>>> upstream/master - '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia. - 'nightly' os: diff --git a/Project.toml b/Project.toml index 2f9eeb62..0362094e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,10 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -<<<<<<< HEAD -version = "1.3.0" -======= version = "1.4.0" ->>>>>>> upstream/master [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" @@ -16,10 +12,7 @@ Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" -<<<<<<< HEAD -======= InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" ->>>>>>> upstream/master IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LiftedMaps = "d22a30c1-52ac-4762-a8c9-5838452405e0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -33,24 +26,6 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] -<<<<<<< HEAD -BlockArrays = "^0.10, ^0.11, ^0.12, ^0.13, ^0.14, 0.15, 0.16" -CollisionDetection = "^0.1" -Combinatorics = "^0.7, ^1" -CompScienceMeshes = "^0.3" -Compat = "^2, ^3" -FFTW = "^0.2.3, ^1" -FastGaussQuadrature = "^0.3, ^0.4" -FillArrays = "0.11, 0.12" -IterativeSolvers = "^0.9" -LinearMaps = "^3.3" -SauterSchwabQuadrature = "^2.1.1, 2.1" -SparseMatrixDicts = "0.2" -SpecialFunctions = "^0.7, ^0.8, ^0.9, ^0.10, ^1" -StaticArrays = "^0.8.3, ^0.9, ^0.10, ^0.11, ^0.12, ^1" -WiltonInts84 = "^0.2" -julia = "^1.3" -======= BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" CollisionDetection = "0.1" Combinatorics = "0.7, 1" @@ -68,7 +43,6 @@ SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" WiltonInts84 = "0.2" julia = "1.6" ->>>>>>> upstream/master [extras] DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" diff --git a/examples/helmholtz3d_neumann.jl b/examples/helmholtz3d_neumann.jl index 83d9c331..0df708b6 100644 --- a/examples/helmholtz3d_neumann.jl +++ b/examples/helmholtz3d_neumann.jl @@ -4,10 +4,7 @@ using LinearAlgebra, Pkg Pkg.activate(@__DIR__) # Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -<<<<<<< HEAD -======= # Γ = meshrectangle(1.0, 1.0, 0.05, 3) ->>>>>>> upstream/master X = lagrangec0d1(Γ) @show numfunctions(X) @@ -39,9 +36,6 @@ scatter!(norm.(fcr2),c=:red,label="2nd") import Plotly Plotly.plot(patch(Γ, norm.(fcr1))) -<<<<<<< HEAD -Plotly.plot(patch(Γ, norm.(fcr2))) -======= Plotly.plot(patch(Γ, norm.(fcr2))) @@ -53,4 +47,3 @@ nf = BEAST.HH3DDoubleLayerNear(wavenumber=κ) near = BEAST.potential(nf, pts, x1, X, type=ComplexF64) inc = uⁱ.(pts) tot = near + inc ->>>>>>> upstream/master diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 63a1f620..82fed954 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -1,33 +1,18 @@ using CompScienceMeshes, BEAST using LinearAlgebra -<<<<<<< HEAD #T = tetmeshcuboid(1.0,1.0,1.0,0.1) T = tetmeshsphere(1.0,0.25) X = nedelecc3d(T) Γ = boundary(T) #Γ = meshcuboid(0.5, 1.0, 1.0, 0.075) #CompScienceMeshes.translate!(Γ, point(-0.25, -0.5, -0.5)) -======= -using BEAST.LinearMaps -Base.axes(A::LinearMaps.LinearCombination) = axes(A.maps[1]) -Base.axes(A::LinearMaps.ScaledMap) = axes(A.lmap) - -Γ = meshcuboid(0.5, 1.0, 1.0, 0.045) -# Γ = meshcuboid(0.5, 1.0, 1.0, 0.15) -CompScienceMeshes.translate!(Γ, point(-0.25, -0.5, -0.5)) ->>>>>>> upstream/master # Γ = meshsphere(1.0, 0.075) X = raviartthomas(Γ) @show numfunctions(X) -<<<<<<< HEAD κ, η = 1.0, 1.0 κ′, η′ = 2.0κ, η/2.0 -======= -κ, η = 6.0, 1.0 -κ′, η′ = 2.4κ, η/2.4 ->>>>>>> upstream/master T = Maxwell3D.singlelayer(wavenumber=κ) T′ = Maxwell3D.singlelayer(wavenumber=κ′) @@ -66,25 +51,13 @@ pmchwt = @discretise( # restart=1000, reltol=1e-5, verbose=true) -<<<<<<< HEAD -======= - -# u = BEAST.gmres(pmchwt, tol=1e-5) -# error() -u = BEAST.solve(pmchwt) - ->>>>>>> upstream/master Θ, Φ = range(0.0,stop=2π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] # Don't forget the far field comprises two contributions ffm = potential(MWFarField3D(κ*im), ffpoints, u[m], X) ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], X) -<<<<<<< HEAD ff = η*im*κ*ffm + im*κ*cross.(ffpoints, ffj) -======= -ff = η*im*κ*ffj + im*κ*cross.(ffpoints, ffj) ->>>>>>> upstream/master using Plots plot(xlabel="theta") @@ -116,19 +89,9 @@ function nearfield(um,uj,Xm,Xj,κ,η,points, return E, H end -<<<<<<< HEAD -#Z = range(-0.1,1.1,length=100) -#Y = range(-0.1,1.1,length=100) -#nfpoints = [point(0.5,y,z) for y in Y, z in Z] - -Z = range(-1,1,length=100) -Y = range(-1,1,length=100) -nfpoints = [point(0,y,z) for y in Y, z in Z] -======= Z = range(-1,1,length=100) Y = range(-1,1,length=100) nfpoints = [point(0,y,z) for z in Z, y in Y] ->>>>>>> upstream/master E_ex, H_ex = nearfield(u[m],u[j],X,X,κ,η,nfpoints,E,H) E_in, H_in = nearfield(-u[m],-u[j],X,X,κ′,η′,nfpoints) @@ -137,38 +100,6 @@ E_tot = E_in + E_ex H_tot = H_in + H_ex contour(real.(getindex.(E_tot,1))) -<<<<<<< HEAD -heatmap(Z, Y, real.(getindex.(E_tot,1)), clim=(0.0,1.0)) -#plot(real.(getindex.(E_tot[:],1))) - -#= -nfpoints = [point(0.0,0.0,z) for z in Z] -E_ex, H_ex = nearfield(u[m],u[j],X,X,κ,η,nfpoints,E,H) -E_in, H_in = nearfield(-u[m],-u[j],X,X,κ′,η′,nfpoints) - -E_tot = E_in + E_ex -H_tot = H_in + H_ex - -plot(real.(getindex.(E_tot[:],1))) - -=# - -#= -contour(real.(getindex.(H_tot,2))) -heatmap(Z, Y, real.(getindex.(H_tot,2))) -#plot(real.(getindex.(H_tot[:,51],2))) - -nfpoints = [point(0,0,z) for z in Z] - -E_ex, H_ex = nearfield(u[m],u[j],X,X,κ,η,nfpoints,E,H) -E_in, H_in = nearfield(-u[m],-u[j],X,X,κ′,η′,nfpoints) - -E_tot = E_in + E_ex -H_tot = H_in + H_ex - -plot(real.(getindex.(E_tot[:],1))) -=# -======= contour(real.(getindex.(H_tot,2))) heatmap(Z, Y, real.(getindex.(E_tot,1))) @@ -176,4 +107,3 @@ heatmap(Z, Y, real.(getindex.(H_tot,2))) plot(real.(getindex.(E_tot[:,51],1))) plot!(real.(getindex.(H_tot[:,51],2))) ->>>>>>> upstream/master diff --git a/src/BEAST.jl b/src/BEAST.jl index 7bf03ade..e06c5be5 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -12,11 +12,8 @@ using SauterSchwabQuadrature using SauterSchwab3D using FastGaussQuadrature using LinearMaps -<<<<<<< HEAD using ShunnHamQuadrature -======= using LiftedMaps ->>>>>>> upstream/master import LinearAlgebra: cross, dot import LinearAlgebra: ×, ⋅ @@ -129,11 +126,7 @@ include("utils/combinatorics.jl") include("utils/linearspace.jl") include("utils/matrixconv.jl") include("utils/polyeig.jl") -<<<<<<< HEAD -include("utils/liftmap.jl") -======= include("utils/zeromap.jl") ->>>>>>> upstream/master include("bases/basis.jl") include("bases/lincomb.jl") @@ -181,12 +174,6 @@ include("quaddata.jl") include("postproc.jl") include("postproc/segcurrents.jl") -<<<<<<< HEAD -include("quadrature/double_quadrature.jl") -include("quadrature/singularity_extraction.jl") - -======= ->>>>>>> upstream/master include("timedomain/tdintegralop.jl") include("timedomain/tdexcitation.jl") include("timedomain/motlu.jl") diff --git a/src/identityop.jl b/src/identityop.jl index 19a05cd3..dc626750 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -18,59 +18,6 @@ function _alloc_workspace(qd, g, f, tels, bels) w, p = q[1], neighborhood(τ,q[2]) a = (w, p, g(p), f(p)) A = Vector{typeof(a)}(undef,length(qd)) -<<<<<<< HEAD -end - -const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace} -function quaddata(op::LocalOperator, g::LinearRefSpaceTriangle, f::LinearRefSpaceTriangle, tels, bels) - u, w = trgauss(6) - qd = [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] - A = _alloc_workspace(qd, g, f, tels, bels) - - return qd, A -end - -function quaddata(op::LocalOperator, g::BDMRefSpace, f::BDMRefSpace, tels, bels) - u, w = trgauss(6) - return [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] -end - -function quaddata(op::LocalOperator, g::subReferenceSpace, f::subReferenceSpace, tels, bels) - u, w = trgauss(6) - qd = [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] - A = _alloc_workspace(qd, g, f, tels, bels) - return qd, A -end - -const LinearRefSpaceTetr = Union{NDLCCRefSpace, NDLCDRefSpace} -function quaddata(op::LocalOperator, g::LinearRefSpaceTetr, f::LinearRefSpaceTetr, tels, bels) - o, x, y, z = CompScienceMeshes.euclidianbasis(3) - reftet = simplex(x,y,z,o) - qps = quadpoints(reftet, 3) - qd = [(w, parametric(p)) for (p,w) in qps] - A = _alloc_workspace(qd, g, f, tels, bels) - return qd, A -end - -function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,2} where {T,Deg}, - f::LagrangeRefSpace, tels::Vector, bels::Vector) - - u, w = legendre(6, 0.0, 1.0) - qd = [(w[i],u[i]) for i in eachindex(w)] - A = _alloc_workspace(qd, g, f, tels, bels) - return qd, A -end - -function quaddata(op::LocalOperator, g::BDM3DRefSpace, f::BDM3DRefSpace, tels, bels) - o, x, y, z, = CompScienceMeshes.euclidianbasis(3) - reftet = simplex(x,y,z,o) - qps = quadpoints(reftet, 6) - [(w, parametric(p)) for (p,w) in qps] -end - -function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,3} where {T,Deg}, - f::LagrangeRefSpace, tels::Vector, bels::Vector) -======= end # defaultquadstrat(op::LocalOperator, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bfs)) @@ -103,7 +50,6 @@ end const LinearRefSpaceTetr = Union{NDLCCRefSpace, NDLCDRefSpace} defaultquadstrat(::LocalOperator, ::LinearRefSpaceTetr, ::LinearRefSpaceTetr) = SingleNumQStrat(3) function quaddata(op::LocalOperator, g::LinearRefSpaceTetr, f::LinearRefSpaceTetr, tels, bels, qs::SingleNumQStrat) ->>>>>>> upstream/master o, x, y, z = CompScienceMeshes.euclidianbasis(3) reftet = simplex(x,y,z,o) @@ -113,9 +59,6 @@ function quaddata(op::LocalOperator, g::LinearRefSpaceTetr, f::LinearRefSpaceTet return qd, A end -<<<<<<< HEAD - u, w = trgauss(6) -======= defaultquadstrat(::LocalOperator, ::LagrangeRefSpace{T,D1,2}, ::LagrangeRefSpace{T,D2,2}) where {T,D1,D2} = SingleNumQStrat(6) function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,2} where {T,Deg}, f::LagrangeRefSpace, tels::Vector, bels::Vector, qs::SingleNumQStrat) @@ -131,20 +74,11 @@ function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,3} where {T,Deg}, f::LagrangeRefSpace, tels::Vector, bels::Vector, qs::SingleNumQStrat) u, w = trgauss(qs.quad_rule) ->>>>>>> upstream/master qd = [(w[i], SVector(u[1,i], u[2,i])) for i in 1:length(w)] A = _alloc_workspace(qd, g, f, tels, bels) return qd, A end -<<<<<<< HEAD -function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,4} where {T,Deg}, - f::LagrangeRefSpace, tels::Vector, bels::Vector) - - o, x, y, z = CompScienceMeshes.euclidianbasis(3) - reftet = simplex(x,y,z,o) - qps = quadpoints(reftet, 6) -======= defaultquadstrat(::LocalOperator, ::LagrangeRefSpace{T,D1,4}, ::LagrangeRefSpace{T,D2,4}) where {T,D1,D2} = SingleNumQStrat(6) function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,4} where {T,Deg}, f::LagrangeRefSpace, tels::Vector, bels::Vector, qs::SingleNumQStrat) @@ -152,18 +86,13 @@ function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,4} where {T,Deg}, o, x, y, z = CompScienceMeshes.euclidianbasis(3) reftet = simplex(x,y,z,o) qps = quadpoints(reftet, qs.quad_rule) ->>>>>>> upstream/master qd = [(w, parametric(p)) for (p,w) in qps] A = _alloc_workspace(qd, g, f, tels, bels) return qd, A end -<<<<<<< HEAD -function quadrule(op::LocalOperator, ψ::RefSpace, ϕ::RefSpace, τ, (qd,A)) -======= function quadrule(op::LocalOperator, ψ::RefSpace, ϕ::RefSpace, τ, (qd,A), qs::SingleNumQStrat) ->>>>>>> upstream/master for i in eachindex(qd) q = qd[i] w, p = q[1], neighborhood(τ,q[2]) diff --git a/src/integralop.jl b/src/integralop.jl index 36ed89a2..74f9a752 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -155,11 +155,7 @@ function assemblechunk_body_nested_meshes!(biop, if new_pctg > pctg + 9 myid == 1 && print(".") pctg = new_pctg -<<<<<<< HEAD - end end -======= end end ->>>>>>> upstream/master myid == 1 && println("") end diff --git a/src/localop.jl b/src/localop.jl index c9d5b02e..dfafa62d 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -2,13 +2,6 @@ using CollisionDetection -<<<<<<< HEAD -# mutable struct SingleQuadStrategy{T} -# coords::Vector{T} -# weights::Vector{T} -# end -======= ->>>>>>> upstream/master abstract type LocalOperator <: Operator end @@ -65,12 +58,8 @@ function allocatestorage(op::LocalOperator, test_functions, trial_functions, end function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, -<<<<<<< HEAD - threading::Type{Threading{:multi}}) -======= threading::Type{Threading{:multi}}; quadstrat=defaultquadstrat(biop, tfs, bfs)) ->>>>>>> upstream/master if geometry(tfs) == geometry(bfs) return assemble_local_matched!(biop, tfs, bfs, store; quadstrat) @@ -80,11 +69,7 @@ function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, return assemble_local_refines!(biop, tfs, bfs, store; quadstrat) end -<<<<<<< HEAD - return assemble_local_mixed!(biop, tfs, bfs, store) -======= return assemble_local_mixed!(biop, tfs, bfs, store; quadstrat) ->>>>>>> upstream/master end function assemble_local_matched!(biop::LocalOperator, tfs::Space, bfs::Space, store; @@ -99,22 +84,14 @@ function assemble_local_matched!(biop::LocalOperator, tfs::Space, bfs::Space, st trefs = refspace(tfs) brefs = refspace(bfs) -<<<<<<< HEAD - qd = quaddata(biop, trefs, brefs, tels, bels) -======= qd = quaddata(biop, trefs, brefs, tels, bels, quadstrat) ->>>>>>> upstream/master locmat = zeros(scalartype(biop, trefs, brefs), numfunctions(trefs), numfunctions(brefs)) for (p,cell) in enumerate(tels) P = ta2g[p] q = bg2a[P] q == 0 && continue -<<<<<<< HEAD - qr = quadrule(biop, trefs, brefs, cell, qd) -======= qr = quadrule(biop, trefs, brefs, cell, qd, quadstrat) ->>>>>>> upstream/master fill!(locmat, 0) cellinteractions_matched!(locmat, biop, trefs, brefs, cell, qr) @@ -142,11 +119,7 @@ function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, st bg2a = zeros(Int, length(geometry(bfs))) for (i,j) in enumerate(ba2g) bg2a[j] = i end -<<<<<<< HEAD - qd = quaddata(biop, trefs, brefs, tels, bels) -======= qd = quaddata(biop, trefs, brefs, tels, bels, quadstrat) ->>>>>>> upstream/master print("dots out of 10: ") todo, done, pctg = length(tels), 0, 0 diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 38d3ac4d..6c65c084 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -171,11 +171,7 @@ function quadrule(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j h2 = volume(σ) xtol2 = 0.2 * 0.2 k2 = abs2(op.gamma) -<<<<<<< HEAD - max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSEStrategy( -======= max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( ->>>>>>> upstream/master qd.tpoints[2,i], DoubleQuadRule( qd.tpoints[2,i], diff --git a/src/postproc.jl b/src/postproc.jl index ec4988f6..b2aec6ab 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -127,14 +127,9 @@ function facecurrents(u, X::DirectProductSpace) fcr, m end -<<<<<<< HEAD -function potential(op, points, coeffs, basis) - T = SVector{3,ComplexF64} -======= function potential(op, points, coeffs, basis; type=SVector{3,ComplexF64}) # T = SVector{3,ComplexF64} T = type ->>>>>>> upstream/master ff = zeros(T, size(points)) store(v,m,n) = (ff[m] += v*coeffs[n]) potential!(store, op, points, basis, type=T) diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index fe427146..66b5876d 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -33,8 +33,6 @@ function solve(solver::GMRESSolver, b) op = operator(solver) x, ch = IterativeSolvers.gmres(op, b, log=true, maxiter=solver.maxiter, restart=solver.restart, reltol=solver.tol, verbose=true) -<<<<<<< HEAD -======= return x, ch end @@ -43,19 +41,10 @@ function solve!(x, solver::GMRESSolver, b) op = operator(solver) x, ch = IterativeSolvers.gmres!(x, op, b, log=true, maxiter=solver.maxiter, restart=solver.restart, reltol=solver.tol, verbose=true) ->>>>>>> upstream/master return x, ch end -<<<<<<< HEAD -# function Base.:*(solver::GMRESSolver, b) -# x, ch = solve(solver, b) -# println("Number of iterations: ", ch.iters) -# ch.isconverged || error("Iterative solver did not converge.") -# return x -# end -======= function Base.:*(A::GMRESSolver, b::AbstractVector) T = promote_type(eltype(A), eltype(b)) @@ -68,23 +57,15 @@ function Base.:*(A::GMRESSolver, b::AbstractVector) # ch.isconverged || error("Iterative solver did not converge.") # return x end ->>>>>>> upstream/master Base.size(solver::GMRESSolver) = reverse(size(solver.linear_operator)) function LinearAlgebra.mul!(y::AbstractVecOrMat, solver::GMRESSolver, x::AbstractVector) -<<<<<<< HEAD - temp, ch = solve(solver, x) - println("Number of iterations: ", ch.iters) - ch.isconverged || error("Iterative solver did not converge.") - y .= temp -======= fill!(y,0) y, ch = solve!(y, solver, x) println("Number of iterations: ", ch.iters) ch.isconverged || error("Iterative solver did not converge.") return y ->>>>>>> upstream/master end diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index f92ddbb1..0d6e8090 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -228,10 +228,7 @@ function assemble_hide(bilform::BilForm, test_space_dict, trial_space_dict) end z = assemble(a, x, y) -<<<<<<< HEAD -======= # @show m n norm(z) ->>>>>>> upstream/master Z[Block(m,n)] += α * z end @@ -239,31 +236,6 @@ function assemble_hide(bilform::BilForm, test_space_dict, trial_space_dict) end -<<<<<<< HEAD -# function assemble(bilform::BilForm, test_space_dict, trial_space_dict) - -# @assert !isempty(terms) -# Z = ZeroMap{Float32} -# for term in bilform.terms - -# x = test_space_dict[term.test_id] -# for op in reverse(term.test_ops) -# x = op[end](op[1:end-1]..., x) -# end - -# y = trial_space_dict[term.trial_id] -# for op in reverse(t.trial_ops) -# y = op[end](op[1:end-1]..., y) -# end - -# end - - - - - -# end -======= function assemble(bilform::BilForm, test_space_dict, trial_space_dict; materialize=BEAST.assemble) @@ -303,7 +275,6 @@ function assemble(bilform::BilForm, test_space_dict, trial_space_dict; return Z end ->>>>>>> upstream/master diff --git a/src/timedomain/tdtimeops.jl b/src/timedomain/tdtimeops.jl index 096269c1..8f73d332 100644 --- a/src/timedomain/tdtimeops.jl +++ b/src/timedomain/tdtimeops.jl @@ -177,11 +177,8 @@ derive(op::AbstractOperator) = TemporalDifferentiation(op) scalartype(op::TemporalDifferentiation) = scalartype(op.operator) Base.:*(a::Number, op::TemporalDifferentiation) = TemporalDifferentiation(a * op.operator) -<<<<<<< HEAD -======= defaultquadstrat(op::TemporalDifferentiation, tfs, bfs) = defaultquadstrat(op.operator, tfs, bfs) ->>>>>>> upstream/master function allocatestorage(op::TemporalDifferentiation, testfns, trialfns, storage_trait, longdelays_trait) @@ -196,12 +193,8 @@ function allocatestorage(op::TemporalDifferentiation, testfns, trialfns, return allocatestorage(op.operator, testfns, trialfns, storage_trait, longdelays_trait) end -<<<<<<< HEAD -function assemble!(operator::TemporalDifferentiation, testfns, trialfns, store, threading = Threading{:multi}) -======= function assemble!(operator::TemporalDifferentiation, testfns, trialfns, store, threading = Threading{:multi}; quadstrat=defaultquadstrat(operator, testfns, trialfns)) ->>>>>>> upstream/master trial_time_fns = temporalbasis(trialfns) trial_space_fns = spatialbasis(trialfns) @@ -211,11 +204,7 @@ function assemble!(operator::TemporalDifferentiation, testfns, trialfns, store, derive(trial_time_fns) ) -<<<<<<< HEAD - assemble!(operator.operator, testfns, trialfns, store, threading) -======= assemble!(operator.operator, testfns, trialfns, store, threading; quadstrat) ->>>>>>> upstream/master end @@ -245,11 +234,7 @@ function allocatestorage(op::TemporalIntegration, testfns, trialfns, end function assemble!(operator::TemporalIntegration, testfns, trialfns, store, -<<<<<<< HEAD - threading = Threading{:multi}) -======= threading = Threading{:multi}; quadstrat=defaultquadstrat(operator, testfns, trialfns)) ->>>>>>> upstream/master trial_time_fns = temporalbasis(trialfns) trial_space_fns = spatialbasis(trialfns) From 515290e2aeedb37da59fddc1cddadb0ae34d8548 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Mon, 15 Nov 2021 15:32:06 +0100 Subject: [PATCH 135/528] merge upstream 3 --- src/utils/zeromap.jl | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/utils/zeromap.jl b/src/utils/zeromap.jl index 7a430de4..518c2241 100644 --- a/src/utils/zeromap.jl +++ b/src/utils/zeromap.jl @@ -1,18 +1,5 @@ import LinearMaps -<<<<<<< HEAD -struct ZeroMap{T} <: LinearMap{T} - num_rows::Int - num_cols::Int -end - -Base.size(A::ZeroMap) = (A.num_rows, A.num_cols,) - -function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector, α::Number, β::Number) - y .= β * y - # LinearAlgebra.mul!(yI, AIJ, xJ, α, β) -end -======= struct ZeroMap{T,U,V} <: LinearMap{T} range::U domain::V @@ -41,4 +28,3 @@ function Base.:(*)(A::ZeroMap, x::AbstractVector) end Base.Matrix{T}(A::ZeroMap) where {T} = zeros(T, length(A.range), length(A.domain)) ->>>>>>> upstream/master From 48171891bb1e2ec31b7fea44da90c5a111205e52 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 18 Nov 2021 12:36:27 +0100 Subject: [PATCH 136/528] fixed far field computation for pmchwt --- Project.toml | 2 +- examples/pmchwt.jl | 22 +++++++++++++--- src/bases/lagrange.jl | 3 ++- src/operator.jl | 2 +- src/quadrature/quadstrats.jl | 26 +++++++++--------- src/solvers/itsolver.jl | 51 ++++++++++++++++++++++++++++++------ src/utils/zeromap.jl | 17 ++++++++++++ 7 files changed, 95 insertions(+), 28 deletions(-) diff --git a/Project.toml b/Project.toml index 0362094e..46857dcc 100644 --- a/Project.toml +++ b/Project.toml @@ -35,7 +35,7 @@ FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4" FillArrays = "0.11, 0.12" IterativeSolvers = "0.9" -LiftedMaps = "0.2.2" +LiftedMaps = "0.3" LinearMaps = "3.5" SauterSchwabQuadrature = "2.1.3" SparseMatrixDicts = "0.2" diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index a7593105..452a2d14 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -63,7 +63,7 @@ ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ # Don't forget the far field comprises two contributions ffm = potential(MWFarField3D(κ*im), ffpoints, u[m], X) ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], X) -ff = η*im*κ*ffj + im*κ*cross.(ffpoints, ffj) +ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) using Plots plot(xlabel="theta") @@ -99,8 +99,12 @@ Z = range(-1,1,length=100) Y = range(-1,1,length=100) nfpoints = [point(0,y,z) for z in Z, y in Y] -E_ex, H_ex = nearfield(u[m],u[j],X,X,κ,η,nfpoints,E,H) -E_in, H_in = nearfield(-u[m],-u[j],X,X,κ′,η′,nfpoints) +import Base.Threads: @spawn +task1 = @spawn nearfield(u[m],u[j],X,X,κ,η,nfpoints,E,H) +task2 = @spawn nearfield(-u[m],-u[j],X,X,κ′,η′,nfpoints) + +E_ex, H_ex = fetch(task1) +E_in, H_in = fetch(task2) E_tot = E_in + E_ex H_tot = H_in + H_ex @@ -112,4 +116,14 @@ heatmap(Z, Y, real.(getindex.(E_tot,1))) heatmap(Z, Y, real.(getindex.(H_tot,2))) plot(real.(getindex.(E_tot[:,51],1))) -plot!(real.(getindex.(H_tot[:,51],2))) \ No newline at end of file +plot!(real.(getindex.(H_tot[:,51],2))) + +# Compare the far field and the field far +ffradius = 100.0 +E_far, H_far = nearfield(u[m],u[j],X,X,κ,η, ffradius .* ffpoints) +nxE_far = cross.(ffpoints, E_far) * (4π*ffradius) / exp(-im*κ*ffradius) +Et_far = -cross.(ffpoints, nxE_far) + +plot() +plot!(Θ, norm.(ff),label="far field") +scatter!(Θ, norm.(Et_far), label="field far") \ No newline at end of file diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 65d048b4..4becb8dc 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -124,11 +124,12 @@ function duallagrangecxd0(mesh, vertexlist::Vector{Int}) fine = barycentric_refinement(mesh) vtoc, vton = vertextocellmap(fine) + verts = vertices(mesh) for (k,v) in enumerate(vertexlist) n = vton[v] F = vtoc[v,1:n] fns[k] = singleduallagd0(fine, F, v) - push!(pos, mesh.vertices[v]) + push!(pos, verts[v]) end NF = 1 diff --git a/src/operator.jl b/src/operator.jl index ae283be2..005bb554 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -158,7 +158,7 @@ function assemble!(operator::Operator, test_functions::Space, trial_functions::S threading::Type{Threading{:multi}} = Threading{:multi}; quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) - @info "Multi-threaded assembly:" + # @info "Multi-threaded assembly:" P = Threads.nthreads() numchunks = P diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index 7eb033f1..b0e536bd 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -18,19 +18,19 @@ end defaultquadstrat(op, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bfs)) -# macro defaultquadstrat(dop, body) -# @assert dop.head == :tuple -# @assert length(dop.args) == 3 -# op = dop.args[1] -# tfs = dop.args[2] -# bfs = dop.args[3] -# ex = quote -# function BEAST.defaultquadstrat(::typeof($op), ::typeof($tfs), ::typeof($bfs)) -# $body -# end -# end -# return esc(ex) -# end +macro defaultquadstrat(dop, body) + @assert dop.head == :tuple + @assert length(dop.args) == 3 + op = dop.args[1] + tfs = dop.args[2] + bfs = dop.args[3] + ex = quote + function BEAST.defaultquadstrat(::typeof($op), ::typeof($tfs), ::typeof($bfs)) + $body + end + end + return esc(ex) +end struct SingleNumQStrat quad_rule::Int diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index 66b5876d..0ec0207c 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -8,13 +8,14 @@ struct GMRESSolver{L,T} <: LinearMap{T} maxiter::Int restart::Int tol::T + verbose::Bool end Base.axes(A::GMRESSolver) = reverse(axes(A.linear_operator)) -function GMRESSolver(op; maxiter=0, restart=0, tol=sqrt(eps(real(eltype(op))))) +function GMRESSolver(op; maxiter=0, restart=0, tol=sqrt(eps(real(eltype(op)))), verbose=true) m, n = size(op) @assert m == n @@ -22,7 +23,7 @@ function GMRESSolver(op; maxiter=0, restart=0, tol=sqrt(eps(real(eltype(op))))) maxiter == 0 && (maxiter = div(n, 5)) restart == 0 && (restart = n) - GMRESSolver(op, maxiter, restart, tol) + GMRESSolver(op, maxiter, restart, tol, verbose) end @@ -30,17 +31,24 @@ operator(solver::GMRESSolver) = solver.linear_operator function solve(solver::GMRESSolver, b) - op = operator(solver) - x, ch = IterativeSolvers.gmres(op, b, log=true, maxiter=solver.maxiter, - restart=solver.restart, reltol=solver.tol, verbose=true) - return x, ch + + T = promote_type(eltype(solver), eltype(b)) + # x = PseudoBlockVector{T}(undef, BlockArrays.blocksizes(solver)[1]) + x = similar(Array{T}, axes(solver)[2]) + fill!(x,0) + x, ch = solve!(x, solver, b) + + # op = operator(solver) + # x, ch = IterativeSolvers.gmres(op, b, log=true, maxiter=solver.maxiter, + # restart=solver.restart, reltol=solver.tol, verbose=solver.verbose) + # return x, ch end function solve!(x, solver::GMRESSolver, b) op = operator(solver) x, ch = IterativeSolvers.gmres!(x, op, b, log=true, maxiter=solver.maxiter, - restart=solver.restart, reltol=solver.tol, verbose=true) + restart=solver.restart, reltol=solver.tol, verbose=solver.verbose) return x, ch end @@ -63,11 +71,38 @@ Base.size(solver::GMRESSolver) = reverse(size(solver.linear_operator)) function LinearAlgebra.mul!(y::AbstractVecOrMat, solver::GMRESSolver, x::AbstractVector) fill!(y,0) y, ch = solve!(y, solver, x) - println("Number of iterations: ", ch.iters) + solver.verbose && println("Number of iterations: ", ch.iters) ch.isconverged || error("Iterative solver did not converge.") return y end +LinearAlgebra.adjoint(A::GMRESSolver) = GMRESSolver(adjoint(A.linear_operator), A.maxiter, A.restart, A.tol, A.verbose) +LinearAlgebra.transpose(A::GMRESSolver) = GMRESSolver(transpose(A.linear_operator), A.maxiter, A.restart, A.tol, A.verbose) + + +# function LinearAlgebra.mul!( +# y::AbstractVecOrMat, +# transA::LinearMaps.AdjointMap{<:Any,<:GMRESSolver}, +# x::AbstractVector) + +# LinearMaps.check_dim_mul(y, transA, x) +# A = transA.lmap +# B = GMRESSolver(adjoint(A.linear_operator), A.maxiter, A.restart, A.tol, A.verbose) +# return mul!(y, B, x) +# end + + +# function LinearAlgebra.mul!( +# y::AbstractVecOrMat, +# transA::LinearMaps.TransposeMap{<:Any,<:GMRESSolver}, +# x::AbstractVector) + +# LinearMaps.check_dim_mul(y, transA, x) +# A = transA.lmap +# B = GMRESSolver(transpose(A.linear_operator), A.maxiter, A.restart, A.tol, A.verbose) +# return mul!(y, B, x) +# end + function gmres(eq::DiscreteEquation; maxiter=0, restart=0, tol=0) diff --git a/src/utils/zeromap.jl b/src/utils/zeromap.jl index cd3d64c6..c0bd5c46 100644 --- a/src/utils/zeromap.jl +++ b/src/utils/zeromap.jl @@ -16,10 +16,27 @@ function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector, y .*= β end +# function LinearAlgebra.mul!(y::AbstractVector, +# Lt::LinearMaps.TransposeMap{<:Any,<:ZeroMap}, +# x::AbstractVector, α::Number, β::Number) + +# y .*= β +# end + + function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector) y .= 0 end +# function LinearAlgebra.mul!(y::AbstractVector, +# Lt::LinearMaps.TransposeMap{<:Any,<:ZeroMap}, x::AbstractVector) + +# y .= 0 +# end + +LinearAlgebra.adjoint(A::ZeroMap{T}) where {T} = ZeroMap{T}(A.domain, A.range) +LinearAlgebra.transpose(A::ZeroMap{T}) where {T} = ZeroMap{T}(A.domain, A.range) + function Base.:(*)(A::ZeroMap, x::AbstractVector) T = eltype(A) y = similar(A.range, T) From e550abdbbaa4a337c28220822eede8c332f1601f Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 19 Nov 2021 15:36:40 +0100 Subject: [PATCH 137/528] fixed bug in ntrace (input space not supported by entire input geo) --- examples/capacitor_new.jl | 106 ++++++++++++++++++++++++++++++++++++++ src/bases/trace.jl | 7 +-- 2 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 examples/capacitor_new.jl diff --git a/examples/capacitor_new.jl b/examples/capacitor_new.jl new file mode 100644 index 00000000..ffb751a9 --- /dev/null +++ b/examples/capacitor_new.jl @@ -0,0 +1,106 @@ +using BEAST +using CompScienceMeshes +using LinearAlgebra +using SparseArrays + +import Plotly +import Plots +Plots.plotly() + +function Base.:+(a::S, b::S) where {S<:BEAST.Space} + @assert geometry(a) == geometry(b) + S(geometry(a), [a.fns; b.fns], [a.pos; b.pos]) +end + +l = w = 1.0 #Length and width of capacitor plates +d = 0.1 #seperation of plates +h = 1/36 #size of meshes + +Γ₀ = meshrectangle(l,w,h) +Γ₁ = CompScienceMeshes.translate(Γ₀, point(0.0,0.0,d)) + +γ₀ = meshsegment(l,h,3) +γ₁ = CompScienceMeshes.translate(γ₀, point(0.0,0.0,d)) + +Γ = weld(Γ₁, Γ₀) + +κ = 2pi / 100 +V₀ = 1.0 +f = ScalarTrace(p -> V₀) + +edges_all = skeleton(Γ, 1) +edges_int = submesh(!in(boundary(Γ)), edges_all) + +on_γ₀ = overlap_gpredicate(γ₀) +edges_pt0 = submesh(edges_all) do edge + ch = chart(edges_all, edge) + return on_γ₀(ch) +end + +on_γ₁ = overlap_gpredicate(γ₁) +edges_pt1 = submesh(edges_all) do edge + ch = chart(edges_all, edge) + return on_γ₁(ch) +end + +RT_int = raviartthomas(Γ, edges_int) +RT_pt0 = raviartthomas(Γ, edges_pt0) +RT_pt1 = raviartthomas(Γ, edges_pt1) + +verts_pt0_int = submesh(!in(boundary(edges_pt0)), skeleton(edges_pt0,0)) +verts_pt1_int = submesh(!in(boundary(edges_pt1)), skeleton(edges_pt1,0)) + +C0 = connectivity(verts_pt0_int, edges_pt0) +C1 = connectivity(verts_pt1_int, edges_pt1) + +X = RT_int +Y0 = RT_pt0 * C0 +Y1 = RT_pt1 * C1 + +D = sparse(reshape([fill(+1.0, length(edges_pt0)); fill(-1.0, length(edges_pt1))],length(edges_pt0)+length(edges_pt1),1)) +RT_pt = BEAST.RTBasis(RT_pt0.geo, [RT_pt0.fns; RT_pt1.fns], [RT_pt0.pos; RT_pt1.pos]) +Z = RT_pt * D + +@hilbertspace x y0 y1 z +@hilbertspace ξ η0 η1 ζ + +T = Maxwell3D.singlelayer(wavenumber=κ) +trc = X->ntrace(X,γ₁) + +efie = @discretise( @varform( + T[ξ,x] + T[ξ,y0] + T[ξ,y1] + T[ξ,z] + + T[η0,x] + T[η0,y0] + T[η0,y1] + T[η0,z] + + T[η1,x] + T[η1,y0] + T[η1,y1] + T[η1,z] + + T[ζ,x] + T[ζ,y0] + T[ζ,y1] + T[ζ,z] == f[trc(ζ)]), + ξ∈X, η0∈Y0, η1∈Y1, ζ∈Z, + x∈X, y0∈Y0, y1∈Y1, z∈Z) +u = solve(efie) + +# You can access the current coefficients pertaining to subspaces +# using the Hilbert space 'placeholder' +@show length(u[x]) +@show length(u[y0]) +@show length(u[y1]) +@show length(u[z]) + +S = X + Y0 + Y1 + Z +fcr, geo = facecurrents(u, S) +Plotly.plot(patch(geo, norm.(fcr))) |> display + +# Compute the Scalar Potential across the ports. Note how a voltage +# gap of 1V comes out as a 'natural' condition on the solution. +zs = range(-0.5, stop=0.5, length=100) +pts = [point(0.5,0.5,z) for z ∈ zs] +Φ = potential(MWSingleLayerPotential3D(κ), pts, u, S, type=ComplexF64) +Plots.plot(zs, real(Φ), xlabel="height",ylabel="scalar potential",label=false) |> display + +# Plot current along γ₀ and γ₁. Note: in this symmetric situation, we +# expect these to be opposite on corresponding points of the two ports. +# In general this is not true; in fact, the two ports could have different shape and size +traceS0 = ntrace(S, γ₀) +traceS1 = ntrace(S, γ₁) +fcr0, geo0 = facecurrents(u, traceS0) +fcr1, geo1 = facecurrents(u, traceS1) +plot() +plot!(imag.(fcr0)) +plot!(imag.(fcr1)) |> display \ No newline at end of file diff --git a/src/bases/trace.jl b/src/bases/trace.jl index a7cd8308..90e88f92 100644 --- a/src/bases/trace.jl +++ b/src/bases/trace.jl @@ -24,7 +24,7 @@ function ntrace(X::Space, γ) # on_target = overlap_gpredicate(γ) # ad = assemblydata(X) x = refspace(X) - E, ad = assemblydata(X) + E, ad, P = assemblydata(X) igeo = geometry(X) @assert dimension(γ) == dimension(igeo)-1 # Γ = geo @@ -37,7 +37,8 @@ function ntrace(X::Space, γ) on_target(chart(ogeo,face)) end - D = copy(transpose(connectivity(ogeo, igeo, abs))) + # D = copy(transpose(connectivity(ogeo, igeo, abs))) + D = connectivity(igeo, ogeo, abs) rows, vals = rowvals(D), nonzeros(D) T = scalartype(X) @@ -53,7 +54,7 @@ function ntrace(X::Space, γ) # @assert norm(Q,Inf) != 0 r = 0 - for k in nzrange(D,p) + for k in nzrange(D,P[p]) vals[k] == q && (r = rows[k]; break) end @assert r != 0 From 8a1a86257325e9545a67effc3f36602b3788a55a Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 19 Nov 2021 15:39:45 +0100 Subject: [PATCH 138/528] capacitor_new uses the ability to add spaces --- examples/capacitor_new.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/capacitor_new.jl b/examples/capacitor_new.jl index ffb751a9..0a029482 100644 --- a/examples/capacitor_new.jl +++ b/examples/capacitor_new.jl @@ -57,8 +57,9 @@ X = RT_int Y0 = RT_pt0 * C0 Y1 = RT_pt1 * C1 +# This is ugly and will fail if the two ports are not equal... D = sparse(reshape([fill(+1.0, length(edges_pt0)); fill(-1.0, length(edges_pt1))],length(edges_pt0)+length(edges_pt1),1)) -RT_pt = BEAST.RTBasis(RT_pt0.geo, [RT_pt0.fns; RT_pt1.fns], [RT_pt0.pos; RT_pt1.pos]) +RT_pt = RT_pt0 + RT_pt1 Z = RT_pt * D @hilbertspace x y0 y1 z From 7cdd77283af2ea242c6cf275600bebd5246debd6 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Tue, 23 Nov 2021 00:26:41 +0000 Subject: [PATCH 139/528] CompatHelper: bump compat for SpecialFunctions to 2, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 46857dcc..b19c9dd8 100644 --- a/Project.toml +++ b/Project.toml @@ -39,7 +39,7 @@ LiftedMaps = "0.3" LinearMaps = "3.5" SauterSchwabQuadrature = "2.1.3" SparseMatrixDicts = "0.2" -SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1" +SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" WiltonInts84 = "0.2" julia = "1.6" From dbcbb04ad9b2d436cbbd6ec5004b54e6103a4738 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Thu, 25 Nov 2021 13:43:37 +0100 Subject: [PATCH 140/528] add SauterSchwab3D dependency --- Project.toml | 2 + examples/pmchwt.jl | 27 ++++++------- src/BEAST.jl | 1 - src/maxwell/timedomain/mwtdops.jl | 4 -- src/operator.jl | 52 ------------------------- src/quadrature/quadstrats.jl | 9 +++++ src/timedomain/tdintegralop.jl | 8 ---- src/volumeintegral/sauterschwab_ints.jl | 2 +- src/volumeintegral/vieops.jl | 30 ++++++++------ 9 files changed, 45 insertions(+), 90 deletions(-) diff --git a/Project.toml b/Project.toml index 0362094e..28f81414 100644 --- a/Project.toml +++ b/Project.toml @@ -17,6 +17,7 @@ IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LiftedMaps = "d22a30c1-52ac-4762-a8c9-5838452405e0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" +SauterSchwab3D = "0a13313b-1c00-422e-8263-562364ed9544" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" @@ -38,6 +39,7 @@ IterativeSolvers = "0.9" LiftedMaps = "0.2.2" LinearMaps = "3.5" SauterSchwabQuadrature = "2.1.3" +SauterSchwab3D = "0.1.0" SparseMatrixDicts = "0.2" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 82fed954..1fc9919b 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -1,13 +1,10 @@ using CompScienceMeshes, BEAST using LinearAlgebra -#T = tetmeshcuboid(1.0,1.0,1.0,0.1) T = tetmeshsphere(1.0,0.25) X = nedelecc3d(T) Γ = boundary(T) -#Γ = meshcuboid(0.5, 1.0, 1.0, 0.075) -#CompScienceMeshes.translate!(Γ, point(-0.25, -0.5, -0.5)) -# Γ = meshsphere(1.0, 0.075) + X = raviartthomas(Γ) @show numfunctions(X) @@ -50,6 +47,7 @@ pmchwt = @discretise( # u, ch = IterativeSolvers.gmres!(u, Z, b, log=true, maxiter=1000, # restart=1000, reltol=1e-5, verbose=true) +u = solve(pmchwt) Θ, Φ = range(0.0,stop=2π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] @@ -63,12 +61,12 @@ using Plots plot(xlabel="theta") plot!(Θ,norm.(ff),label="far field",title="PMCHWT") -import Plotly -using LinearAlgebra -fcrj, _ = facecurrents(u[j],X) -fcrm, _ = facecurrents(u[m],X) -Plotly.plot(patch(Γ, norm.(fcrj))) -Plotly.plot(patch(Γ, norm.(fcrm))) +#import Plotly +#using LinearAlgebra +#fcrj, _ = facecurrents(u[j],X) +#fcrm, _ = facecurrents(u[m],X) +#Plotly.plot(patch(Γ, norm.(fcrj))) +#Plotly.plot(patch(Γ, norm.(fcrm))) function nearfield(um,uj,Xm,Xj,κ,η,points, @@ -89,6 +87,7 @@ function nearfield(um,uj,Xm,Xj,κ,η,points, return E, H end + Z = range(-1,1,length=100) Y = range(-1,1,length=100) nfpoints = [point(0,y,z) for z in Z, y in Y] @@ -103,7 +102,9 @@ contour(real.(getindex.(E_tot,1))) contour(real.(getindex.(H_tot,2))) heatmap(Z, Y, real.(getindex.(E_tot,1))) -heatmap(Z, Y, real.(getindex.(H_tot,2))) +#heatmap(Z, Y, real.(getindex.(H_tot,2))) + +#plot(real.(getindex.(E_tot[:,51],1))) +#plot!(real.(getindex.(H_tot[:,51],2))) + -plot(real.(getindex.(E_tot[:,51],1))) -plot!(real.(getindex.(H_tot[:,51],2))) diff --git a/src/BEAST.jl b/src/BEAST.jl index e06c5be5..eade439c 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -12,7 +12,6 @@ using SauterSchwabQuadrature using SauterSchwab3D using FastGaussQuadrature using LinearMaps -using ShunnHamQuadrature using LiftedMaps import LinearAlgebra: cross, dot diff --git a/src/maxwell/timedomain/mwtdops.jl b/src/maxwell/timedomain/mwtdops.jl index ccd486c0..5a0becea 100644 --- a/src/maxwell/timedomain/mwtdops.jl +++ b/src/maxwell/timedomain/mwtdops.jl @@ -139,11 +139,7 @@ end # end function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, store, -<<<<<<< HEAD - threading=Threading{:multi}) -======= threading=Threading{:multi}; quadstrat=defaultquadstrat(dl,W,V)) ->>>>>>> upstream/master X, T = spatialbasis(W), temporalbasis(W) Y, U = spatialbasis(V), temporalbasis(V) diff --git a/src/operator.jl b/src/operator.jl index c76d3ab7..ae283be2 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -81,22 +81,12 @@ defaultquadstrat(lc::LinearCombinationOfOperators, tfs, bfs) = function assemble(operator::AbstractOperator, test_functions, trial_functions; storage_policy = Val{:bandedstorage}, long_delays_policy = LongDelays{:compress}, -<<<<<<< HEAD - threading = Threading{:multi}) - # This is a convenience function whose only job is to allocate - # the storage for the interaction matrix. Further dispatch on - # operator and space types is handled by the 4-argument version - Z, store = allocatestorage(operator, test_functions, trial_functions, - storage_policy, long_delays_policy) - assemble!(operator, test_functions, trial_functions, store, threading) -======= threading = Threading{:multi}, quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) assemble!(operator, test_functions, trial_functions, store, threading; quadstrat) ->>>>>>> upstream/master return Z() end @@ -107,12 +97,8 @@ function assemblerow(operator::AbstractOperator, test_functions, trial_functions Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) -<<<<<<< HEAD - assemblerow!(operator, test_functions, trial_functions, store) -======= assemblerow!(operator, test_functions, trial_functions, store; quadstrat) ->>>>>>> upstream/master Z() end @@ -123,12 +109,8 @@ function assemblecol(operator::AbstractOperator, test_functions, trial_functions Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) -<<<<<<< HEAD - assemblecol!(operator, test_functions, trial_functions, store) -======= assemblecol!(operator, test_functions, trial_functions, store; quadstrat) ->>>>>>> upstream/master Z() end @@ -173,12 +155,8 @@ end function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, -<<<<<<< HEAD - threading::Type{Threading{:multi}} = Threading{:multi}) -======= threading::Type{Threading{:multi}} = Threading{:multi}; quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) ->>>>>>> upstream/master @info "Multi-threaded assembly:" @@ -198,18 +176,6 @@ function assemble!(operator::Operator, test_functions::Space, trial_functions::S end function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, -<<<<<<< HEAD - threading::Type{Threading{:single}}) - - @info "Single-threaded assembly" - - assemblechunk!(operator, test_functions, trial_functions, store) -end - - - -function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, store) -======= threading::Type{Threading{:single}}; quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) @@ -222,7 +188,6 @@ end function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, store; quadstrat=defaultquadstrat(op, tfs, bfs)) ->>>>>>> upstream/master store1(v,m,n) = store(v,n,m) assemble!(op.op, bfs, tfs, store1; quadstrat) @@ -230,15 +195,10 @@ end function assemble!(op::LinearCombinationOfOperators, tfs::AbstractSpace, bfs::AbstractSpace, -<<<<<<< HEAD - store, threading = Threading{:multi}) - for (a,A) in zip(op.coeffs, op.ops) -======= store, threading = Threading{:multi}; quadstrat=defaultquadstrat(op, tfs, bfs)) for (a,A,qs) in zip(op.coeffs, op.ops, quadstrat) ->>>>>>> upstream/master store1(v,m,n) = store(a*v,m,n) assemble!(A, tfs, bfs, store1; quadstrat=qs) end @@ -246,12 +206,8 @@ end # Support for direct product spaces -<<<<<<< HEAD -function assemble!(op::Operator, tfs::DirectProductSpace, bfs::Space, store, threading = Threading{:multi}) -======= function assemble!(op::Operator, tfs::DirectProductSpace, bfs::Space, store, threading = Threading{:multi}; quadstrat=defaultquadstrat(op, tfs[1], bfs)) ->>>>>>> upstream/master I = Int[0] for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) @@ -261,12 +217,8 @@ function assemble!(op::Operator, tfs::DirectProductSpace, bfs::Space, store, thr end -<<<<<<< HEAD -function assemble!(op::Operator, tfs::Space, bfs::DirectProductSpace, store, threading=Threading{:multi}) -======= function assemble!(op::Operator, tfs::Space, bfs::DirectProductSpace, store, threading=Threading{:multi}; quadstrat=defaultquadstrat(op, tfs, bfs[1])) ->>>>>>> upstream/master J = Int[0] for s in bfs.factors push!(J, last(J) + numfunctions(s)) end for (j,s) in enumerate(bfs.factors) @@ -275,12 +227,8 @@ function assemble!(op::Operator, tfs::Space, bfs::DirectProductSpace, store, thr end end -<<<<<<< HEAD -function assemble!(op::Operator, tfs::DirectProductSpace, bfs::DirectProductSpace, store, threading=Threading{:multi}) -======= function assemble!(op::Operator, tfs::DirectProductSpace, bfs::DirectProductSpace, store, threading=Threading{:multi}; quadstrat=defaultquadstrat(op, tfs[1], bfs[1])) ->>>>>>> upstream/master I = Int[0] for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index 7eb033f1..249f2d00 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -16,6 +16,15 @@ struct DoubleNumQStrat{R} inner_rule::R end +struct SauterSchwab3DQStrat{R,S} + outer_rule::R + inner_rule::R + sauter_schwab_1D::S + sauter_schwab_2D::S + sauter_schwab_3D::S + sauter_schwab_4D::S +end + defaultquadstrat(op, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bfs)) # macro defaultquadstrat(dop, body) diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 028eecea..543b3044 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -130,11 +130,7 @@ function allocatestorage(op::RetardedPotential, testST, basisST, end function assemble!(op::LinearCombinationOfOperators, tfs::SpaceTimeBasis, bfs::SpaceTimeBasis, store, -<<<<<<< HEAD - threading=Threading{:multi}) -======= threading=Threading{:multi}; quadstrat=defaultquadstrat(op, tfs, bfs)) ->>>>>>> upstream/master for (a,A) in zip(op.coeffs, op.ops) store1(v,m,n,k) = store(a*v,m,n,k) @@ -143,11 +139,7 @@ function assemble!(op::LinearCombinationOfOperators, tfs::SpaceTimeBasis, bfs::S end function assemble!(op::RetardedPotential, testST, trialST, store, -<<<<<<< HEAD - threading=Threading{:multi}) -======= threading=Threading{:multi}; quadstrat=defaultquadstrat(op, testST, trialST)) ->>>>>>> upstream/master Y, S = spatialbasis(testST), temporalbasis(testST) diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index fe39aec4..b62b3e7f 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -130,7 +130,7 @@ function momintegrals!(op::VIEOperator, nothing end -function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::DoubleQuadStrategy) +function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) # memory allocation here is a result from the type instability on strat # which is on purpose, i.e. the momintegrals! method is chosen based diff --git a/src/volumeintegral/vieops.jl b/src/volumeintegral/vieops.jl index c212f349..57a0ed5b 100644 --- a/src/volumeintegral/vieops.jl +++ b/src/volumeintegral/vieops.jl @@ -178,28 +178,35 @@ function integrand(viop::VIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) end +defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) + + function quaddata(op::VIEOperator, test_local_space::RefSpace, trial_local_space::RefSpace, - test_charts, trial_charts) + test_charts, trial_charts, qs::SauterSchwab3DQStrat) #The combinations of rules (6,7) and (5,7 are) BAAAADDDD # they result in many near singularity evaluations with any # resemblence of accuracy going down the drain! Simply don't! # (same for (5,7) btw...). - t_qp = quadpoints(test_local_space, test_charts, (3,)) - b_qp = quadpoints(trial_local_space, trial_charts, (3,)) + t_qp = quadpoints(test_local_space, test_charts, (qs.outer_rule)) + b_qp = quadpoints(trial_local_space, trial_charts, (qs.inner_rule)) - a, b = 0.0, 1.0 - sing_qp = (SauterSchwab3D._legendre(3,a,b), SauterSchwab3D._shunnham2D(3), SauterSchwab3D._shunnham3D(3), SauterSchwab3D._shunnham4D(3),) + + sing_qp = (SauterSchwab3D._legendre(qs.sauter_schwab_1D,0,1), + SauterSchwab3D._shunnham2D(qs.sauter_schwab_2D), + SauterSchwab3D._shunnham3D(qs.sauter_schwab_3D), + SauterSchwab3D._shunnham4D(qs.sauter_schwab_4D),) return (tpoints=t_qp, bpoints=b_qp, sing_qp=sing_qp) end -quadrule(op::VolumeOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd) = qr_volume(op, g, f, i, τ, j, σ, qd) +quadrule(op::VolumeOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs) = qr_volume(op, g, f, i, τ, j, σ, qd, qs) -function qr_volume(op::VolumeOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd) +function qr_volume(op::VolumeOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, + qs::SauterSchwab3DQStrat) dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) @@ -233,15 +240,16 @@ function qr_volume(op::VolumeOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, q - return DoubleQuadStrategy( + return DoubleQuadRule( qd[1][1,i], qd[2][1,j]) end -quadrule(op::BoundaryOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd) = qr_boundary(op, g, f, i, τ, j, σ, qd) +quadrule(op::BoundaryOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs) = qr_boundary(op, g, f, i, τ, j, σ, qd, qs) -function qr_boundary(op::BoundaryOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd) +function qr_boundary(op::BoundaryOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, + qs::SauterSchwab3DQStrat) dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) @@ -273,7 +281,7 @@ function qr_boundary(op::BoundaryOperator, g::RefSpace, f::RefSpace, i, τ, j, hits == 1 && return SauterSchwab3D.CommonVertex5D_S(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),(qd.sing_qp[3],qd.sing_qp[2])) - return DoubleQuadStrategy( + return DoubleQuadRule( qd[1][1,i], qd[2][1,j]) From b44089d8bed9d31a6eedf1ee235569f86e94512c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 1 Dec 2021 16:49:55 +0100 Subject: [PATCH 141/528] dual 0-forms in 2d correct for all bnd conds --- examples/capacitor_new.jl | 8 +- src/BEAST.jl | 1 + src/bases/basis.jl | 10 +++ src/bases/lagrange.jl | 150 ++++++++++++++++++++++++++++++++++++ src/bases/local/laglocal.jl | 38 ++++++++- src/helmholtz3d/hh3dfar.jl | 19 +++++ src/localop.jl | 15 ++++ src/postproc/segcurrents.jl | 7 +- src/solvers/lusolver.jl | 13 +++- src/utils/zeromap.jl | 8 ++ test/test_nitsche.jl | 2 +- 11 files changed, 259 insertions(+), 12 deletions(-) create mode 100644 src/helmholtz3d/hh3dfar.jl diff --git a/examples/capacitor_new.jl b/examples/capacitor_new.jl index 0a029482..75326a4b 100644 --- a/examples/capacitor_new.jl +++ b/examples/capacitor_new.jl @@ -14,7 +14,7 @@ end l = w = 1.0 #Length and width of capacitor plates d = 0.1 #seperation of plates -h = 1/36 #size of meshes +h = 1/60 #size of meshes Γ₀ = meshrectangle(l,w,h) Γ₁ = CompScienceMeshes.translate(Γ₀, point(0.0,0.0,d)) @@ -102,6 +102,6 @@ traceS0 = ntrace(S, γ₀) traceS1 = ntrace(S, γ₁) fcr0, geo0 = facecurrents(u, traceS0) fcr1, geo1 = facecurrents(u, traceS1) -plot() -plot!(imag.(fcr0)) -plot!(imag.(fcr1)) |> display \ No newline at end of file +Plots.plot() +Plots.plot!(imag.(fcr0)) +Plots.plot!(imag.(fcr1)) |> display \ No newline at end of file diff --git a/src/BEAST.jl b/src/BEAST.jl index a13d7e11..6a16e7ed 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -190,6 +190,7 @@ include("helmholtz3d/hh3dexc.jl") include("helmholtz3d/hh3dops.jl") include("helmholtz3d/nitsche.jl") include("helmholtz3d/hh3dnear.jl") +include("helmholtz3d/hh3dfar.jl") include("decoupled/dpops.jl") include("decoupled/potentials.jl") diff --git a/src/bases/basis.jl b/src/bases/basis.jl index 9a6c4618..cd4bfaa6 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -250,3 +250,13 @@ function Base.:*(space::Space, A::SparseMatrixCSC) end return (typeof(space))(space.geo, fns, pos) end + + +function addf!(fn::Vector{<:Shape}, x::Vector, space::Space, idcs::Vector{Int}) + for (m,bf) in enumerate(space.fns) + for sh in bf + cellid = idcs[sh.cellid] + BEAST.add!(fn, cellid, sh.refid, sh.coeff * x[m]) + end + end +end diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 4becb8dc..cc05740e 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -479,6 +479,13 @@ end gradient(space::LagrangeBasis{1,0}, geo, fns) = NDLCCBasis(geo, fns) curl(space::LagrangeBasis{1,0}, geo, fns) = RTBasis(geo, fns) +gradient(space::LagrangeBasis{1,0,<:CompScienceMeshes.AbstractMesh{<:Any,2}}, geo, fns) = + LagrangeBasis{0,-1,1}(geo, fns, space.pos) + + +gradient(space::LagrangeBasis{1,0,<:CompScienceMeshes.AbstractMesh{<:Any,3}}, geo, fns) = + NDBasis(geo, fns, space.pos) + # # Sclar trace for Laggrange element based spaces # @@ -493,3 +500,146 @@ function strace(X::LagrangeBasis{1,0}, geo, fns::Vector) trpos = deepcopy(positions(X)) LagrangeBasis{1,0,NF}(geo, fns, trpos) end + + + +function extend_0_form(supp, dirichlet, x_prt, Lg_prt) + + Id = BEAST.Identity() + + bnd_supp = boundary(supp) + supp_nodes = CompScienceMeshes.skeleton_fast(supp, 0) + + dir_compl = submesh(!in(dirichlet), bnd_supp) + dir_compl_nodes = CompScienceMeshes.skeleton_fast(dir_compl, 0) + int_nodes = submesh(!in(dir_compl_nodes), supp_nodes) + + if length(int_nodes) == 0 + T = scalartype(Id, Lg_prt, Lg_prt) + S = typeof(Lg_prt) + P = eltype(Lg_prt.pos) + return T[], int_nodes, S(supp, Vector{Vector{Shape{T}}}(), Vector{P}()) + end + + Lg_int = BEAST.lagrangec0d1(supp, int_nodes) + + grad_Lg_prt = gradient(Lg_prt) + grad_Lg_int = gradient(Lg_int) + + A = assemble(Id, grad_Lg_int, grad_Lg_int, threading=Threading{:single}) + a = -assemble(Id, grad_Lg_int, grad_Lg_prt, threading=Threading{:single}) * x_prt + + x_int = A \ a + return x_int, int_nodes, Lg_int +end + + +function dualforms_init(Supp, Dir) + tetrs = barycentric_refinement(Supp) + v2t, v2n = CompScienceMeshes.vertextocellmap(tetrs) + bnd = boundary(tetrs) + gpred = CompScienceMeshes.overlap_gpredicate(Dir) + dir = submesh(face -> gpred(chart(bnd,face)), bnd) + return tetrs, bnd, dir, v2t, v2n +end + +function duallagrangec0d1(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}, nodes, dirbnd) + refd, bnd, dir, v2t, v2n = dualforms_init(mesh, dirbnd) + dual0forms_body(mesh, refd, bnd, dir, v2t, v2n) +end + +function dual0forms_body(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}, refd, bnd, dir, v2t, v2n) + + T = coordtype(refd) + S = Shape{T} + V = vertextype(mesh) + bfs = Vector{Vector{S}}(undef, length(mesh)) + pos = Vector{V}(undef, length(mesh)) + + Cells = cells(mesh) + num_threads = Threads.nthreads() + for F in 1:length(mesh) + Cell = Cells[F] + + myid = Threads.threadid() + myid == 1 && F % 20 == 0 && + println("Constructing dual 1-forms: $(F*num_threads) out of $(length(mesh)).") + + idcs1 = v2t[Cell[1],1:v2n[Cell[1]]] + idcs2 = v2t[Cell[2],1:v2n[Cell[2]]] + idcs3 = v2t[Cell[3],1:v2n[Cell[3]]] + + supp1 = refd[idcs1] # 2D + supp2 = refd[idcs2] + supp3 = refd[idcs3] + + bnd_supp1 = boundary(supp1) # 1D + bnd_supp2 = boundary(supp2) + bnd_supp3 = boundary(supp3) + + supp23 = submesh(in(bnd_supp2), bnd_supp3) # 1D + supp31 = submesh(in(bnd_supp3), bnd_supp1) + supp12 = submesh(in(bnd_supp1), bnd_supp2) + + port = boundary(supp23) # 0D + port = submesh(in(boundary(supp31)), port) + port = submesh(in(boundary(supp12)), port) + @assert length(port) == 1 + + in_dir = in(dir) + dir1_edges = submesh(in_dir, bnd_supp1) # 1D + dir2_edges = submesh(in_dir, bnd_supp2) + dir3_edges = submesh(in_dir, bnd_supp3) + + bnd_dir1 = boundary(dir1_edges) # 0D + bnd_dir2 = boundary(dir2_edges) + bnd_dir3 = boundary(dir3_edges) + + dir23_nodes = submesh(in(bnd_dir2), bnd_dir3) # 0D + dir31_nodes = submesh(in(bnd_dir3), bnd_dir1) + dir12_nodes = submesh(in(bnd_dir1), bnd_dir2) + + # Step 1: set port flux and extend to dual faces + x0 = ones(T,1) + + Lg12_prt = BEAST.lagrangec0d1(supp12, port) + Lg23_prt = BEAST.lagrangec0d1(supp23, port) + Lg31_prt = BEAST.lagrangec0d1(supp31, port) + + x23, supp23_int_nodes, _ = extend_0_form(supp23, dir23_nodes, x0, Lg23_prt) + x31, supp31_int_nodes, _ = extend_0_form(supp31, dir31_nodes, x0, Lg31_prt) + x12, supp12_int_nodes, _ = extend_0_form(supp12, dir12_nodes, x0, Lg12_prt) + + port1_nodes = union(port, supp31_int_nodes, supp12_int_nodes) + port2_nodes = union(port, supp12_int_nodes, supp23_int_nodes) + port3_nodes = union(port, supp23_int_nodes, supp31_int_nodes) + + x1_prt = [x0; x31; x12] + x2_prt = [x0; x12; x23] + x3_prt = [x0; x23; x31] + + Lg1_prt = BEAST.lagrangec0d1(supp1, port1_nodes) + Lg2_prt = BEAST.lagrangec0d1(supp2, port2_nodes) + Lg3_prt = BEAST.lagrangec0d1(supp3, port3_nodes) + + x1_int, _, Lg1_int = extend_0_form(supp1, dir1_edges, x1_prt, Lg1_prt) + x2_int, _, Lg2_int = extend_0_form(supp2, dir2_edges, x2_prt, Lg2_prt) + x3_int, _, Lg3_int = extend_0_form(supp3, dir3_edges, x3_prt, Lg3_prt) + + # inject in the global space + fn = BEAST.Shape{Float64}[] + addf!(fn, x1_prt, Lg1_prt, idcs1) + addf!(fn, x1_int, Lg1_int, idcs1) + + addf!(fn, x2_prt, Lg2_prt, idcs2) + addf!(fn, x2_int, Lg2_int, idcs2) + + addf!(fn, x3_prt, Lg3_prt, idcs3) + addf!(fn, x3_int, Lg3_int, idcs3) + + pos[F] = cartesian(CompScienceMeshes.center(chart(mesh, Cell))) + bfs[F] = fn + end + + LagrangeBasis{1,0,3}(refd, bfs, pos) +end diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 4ff1cc88..a619846f 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -75,7 +75,7 @@ function gradient(ref::LagrangeRefSpace{T,1,4}, sh, tet) where {T} n = normal(ctr_opp_face) h = -dot(this_vert - cartesian(ctr_opp_face), n) @assert h > 0 - gradval = -h*n + gradval = -(1/h)*n output = Vector{Shape{T}}() for (i,edge) in enumerate(CompScienceMeshes.edges(tet)) ctr_edge = center(edge) @@ -84,12 +84,46 @@ function gradient(ref::LagrangeRefSpace{T,1,4}, sh, tet) where {T} lgt = volume(edge) cff = -lgt * dot(tgt, gradval) isapprox(cff, 0, atol=sqrt(eps(T))) && continue - push!(output, Shape(sh.cellid, i, cff)) + push!(output, Shape(sh.cellid, i, sh.coeff * cff)) end return output end +function gradient(ref::LagrangeRefSpace{T,1,3}, sh, tri) where {T} + + this_vert = tri.vertices[sh.refid] + opp_edge = faces(tri)[sh.refid] + ctr_opp_face = center(opp_edge) + # n = normal(ctr_opp_face) + n = -normalize(cross(opp_edge.tangents[1], normal(tri))) + h = -dot(this_vert - cartesian(ctr_opp_face), n) + @assert h > 0 "h = $h" + gradval = -(1/h)*n + output = Vector{Shape{T}}() + for (i,edge) in enumerate(CompScienceMeshes.edges(tri)) + ctr_edge = center(edge) + tgt = tangents(ctr_edge,1) + tgt = normalize(tgt) + lgt = volume(edge) + cff = -lgt * dot(tgt, gradval) + isapprox(cff, 0, atol=sqrt(eps(T))) && continue + push!(output, Shape(sh.cellid, i, sh.coeff * cff)) + end + return output +end + + +function gradient(ref::LagrangeRefSpace{T,1,2}, sh, seg) where {T} + + sh.refid == 1 && return [Shape(sh.cellid, 1, +sh.coeff/volume(seg))] + @assert sh.refid == 2 + return [Shape(sh.cellid, 1, -sh.coeff/volume(seg))] + +end + + + function strace(x::LagrangeRefSpace, cell, localid, face) Q = zeros(scalartype(x),2,3) diff --git a/src/helmholtz3d/hh3dfar.jl b/src/helmholtz3d/hh3dfar.jl new file mode 100644 index 00000000..cd37fc07 --- /dev/null +++ b/src/helmholtz3d/hh3dfar.jl @@ -0,0 +1,19 @@ +struct HH3DFarField{K} + gamma::K +end + +function HH3DFarField(;wavenumber=error("wavenumber is a required argument")) + if iszero(real(wavenumber)) + HH3DFarField(-imag(wavenumber)) + else + HH3DFarField(wavenumber*im) + end +end + +quaddata(op::HH3DFarField,rs,els) = quadpoints(rs,els,(3,)) +quadrule(op::HH3DFarField,refspace,p,y,q,el,qdata) = qdata[1,q] + +kernelvals(op::HH3DFarField,y,p) = exp(op.gamma*dot(y,cartesian(p))) +function integrand(op::HH3DFarField,krn,y,f,p) + krn * f.value +end \ No newline at end of file diff --git a/src/localop.jl b/src/localop.jl index dfafa62d..6d4852e0 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -72,6 +72,21 @@ function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, return assemble_local_mixed!(biop, tfs, bfs, store; quadstrat) end +function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, + threading::Type{Threading{:single}}; + quadstrat=defaultquadstrat(biop, tfs, bfs)) + +if geometry(tfs) == geometry(bfs) + return assemble_local_matched!(biop, tfs, bfs, store; quadstrat) +end + +if CompScienceMeshes.refines(geometry(tfs), geometry(bfs)) + return assemble_local_refines!(biop, tfs, bfs, store; quadstrat) +end + +return assemble_local_mixed!(biop, tfs, bfs, store; quadstrat) +end + function assemble_local_matched!(biop::LocalOperator, tfs::Space, bfs::Space, store; quadstrat=defaultquadstrat(biop, tfs, bfs)) diff --git a/src/postproc/segcurrents.jl b/src/postproc/segcurrents.jl index 88ac3b58..0d45d3ff 100644 --- a/src/postproc/segcurrents.jl +++ b/src/postproc/segcurrents.jl @@ -19,7 +19,7 @@ end """ Hi """ -function grideval(points, coeffs, basis) +function grideval(points, coeffs, basis; type=nothing) # charts: active charts # ad: assembly data (active_cell_idx, local_shape_idx) -> [dof1, dfo2, ...] @@ -31,12 +31,15 @@ function grideval(points, coeffs, basis) T = promote_type(eltype(coeffs), eltype(V)) P = similar_type(V, T) - values = zeros(P, length(points)) + type != nothing && (P = type) + + values = zeros(P, size(points)) chart_tree = BEAST.octree(charts) for (j,point) in enumerate(points) i = CompScienceMeshes.findchart(charts, chart_tree, point) if i != nothing + # @show i chart = charts[i] u = carttobary(chart, point) vals = refs(neighborhood(chart,u)) diff --git a/src/solvers/lusolver.jl b/src/solvers/lusolver.jl index c3944834..f32057e6 100644 --- a/src/solvers/lusolver.jl +++ b/src/solvers/lusolver.jl @@ -1,13 +1,20 @@ +using LinearAlgebra + function convert_to_dense(A::LinearMaps.LinearCombination) + @info "convert matrix to dense..." T = eltype(A) # M = zeros(T, size(A)) - M = PseudoBlockMatrix{T}(undef, BlockArrays.blocksizes(A)...) - fill!(M,0) + # M = PseudoBlockMatrix{T}(undef, BlockArrays.blocksizes(A)...) + # fill!(M,0) + M = zeros(eltype(A), axes(A)) + @show typeof(M) for map in A.maps - M .+= Matrix(map) + mul!(M, 1, map, 1, 1) + # M .+= Matrix(map) end + @info "matrix converted to dense" return M end diff --git a/src/utils/zeromap.jl b/src/utils/zeromap.jl index c0bd5c46..6d297289 100644 --- a/src/utils/zeromap.jl +++ b/src/utils/zeromap.jl @@ -9,6 +9,7 @@ ZeroMap{T}(range::U, domain::V) where {T,U,V} = ZeroMap{T,U,V}(range, domain) LinearMaps.MulStyle(A::ZeroMap) = LinearMaps.FiveArg() Base.size(A::ZeroMap) = (length(A.range), length(A.domain),) +Base.axes(A::ZeroMap) = (A.range, A.domain) function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector, α::Number, β::Number) @@ -16,6 +17,13 @@ function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector, y .*= β end +function LinearAlgebra.mul!(Y::PseudoBlockMatrix, c::Number, X::ZeroMap, a::Number, b::Number) + @assert b == 1 + return Y +end + + + # function LinearAlgebra.mul!(y::AbstractVector, # Lt::LinearMaps.TransposeMap{<:Any,<:ZeroMap}, # x::AbstractVector, α::Number, β::Number) diff --git a/test/test_nitsche.jl b/test/test_nitsche.jl index 16994ac5..75bd67f9 100644 --- a/test/test_nitsche.jl +++ b/test/test_nitsche.jl @@ -24,7 +24,7 @@ X = raviartthomas(Γ, edges) x = divergence(X) y = ntrace(X,γ) -Z = assemble(S,y,x) +Z = assemble(S,y,x; threading = BEAST.Threading{:single}) # test for the correct sparsity pattern # I, J, V = findall(!iszero, Z) From fb57b9013d8fdc5a8981a733d6a66c6ef950b2b7 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Tue, 14 Dec 2021 15:27:59 +0100 Subject: [PATCH 142/528] dsvie --- examples/dsvie.jl | 73 ++++++ examples/pmchwt.jl | 2 + examples/tdefie_neumann.jl | 4 - src/BEAST.jl | 4 + src/operator.jl | 9 +- src/volumeintegral/sauterschwab_ints.jl | 102 +++++++- src/volumeintegral/vsie.jl | 236 +++++++++++++++++++ src/volumeintegral/vsieops.jl | 300 ++++++++++++++++++++++++ 8 files changed, 721 insertions(+), 9 deletions(-) create mode 100644 examples/dsvie.jl create mode 100644 src/volumeintegral/vsie.jl create mode 100644 src/volumeintegral/vsieops.jl diff --git a/examples/dsvie.jl b/examples/dsvie.jl new file mode 100644 index 00000000..ebb0ea10 --- /dev/null +++ b/examples/dsvie.jl @@ -0,0 +1,73 @@ +using CompScienceMeshes, BEAST +using LinearAlgebra +using Profile +using StaticArrays + + + +ntrc = X->ntrace(X,Γ) + +T = tetmeshsphere(1.0,0.5) +X = nedelecd3d(T) +Γ = boundary(T) +Y = raviartthomas(Γ) + +@show numfunctions(X) +@show numfunctions(Y) + + +κ, η = 1.0, 1.0 +κ′, η′ = √2.0κ, η/√2.0 +ϵ_r =2.0 + + +χ = x->(1.0-1.0/ϵ_r) + +#Volume-Volume +L,I,B = VIE.singlelayer(wavenumber=κ', tau=χ), Identity(), VIE.boundary(wavenumber=κ', tau=χ) +#Volume-Surface +Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ', tau=χ)), transpose(VSIE.boundary(wavenumber=κ', tau=χ)), transpose(VSIE.doublelayer(wavenumber=κ', tau=χ)) +Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ'), VSIE.boundary(wavenumber=κ'), VSIE.doublelayer(wavenumber=κ') +#Surface-Surface +T = Maxwell3D.singlelayer(wavenumber=κ) #Outside +T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside +K = Maxwell3D.doublelayer(wavenumber=κ) #Outside +K′ = Maxwell3D.doublelayer(wavenumber=κ′) #Inside + +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +H = -1/(im*κ*η)*curl(E) + +e, h = (n × E) × n, (n × H) × n + +@hilbertspace D j m +@hilbertspace k l o +β = 1.0/ϵ_r +α, α′ = 1/η, 1/η′ + +eq = @varform (β*I[k,D]-L[k,D]-B[ntrc(k),D] + Lt[k,j]+Bt[ntrc(k),j] + Kt[k,m] + + Ls[l,D]+Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + + Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) + + +dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y + +u = solve(dvsie) + + +#Post processing +Θ, Φ = range(0.0,stop=2π,length=100), 0.0 +ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] + +# Don't forget the far field comprises two contributions +ffm = potential(MWFarField3D(κ*im), ffpoints, u[m], Y) +ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], Y) +ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) + + +ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u[D], X) +ff2 = im*κ*ffd + +using Plots +plot(xlabel="theta") +plot!(Θ,norm.(ff),label="far field",title="DVSIE") +plot!(Θ,norm.(ff2),label="far field",title="DVSIE 2") diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 254855ea..2094cb02 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -61,6 +61,7 @@ using Plots plot(xlabel="theta") plot!(Θ,norm.(ff),label="far field",title="PMCHWT") +#= #import Plotly #using LinearAlgebra #fcrj, _ = facecurrents(u[j],X) @@ -121,3 +122,4 @@ Et_far = -cross.(ffpoints, nxE_far) plot() plot!(Θ, norm.(ff),label="far field") scatter!(Θ, norm.(Et_far), label="field far") +=# \ No newline at end of file diff --git a/examples/tdefie_neumann.jl b/examples/tdefie_neumann.jl index f8a31ae4..3746417c 100644 --- a/examples/tdefie_neumann.jl +++ b/examples/tdefie_neumann.jl @@ -43,11 +43,7 @@ using Plotly Xefie, Δω, ω0 = fouriertransform(xefie, Δt, 0.0, 2) -<<<<<<< HEAD -ω = collect(ω0 + (0:Nt-1)*Δω) -======= ω = collect(ω0 .+ (0:Nt-1)*Δω) ->>>>>>> upstream/master _, i1 = findmin(abs.(ω.-sol)) ω1 = ω[i1] diff --git a/src/BEAST.jl b/src/BEAST.jl index eade439c..36d82d0b 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -70,6 +70,7 @@ export DoubleLayerRotatedMW3D export MWSingleLayerPotential3D export VIEOperator +export VSIEOperator export gmres export @hilbertspace, @varform, @discretise @@ -208,8 +209,11 @@ include("volumeintegral/vie.jl") include("volumeintegral/vieexc.jl") include("volumeintegral/vieops.jl") include("volumeintegral/farfield.jl") +include("volumeintegral/vsie.jl") +include("volumeintegral/vsieops.jl") include("volumeintegral/sauterschwab_ints.jl") + include("decoupled/dpops.jl") include("decoupled/potentials.jl") diff --git a/src/operator.jl b/src/operator.jl index 005bb554..0162d3ac 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -186,7 +186,8 @@ end -function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, store; +function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, store, + threading::Type{Threading{:multi}} = Threading{:multi}; quadstrat=defaultquadstrat(op, tfs, bfs)) store1(v,m,n) = store(v,n,m) @@ -212,7 +213,7 @@ function assemble!(op::Operator, tfs::DirectProductSpace, bfs::Space, store, thr for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) store1(v,m,n) = store(v,m + I[i], n) - assemble!(op, s, bfs, store1; quadstrat) + assemble!(op, s, bfs, store1, threading; quadstrat) end end @@ -223,7 +224,7 @@ function assemble!(op::Operator, tfs::Space, bfs::DirectProductSpace, store, thr for s in bfs.factors push!(J, last(J) + numfunctions(s)) end for (j,s) in enumerate(bfs.factors) store1(v,m,n) = store(v,m,n + J[j]) - assemble!(op, tfs, s, store1; quadstrat) + assemble!(op, tfs, s, store1, threading; quadstrat) end end @@ -233,6 +234,6 @@ function assemble!(op::Operator, tfs::DirectProductSpace, bfs::DirectProductSpac for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) store1(v,m,n) = store(v,m + I[i],n) - assemble!(op, s, bfs, store1; quadstrat) + assemble!(op, s, bfs, store1, threading; quadstrat) end end diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index b62b3e7f..2746604c 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -71,7 +71,7 @@ function reorder_dof(space::LagrangeRefSpace{Float64,0,3,1},I) return SVector{1,Int64}(1),SVector{1,Int64}(1) end - +#Method of moments volume integrals operators function momintegrals!(op::VIEOperator, test_local_space::RefSpace, trial_local_space::RefSpace, test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) @@ -168,3 +168,103 @@ function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::Do return z end + + +#Method of moments volume surface integrals operators +function momintegrals!(op::VSIEOperator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) + + #Find permutation of vertices to match location of singularity to SauterSchwab + J, I= SauterSchwab3D.reorder(strat.sing) + + #Get permutation and rel. orientatio of DoFs + K,O1 = reorder_dof(test_local_space, I) + L,O2 = reorder_dof(trial_local_space, J) + #Apply permuation to elements + + if length(I) == 4 + test_tetrahedron_element = simplex( + test_tetrahedron_element.vertices[I[1]], + test_tetrahedron_element.vertices[I[2]], + test_tetrahedron_element.vertices[I[3]], + test_tetrahedron_element.vertices[I[4]]) + elseif length(I) == 3 + test_tetrahedron_element = simplex( + test_tetrahedron_element.vertices[I[1]], + test_tetrahedron_element.vertices[I[2]], + test_tetrahedron_element.vertices[I[3]]) + end + + #test_tetrahedron_element = simplex(test_tetrahedron_element.vertices[I]...) + + if length(J) == 4 + trial_tetrahedron_element = simplex( + trial_tetrahedron_element.vertices[J[1]], + trial_tetrahedron_element.vertices[J[2]], + trial_tetrahedron_element.vertices[J[3]], + trial_tetrahedron_element.vertices[J[4]]) + elseif length(J) == 3 + trial_tetrahedron_element = simplex( + trial_tetrahedron_element.vertices[J[1]], + trial_tetrahedron_element.vertices[J[2]], + trial_tetrahedron_element.vertices[J[3]]) + end + + #trial_tetrahedron_element = simplex(trial_tetrahedron_element.vertices[J]...) + + #Define integral (returns a function that only needs barycentric coordinates) + igd = VSIEIntegrand(test_tetrahedron_element, trial_tetrahedron_element, + op, test_local_space, trial_local_space) + + #Evaluate integral + Q = SauterSchwab3D.sauterschwab_parameterized(igd, strat) + + #Undo permuation on DoFs + for j in 1 : length(L) + for i in 1 : length(K) + out[i,j] += Q[K[i],L[j]]*O1[i]*O2[j] + end + end + nothing +end + +function momintegrals!(biop::VSIEOperator, tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) + + # memory allocation here is a result from the type instability on strat + # which is on purpose, i.e. the momintegrals! method is chosen based + # on dynamic polymorphism. + womps = strat.outer_quad_points + wimps = strat.inner_quad_points + + M, N = size(z) + + for womp in womps + tgeo = womp.point + tvals = womp.value + jx = womp.weight + + for wimp in wimps + bgeo = wimp.point + bvals = wimp.value + jy = wimp.weight + + j = jx * jy + kernel = kernelvals(biop, tgeo, bgeo) + + + igd = integrand(biop, kernel, tvals, tgeo, bvals, bgeo) + + for m in 1 : length(tvals) + + for n in 1 : length(bvals) + + z[m,n] += j * igd[m,n] + end + end + end + end + + return z +end + diff --git a/src/volumeintegral/vsie.jl b/src/volumeintegral/vsie.jl new file mode 100644 index 00000000..b0dcc2b4 --- /dev/null +++ b/src/volumeintegral/vsie.jl @@ -0,0 +1,236 @@ +module VSIE + using ..BEAST + Mod = BEAST + + """ + singlelayer(;gamma, alpha, beta, tau) + singlelayer(;wavenumber, alpha, beta, tau) + + Bilinear form given by: + + ```math + α ∬_{Γ×Ω} j(x)⋅τ(y)⋅k(y) G_{γ}(x,y) + β ∬_{Γ×Ω} div j(x) τ(y) div k(y) G_{γ}(x,y) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast dyadic + """ + + function singlelayer(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = wavenumber*wavenumber) + beta == nothing && (beta = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIESingleLayer(gamma, alpha, beta, tau) + end + + function singlelayerT(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = wavenumber*wavenumber) + beta == nothing && (beta = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIESingleLayerT(gamma, alpha, beta, tau) + end + + """ + boundary(;gamma, alpha, tau) + boundary(;wavenumber, alpha, tau) + + Bilinear form given by: + + ```math + α ∬_{Γ×Ω} T_n j(x) grad G_{γ}(x,y)⋅τ(y) div k(y) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast dyadic + """ + + function boundary(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIEBoundary(gamma, alpha, tau) + end + + function boundaryT(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIEBoundaryT(gamma, alpha, tau) + end + + + + """ + doublelayer(;gamma, alpha, beta, tau) + doublelayer(;wavenumber, alpha, beta, tau) + + Bilinear form given by: + + ```math + α ∬_{Ω×Γ} j(x) ⋅ grad G_{γ}(x,y) × τ(y)⋅k(y) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast dyadic + """ + + function doublelayer(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIEDoubleLayer(gamma, alpha, tau) + end + + + function doublelayerT(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIEDoubleLayerT(gamma, alpha, tau) + end + +end \ No newline at end of file diff --git a/src/volumeintegral/vsieops.jl b/src/volumeintegral/vsieops.jl new file mode 100644 index 00000000..1235cd7c --- /dev/null +++ b/src/volumeintegral/vsieops.jl @@ -0,0 +1,300 @@ +abstract type VSIEOperator <: IntegralOperator end +abstract type VolumeSurfaceOperator <: VSIEOperator end +abstract type BoundarySurfaceOperator <: VSIEOperator end + +struct KernelValsVSIE{T,U,P,Q,K} + gamma::U + vect::P + dist::T + green::U + gradgreen::Q + tau::K +end + +function kernelvals(viop::VSIEOperator, p ,q) + Y = viop.gamma; + r = cartesian(p)-cartesian(q) + R = norm(r) + yR = Y*R + + expn = exp(-yR) + green = expn / (4*pi*R) + gradgreen = - (Y +1/R)*green/R*r + + + tau = viop.tau(cartesian(q)) + + KernelValsVSIE(Y,r,R, green, gradgreen,tau) +end + +struct VSIESingleLayer{T,U,P} <: VolumeSurfaceOperator + gamma::T + α::U + β::U + tau::P +end + +struct VSIEBoundary{T,U,P} <: BoundarySurfaceOperator + gamma::T + α::U + tau::P +end + +struct VSIEDoubleLayer{T,U,P} <: VolumeSurfaceOperator + gamma::T + α::U + tau::P +end + + +struct VSIESingleLayerT{T,U,P} <: VolumeSurfaceOperator + gamma::T + α::U + β::U + tau::P +end + +struct VSIEBoundaryT{T,U,P} <: BoundarySurfaceOperator + gamma::T + α::U + tau::P +end + +struct VSIEDoubleLayerT{T,U,P} <: VolumeSurfaceOperator + gamma::T + α::U + tau::P +end + +scalartype(op::VSIEOperator) = typeof(op.gamma) + +export VSIE + +struct VSIEIntegrand{S,T,O,K,L} + test_tetrahedron_element::S + trial_triangle_element::T + op::O + test_local_space::K + trial_local_space::L +end + + +function (igd::VSIEIntegrand)(u,v) + + #mesh points + tgeo = neighborhood(igd.test_tetrahedron_element,v) + bgeo = neighborhood(igd.trial_triangle_element,u) + + #kernel values + kerneldata = kernelvals(igd.op,tgeo,bgeo) + + #values & grad/div/curl of local shape functions + tval = igd.test_local_space(tgeo) + bval = igd.trial_local_space(bgeo) + + #jacobian + j = jacobian(tgeo) * jacobian(bgeo) + + integrand(igd.op, kerneldata,tval,tgeo,bval,tgeo) * j +end + + +function integrand(viop::VSIESingleLayer, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:3] + fy = @SVector[bvals[i].value for i in 1:4] + + dgx = @SVector[tvals[i].divergence for i in 1:3] + dfy = @SVector[bvals[i].divergence for i in 1:4] + + G = kerneldata.green + + Ty = kerneldata.tau + + α = viop.α + β = viop.β + + @SMatrix[α * dot(gx[i],Ty*fy[j]) * G + β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] +end + +function integrand(viop::VSIEBoundary, kerneldata, tvals, tgeo, bvals, bgeo) + + dgx = @SVector[tvals[i].divergence for i in 1:3] + fy = @SVector[bvals[i].value for i in 1:1] + + G = kerneldata.green + + Tx = kerneldata.tau + + α = viop.α + + @SMatrix[α * Tx * dgx[i] * fy[j] * G for i in 1:3, j in 1:1] +end + +function integrand(viop::VSIESingleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:4] + fy = @SVector[bvals[i].value for i in 1:3] + + dgx = @SVector[tvals[i].divergence for i in 1:4] + dfy = @SVector[bvals[i].divergence for i in 1:3] + + G = kerneldata.green + + Ty = kerneldata.tau + + α = viop.α + β = viop.β + + @SMatrix[α * dot(gx[i],Ty*fy[j]) * G + β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] +end + + +function integrand(viop::VSIEBoundaryT, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:1] + dfy = @SVector[bvals[i].divergence for i in 1:3] + + G = kerneldata.green + + Tx = kerneldata.tau + + α = viop.α + + @SMatrix[α * Tx * gx[i] * dfy[j] * G for i in 1:3, j in 1:1] +end + + +function integrand(viop::VSIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) + gx = @SVector[tvals[i].value for i in 1:3] + fy = @SVector[bvals[i].value for i in 1:4] + + gradG = kerneldata.gradgreen + + Ty = kerneldata.tau + + α = viop.α + + @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:3, j in 1:4] +end + +function integrand(viop::VSIEDoubleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) + gx = @SVector[tvals[i].value for i in 1:4] + fy = @SVector[bvals[i].value for i in 1:3] + + gradG = kerneldata.gradgreen + + Ty = kerneldata.tau + + α = viop.α + + @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:4, j in 1:3] +end + + +defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) + + +function quaddata(op::VSIEOperator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_charts, trial_charts, qs::SauterSchwab3DQStrat) + + #The combinations of rules (6,7) and (5,7 are) BAAAADDDD + # they result in many near singularity evaluations with any + # resemblence of accuracy going down the drain! Simply don't! + # (same for (5,7) btw...). + t_qp = quadpoints(test_local_space, test_charts, (qs.outer_rule)) + b_qp = quadpoints(trial_local_space, trial_charts, (qs.inner_rule)) + + + sing_qp = (SauterSchwab3D._legendre(qs.sauter_schwab_1D,0,1), + SauterSchwab3D._shunnham2D(qs.sauter_schwab_2D), + SauterSchwab3D._shunnham3D(qs.sauter_schwab_3D), + SauterSchwab3D._shunnham4D(qs.sauter_schwab_4D),) + + + return (tpoints=t_qp, bpoints=b_qp, sing_qp=sing_qp) +end + +quadrule(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs) = qr_volume(op, g, f, i, τ, j, σ, qd, qs) + + +function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, + qs::SauterSchwab3DQStrat) + + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) + + hits = 0 + idx_t = Int64[] + idx_s = Int64[] + sizehint!(idx_t,4) + sizehint!(idx_s,4) + dmin2 = floatmax(eltype(eltype(τ.vertices))) + D = dimension(τ)+dimension(σ) + for (i,t) in enumerate(τ.vertices) + for (j,s) in enumerate(σ.vertices) + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + if d2 < dtol + push!(idx_t,i) + push!(idx_s,j) + hits +=1 + break + end + end + end + + #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) + + hits == 3 && return SauterSchwab3D.CommonFace5D_S(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) + hits == 2 && return SauterSchwab3D.CommonEdge5D_S(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) + hits == 1 && return SauterSchwab3D.CommonVertex5D_S(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),(qd.sing_qp[3],qd.sing_qp[2])) + + + return DoubleQuadRule( + qd[1][1,i], + qd[2][1,j]) + +end + +quadrule(op::BoundarySurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs) = qr_boundary(op, g, f, i, τ, j, σ, qd, qs) + +function qr_boundary(op::BoundarySurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, + qs::SauterSchwab3DQStrat) + + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) + + hits = 0 + idx_t = Int64[] + idx_s = Int64[] + sizehint!(idx_t,4) + sizehint!(idx_s,4) + dmin2 = floatmax(eltype(eltype(τ.vertices))) + D = dimension(τ)+dimension(σ) + for (i,t) in enumerate(τ.vertices) + for (j,s) in enumerate(σ.vertices) + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + if d2 < dtol + push!(idx_t,i) + push!(idx_s,j) + hits +=1 + break + end + end + end + + #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) + + + hits == 3 && return SauterSchwab3D.CommonFace4D_S(SauterSchwab3D.Singularity4DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[3])) + hits == 2 && return SauterSchwab3D.CommonEdge4D_S(SauterSchwab3D.Singularity4DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2])) + hits == 1 && return SauterSchwab3D.CommonVertex4D_S(SauterSchwab3D.Singularity4DPoint(idx_t,idx_s),(qd.sing_qp[2])) + + + return DoubleQuadRule( + qd[1][1,i], + qd[2][1,j]) + +end + From 72ee5ecb61beb91908ac304de340b06ba7a415b0 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Sat, 18 Dec 2021 15:43:34 +0100 Subject: [PATCH 143/528] update vsie implementation --- examples/dsvie.jl | 38 +++++++++++++++++++++++------------ examples/dvie.jl | 7 ++++--- examples/pmchwt.jl | 5 ++--- src/volumeintegral/vieops.jl | 2 +- src/volumeintegral/vsie.jl | 4 ++-- src/volumeintegral/vsieops.jl | 2 +- 6 files changed, 35 insertions(+), 23 deletions(-) diff --git a/examples/dsvie.jl b/examples/dsvie.jl index ebb0ea10..fa12c2ef 100644 --- a/examples/dsvie.jl +++ b/examples/dsvie.jl @@ -7,7 +7,7 @@ using StaticArrays ntrc = X->ntrace(X,Γ) -T = tetmeshsphere(1.0,0.5) +T = tetmeshsphere(1.0,0.25) X = nedelecd3d(T) Γ = boundary(T) Y = raviartthomas(Γ) @@ -18,7 +18,8 @@ Y = raviartthomas(Γ) κ, η = 1.0, 1.0 κ′, η′ = √2.0κ, η/√2.0 -ϵ_r =2.0 +ϵ_r =3.0 +ϵ_b =2.0 χ = x->(1.0-1.0/ϵ_r) @@ -26,8 +27,8 @@ Y = raviartthomas(Γ) #Volume-Volume L,I,B = VIE.singlelayer(wavenumber=κ', tau=χ), Identity(), VIE.boundary(wavenumber=κ', tau=χ) #Volume-Surface -Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ', tau=χ)), transpose(VSIE.boundary(wavenumber=κ', tau=χ)), transpose(VSIE.doublelayer(wavenumber=κ', tau=χ)) -Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ'), VSIE.boundary(wavenumber=κ'), VSIE.doublelayer(wavenumber=κ') +Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ')), transpose(VSIE.boundary(wavenumber=κ')), transpose(VSIE.doublelayer(wavenumber=κ')) +Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ', tau=χ), VSIE.boundary(wavenumber=κ', tau=χ), VSIE.doublelayer(wavenumber=κ', tau=χ) #Surface-Surface T = Maxwell3D.singlelayer(wavenumber=κ) #Outside T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside @@ -41,17 +42,21 @@ e, h = (n × E) × n, (n × H) × n @hilbertspace D j m @hilbertspace k l o -β = 1.0/ϵ_r +β = 1.0/(ϵ_r*ϵ_b) +ν = 1/ϵ_b α, α′ = 1/η, 1/η′ +γ′ = im*η′/κ′ +ζ′ = im*η′*κ′ +δ′ = im*κ′/ϵ_b -eq = @varform (β*I[k,D]-L[k,D]-B[ntrc(k),D] + Lt[k,j]+Bt[ntrc(k),j] + Kt[k,m] + - Ls[l,D]+Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + - Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) +eq = @varform (β*I[k,D]-ν*L[k,D]-ν*B[ntrc(k),D] + η′*Lt[k,j]-γ′*Bt[ntrc(k),j] + Kt[k,m] + + -δ′*Ls[l,D]-ν*Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + + ζ′*Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y -u = solve(dvsie) +u_n = solve(dvsie) #Post processing @@ -59,15 +64,22 @@ u = solve(dvsie) ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] # Don't forget the far field comprises two contributions -ffm = potential(MWFarField3D(κ*im), ffpoints, u[m], Y) -ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], Y) +ffm = potential(MWFarField3D(κ*im), ffpoints, u_n[m], Y) +ffj = potential(MWFarField3D(κ*im), ffpoints, u_n[j], Y) ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) -ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u[D], X) +ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u_n[D], X) ff2 = im*κ*ffd using Plots plot(xlabel="theta") plot!(Θ,norm.(ff),label="far field",title="DVSIE") -plot!(Θ,norm.(ff2),label="far field",title="DVSIE 2") +plot!(Θ,√6.0*norm.(ff),label="far field",title="DVSIE 2") + +import Plotly +using LinearAlgebra +fcrj, _ = facecurrents(u_n[j],Y) +fcrm, _ = facecurrents(u_n[m],Y) +Plotly.plot(patch(Γ, norm.(fcrj))) +Plotly.plot(patch(Γ, norm.(fcrm))) \ No newline at end of file diff --git a/examples/dvie.jl b/examples/dvie.jl index 05ffa0db..535f57a3 100644 --- a/examples/dvie.jl +++ b/examples/dvie.jl @@ -4,7 +4,7 @@ using Profile using StaticArrays function tau(x::SVector{U,T}) where {U,T} - 1.0-1.0/4.0 + 1.0-1.0/9.0 end ntrc = X->ntrace(X,y) @@ -15,7 +15,7 @@ y = boundary(T) @show numfunctions(X) ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) -ϵ_r =4.0 +ϵ_r =25.0 χ = tau K, I, B = VIE.singlelayer(wavenumber=κ, tau=χ), Identity(), VIE.boundary(wavenumber=κ, tau=χ) E = VIE.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) @@ -41,6 +41,7 @@ using Plots plot(xlabel="theta") plot!(Θ, norm.(ff), label="far field", title="D-VIE") +#= #NearField Z = range(-1,1,length=100) Y = range(-1,1,length=100) @@ -52,6 +53,6 @@ Enear = reshape(Enear,100,100) contour(real.(getindex.(Enear,1))) heatmap(Z, Y, real.(getindex.(Enear,1)), clim=(0.0,1.0)) - +=# diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 2094cb02..504a0104 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -9,7 +9,7 @@ X = raviartthomas(Γ) @show numfunctions(X) κ, η = 1.0, 1.0 -κ′, η′ = 2.0κ, η/2.0 +κ′, η′ = √6.0κ, η/√6.0 T = Maxwell3D.singlelayer(wavenumber=κ) T′ = Maxwell3D.singlelayer(wavenumber=κ′) @@ -61,7 +61,7 @@ using Plots plot(xlabel="theta") plot!(Θ,norm.(ff),label="far field",title="PMCHWT") -#= + #import Plotly #using LinearAlgebra #fcrj, _ = facecurrents(u[j],X) @@ -122,4 +122,3 @@ Et_far = -cross.(ffpoints, nxE_far) plot() plot!(Θ, norm.(ff),label="far field") scatter!(Θ, norm.(Et_far), label="field far") -=# \ No newline at end of file diff --git a/src/volumeintegral/vieops.jl b/src/volumeintegral/vieops.jl index 57a0ed5b..0879f9dd 100644 --- a/src/volumeintegral/vieops.jl +++ b/src/volumeintegral/vieops.jl @@ -178,7 +178,7 @@ function integrand(viop::VIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) end -defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) +defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) function quaddata(op::VIEOperator, diff --git a/src/volumeintegral/vsie.jl b/src/volumeintegral/vsie.jl index b0dcc2b4..57d2cb35 100644 --- a/src/volumeintegral/vsie.jl +++ b/src/volumeintegral/vsie.jl @@ -41,8 +41,8 @@ module VSIE @assert gamma != nothing - alpha == nothing && (alpha = wavenumber*wavenumber) - beta == nothing && (beta = 1.0) + alpha == nothing && (alpha = gamma) + beta == nothing && (beta = im*im/gamma) tau == nothing && (tau = x->1.0) Mod.VSIESingleLayer(gamma, alpha, beta, tau) diff --git a/src/volumeintegral/vsieops.jl b/src/volumeintegral/vsieops.jl index 1235cd7c..85222b23 100644 --- a/src/volumeintegral/vsieops.jl +++ b/src/volumeintegral/vsieops.jl @@ -192,7 +192,7 @@ function integrand(viop::VSIEDoubleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) end -defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) +defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) function quaddata(op::VSIEOperator, From 44be41a27c6ab2cb6b9ecf43a6a90a4ff8f3488a Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 5 Jan 2022 16:56:07 +0100 Subject: [PATCH 144/528] sauterschwab quadrature for helmholtz3d --- Project.toml | 2 +- docs/Project.toml | 3 - src/BEAST.jl | 1 + src/bases/basis.jl | 11 +- src/bases/lagrange.jl | 2 +- src/bases/local/laglocal.jl | 13 ++- src/excitation.jl | 2 +- src/helmholtz3d/hh3d_sauterschwabqr.jl | 131 ++++++++++++++++++++++ src/helmholtz3d/hh3dops.jl | 146 +++++++++++++++++++++++-- src/identityop.jl | 6 +- src/maxwell/mwops.jl | 4 +- 11 files changed, 297 insertions(+), 24 deletions(-) create mode 100644 src/helmholtz3d/hh3d_sauterschwabqr.jl diff --git a/Project.toml b/Project.toml index 86151c6a..973262f7 100644 --- a/Project.toml +++ b/Project.toml @@ -38,8 +38,8 @@ FillArrays = "0.11, 0.12" IterativeSolvers = "0.9" LiftedMaps = "0.3" LinearMaps = "3.5" -SauterSchwabQuadrature = "2.1.3" SauterSchwab3D = "0.1.0" +SauterSchwabQuadrature = "2.1.3" SparseMatrixDicts = "0.2" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" diff --git a/docs/Project.toml b/docs/Project.toml index 464a80dc..ad968743 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,7 +1,4 @@ [deps] BEAST = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -<<<<<<< HEAD -======= CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" ->>>>>>> upstream/master Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" diff --git a/src/BEAST.jl b/src/BEAST.jl index 598f8b16..ca634a3f 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -203,6 +203,7 @@ include("helmholtz3d/hh3dops.jl") include("helmholtz3d/nitsche.jl") include("helmholtz3d/hh3dnear.jl") include("helmholtz3d/hh3dfar.jl") +include("helmholtz3d/hh3d_sauterschwabqr.jl") #suport for Volume Integral equation include("volumeintegral/vie.jl") diff --git a/src/bases/basis.jl b/src/bases/basis.jl index cd4bfaa6..71f10f8b 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -57,9 +57,14 @@ mutable struct DirectProductSpace{T} <: AbstractSpace factors::Vector{Space{T}} end -# defaultquadstrat(op, tfs::DirectProductSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs[1], bfs[1]) -# defaultquadstrat(op, tfs, bfs::DirectProductSpace) = defaultquadstrat(op, tfs, bfs[1]) -# defaultquadstrat(op, tfs::DirectProductSpace, bfs) = defaultquadstrat(op, tfs[1], bfs) +defaultquadstrat(op, tfs::DirectProductSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs.factors[1], bfs.factors[1]) +defaultquadstrat(op, tfs::Space, bfs::DirectProductSpace) = defaultquadstrat(op, tfs, bfs.factors[1]) +defaultquadstrat(op, tfs::DirectProductSpace, bfs::Space) = defaultquadstrat(op, tfs.factors[1], bfs) + +# defaultquadstrat(op, tfs::DirectProductSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs.factors[1], bfs.factors[1]) +# defaultquadstrat(op, tfs::RefSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs, bfs.factors[1]) +# defaultquadstrat(op, tfs::DirectProductSpace, bfs::RefSpace) = defaultquadstrat(op, tfs.factors[1], bfs) + export cross, × diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index cc05740e..e4080f23 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -563,7 +563,7 @@ function dual0forms_body(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}, refd, bn myid = Threads.threadid() myid == 1 && F % 20 == 0 && - println("Constructing dual 1-forms: $(F*num_threads) out of $(length(mesh)).") + println("Constructing dual 1-forms: $(F) out of $(length(mesh)).") idcs1 = v2t[Cell[1],1:v2n[Cell[1]]] idcs2 = v2t[Cell[2],1:v2n[Cell[2]]] diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index a619846f..e4cc02f4 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -25,10 +25,17 @@ end # Evaluete linear lagrange elements on a triangle function (f::LagrangeRefSpace{T,1,3})(t) where T u,v,w, = barycentric(t) + # SVector( + # (value=u,), + # (value=v,), + # (value=w,)) + + j = jacobian(t) + p = t.patch SVector( - (value=u,), - (value=v,), - (value=w,)) + (value=u, curl=(p[3]-p[2])/j), + (value=v, curl=(p[1]-p[3])/j), + (value=w, curl=(p[2]-p[1])/j)) end diff --git a/src/excitation.jl b/src/excitation.jl index 49f7c35c..6e774370 100644 --- a/src/excitation.jl +++ b/src/excitation.jl @@ -2,7 +2,7 @@ abstract type Functional end -quaddata(fn::Functional, refs, cells) = quadpoints(refs, cells, [5]) +quaddata(fn::Functional, refs, cells) = quadpoints(refs, cells, [8]) quadrule(fn::Functional, refs, p, cell, qd) = qd[1,p] """ diff --git a/src/helmholtz3d/hh3d_sauterschwabqr.jl b/src/helmholtz3d/hh3d_sauterschwabqr.jl new file mode 100644 index 00000000..5fffdb52 --- /dev/null +++ b/src/helmholtz3d/hh3d_sauterschwabqr.jl @@ -0,0 +1,131 @@ +function pulled_back_integrand(op::HH3DSingleLayerFDBIO, + test_local_space::LagrangeRefSpace{<:Any,0}, + trial_local_space::LagrangeRefSpace{<:Any,0}, + test_chart, trial_chart) + + (u,v) -> begin + + x = neighborhood(test_chart,u) + y = neighborhood(trial_chart,v) + + f = test_local_space(x) + g = trial_local_space(y) + + j = jacobian(x) * jacobian(y) + + α = op.alpha + γ = op.gamma + R = norm(cartesian(x)-cartesian(y)) + G = exp(-γ*R)/(4*π*R) + + αjG = α*G*j + + SA[f[1].value * αjG * g[1].value] + end +end + + +function pulled_back_integrand(op::HH3DHyperSingularFDBIO, + test_local_space::LagrangeRefSpace{<:Any,1}, + trial_local_space::LagrangeRefSpace{<:Any,1}, + test_chart, trial_chart) + + (u,v) -> begin + + x = neighborhood(test_chart,u) + y = neighborhood(trial_chart,v) + + nx = normal(x) + ny = normal(y) + + f = test_local_space(x) + g = trial_local_space(y) + + j = jacobian(x) * jacobian(y) + + α = op.alpha + β = op.beta + γ = op.gamma + R = norm(cartesian(x)-cartesian(y)) + G = exp(-γ*R)/(4*π*R) + + αjG = ny*α*G*j + βjG = β*G*j + + A = SA[ αjG*g[1].value, αjG*g[2].value, αjG*g[3].value] + B = SA[ βjG*g[1].curl, βjG*g[2].curl, βjG*g[3].curl ] + + SMatrix{3,3}(( + dot(nx*f[1].value, A[1]) + dot(f[1].curl, B[1]), + dot(nx*f[2].value, A[1]) + dot(f[2].curl, B[1]), + dot(nx*f[3].value, A[1]) + dot(f[3].curl, B[1]), + dot(nx*f[1].value, A[2]) + dot(f[1].curl, B[2]), + dot(nx*f[2].value, A[2]) + dot(f[2].curl, B[2]), + dot(nx*f[3].value, A[2]) + dot(f[3].curl, B[2]), + dot(nx*f[1].value, A[3]) + dot(f[1].curl, B[3]), + dot(nx*f[2].value, A[3]) + dot(f[2].curl, B[3]), + dot(nx*f[3].value, A[3]) + dot(f[3].curl, B[3]))) + end +end + + +function momintegrals!(op::HH3DSingleLayerFDBIO, + test_local_space::LagrangeRefSpace{<:Any,0}, trial_local_space::LagrangeRefSpace{<:Any,0}, + test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) + + I, J, K, L = SauterSchwabQuadrature.reorder( + test_triangular_element.vertices, + trial_triangular_element.vertices, strat) + + test_triangular_element = simplex( + test_triangular_element.vertices[I[1]], + test_triangular_element.vertices[I[2]], + test_triangular_element.vertices[I[3]]) + + trial_triangular_element = simplex( + trial_triangular_element.vertices[J[1]], + trial_triangular_element.vertices[J[2]], + trial_triangular_element.vertices[J[3]]) + + # igd = MWSL3DIntegrand(test_triangular_element, trial_triangular_element, + # op, test_local_space, trial_local_space) + igd = pulled_back_integrand(op, test_local_space, trial_local_space, + test_triangular_element, trial_triangular_element) + G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) + out[1,1] += G[1,1] + + nothing +end + + +function momintegrals!(op::HH3DHyperSingularFDBIO, + test_local_space::LagrangeRefSpace{<:Any,1}, trial_local_space::LagrangeRefSpace{<:Any,1}, + test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) + + I, J, K, L = SauterSchwabQuadrature.reorder( + test_triangular_element.vertices, + trial_triangular_element.vertices, strat) + + test_triangular_element = simplex( + test_triangular_element.vertices[I[1]], + test_triangular_element.vertices[I[2]], + test_triangular_element.vertices[I[3]]) + + trial_triangular_element = simplex( + trial_triangular_element.vertices[J[1]], + trial_triangular_element.vertices[J[2]], + trial_triangular_element.vertices[J[3]]) + + test_sign = Combinatorics.levicivita(I) + trial_sign = Combinatorics.levicivita(J) + σ = test_sign * trial_sign + + igd = pulled_back_integrand(op, test_local_space, trial_local_space, + test_triangular_element, trial_triangular_element) + G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) + for j ∈ 1:3, i ∈ 1:3 + out[i,j] += G[K[i],L[j]] * σ + end + + nothing +end \ No newline at end of file diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index 2a423299..e3c5367a 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -58,7 +58,7 @@ function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, test_eval(x) = test_refspace(x, Val{:withcurl}) trial_eval(x) = trial_refspace(x, Val{:withcurl}) - + # The combinations of rules (6,7) and (5,7 are) BAAAADDDD # they result in many near singularity evaluations with any # resemblence of accuracy going down the drain! Simply don't! @@ -69,7 +69,37 @@ function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, test_qp = quadpoints(test_eval, test_elements, (qs.outer_rule_far,)) bsis_qp = quadpoints(trial_eval, trial_elements, (qs.inner_rule_far,)) - return test_qp, bsis_qp + gausslegendre = ( + _legendre(qs.sauter_schwab_common_vert,0,1), + _legendre(qs.sauter_schwab_common_edge,0,1), + _legendre(qs.sauter_schwab_common_face,0,1),) + + return (;test_qp, bsis_qp, gausslegendre) +end + +function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, + trial_refspace::LagrangeRefSpace, test_elements, trial_elements, + qs::DoubleNumQStrat) + + test_eval(x) = test_refspace(x, Val{:withcurl}) + trial_eval(x) = trial_refspace(x, Val{:withcurl}) + + # The combinations of rules (6,7) and (5,7 are) BAAAADDDD + # they result in many near singularity evaluations with any + # resemblence of accuracy going down the drain! Simply don't! + # (same for (5,7) btw...). + # test_qp = quadpoints(test_eval, test_elements, (6,)) + # bssi_qp = quadpoints(trial_eval, trial_elements, (7,)) + + test_qp = quadpoints(test_eval, test_elements, (qs.outer_rule,)) + bsis_qp = quadpoints(trial_eval, trial_elements, (qs.inner_rule,)) + + # gausslegendre = ( + # _legendre(qs.sauter_schwab_common_vert,0,1), + # _legendre(qs.sauter_schwab_common_edge,0,1), + # _legendre(qs.sauter_schwab_common_face,0,1),) + + return (;test_qp, bsis_qp) end defaultquadstrat(::Helmholtz3DOp, ::subReferenceSpace, ::subReferenceSpace) = DoubleNumWiltonSauterQStrat(4,7,4,7,4,4,4,4) @@ -89,17 +119,55 @@ end function quadrule(op::HH3DSingleLayerFDBIO, test_refspace::LagrangeRefSpace{T,0} where T, trial_refspace::LagrangeRefSpace{T,0} where T, - i, test_element, j, trial_element, quadrature_data, + i, test_element, j, trial_element, qd, qs::DoubleNumWiltonSauterQStrat) + tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 for t in test_element.vertices for s in trial_element.vertices - norm(t-s) < tol && (hits +=1; break) + norm(t-s) < tol && (hits +=1) end end - test_quadpoints = quadrature_data[1] - trial_quadpoints = quadrature_data[2] + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) + hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) + hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + + test_quadpoints = qd.test_qp + trial_quadpoints = qd.bsis_qp + + # hits != 0 && return WiltonSERule( + # test_quadpoints[1,i], + # DoubleQuadRule( + # test_quadpoints[1,i], + # trial_quadpoints[1,j])) + + return DoubleQuadRule( + qd.test_qp[1,i], + qd.bsis_qp[1,j]) +end + + +function quadrule(op::HH3DSingleLayerFDBIO, + test_refspace::LagrangeRefSpace{T,0} where T, + trial_refspace::LagrangeRefSpace{T,0} where T, + i, test_element, j, trial_element, qd, + qs::DoubleNumQStrat) + + + tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 + for t in test_element.vertices + for s in trial_element.vertices + norm(t-s) < tol && (hits +=1) + end end + + + # hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) + # hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) + # hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + + test_quadpoints = qd.test_qp + trial_quadpoints = qd.bsis_qp hits != 0 && return WiltonSERule( test_quadpoints[1,i], @@ -108,8 +176,70 @@ function quadrule(op::HH3DSingleLayerFDBIO, trial_quadpoints[1,j])) return DoubleQuadRule( - quadrature_data[1][1,i], - quadrature_data[2][1,j]) + qd.test_qp[1,i], + qd.bsis_qp[1,j]) +end + + +function quadrule(op::HH3DHyperSingularFDBIO, + test_refspace::LagrangeRefSpace{T,1} where T, + trial_refspace::LagrangeRefSpace{T,1} where T, + i, test_element, j, trial_element, qd, + qs::DoubleNumWiltonSauterQStrat) + + tol2, hits = eps(eltype(eltype(test_element.vertices))), 0 + for t in test_element.vertices + for s in trial_element.vertices + hits += (LinearAlgebra.norm_sqr(t-s) < tol2) + end end + + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) + hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) + hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + + # test_quadpoints = qd.test_qp + # trial_quadpoints = qd.bsis_qp + + # hits != 0 && return WiltonSERule( + # test_quadpoints[1,i], + # DoubleQuadRule( + # test_quadpoints[1,i], + # trial_quadpoints[1,j])) + + return DoubleQuadRule( + qd.test_qp[1,i], + qd.bsis_qp[1,j]) +end + + +function quadrule(op::HH3DHyperSingularFDBIO, + test_refspace::LagrangeRefSpace{T,1} where T, + trial_refspace::LagrangeRefSpace{T,1} where T, + i, test_element, j, trial_element, qd, + qs::DoubleNumQStrat) + + tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 + for t in test_element.vertices + for s in trial_element.vertices + norm(t-s) < tol && (hits +=1) + end end + + # hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) + # hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) + # hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + + test_quadpoints = qd.test_qp + trial_quadpoints = qd.bsis_qp + + # hits != 0 && return WiltonSERule( + # test_quadpoints[1,i], + # DoubleQuadRule( + # test_quadpoints[1,i], + # trial_quadpoints[1,j])) + + return DoubleQuadRule( + qd.test_qp[1,i], + qd.bsis_qp[1,j]) end diff --git a/src/identityop.jl b/src/identityop.jl index dc626750..ad362fb5 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -21,9 +21,9 @@ function _alloc_workspace(qd, g, f, tels, bels) end # defaultquadstrat(op::LocalOperator, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bfs)) -defaultquadstrat(op::LocalOperator, tfs::Space, bfs::DirectProductSpace) = defaultquadstrat(op, tfs, bfs.factors[1]) -defaultquadstrat(op::LocalOperator, tfs::DirectProductSpace, bfs::Space) = defaultquadstrat(op, tfs.factors[1], bfs) -defaultquadstrat(op::LocalOperator, tfs::DirectProductSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs.factors[1], bfs.factors[1]) +# defaultquadstrat(op::LocalOperator, tfs::Space, bfs::DirectProductSpace) = defaultquadstrat(op, tfs, bfs.factors[1]) +# defaultquadstrat(op::LocalOperator, tfs::DirectProductSpace, bfs::Space) = defaultquadstrat(op, tfs.factors[1], bfs) +# defaultquadstrat(op::LocalOperator, tfs::DirectProductSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs.factors[1], bfs.factors[1]) const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace, BDMRefSpace} defaultquadstrat(::LocalOperator, ::LinearRefSpaceTriangle, ::LinearRefSpaceTriangle) = SingleNumQStrat(6) diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 6c65c084..6370dd10 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -80,7 +80,9 @@ function _legendre(n,a,b) collect(zip(x,w)) end -defaultquadstrat(op::MaxwellOperator3D, tfs, bfs) = DoubleNumWiltonSauterQStrat(2,3,6,7,5,5,4,3) +defaultquadstrat(op::MaxwellOperator3D, tfs::Space, bfs::Space) = DoubleNumWiltonSauterQStrat(2,3,6,7,5,5,4,3) +defaultquadstrat(op::MaxwellOperator3D, tfs::RefSpace, bfs::RefSpace) = DoubleNumWiltonSauterQStrat(2,3,6,7,5,5,4,3) + function quaddata(op::MaxwellOperator3D, test_local_space::RefSpace, trial_local_space::RefSpace, From ee9c607b77e0dd5ea627d485fd533d9537b61fee Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 10 Jan 2022 12:50:42 +0100 Subject: [PATCH 145/528] Fixed test of curl in 2D --- docs/Manifest.toml | 375 +++++++++++++++++++++++++++++++++++++++++++++ test/test_basis.jl | 5 +- 2 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 docs/Manifest.toml diff --git a/docs/Manifest.toml b/docs/Manifest.toml new file mode 100644 index 00000000..450f9c59 --- /dev/null +++ b/docs/Manifest.toml @@ -0,0 +1,375 @@ +# This file is machine-generated - editing it directly is not advised + +[[ANSIColoredPrinters]] +git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" +uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" +version = "0.0.1" + +[[AbstractFFTs]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "485ee0867925449198280d4af84bdb46a2a404d0" +uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" +version = "1.0.1" + +[[ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" + +[[ArrayLayouts]] +deps = ["FillArrays", "LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "d68733034d1d5d2cd3ea68e2b4cb11f456f6d015" +uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" +version = "0.6.5" + +[[Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[BEAST]] +deps = ["BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "Distributed", "FFTW", "FastGaussQuadrature", "IterativeSolvers", "LinearAlgebra", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SparseMatrixDicts", "SpecialFunctions", "StaticArrays", "WiltonInts84"] +git-tree-sha1 = "7754b0c61dac72ec7ea524f2bfa831966edd2d97" +uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" +version = "1.3.0" + +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[BlockArrays]] +deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] +git-tree-sha1 = "a048ffafcf6eb52a1f59e32ea7d9e74419736a17" +uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" +version = "0.14.5" + +[[ChainRulesCore]] +deps = ["Compat", "LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "e8a30e8019a512e4b6c56ccebc065026624660e8" +uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +version = "1.7.0" + +[[ClusterTrees]] +deps = ["DelimitedFiles", "LinearAlgebra", "Pkg"] +git-tree-sha1 = "4114d60c95974edf9272d88571d14abeed598942" +uuid = "5100927d-02aa-593a-b4f9-7235df19f0db" +version = "0.2.1" + +[[CollisionDetection]] +deps = ["Compat", "LinearAlgebra", "StaticArrays"] +git-tree-sha1 = "8edb46db045228884ac57fc4a0d45ce3ce1ec399" +uuid = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" +version = "0.1.3" + +[[Combinatorics]] +git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" +uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" +version = "1.0.2" + +[[CompScienceMeshes]] +deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays"] +git-tree-sha1 = "8e54028f066cc4559e3a6305747d7d7866cbc58a" +uuid = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" +version = "0.2.8" + +[[Compat]] +deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] +git-tree-sha1 = "31d0151f5716b655421d9d75b7fa74cc4e744df2" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "3.39.0" + +[[CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" + +[[DataStructures]] +deps = ["Compat", "InteractiveUtils", "OrderedCollections"] +git-tree-sha1 = "7d9d316f04214f7efdbb6398d545446e246eff02" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.18.10" + +[[Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[DelimitedFiles]] +deps = ["Mmap"] +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" + +[[Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[DocStringExtensions]] +deps = ["LibGit2"] +git-tree-sha1 = "a32185f5428d3986f47c2ab78b1f216d5e6cc96f" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.8.5" + +[[Documenter]] +deps = ["ANSIColoredPrinters", "Base64", "Dates", "DocStringExtensions", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "REPL", "Test", "Unicode"] +git-tree-sha1 = "8b43e37cfb4f4edc2b6180409acc0cebce7fede8" +uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +version = "0.27.7" + +[[Downloads]] +deps = ["ArgTools", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" + +[[FFTW]] +deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] +git-tree-sha1 = "463cb335fa22c4ebacfd1faba5fde14edb80d96c" +uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +version = "1.4.5" + +[[FFTW_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea" +uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" +version = "3.3.10+0" + +[[FastGaussQuadrature]] +deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] +git-tree-sha1 = "5829b25887e53fb6730a9df2ff89ed24baa6abf6" +uuid = "442a2c76-b920-505d-bb47-c5924d526838" +version = "0.4.7" + +[[FillArrays]] +deps = ["LinearAlgebra", "Random", "SparseArrays"] +git-tree-sha1 = "693210145367e7685d8604aee33d9bfb85db8b31" +uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" +version = "0.11.9" + +[[IOCapture]] +deps = ["Logging", "Random"] +git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a" +uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" +version = "0.2.2" + +[[IntelOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "d979e54b71da82f3a65b62553da4fc3d18c9004c" +uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" +version = "2018.0.3+2" + +[[InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[IrrationalConstants]] +git-tree-sha1 = "f76424439413893a832026ca355fe273e93bce94" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.1.0" + +[[IterativeSolvers]] +deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] +git-tree-sha1 = "1a8c6237e78b714e901e406c096fc8a65528af7d" +uuid = "42fd0dbc-a981-5370-80f2-aaf504508153" +version = "0.9.1" + +[[JLLWrappers]] +deps = ["Preferences"] +git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.3.0" + +[[JSON]] +deps = ["Dates", "Mmap", "Parsers", "Unicode"] +git-tree-sha1 = "8076680b162ada2a031f707ac7b4953e30667a37" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.21.2" + +[[LazyArtifacts]] +deps = ["Artifacts", "Pkg"] +uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" + +[[LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" + +[[LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" + +[[LibGit2]] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[LinearAlgebra]] +deps = ["Libdl"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[LogExpFunctions]] +deps = ["ChainRulesCore", "DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "34dc30f868e368f8a17b728a1238f3fcda43931a" +uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" +version = "0.3.3" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[MKL_jll]] +deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"] +git-tree-sha1 = "5455aef09b40e5020e1520f551fa3135040d4ed0" +uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" +version = "2021.1.1+2" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" + +[[Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" + +[[NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" + +[[OpenLibm_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "05823500-19ac-5b8b-9628-191a04bc5112" + +[[OpenSpecFun_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" +uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" +version = "0.5.5+0" + +[[OrderedCollections]] +git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.4.1" + +[[Parsers]] +deps = ["Dates"] +git-tree-sha1 = "9d8c00ef7a8d110787ff6f170579846f776133a9" +uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" +version = "2.0.4" + +[[Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[Preferences]] +deps = ["TOML"] +git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.2.2" + +[[Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[Random]] +deps = ["Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[RecipesBase]] +git-tree-sha1 = "44a75aa7a527910ee3d1751d1f0e4148698add9e" +uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" +version = "1.1.2" + +[[Reexport]] +git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "1.2.2" + +[[Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.1.3" + +[[SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[SauterSchwabQuadrature]] +deps = ["CompScienceMeshes", "FastGaussQuadrature", "LinearAlgebra", "StaticArrays"] +git-tree-sha1 = "567198b98952f11178854bce14ee1caa663b7652" +uuid = "535c7bfe-2023-5c1d-b712-654ef9d93a38" +version = "2.1.2" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[SparseMatrixDicts]] +deps = ["LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "6ad782435088b00f7abdd4b6ae79fa522cc18758" +uuid = "5cb6c4b0-9b79-11e8-24c9-f9621d252589" +version = "0.2.4" + +[[SpecialFunctions]] +deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "793793f1df98e3d7d554b65a107e9c9a6399a6ed" +uuid = "276daf66-3868-5448-9aa4-cd146d93841b" +version = "1.7.0" + +[[StaticArrays]] +deps = ["LinearAlgebra", "Random", "Statistics"] +git-tree-sha1 = "3240808c6d463ac46f1c1cd7638375cd22abbccb" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.2.12" + +[[Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" + +[[Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" + +[[Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[WiltonInts84]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "b7374c784a208b377157146e3bdd94287e41f95a" +uuid = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" +version = "0.2.2" + +[[Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" + +[[nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" + +[[p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" diff --git a/test/test_basis.jl b/test/test_basis.jl index 9f33ce53..7a74b40f 100644 --- a/test/test_basis.jl +++ b/test/test_basis.jl @@ -209,8 +209,11 @@ crl = BEAST.curl(lag) nxgrad = BEAST.n × BEAST.gradient(lag) for i in eachindex(crl.fns) + crli = sort(crl.fns[i], by=sh->(sh.cellid, sh.refid)) + nxgradi = sort(nxgrad.fns[i], by=sh->(sh.cellid, sh.refid)) for j in eachindex(crl.fns[i]) - @test crl.fns[i][j] == nxgrad.fns[i][j] + # @test crl.fns[i][j] == nxgrad.fns[i][j] + @test crli[j].coeff == -nxgradi[j].coeff end end From a45364ddc4fa6c909e2119d04de27e621cb73391 Mon Sep 17 00:00:00 2001 From: Simon Adrian Date: Thu, 27 Jan 2022 11:35:06 +0100 Subject: [PATCH 146/528] Changed Maxwell near field interface --- src/maxwell/nearfield.jl | 90 ++++++++++++++++++++++++++++++++++------ test/test_dipole.jl | 4 +- 2 files changed, 79 insertions(+), 15 deletions(-) diff --git a/src/maxwell/nearfield.jl b/src/maxwell/nearfield.jl index 5d9fcabc..44f047f8 100644 --- a/src/maxwell/nearfield.jl +++ b/src/maxwell/nearfield.jl @@ -1,42 +1,106 @@ -mutable struct MWSingleLayerField3D{K} - wavenumber::K +mutable struct MWSingleLayerField3D{T, U} + gamma::T + α::U + β::U end -mutable struct MWDoubleLayerField3D{K} - wavenumber::K +mutable struct MWDoubleLayerField3D{T} + gamma::T end """ - MWSingleLayerField3D(wavenumber = error()) + MWSingleLayerField3D(;gamma, wavenumber, alpha, beta) Create the single layer near field operator, for use with `potential`. """ -MWSingleLayerField3D(;wavenumber = error("Missing arg: `wavenumber`")) = MWSingleLayerField3D(wavenumber) -MWDoubleLayerField3D(;wavenumber = error("Missing arg: `wavenumber`")) = MWDoubleLayerField3D(wavenumber) +function MWSingleLayerField3D(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing +) + + if (gamma === nothing) && (wavenumber === nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma !== nothing) && (wavenumber !== nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma === nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma !== nothing + + alpha === nothing && (alpha = -gamma) + beta === nothing && (beta = -1/gamma) + + MWSingleLayerField3D(gamma, alpha, beta) +end + +MWSingleLayerField3D(op::MWSingleLayer3D{T,U}) where {T,U} = MWSingleLayerField3D(op.gamma, op.α, op.β) + +""" + MWDoubleLayerField3D(; gamma, wavenumber) + +Create the double layer near field operator, for use with `potential`. +""" +function MWDoubleLayerField3D(; + gamma=nothing, + wavenumber=nothing +) + + if (gamma === nothing) && (wavenumber === nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma !== nothing) && (wavenumber !== nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma === nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma !== nothing + + MWDoubleLayerField3D(gamma) +end +MWDoubleLayerField3D(op::MWDoubleLayer3D) = MWDoubleLayerField3D(op.gamma) const MWField3D = Union{MWSingleLayerField3D,MWDoubleLayerField3D} quaddata(op::MWField3D,rs,els) = quadpoints(rs,els,(2,)) quadrule(op::MWField3D,refspace,p,y,q,el,qdata) = qdata[1,q] function kernelvals(op::MWField3D,y,p) - k = op.wavenumber + γ = op.gamma r = y - cartesian(p) R = norm(r) - kr = k * R - expn = exp(-im * k * R) + γR = γ*R + expn = exp(-γR) green = expn / (4pi*R) - gradgreen = -(im*k + 1/R) * green / R * r + gradgreen = -(γ + 1/R) * green / R * r krn = (trans=r, dist=R, green=green, gradgreen=gradgreen) end function integrand(op::MWSingleLayerField3D, krn, y, fp, p) - γ = im * op.wavenumber + γ = op.gamma j = fp.value ρ = fp.divergence @@ -44,7 +108,7 @@ function integrand(op::MWSingleLayerField3D, krn, y, fp, p) G = krn.green ∇G = krn.gradgreen - -γ*G*j + ∇G*ρ/γ + op.α*G*j - op.β*∇G*ρ end function integrand(op::MWDoubleLayerField3D, krn, y, fp, p) diff --git a/test/test_dipole.jl b/test/test_dipole.jl index 0742a0f7..6b78ec54 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -45,8 +45,8 @@ T = Matrix(assemble(𝓣,X,X)) e = Vector(assemble(𝒆,X)) j_EFIE = T\e -nf_E_EFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_EFIE, X) -nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_EFIE, X) ./ η +nf_E_EFIE = potential(MWSingleLayerField3D(𝓣), pts, j_EFIE, X) +nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(𝓚), pts, j_EFIE, X) ./ η ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X) @test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 From 1503e44445ed6591a4d87933ac4ab5ec7471c673 Mon Sep 17 00:00:00 2001 From: Simon Adrian Date: Thu, 27 Jan 2022 14:39:11 +0100 Subject: [PATCH 147/528] Adapted Maxwell FarField-Operators + added DoubleLayerFarfield - MWFarField3D can be constructed from a MWSingleLayer3D. The waveimpedance is computed from alpha and beta. - MWDoubleLayerFarField3D added, which can be constructed from Maxwell double layer. This far-field operator is useful in the context of combined source problems, where the far-field must be computed from a single and a double layer far field operator. - For convenience for both farfield operators, an extra member waveimpedance was introduced which can serve as scaling factor. In case of the MWFarField3D type being constructed from a MWSingleLayer3D, the waveimpedance will be set accordingly. TO CONSIDER FOR FUTURE CHANGES: - In order to not break anything, MWFarField3D behaves as before. It should probably become a MWSingleLayerFarField3D, while MWFarField3D could replace the newly introduced abstract type MWFarField --- src/maxwell/farfield.jl | 85 +++++++++++++++++++++++++++++++++++------ test/test_dipole.jl | 31 ++++++++++----- 2 files changed, 94 insertions(+), 22 deletions(-) diff --git a/src/maxwell/farfield.jl b/src/maxwell/farfield.jl index 7e252383..d5e41e4e 100644 --- a/src/maxwell/farfield.jl +++ b/src/maxwell/farfield.jl @@ -1,4 +1,4 @@ - +abstract type MWFarField end """ Operator to compute the far field of a current distribution. In particular, given the current distribution ``j`` this operator allows for the computation of @@ -9,22 +9,83 @@ A j = n × ∫_Γ e^{γ ̂x ⋅ y} dy where ``̂x`` is the unit vector in the direction of observation. Note that the assembly routing expects the observation directions to be normalised by the caller. """ -struct MWFarField3D{K} +struct MWFarField3D{K, U} <: MWFarField + gamma::K + waveimpedance::U +end +struct MWDoubleLayerFarField3D{K, U} <: MWFarField gamma::K + waveimpedance::U end -function MWFarField3D(;wavenumber=error("wavenumber is a required argument")) - if iszero(real(wavenumber)) - MWFarField3D(-imag(wavenumber)) - else - MWFarField3D(wavenumber*im) - end +function MWFarField3D(; + gamma=nothing, + wavenumber=nothing, + waveimpedance=nothing +) + if (gamma === nothing) && (wavenumber === nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma !== nothing) && (wavenumber !== nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma === nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma !== nothing + + waveimpedance === nothing && (waveimpedance = 1.0) + + return MWFarField3D(gamma, waveimpedance) end -quaddata(op::MWFarField3D,rs,els) = quadpoints(rs,els,(3,)) -quadrule(op::MWFarField3D,refspace,p,y,q,el,qdata) = qdata[1,q] +MWFarField3D(op::MWSingleLayer3D{T,U}) where {T,U} = MWFarField3D(op.gamma, sqrt(op.α*op.β)) + +function MWDoubleLayerFarField3D(; + gamma=nothing, + wavenumber=nothing, + waveimpedance=nothing +) + if (gamma === nothing) && (wavenumber === nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma !== nothing) && (wavenumber !== nothing) + error("Supply one of (not both) gamma or wavenumber") + end -kernelvals(op::MWFarField3D,y,p) = exp(op.gamma*dot(y,cartesian(p))) + if gamma === nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma !== nothing + + waveimpedance === nothing && (waveimpedance = 1.0) + + return MWDoubleLayerFarField3D(gamma, waveimpedance) +end + +MWDoubleLayerFarField3D(op::MWDoubleLayer3D{T}) where {T} = MWDoubleLayerFarField3D(op.gamma, 1.0) + +quaddata(op::MWFarField,rs,els) = quadpoints(rs,els,(3,)) +quadrule(op::MWFarField,refspace,p,y,q,el,qdata) = qdata[1,q] + +kernelvals(op::MWFarField,y,p) = exp(op.gamma*dot(y,cartesian(p))) function integrand(op::MWFarField3D,krn,y,f,p) - (y × (krn * f[1])) × y + op.waveimpedance*(y × (krn * f[1])) × y +end + +function integrand(op::MWDoubleLayerFarField3D,krn,y,f,p) + op.waveimpedance*(y × (krn * f[1])) end diff --git a/test/test_dipole.jl b/test/test_dipole.jl index 6b78ec54..2d2a9456 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -6,8 +6,13 @@ using StaticArrays using LinearAlgebra c = 3e8 -μ = 4*π*1e-7 -ε = 1/(μ*c^2) +μ0 = 4*π*1e-7 +μr = 1.0 +μ = μ0*μr +ε0 = 8.854187812e-12 +εr = 5 +ε = ε0*εr +c = 1/sqrt(ε*μ) f = 5e7 λ = c/f k = 2*π/λ @@ -18,7 +23,7 @@ a = 1 Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.1) Γ = translate(Γ_orig,SVector(-a/2,-a/2,-a/2)) -Φ, Θ = [0.0], range(0,stop=π,length=100) +Φ, Θ = [0.0], range(0,stop=π,length=3) pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] # This is an electric dipole @@ -34,7 +39,7 @@ n = BEAST.NormalVector() H = (-1/(im*μ*ω))*curl(E) 𝒉 = (n × H) × n -𝓣 = Maxwell3D.singlelayer(wavenumber=k) +𝓣 = Maxwell3D.singlelayer(wavenumber=k, alpha=-im*ω*μ, beta=-1/(im*ω*ε)) 𝓝 = BEAST.NCross() 𝓚 = Maxwell3D.doublelayer(wavenumber=k) @@ -46,22 +51,24 @@ e = Vector(assemble(𝒆,X)) j_EFIE = T\e nf_E_EFIE = potential(MWSingleLayerField3D(𝓣), pts, j_EFIE, X) -nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(𝓚), pts, j_EFIE, X) ./ η -ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X) +nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(𝓚), pts, j_EFIE, X) +ff_E_EFIE = potential(MWFarField3D(𝓣), pts, j_EFIE, X) +ff_H_EFIE = potential(BEAST.MWDoubleLayerFarField3D(𝓚), pts, j_EFIE, X) @test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 @test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.001 +@test norm(ff_H_EFIE - H.(pts, isfarfield=true))/norm(H.(pts, isfarfield=true)) ≈ 0 atol=0.001 K_bc = Matrix(assemble(𝓚,Y,X)) G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) -h_bc = η*Vector(assemble(𝒉,Y)) +h_bc = Vector(assemble(𝒉,Y)) M_bc = -0.5*G_nxbc_rt + K_bc j_BCMFIE = M_bc\h_bc -nf_E_BCMFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) -nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) ./ η -ff_E_BCMFIE = potential(MWFarField3D(wavenumber=k), pts, j_BCMFIE, X) +nf_E_BCMFIE = potential(MWSingleLayerField3D(𝓣), pts, j_BCMFIE, X) +nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(𝓚), pts, j_BCMFIE, X) +ff_E_BCMFIE = potential(MWFarField3D(𝓣), pts, j_BCMFIE, X) @test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @@ -71,6 +78,10 @@ H = dipolemw3d(location=SVector(0.0,0.0,0.3), orientation=1e-9.*SVector(0.5,0.5,0), wavenumber=k) +# This time, we do not specify alpha and beta +# We include η in the magnetic RHS +𝓣 = Maxwell3D.singlelayer(wavenumber=k) + 𝒉 = (n × H) × n E = (1/(im*ε*ω))*curl(H) 𝒆 = (n × E) × n From 9cb5c2040918f02f8a220466e23ecd79e9346c88 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 31 Jan 2022 16:11:28 +0100 Subject: [PATCH 148/528] gradient of laglocals fixed --- examples/ex_duals.jl | 11 ++++++--- src/bases/dual3d.jl | 16 ++++++------- src/bases/local/laglocal.jl | 48 ++++++++++++++++++------------------- 3 files changed, 40 insertions(+), 35 deletions(-) diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl index 1c77fb4f..f54b2bb0 100644 --- a/examples/ex_duals.jl +++ b/examples/ex_duals.jl @@ -25,15 +25,20 @@ int_Nodes = submesh(!in(bnd_Nodes), Nodes) @show length(int_Edges) @show length(int_Faces) -primal1 = BEAST.nedelecc3d(Tetrs, int_Edges) primal2 = BEAST.nedelecd3d(Tetrs, Faces) +primal1 = BEAST.nedelecc3d(Tetrs, int_Edges) Dir = bnd_Faces Neu = submesh(!in(Dir), bnd_Faces) -@time dual1 = BEAST.dual1forms(Tetrs, Faces, Dir) +# tetrs, bnd, dir, v2t, v2n = BEAST.dualforms_init(Tetrs, Dir) +# dual1 = BEAST.dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) + # error() -@time dual2 = BEAST.dual2forms(Tetrs, int_Edges, Dir) + +dual2 = BEAST.dual2forms(Tetrs, int_Edges, Dir) +dual1 = BEAST.dual1forms(Tetrs, Faces, Dir) + @assert numfunctions(primal1) == numfunctions(dual2) @assert numfunctions(primal2) == numfunctions(dual1) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 03b9d56e..d048cb27 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -1,14 +1,14 @@ using LinearAlgebra -function addf!(fn::Vector{<:Shape}, x::Vector, space::Space, idcs::Vector{Int}) - for (m,bf) in enumerate(space.fns) - for sh in bf - cellid = idcs[sh.cellid] - BEAST.add!(fn, cellid, sh.refid, sh.coeff * x[m]) - end - end -end +# function addf!(fn::Vector{<:Shape}, x::Vector, space::Space, idcs::Vector{Int}) +# for (m,bf) in enumerate(space.fns) +# for sh in bf +# cellid = idcs[sh.cellid] +# BEAST.add!(fn, cellid, sh.refid, sh.coeff * x[m]) +# end +# end +# end function dual2forms_body(Edges, tetrs, bnd, dir, v2t, v2n) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index f78230bf..6c6eca63 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -97,8 +97,8 @@ function gradient(ref::LagrangeRefSpace{T,1,4}, sh, tet) where {T} end function gradient(ref::LagrangeRefSpace{T,1,3} where {T}, sh, el) - sh1 = Shape(sh.cellid, mod1(sh.refid+1,3), -sh.coeff) - sh2 = Shape(sh.cellid, mod1(sh.refid+2,3), +sh.coeff) + sh1 = Shape(sh.cellid, mod1(sh.refid+1,3), +sh.coeff) + sh2 = Shape(sh.cellid, mod1(sh.refid+2,3), -sh.coeff) return [sh1, sh2] end @@ -117,28 +117,28 @@ end # end -function gradient(ref::LagrangeRefSpace{T,1,3}, sh, tri) where {T} - - this_vert = tri.vertices[sh.refid] - opp_edge = faces(tri)[sh.refid] - ctr_opp_face = center(opp_edge) - # n = normal(ctr_opp_face) - n = -normalize(cross(opp_edge.tangents[1], normal(tri))) - h = -dot(this_vert - cartesian(ctr_opp_face), n) - @assert h > 0 "h = $h" - gradval = -(1/h)*n - output = Vector{Shape{T}}() - for (i,edge) in enumerate(CompScienceMeshes.edges(tri)) - ctr_edge = center(edge) - tgt = tangents(ctr_edge,1) - tgt = normalize(tgt) - lgt = volume(edge) - cff = -lgt * dot(tgt, gradval) - isapprox(cff, 0, atol=sqrt(eps(T))) && continue - push!(output, Shape(sh.cellid, i, sh.coeff * cff)) - end - return output -end +# function gradient(ref::LagrangeRefSpace{T,1,3}, sh, tri) where {T} + +# this_vert = tri.vertices[sh.refid] +# opp_edge = faces(tri)[sh.refid] +# ctr_opp_face = center(opp_edge) +# # n = normal(ctr_opp_face) +# n = -normalize(cross(opp_edge.tangents[1], normal(tri))) +# h = -dot(this_vert - cartesian(ctr_opp_face), n) +# @assert h > 0 "h = $h" +# gradval = -(1/h)*n +# output = Vector{Shape{T}}() +# for (i,edge) in enumerate(CompScienceMeshes.edges(tri)) +# ctr_edge = center(edge) +# tgt = tangents(ctr_edge,1) +# tgt = normalize(tgt) +# lgt = volume(edge) +# cff = -lgt * dot(tgt, gradval) +# isapprox(cff, 0, atol=sqrt(eps(T))) && continue +# push!(output, Shape(sh.cellid, i, sh.coeff * cff)) +# end +# return output +# end function gradient(ref::LagrangeRefSpace{T,1,2}, sh, seg) where {T} From f28a7fbf429b72c46d3b0740ecd4d6ddb0eb7d9c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 8 Feb 2022 15:33:50 +0100 Subject: [PATCH 149/528] gradient of Lag on triangles more efficient --- src/bases/local/laglocal.jl | 49 +++++++++++++++++++++---------------- test/test_rt3d.jl | 22 +++++++++++++++++ 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index e4cc02f4..e528e010 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -97,30 +97,37 @@ function gradient(ref::LagrangeRefSpace{T,1,4}, sh, tet) where {T} end -function gradient(ref::LagrangeRefSpace{T,1,3}, sh, tri) where {T} - - this_vert = tri.vertices[sh.refid] - opp_edge = faces(tri)[sh.refid] - ctr_opp_face = center(opp_edge) - # n = normal(ctr_opp_face) - n = -normalize(cross(opp_edge.tangents[1], normal(tri))) - h = -dot(this_vert - cartesian(ctr_opp_face), n) - @assert h > 0 "h = $h" - gradval = -(1/h)*n - output = Vector{Shape{T}}() - for (i,edge) in enumerate(CompScienceMeshes.edges(tri)) - ctr_edge = center(edge) - tgt = tangents(ctr_edge,1) - tgt = normalize(tgt) - lgt = volume(edge) - cff = -lgt * dot(tgt, gradval) - isapprox(cff, 0, atol=sqrt(eps(T))) && continue - push!(output, Shape(sh.cellid, i, sh.coeff * cff)) - end - return output +function gradient(ref::LagrangeRefSpace{T,1,3} where {T}, sh, el) + sh1 = Shape(sh.cellid, mod1(sh.refid+1,3), +sh.coeff) + sh2 = Shape(sh.cellid, mod1(sh.refid+2,3), -sh.coeff) + return [sh1, sh2] end +# function gradient(ref::LagrangeRefSpace{T,1,3}, sh, tri) where {T} + +# this_vert = tri.vertices[sh.refid] +# opp_edge = faces(tri)[sh.refid] +# ctr_opp_face = center(opp_edge) +# # n = normal(ctr_opp_face) +# n = -normalize(cross(opp_edge.tangents[1], normal(tri))) +# h = -dot(this_vert - cartesian(ctr_opp_face), n) +# @assert h > 0 "h = $h" +# gradval = -(1/h)*n +# output = Vector{Shape{T}}() +# for (i,edge) in enumerate(CompScienceMeshes.edges(tri)) +# ctr_edge = center(edge) +# tgt = tangents(ctr_edge,1) +# tgt = normalize(tgt) +# lgt = volume(edge) +# cff = -lgt * dot(tgt, gradval) +# isapprox(cff, 0, atol=sqrt(eps(T))) && continue +# push!(output, Shape(sh.cellid, i, sh.coeff * cff)) +# end +# return output +# end + + function gradient(ref::LagrangeRefSpace{T,1,2}, sh, seg) where {T} sh.refid == 1 && return [Shape(sh.cellid, 1, +sh.coeff/volume(seg))] diff --git a/test/test_rt3d.jl b/test/test_rt3d.jl index 82e0ef07..ba78a4c3 100644 --- a/test/test_rt3d.jl +++ b/test/test_rt3d.jl @@ -18,6 +18,28 @@ fcs = BEAST.faces(tet) @test dot(rs(nbd3)[3].value, normal(fcs[3])) > 0 @test dot(rs(nbd4)[4].value, normal(fcs[4])) > 0 +dot(rs(nbd1)[1].value, normal(fcs[1])) * volume(fcs[1]) ≈ 1 +dot(rs(nbd2)[2].value, normal(fcs[2])) * volume(fcs[2]) ≈ 1 +dot(rs(nbd3)[3].value, normal(fcs[3])) * volume(fcs[2]) ≈ 1 +dot(rs(nbd4)[4].value, normal(fcs[4])) * volume(fcs[4]) ≈ 1 + +@test dot(rs(nbd1)[2].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=1e-8 +@test dot(rs(nbd1)[3].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=1e-8 +@test dot(rs(nbd1)[4].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=1e-8 + +@test dot(rs(nbd2)[1].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=1e-8 +@test dot(rs(nbd2)[3].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=1e-8 +@test dot(rs(nbd2)[4].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=1e-8 + +@test dot(rs(nbd3)[1].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=1e-8 +@test dot(rs(nbd3)[2].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=1e-8 +@test dot(rs(nbd3)[4].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=1e-8 + +@test dot(rs(nbd4)[1].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=1e-8 +@test dot(rs(nbd4)[2].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=1e-8 +@test dot(rs(nbd4)[3].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=1e-8 + + ctr = cartesian(center(tet)) @test dot(cartesian(nbd1)-ctr, normal(fcs[1])) > 0 @test dot(cartesian(nbd2)-ctr, normal(fcs[2])) > 0 From 1c36ad5a9579e0b1287e931e061881fbee0a33e5 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 10 Feb 2022 09:58:07 +0100 Subject: [PATCH 150/528] remove redundant def dualforms_init --- src/bases/dual3d.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index d048cb27..8014a011 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -165,14 +165,14 @@ function dual2forms(Tetrs, Faces, Dir) dual2forms_body(Faces, tetrs, bnd, dir, v2t, v2n) end -function dualforms_init(Tetrs, Dir) - tetrs = barycentric_refinement(Tetrs) - v2t, v2n = CompScienceMeshes.vertextocellmap(tetrs) - bnd = boundary(tetrs) - gpred = CompScienceMeshes.overlap_gpredicate(Dir) - dir = submesh(face -> gpred(chart(bnd,face)), bnd) - return tetrs, bnd, dir, v2t, v2n -end +# function dualforms_init(Tetrs, Dir) +# tetrs = barycentric_refinement(Tetrs) +# v2t, v2n = CompScienceMeshes.vertextocellmap(tetrs) +# bnd = boundary(tetrs) +# gpred = CompScienceMeshes.overlap_gpredicate(Dir) +# dir = submesh(face -> gpred(chart(bnd,face)), bnd) +# return tetrs, bnd, dir, v2t, v2n +# end function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) From a8e9fae047f5414d46d68525c629d1448ae5c939 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Wed, 16 Feb 2022 00:30:11 +0000 Subject: [PATCH 151/528] CompatHelper: bump compat for FillArrays to 0.13, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 973262f7..a7de95f6 100644 --- a/Project.toml +++ b/Project.toml @@ -34,7 +34,7 @@ CompScienceMeshes = "0.3.2" Compat = "2, 3" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4" -FillArrays = "0.11, 0.12" +FillArrays = "0.11, 0.12, 0.13" IterativeSolvers = "0.9" LiftedMaps = "0.3" LinearMaps = "3.5" From 70259c64b82befecc60a228b7feac196199ff0ce Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Wed, 16 Mar 2022 15:24:29 +0100 Subject: [PATCH 152/528] working volume surface formulation --- examples/dsvie.jl | 85 ---------- examples/dvie.jl | 46 ++++-- examples/dvsie.jl | 198 ++++++++++++++++++++++++ examples/evie.jl | 43 ++--- examples/pmchwt.jl | 64 ++++++-- src/BEAST.jl | 4 + src/volumeintegral/sauterschwab_ints.jl | 14 +- src/volumeintegral/vieexc.jl | 9 ++ src/volumeintegral/vieops.jl | 25 +-- src/volumeintegral/vsie.jl | 12 +- src/volumeintegral/vsieops.jl | 86 +++++++--- 11 files changed, 415 insertions(+), 171 deletions(-) delete mode 100644 examples/dsvie.jl create mode 100644 examples/dvsie.jl diff --git a/examples/dsvie.jl b/examples/dsvie.jl deleted file mode 100644 index fa12c2ef..00000000 --- a/examples/dsvie.jl +++ /dev/null @@ -1,85 +0,0 @@ -using CompScienceMeshes, BEAST -using LinearAlgebra -using Profile -using StaticArrays - - - -ntrc = X->ntrace(X,Γ) - -T = tetmeshsphere(1.0,0.25) -X = nedelecd3d(T) -Γ = boundary(T) -Y = raviartthomas(Γ) - -@show numfunctions(X) -@show numfunctions(Y) - - -κ, η = 1.0, 1.0 -κ′, η′ = √2.0κ, η/√2.0 -ϵ_r =3.0 -ϵ_b =2.0 - - -χ = x->(1.0-1.0/ϵ_r) - -#Volume-Volume -L,I,B = VIE.singlelayer(wavenumber=κ', tau=χ), Identity(), VIE.boundary(wavenumber=κ', tau=χ) -#Volume-Surface -Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ')), transpose(VSIE.boundary(wavenumber=κ')), transpose(VSIE.doublelayer(wavenumber=κ')) -Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ', tau=χ), VSIE.boundary(wavenumber=κ', tau=χ), VSIE.doublelayer(wavenumber=κ', tau=χ) -#Surface-Surface -T = Maxwell3D.singlelayer(wavenumber=κ) #Outside -T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside -K = Maxwell3D.doublelayer(wavenumber=κ) #Outside -K′ = Maxwell3D.doublelayer(wavenumber=κ′) #Inside - -E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) -H = -1/(im*κ*η)*curl(E) - -e, h = (n × E) × n, (n × H) × n - -@hilbertspace D j m -@hilbertspace k l o -β = 1.0/(ϵ_r*ϵ_b) -ν = 1/ϵ_b -α, α′ = 1/η, 1/η′ -γ′ = im*η′/κ′ -ζ′ = im*η′*κ′ -δ′ = im*κ′/ϵ_b - -eq = @varform (β*I[k,D]-ν*L[k,D]-ν*B[ntrc(k),D] + η′*Lt[k,j]-γ′*Bt[ntrc(k),j] + Kt[k,m] + - -δ′*Ls[l,D]-ν*Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + - ζ′*Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) - - -dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y - -u_n = solve(dvsie) - - -#Post processing -Θ, Φ = range(0.0,stop=2π,length=100), 0.0 -ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] - -# Don't forget the far field comprises two contributions -ffm = potential(MWFarField3D(κ*im), ffpoints, u_n[m], Y) -ffj = potential(MWFarField3D(κ*im), ffpoints, u_n[j], Y) -ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) - - -ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u_n[D], X) -ff2 = im*κ*ffd - -using Plots -plot(xlabel="theta") -plot!(Θ,norm.(ff),label="far field",title="DVSIE") -plot!(Θ,√6.0*norm.(ff),label="far field",title="DVSIE 2") - -import Plotly -using LinearAlgebra -fcrj, _ = facecurrents(u_n[j],Y) -fcrm, _ = facecurrents(u_n[m],Y) -Plotly.plot(patch(Γ, norm.(fcrj))) -Plotly.plot(patch(Γ, norm.(fcrm))) \ No newline at end of file diff --git a/examples/dvie.jl b/examples/dvie.jl index 535f57a3..26eb2774 100644 --- a/examples/dvie.jl +++ b/examples/dvie.jl @@ -1,21 +1,22 @@ using CompScienceMeshes, BEAST using LinearAlgebra using Profile +using TimerOutputs using StaticArrays function tau(x::SVector{U,T}) where {U,T} - 1.0-1.0/9.0 + 1.0-1.0/5.0 end ntrc = X->ntrace(X,y) -T = tetmeshsphere(1.0,0.25) +T = tetmeshsphere(1.0,0.2) X = nedelecd3d(T) y = boundary(T) @show numfunctions(X) ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) -ϵ_r =25.0 +ϵ_r =5.0 χ = tau K, I, B = VIE.singlelayer(wavenumber=κ, tau=χ), Identity(), VIE.boundary(wavenumber=κ, tau=χ) E = VIE.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) @@ -27,8 +28,8 @@ H = -1/(im*κ*η)*curl(E) eq = @varform α*I[j,k]-K[j,k]-B[ntrc(j),k] == E[j] dbvie = @discretise eq j∈X k∈X -u = solve(dbvie) - +u_m = solve(dbvie) +#= #postprocessing Φ, Θ = [0.0], range(0,stop=2π,length=100) pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] @@ -40,19 +41,42 @@ using Plots #Farfield plot(xlabel="theta") plot!(Θ, norm.(ff), label="far field", title="D-VIE") - -#= +=# +using Plots #NearField Z = range(-1,1,length=100) Y = range(-1,1,length=100) -nfpoints = [point(0,y,z) for y in Y, z in Z] +nfpoints = [point(0,y,z) for z in Z, y in Y] -Enear = BEAST.grideval(nfpoints,α.*u,X) +Enear = BEAST.grideval(nfpoints,α.*u_m,X) Enear = reshape(Enear,100,100) contour(real.(getindex.(Enear,1))) -heatmap(Z, Y, real.(getindex.(Enear,1)), clim=(0.0,1.0)) +heatmap(Z, Y, real.(getindex.(Enear,1))) -=# +plot!(Y[2:99],real.(getindex.(Enear[2:99,50],1)),label="D-VIE (simplex)", linestyle=:dash, linecolor=:darkorange4) +#plot!(Y[2:99],real.(getindex.(Enear_simplex[2:99,50],1)),label="D-VIE (simplex)", linestyle=:solid, linecolor=:darkorange3) + +#= +import Cairo +using DataFrames, Gadfly, RDatasets +D = dataset("datasets","HairEyeColor") +palette = ["skyblue","skyblue3"] + + +D = DataFrame( + Type=["D-VIE \n (1,190 Tets.)","D-VIE \n (1,190 Tets.)","D-VIE \n (1,190 Tets.)","D-VIE \n (1,190 Tets.)","E-VIE \n (1,190 Tets.)","E-VIE \n (1,190 Tets.)","E-VIE \n (1,190 Tets.)","E-VIE \n (1,190 Tets.)","D-VIE \n (2,706 Tets.)","D-VIE \n (2,706 Tets.)","D-VIE \n (2,706 Tets.)","D-VIE \n (2,706 Tets.)","E-VIE \n (2,706 Tets.)","E-VIE \n (2,706 Tets.)","E-VIE \n (2,706 Tets.)","E-VIE \n (2,706 Tets.)"], + Quad=["Simplex","Simplex","Classic","Classic","Simplex","Simplex","Classic","Classic","Simplex","Simplex","Classic","Classic","Simplex","Simplex","Classic","Classic"], + Sing=["Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular"], + Time=[24,4.77,23.1,27.9,41.9,8.49,41.5,45.7,125,13.1,125,68.6,222,20.5,222,115], +) + +p = Gadfly.plot(D, x=:Quad, y=:Time, color=:Sing, xgroup=:Type, + Geom.subplot_grid(Geom.bar(position=:stack)), + Scale.color_discrete_manual(palette...), + Guide.xlabel(""),Guide.ylabel("Assembly time [s]"), Guide.colorkey("Interaction"),Theme(major_label_font="Times",minor_label_font="Times",key_title_font="Times", key_label_font="Times",background_color=color("white"))) +# draw(PNG("haireyecolor.png", 6.6inch, 4inch), p) +draw(PNG(6.6inch, 4inch), p) +=# \ No newline at end of file diff --git a/examples/dvsie.jl b/examples/dvsie.jl new file mode 100644 index 00000000..dfa822c5 --- /dev/null +++ b/examples/dvsie.jl @@ -0,0 +1,198 @@ +using CompScienceMeshes, BEAST +using LinearAlgebra +using Profile +using LiftedMaps +using BlockArrays +using StaticArrays + + + +ntrc = X->ntrace(X,Γ) + +T = tetmeshsphere(1.0,0.3) +X = nedelecd3d(T) +Γ = boundary(T) +Y = raviartthomas(Γ) + +@show numfunctions(X) +@show numfunctions(Y) + +Z = BEAST.buffachristiansen2(Γ) + + +κ, η = 1.0, 1.0 +κ′, η′ = √2κ, η/√2 +ϵ_r =3 +ϵ_b =2.0 +κ_in, η_in = √6κ, η/√6 + + +#χ = x->(1.0-1.0/ϵ_r) + +function tau(x::SVector{U,T}) where {U,T} + 1.0-1.0/3.0 +end + +χ = tau + + + +N = NCross() +#Volume-Volume +L,I,B = VIE.singlelayer(wavenumber=κ′, tau=χ), Identity(), VIE.boundary(wavenumber=κ′, tau=χ) +#Volume-Surface +Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ′)), transpose(VSIE.boundary(wavenumber=κ′)), transpose(VSIE.doublelayer(wavenumber=κ′)) +#Kt = VSIE.doublelayerT(wavenumber=κ′) + +Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ′, tau=χ), VSIE.boundary(wavenumber=κ′, tau=χ), VSIE.doublelayer(wavenumber=κ′, tau=χ) +#Surface-Surface +T = Maxwell3D.singlelayer(wavenumber=κ) #Outside +T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside +K = Maxwell3D.doublelayer(wavenumber=κ) #Outside +K′ = Maxwell3D.doublelayer(wavenumber=κ′) #Inside + +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +H = -1/(im*κ*η)*curl(E) + +e, h = (n × E) × n, (n × H) × n + +@hilbertspace D j m +@hilbertspace k l o +β = 1/(ϵ_r*ϵ_b) +ν = 1/ϵ_b +α, α′ = 1/η, 1/η′ +γ′ = im*η′/κ′ +ζ′ = im*κ′/(ϵ_b*η′) +δ′ = im*κ′/ϵ_b + +#= +eq = @varform (β*I[k,D] + η′*Lt[k,j]-γ′*Bt[ntrc(k),j] - Kt[k,m] + + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) + +=# + +eq = @varform (β*I[k,D]-ν*L[k,D]-ν*B[ntrc(k),D] + η′*Lt[k,j]-γ′*Bt[ntrc(k),j] - Kt[k,m] + + -δ′*Ls[l,D]-ν*Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + + -ζ′*Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) + + +dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y + +u_n = solve(dvsie) + + +#= +#preconditioner +Mxx = assemble(I,X,X) +iMxx = inv(Matrix(Mxx)) + +Tzz = assemble(T,Z,Z); println("dual discretisation assembled.") +Nyz = Matrix(assemble(N,Y,Z)); println("duality form assembled.") + +iNyz = inv(Nyz); println("duality form inverted.") +NTN = iNyz' * Tzz * iNyz + +M = zeros(Int, 3) +N = zeros(Int, 3) + +M[1] = numfunctions(X) +N[1] = numfunctions(X) +M[2] = M[3] = numfunctions(Y) +N[2] = N[3] = numfunctions(Y) + +U = BlockArrays.blockedrange(M) +V = BlockArrays.blockedrange(N) + +precond = BEAST.ZeroMap{Float32}(U, V) + +z1 = LiftedMap(iMxx,Block(1),Block(1),U,V) +z2 = LiftedMap(NTN,Block(2),Block(2),U,V) +z3 = LiftedMap(NTN,Block(3),Block(3),U,V) +precond = precond +ϵ_r*z1 + z2 + z3 + +A_dvsie_precond = precond*A_dvsie + +#GMREs +import IterativeSolvers +cT = promote_type(eltype(A_dvsie), eltype(rhs)) +x = PseudoBlockVector{cT}(undef, M) +fill!(x, 0) +x, ch = IterativeSolvers.gmres!(x, A_dvsie, Rhs, log=true, reltol=1e-4) +fill!(x, 0) +x, ch = IterativeSolvers.gmres!(x, precond*A_dvsie, precond*Rhs, log=true, reltol=1e-8) +=# + +#Post processing +Θ, Φ = range(0.0,stop=2π,length=100), 0.0 +ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] + +# Don't forget the far field comprises two contributions +ffm = potential(MWFarField3D(κ*im), ffpoints, u_n[m], Y) +ffj = potential(MWFarField3D(κ*im), ffpoints, u_n[j], Y) +ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) + + +ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u_n[D], X) +ff2 = im*κ*ffd + +using Plots +plot(xlabel="theta") +plot!(Θ,norm.(ff),label="far field",title="DVSIE") +plot!(Θ,√6.0*norm.(ff),label="far field",title="DVSIE 2") + +import Plotly +using LinearAlgebra +fcrj, _ = facecurrents(u_n[j],Y) +fcrm, _ = facecurrents(u_n[m],Y) +vsie_j = Plotly.plot([patch(Γ, norm.(fcrj))], Plotly.Layout(title="j D-VSIE")) +vsie_m = Plotly.plot(patch(Γ, norm.(fcrm)), Plotly.Layout(title="m D-VSIE")) + +#NearField +function nearfield(um,uj,Xm,Xj,κ,η,points, + Einc=(x->point(0,0,0)), + Hinc=(x->point(0,0,0))) + + K = BEAST.MWDoubleLayerField3D(wavenumber=κ) + T = BEAST.MWSingleLayerField3D(wavenumber=κ) + + Em = potential(K, points, um, Xm) + Ej = potential(T, points, uj, Xj) + E = -Em + η*Ej + Einc.(points) + + Hm = potential(T, points, um, Xm) + Hj = potential(K, points, uj, Xj) + H = 1/η*Hm + Hj + Hinc.(points) + + return E, H +end + +Zz = range(-1,1,length=100) +Yy = range(-1,1,length=100) +nfpoints = [point(0.0,y,z) for z in Zz, y in Yy] + + +import Base.Threads: @spawn +task1 = @spawn nearfield(u_n[m],u_n[j],Y,Y,κ,η,nfpoints,E,H) +task2 = @spawn nearfield(-u_n[m],-u_n[j],Y,Y,κ_in,η_in,nfpoints) + +E_ex, H_ex = fetch(task1) +E_in, H_in = fetch(task2) + + + +Enear = BEAST.grideval(nfpoints,β.* u_n[D],X) +Enear = reshape(Enear,100,100) + +contour(real.(getindex.(Enear,1))) +heatmap(Zz, Yy, real.(getindex.(Enear,1))) + +heatmap(Zz, Yy, real.(getindex.(E_in,1))) +heatmap(Zz, Yy, real.(getindex.(E_ex,1))) + +contour(real.(getindex.(E_ex,1))) + + +Dd = range(1,100,step=1) +plot!(Yy,real.(getindex.(E_in[Dd,50],1))) +plot(collect(Yy)[2:99],real.(getindex.(Enear[2:99,50],1))) \ No newline at end of file diff --git a/examples/evie.jl b/examples/evie.jl index e2bc80b8..358512bb 100644 --- a/examples/evie.jl +++ b/examples/evie.jl @@ -3,19 +3,22 @@ using LinearAlgebra using Profile using StaticArrays -function tau(x::SVector{U,T}) where {U,T} - 4.0-1.0 -end + ttrc = X->ttrace(X,y) -T= tetmeshsphere(1.0,0.25) +T= tetmeshsphere(1.0,0.2) X = nedelecc3d(T) y = boundary(T) @show numfunctions(X) ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) -ϵ_r = 4.0 +ϵ_r = 5.0 + +function tau(x::SVector{U,T}) where {U,T} + 5.0 -1.0 +end + χ = tau K, I, B = VIE.singlelayer2(wavenumber=κ, tau=χ), Identity(), VIE.boundary2(wavenumber=κ, tau=χ) E = VIE.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) @@ -27,29 +30,31 @@ H = -1/(im*κ*η)*curl(E) eq = @varform α*I[j,k]-K[j,k]+B[ttrc(j),k] == E[j] evie = @discretise eq j∈X k∈X -u = solve(evie) +u_n = solve(evie) #postprocessing -Φ, Θ = [0.0], range(0,stop=2π,length=100) -pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ ] -ffe = potential(VIE.farfield(wavenumber=κ, tau=χ), pts, u, X) -ff = ffe - -using Plots +#Φ, Θ = [0.0], range(0,stop=2π,length=100) +#pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ ] +#ffe = potential(VIE.farfield(wavenumber=κ, tau=χ), pts, u_n, X) +#ff = ffe #Farfield -plot(xlabel="theta") -plot!(Θ, norm.(ff), label="far field", title="E-VIE") +#plot(xlabel="theta") +#plot!(Θ, norm.(ff), label="far field", title="E-VIE") + +using Plots #Nearfield Z = range(-1,1,length=100) Y = range(-1,1,length=100) -nfpoints = [point(0,y,z) for y in Y, z in Z] +nfpoints = [point(0,y,z) for z in Z, y in Y] + +Enear2 = BEAST.grideval(nfpoints,u_n,X) +Enear2 = reshape(Enear2,100,100) -Enear = BEAST.grideval(nfpoints,u,X) -Enear = reshape(Enear,100,100) +contour(real.(getindex.(Enear2,1))) +heatmap(Z, Y, real.(getindex.(Enear2,1))) -contour(real.(getindex.(Enear,1))) -heatmap(Z, Y, real.(getindex.(Enear,1)), clim=(0.0,1.0)) +plot!(Y[2:99],real.(getindex.(Enear2[2:99,50],1)),label="E-VIE (simplex)", linestyle=:dash, linecolor=:darkolivegreen4) diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 504a0104..261ea3f4 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -1,5 +1,7 @@ using CompScienceMeshes, BEAST using LinearAlgebra +using LiftedMaps +using BlockArrays T = tetmeshsphere(1.0,0.25) X = nedelecc3d(T) @@ -7,9 +9,13 @@ X = nedelecc3d(T) X = raviartthomas(Γ) @show numfunctions(X) +Y = BEAST.buffachristiansen2(Γ) + κ, η = 1.0, 1.0 -κ′, η′ = √6.0κ, η/√6.0 +κ′, η′ = √5.0κ, η/√5.0 + +N = NCross() T = Maxwell3D.singlelayer(wavenumber=κ) T′ = Maxwell3D.singlelayer(wavenumber=κ′) @@ -49,6 +55,40 @@ pmchwt = @discretise( u = solve(pmchwt) +#preconditioner +#= +Tyy = assemble(T,Y,Y); println("dual discretisation assembled.") +Nxy = Matrix(assemble(N,X,Y)); println("duality form assembled.") + +iNxy = inv(Nxy); println("duality form inverted.") +NTN = iNxy' * Tyy * iNxy + +M = zeros(Int, 2) +N = zeros(Int, 2) + +M[1] = M[2] = numfunctions(X) +N[1] = N[2] = numfunctions(X) + +U = BlockArrays.blockedrange(M) +V = BlockArrays.blockedrange(N) + +precond = BEAST.ZeroMap{Float32}(U, V) + +z1 = LiftedMap(NTN,Block(1),Block(1),U,V) +z2 = LiftedMap(NTN,Block(2),Block(2),U,V) +precond = precond + z1 + z2 + +A_pmchwt_precond = precond*A_pmchwt + +#GMREs +import IterativeSolvers +cT = promote_type(eltype(A_pmchwt), eltype(rhs)) +x = PseudoBlockVector{cT}(undef, M) +fill!(x, 0) +x, ch = IterativeSolvers.gmres!(x, A_pmchwt, rhs, log=true, reltol=1e-6) +fill!(x, 0) +x, ch = IterativeSolvers.gmres!(x, precond*A_pmchwt, precond*rhs, log=true, reltol=1e-6) +=# Θ, Φ = range(0.0,stop=2π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] @@ -62,12 +102,12 @@ plot(xlabel="theta") plot!(Θ,norm.(ff),label="far field",title="PMCHWT") -#import Plotly -#using LinearAlgebra -#fcrj, _ = facecurrents(u[j],X) -#fcrm, _ = facecurrents(u[m],X) -#Plotly.plot(patch(Γ, norm.(fcrj))) -#Plotly.plot(patch(Γ, norm.(fcrm))) +import Plotly +using LinearAlgebra +fcrj, _ = facecurrents(u[j],X) +fcrm, _ = facecurrents(u[m],X) +Plotly.plot(patch(Γ, norm.(fcrj)),Plotly.Layout(title="j PMCHWT")) +Plotly.plot(patch(Γ, norm.(fcrm)),Plotly.Layout(title="m PMCHWT")) function nearfield(um,uj,Xm,Xj,κ,η,points, @@ -106,13 +146,14 @@ H_tot = H_in + H_ex contour(real.(getindex.(E_tot,1))) contour(real.(getindex.(H_tot,2))) -heatmap(Z, Y, real.(getindex.(E_tot,1))) +heatmap(Z, Y, real.(getindex.(E_tot,1)),title="E_tot PMCHWT") #heatmap(Z, Y, real.(getindex.(H_tot,2))) - -#plot(real.(getindex.(E_tot[:,51],1))) +#heatmap(Z, Y, real.(getindex.(E_in,1))) +#heatmap(Z, Y, real.(getindex.(E_ex,1))) +#plot(fontfamily="Times",Y[2:99],real.(getindex.(E_tot[2:99,50],1)),label="PMCHWT",title="x-Component of Electrical Near Field inside Sphere") #plot!(real.(getindex.(H_tot[:,51],2))) - +#= # Compare the far field and the field far ffradius = 100.0 E_far, H_far = nearfield(u[m],u[j],X,X,κ,η, ffradius .* ffpoints) @@ -122,3 +163,4 @@ Et_far = -cross.(ffpoints, nxE_far) plot() plot!(Θ, norm.(ff),label="far field") scatter!(Θ, norm.(Et_far), label="field far") +=# \ No newline at end of file diff --git a/src/BEAST.jl b/src/BEAST.jl index 36d82d0b..09907cba 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -14,6 +14,8 @@ using FastGaussQuadrature using LinearMaps using LiftedMaps +using TimerOutputs + import LinearAlgebra: cross, dot import LinearAlgebra: ×, ⋅ @@ -243,4 +245,6 @@ export x̂, ŷ, ẑ const n = NormalVector() export n +const to = TimerOutput() + end # module diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index 2746604c..3c140015 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -75,7 +75,7 @@ end function momintegrals!(op::VIEOperator, test_local_space::RefSpace, trial_local_space::RefSpace, test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) - + @timeit to "singular" begin #Find permutation of vertices to match location of singularity to SauterSchwab J, I= SauterSchwab3D.reorder(strat.sing) @@ -127,11 +127,13 @@ function momintegrals!(op::VIEOperator, out[i,j] += Q[K[i],L[j]]*O1[i]*O2[j] end end + + end nothing end function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) - + @timeit to "regular" begin # memory allocation here is a result from the type instability on strat # which is on purpose, i.e. the momintegrals! method is chosen based # on dynamic polymorphism. @@ -165,7 +167,8 @@ function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::Do end end end - + + end return z end @@ -238,6 +241,7 @@ function momintegrals!(biop::VSIEOperator, tshs, bshs, tcell, bcell, z, strat::D wimps = strat.inner_quad_points M, N = size(z) + for womp in womps tgeo = womp.point @@ -254,7 +258,7 @@ function momintegrals!(biop::VSIEOperator, tshs, bshs, tcell, bcell, z, strat::D igd = integrand(biop, kernel, tvals, tgeo, bvals, bgeo) - + for m in 1 : length(tvals) for n in 1 : length(bvals) @@ -265,6 +269,6 @@ function momintegrals!(biop::VSIEOperator, tshs, bshs, tcell, bcell, z, strat::D end end - return z + #return z end diff --git a/src/volumeintegral/vieexc.jl b/src/volumeintegral/vieexc.jl index d1f5e92b..2dc1cf32 100644 --- a/src/volumeintegral/vieexc.jl +++ b/src/volumeintegral/vieexc.jl @@ -19,6 +19,15 @@ planewavevie(; ) = PlaneWaveVIE(direction, polarization, wavenumber, amplitude) function (e::PlaneWaveVIE)(p) + k = e.wavenumber + d = e.direction + u = e.polarisation + a = e.amplitude + x = p + a * exp(-im * k * dot(d, x)) * u + end + + function (e::PlaneWaveVIE)(p::CompScienceMeshes.MeshPointNM) k = e.wavenumber d = e.direction u = e.polarisation diff --git a/src/volumeintegral/vieops.jl b/src/volumeintegral/vieops.jl index 0879f9dd..611b2fe4 100644 --- a/src/volumeintegral/vieops.jl +++ b/src/volumeintegral/vieops.jl @@ -178,7 +178,7 @@ function integrand(viop::VIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) end -defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) +defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) function quaddata(op::VIEOperator, @@ -230,15 +230,18 @@ function qr_volume(op::VolumeOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, q end end - #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) - - + #Simplex-product quadrature rules hits == 4 && return SauterSchwab3D.CommonVolume6D_S(SauterSchwab3D.Singularity6DVolume(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[4])) hits == 3 && return SauterSchwab3D.CommonFace6D_S(SauterSchwab3D.Singularity6DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 2 && return SauterSchwab3D.CommonEdge6D_S(SauterSchwab3D.Singularity6DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3],qd.sing_qp[4])) hits == 1 && return SauterSchwab3D.CommonVertex6D_S(SauterSchwab3D.Singularity6DPoint(idx_t,idx_s),qd.sing_qp[3]) - - + #= + #Classic tensor-product quadrature rules + hits == 4 && return SauterSchwab3D.CommonVolume6D(SauterSchwab3D.Singularity6DVolume(idx_t,idx_s),qd.sing_qp[1]) + hits == 3 && return SauterSchwab3D.CommonFace6D(SauterSchwab3D.Singularity6DFace(idx_t,idx_s),qd.sing_qp[1]) + hits == 2 && return SauterSchwab3D.CommonEdge6D(SauterSchwab3D.Singularity6DEdge(idx_t,idx_s),qd.sing_qp[1]) + hits == 1 && return SauterSchwab3D.CommonVertex6D(SauterSchwab3D.Singularity6DPoint(idx_t,idx_s),qd.sing_qp[1]) + =# return DoubleQuadRule( qd[1][1,i], @@ -273,13 +276,15 @@ function qr_boundary(op::BoundaryOperator, g::RefSpace, f::RefSpace, i, τ, j, end end - #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) - - + hits == 3 && return SauterSchwab3D.CommonFace5D_S(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 2 && return SauterSchwab3D.CommonEdge5D_S(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 1 && return SauterSchwab3D.CommonVertex5D_S(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),(qd.sing_qp[3],qd.sing_qp[2])) - + #= + hits == 3 && return SauterSchwab3D.CommonFace5D(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),qd.sing_qp[1]) + hits == 2 && return SauterSchwab3D.CommonEdge5D(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),qd.sing_qp[1]) + hits == 1 && return SauterSchwab3D.CommonVertex5D(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),qd.sing_qp[1]) + =# return DoubleQuadRule( qd[1][1,i], diff --git a/src/volumeintegral/vsie.jl b/src/volumeintegral/vsie.jl index 57d2cb35..9947f567 100644 --- a/src/volumeintegral/vsie.jl +++ b/src/volumeintegral/vsie.jl @@ -41,14 +41,14 @@ module VSIE @assert gamma != nothing - alpha == nothing && (alpha = gamma) - beta == nothing && (beta = im*im/gamma) + alpha == nothing && (alpha = -gamma) + beta == nothing && (beta = -1/gamma) tau == nothing && (tau = x->1.0) Mod.VSIESingleLayer(gamma, alpha, beta, tau) end - function singlelayerT(; + function singlelayer2(; gamma=nothing, wavenumber=nothing, alpha=nothing, @@ -73,11 +73,11 @@ module VSIE @assert gamma != nothing - alpha == nothing && (alpha = wavenumber*wavenumber) - beta == nothing && (beta = 1.0) + alpha == nothing && (alpha = -gamma) + beta == nothing && (beta = -1/gamma) tau == nothing && (tau = x->1.0) - Mod.VSIESingleLayerT(gamma, alpha, beta, tau) + Mod.VSIESingleLayer2(gamma, alpha, beta, tau) end """ diff --git a/src/volumeintegral/vsieops.jl b/src/volumeintegral/vsieops.jl index 85222b23..7338e7d3 100644 --- a/src/volumeintegral/vsieops.jl +++ b/src/volumeintegral/vsieops.jl @@ -1,6 +1,9 @@ abstract type VSIEOperator <: IntegralOperator end abstract type VolumeSurfaceOperator <: VSIEOperator end abstract type BoundarySurfaceOperator <: VSIEOperator end +abstract type VSIEOperatorT <: VSIEOperator end +abstract type VolumeSurfaceOperatorT <: VolumeSurfaceOperator end +abstract type BoundarySurfaceOperatorT <: BoundarySurfaceOperator end struct KernelValsVSIE{T,U,P,Q,K} gamma::U @@ -20,7 +23,6 @@ function kernelvals(viop::VSIEOperator, p ,q) expn = exp(-yR) green = expn / (4*pi*R) gradgreen = - (Y +1/R)*green/R*r - tau = viop.tau(cartesian(q)) @@ -46,26 +48,26 @@ struct VSIEDoubleLayer{T,U,P} <: VolumeSurfaceOperator tau::P end - -struct VSIESingleLayerT{T,U,P} <: VolumeSurfaceOperator +#= +struct VSIESingleLayer2{T,U,P} <: VolumeSurfaceOperator gamma::T α::U β::U tau::P end -struct VSIEBoundaryT{T,U,P} <: BoundarySurfaceOperator +struct VSIEBoundaryT{T,U,P} <: BoundarySurfaceOperatorT gamma::T α::U tau::P end -struct VSIEDoubleLayerT{T,U,P} <: VolumeSurfaceOperator +struct VSIEDoubleLayerT{T,U,P} <: VolumeSurfaceOperatorT gamma::T α::U tau::P end - +=# scalartype(op::VSIEOperator) = typeof(op.gamma) export VSIE @@ -78,6 +80,15 @@ struct VSIEIntegrand{S,T,O,K,L} trial_local_space::L end +#= +struct VSIEIntegrandT{S,T,O,K,L} + test_tetrahedron_element::S + trial_triangle_element::T + op::O + test_local_space::K + trial_local_space::L +end +=# function (igd::VSIEIntegrand)(u,v) @@ -99,6 +110,28 @@ function (igd::VSIEIntegrand)(u,v) end +#= +function (igd::VSIEIntegrandT)(u,v) + + #mesh points + tgeo = neighborhood(igd.test_tetrahedron_element,u) + bgeo = neighborhood(igd.trial_triangle_element,v) + + #kernel values + kerneldata = kernelvals(igd.op,tgeo,bgeo) + + #values & grad/div/curl of local shape functions + tval = igd.test_local_space(tgeo) + bval = igd.trial_local_space(bgeo) + + #jacobian + j = jacobian(tgeo) * jacobian(bgeo) + + integrand(igd.op, kerneldata,tval,tgeo,bval,tgeo) * j +end +=# + + function integrand(viop::VSIESingleLayer, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:3] @@ -124,20 +157,21 @@ function integrand(viop::VSIEBoundary, kerneldata, tvals, tgeo, bvals, bgeo) G = kerneldata.green - Tx = kerneldata.tau + Ty = kerneldata.tau α = viop.α - @SMatrix[α * Tx * dgx[i] * fy[j] * G for i in 1:3, j in 1:1] + @SMatrix[α * Ty * dgx[i] * fy[j] * G for i in 1:3, j in 1:1] end -function integrand(viop::VSIESingleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) +#= +function integrand(viop::VSIESingleLayer2, kerneldata, tvals, tgeo, bvals, bgeo) - gx = @SVector[tvals[i].value for i in 1:4] - fy = @SVector[bvals[i].value for i in 1:3] + gx = @SVector[tvals[i].value for i in 1:3] + fy = @SVector[bvals[i].value for i in 1:4] - dgx = @SVector[tvals[i].divergence for i in 1:4] - dfy = @SVector[bvals[i].divergence for i in 1:3] + dgx = @SVector[tvals[i].divergence for i in 1:3] + dfy = @SVector[bvals[i].divergence for i in 1:4] G = kerneldata.green @@ -146,7 +180,7 @@ function integrand(viop::VSIESingleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) α = viop.α β = viop.β - @SMatrix[α * dot(gx[i],Ty*fy[j]) * G + β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] + @SMatrix[α * dot(gx[i],Ty*fy[j]) * G - β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] end @@ -163,7 +197,7 @@ function integrand(viop::VSIEBoundaryT, kerneldata, tvals, tgeo, bvals, bgeo) @SMatrix[α * Tx * gx[i] * dfy[j] * G for i in 1:3, j in 1:1] end - +=# function integrand(viop::VSIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:3] @@ -178,21 +212,23 @@ function integrand(viop::VSIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:3, j in 1:4] end +#= function integrand(viop::VSIEDoubleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:4] fy = @SVector[bvals[i].value for i in 1:3] + gradG = kerneldata.gradgreen - + Ty = kerneldata.tau α = viop.α - @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:4, j in 1:3] + @SMatrix[α * transpose(cross(fy[j],gx[i])) *gradG for i in 1:4, j in 1:3] end +=# - -defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) +defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) function quaddata(op::VSIEOperator, @@ -221,7 +257,9 @@ quadrule(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs::SauterSchwab3DQStrat) - + + @assert (length(τ.vertices)==3 && length(σ.vertices)==4) "Expected simplex wrong" + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) hits = 0 @@ -236,8 +274,8 @@ function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j d2 = LinearAlgebra.norm_sqr(t-s) dmin2 = min(dmin2, d2) if d2 < dtol - push!(idx_t,i) - push!(idx_s,j) + push!(idx_t,j) + push!(idx_s,i) hits +=1 break end @@ -249,8 +287,8 @@ function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j hits == 3 && return SauterSchwab3D.CommonFace5D_S(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 2 && return SauterSchwab3D.CommonEdge5D_S(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 1 && return SauterSchwab3D.CommonVertex5D_S(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),(qd.sing_qp[3],qd.sing_qp[2])) - - + #hits == 0 && return SauterSchwab3D.PositiveDistance5D_S(SauterSchwab3D.Singularity5DPositiveDistance(),(qd.sing_qp[3],qd.sing_qp[2])) + return DoubleQuadRule( qd[1][1,i], qd[2][1,j]) From f0a8c2d5a0eab584cd7deb2b59ba5d591008119a Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Wed, 16 Mar 2022 15:33:53 +0100 Subject: [PATCH 153/528] mixed mueller formulation --- examples/mueller.jl | 88 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 examples/mueller.jl diff --git a/examples/mueller.jl b/examples/mueller.jl new file mode 100644 index 00000000..5c95731a --- /dev/null +++ b/examples/mueller.jl @@ -0,0 +1,88 @@ +using CompScienceMeshes, BEAST + + +T = tetmeshsphere(1.0,0.3) +X = nedelecc3d(T) +Γ = boundary(T) + +X, Y = raviartthomas(Γ), buffachristiansen(Γ) + +ω = 1.0 +ϵ, ϵ′ = 1.0, 5.0 +μ, μ′ = 1.0, 1.0 +κ, η = ω*√(ϵ*μ), √(μ/ϵ) +κ′, η′ = ω*√(ϵ′*μ′), √(μ′/ϵ′) + + + +N = NCross() + +T = Maxwell3D.singlelayer(wavenumber=κ) +T′ = Maxwell3D.singlelayer(wavenumber=κ′) +K = Maxwell3D.doublelayer(wavenumber=κ) +K′ = Maxwell3D.doublelayer(wavenumber=κ′) + +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +H = -1/(im*κ*η)*curl(E) +e, h = (n × E) × n, (n × H) × n + +@hilbertspace j m +@hilbertspace k l + +α, α′ = μ/η, μ′/η′ +mueller = @discretise( + (ϵ*η*T-ϵ′*η′*T′)[k,j] - ((ϵ+ϵ′)/2*N+ϵ*K-ϵ′*K′)[k,m] + +((μ+μ′)/2*N+μ*K-μ′*K′)[l,j] + (α*T-α′*T′)[l,m] == -ϵ*e[k] - μ*h[l], +j∈X, m∈X, k∈Y, l∈Y) +u,A_mueller,rhs_mueller = solve(mueller) + +#postprocessing +using Plots +import Plotly +using LinearAlgebra +fcrj, _ = facecurrents(u[j],X) +fcrm, _ = facecurrents(u[m],X) +Plotly.plot(patch(Γ, norm.(fcrj)),Plotly.Layout(title="j Mueller")) +Plotly.plot(patch(Γ, norm.(fcrm)),Plotly.Layout(title="m Mueller")) + + +function nearfield(um,uj,Xm,Xj,κ,η,points, + Einc=(x->point(0,0,0)), + Hinc=(x->point(0,0,0))) + + K = BEAST.MWDoubleLayerField3D(wavenumber=κ) + T = BEAST.MWSingleLayerField3D(wavenumber=κ) + + Em = potential(K, points, um, Xm) + Ej = potential(T, points, uj, Xj) + E = -Em + η * Ej + Einc.(points) + + Hm = potential(T, points, um, Xm) + Hj = potential(K, points, uj, Xj) + H = 1/η*Hm + Hj + Hinc.(points) + + return E, H +end + + +Z = range(-1,1,length=100) +Y = range(-1,1,length=100) +nfpoints = [point(0,y,z) for z in Z, y in Y] + +import Base.Threads: @spawn +task1 = @spawn nearfield(u[m],u[j],X,X,κ,η,nfpoints,E,H) +task2 = @spawn nearfield(-u[m],-u[j],X,X,κ′,η′,nfpoints) + +E_ex, H_ex = fetch(task1) +E_in, H_in = fetch(task2) + +E_tot = E_in + E_ex +H_tot = H_in + H_ex + +contour(real.(getindex.(E_tot,1))) +contour(real.(getindex.(H_tot,2))) + +heatmap(Z, Y, real.(getindex.(E_tot,1)),title="E_tot Mueller") +#heatmap(Z, Y, real.(getindex.(H_tot,2))) +#heatmap(Z, Y, real.(getindex.(E_in,1))) +#heatmap(Z, Y, real.(getindex.(E_ex,1))) \ No newline at end of file From 49572965385f211d888c897117db9d3ce256964e Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Tue, 24 May 2022 00:47:23 +0000 Subject: [PATCH 154/528] CompatHelper: bump compat for Compat to 4, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index a7de95f6..88afa7b8 100644 --- a/Project.toml +++ b/Project.toml @@ -31,7 +31,7 @@ BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" CollisionDetection = "0.1" Combinatorics = "0.7, 1" CompScienceMeshes = "0.3.2" -Compat = "2, 3" +Compat = "2, 3, 4" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4" FillArrays = "0.11, 0.12, 0.13" From d00730552da959d850fce9e2b9ff3e20126ba643 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 25 May 2022 14:39:35 +0200 Subject: [PATCH 155/528] better support for cartesian product spaces --- .gitignore | 1 + Project.toml | 3 +- examples/pmchwt.jl | 24 +++++++++++---- src/BEAST.jl | 1 + src/bases/basis.jl | 17 +++++++---- src/bases/lagrange.jl | 4 +-- src/identityop.jl | 5 ---- src/integralop.jl | 10 +++++-- src/operator.jl | 41 ++++++++++++++++++++++++-- src/solvers/lusolver.jl | 4 +-- src/solvers/solver.jl | 24 +++++++++++++-- src/utils/variational.jl | 33 ++++++++++++++++++++- src/utils/zeromap.jl | 32 +++++++++++--------- test/runtests.jl | 2 ++ test/test_basis.jl | 6 ++-- test/test_dipole.jl | 2 +- test/test_mixed_blkassm.jl | 2 +- test/test_variational.jl | 60 ++++++++++++++++++++++++++++++++++++++ 18 files changed, 225 insertions(+), 46 deletions(-) create mode 100644 test/test_variational.jl diff --git a/.gitignore b/.gitignore index d29bd28c..63cfd453 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ temp/ dev/ profile/ Manifest.toml +envs/ \ No newline at end of file diff --git a/Project.toml b/Project.toml index 973262f7..5f193920 100644 --- a/Project.toml +++ b/Project.toml @@ -17,6 +17,7 @@ IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LiftedMaps = "d22a30c1-52ac-4762-a8c9-5838452405e0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" +Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" SauterSchwab3D = "0a13313b-1c00-422e-8263-562364ed9544" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" @@ -34,7 +35,7 @@ CompScienceMeshes = "0.3.2" Compat = "2, 3" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4" -FillArrays = "0.11, 0.12" +FillArrays = "0.11, 0.12, 0.13" IterativeSolvers = "0.9" LiftedMaps = "0.3" LinearMaps = "3.5" diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 254855ea..f77bbef4 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -1,14 +1,14 @@ using CompScienceMeshes, BEAST using LinearAlgebra -T = tetmeshsphere(1.0,0.25) -X = nedelecc3d(T) +T = CompScienceMeshes.tetmeshsphere(1.0,0.12) +X = BEAST.nedelecc3d(T) Γ = boundary(T) X = raviartthomas(Γ) @show numfunctions(X) -κ, η = 1.0, 1.0 +κ, η = π, 1.0 κ′, η′ = 2.0κ, η/2.0 T = Maxwell3D.singlelayer(wavenumber=κ) @@ -30,7 +30,12 @@ pmchwt = @discretise( (K+K′)[l,j] + (α*T+α′*T′)[l,m] == -e[k] - h[l], j∈X, m∈X, k∈X, l∈X) -# Z = BEAST.sysmatrix(pmchwt) + +# Q = BEAST.sysmatrix(pmchwt) +# M = BEAST.convert_to_dense(Q) +# error() +# Q = assemble(T, X, X) +# error() # b = BEAST.rhs(pmchwt) # M = BEAST.convert_to_dense(Z) # u = M \ b @@ -47,20 +52,27 @@ pmchwt = @discretise( # u, ch = IterativeSolvers.gmres!(u, Z, b, log=true, maxiter=1000, # restart=1000, reltol=1e-5, verbose=true) -u = solve(pmchwt) +u = gmres(pmchwt) + +Z = BEAST.sysmatrix(pmchwt) +u = zeros(ComplexF64, axes(Z,2)) +y = zeros(ComplexF64, axes(Z,1)) + +@time for i in 1:300; BEAST.LinearMaps.mul!(y, Z, u); end Θ, Φ = range(0.0,stop=2π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] # Don't forget the far field comprises two contributions ffm = potential(MWFarField3D(κ*im), ffpoints, u[m], X) -ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], X) +ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], X) ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) using Plots plot(xlabel="theta") plot!(Θ,norm.(ff),label="far field",title="PMCHWT") +error() #import Plotly #using LinearAlgebra #fcrj, _ = facecurrents(u[j],X) diff --git a/src/BEAST.jl b/src/BEAST.jl index ca634a3f..25b37c90 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -7,6 +7,7 @@ using SparseArrays using FillArrays using BlockArrays using SparseMatrixDicts +using Polyester using SauterSchwabQuadrature using SauterSchwab3D diff --git a/src/bases/basis.jl b/src/bases/basis.jl index 71f10f8b..9a700d16 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -148,7 +148,7 @@ function. *Note*: the indices `c` correspond to the position of the corresponding element whilst iterating over `geometry(basis)`. """ -function assemblydata(basis::Space) +function assemblydata(basis::Space; onlyactives=true) @assert numfunctions(basis) != 0 @@ -163,10 +163,17 @@ function assemblydata(basis::Space) # # determine the maximum number of function defined over a given cell celltonum = make_celltonum(num_cells, num_refs, num_bfs, basis) - # # mark the active elements, i.e. elements over which - # # at least one function is defined. - active, index_among_actives, num_active_cells, act_to_global = - index_actives(num_cells, celltonum) + # mark the active elements, i.e. elements over which + # at least one function is defined. + if onlyactives + active, index_among_actives, num_active_cells, act_to_global = + index_actives(num_cells, celltonum) + else + active = trues(num_cells) + num_active_cells = num_cells + index_among_actives = collect(1:num_cells) + act_to_global = collect(1:num_cells) + end @assert num_active_cells != 0 elements = instantiate_charts(geo, num_active_cells, active) diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index e4080f23..f7bc6794 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -562,8 +562,8 @@ function dual0forms_body(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}, refd, bn Cell = Cells[F] myid = Threads.threadid() - myid == 1 && F % 20 == 0 && - println("Constructing dual 1-forms: $(F) out of $(length(mesh)).") + # myid == 1 && F % 20 == 0 && + # println("Constructing dual 1-forms: $(F) out of $(length(mesh)).") idcs1 = v2t[Cell[1],1:v2n[Cell[1]]] idcs2 = v2t[Cell[2],1:v2n[Cell[2]]] diff --git a/src/identityop.jl b/src/identityop.jl index ad362fb5..c2775f17 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -20,11 +20,6 @@ function _alloc_workspace(qd, g, f, tels, bels) A = Vector{typeof(a)}(undef,length(qd)) end -# defaultquadstrat(op::LocalOperator, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bfs)) -# defaultquadstrat(op::LocalOperator, tfs::Space, bfs::DirectProductSpace) = defaultquadstrat(op, tfs, bfs.factors[1]) -# defaultquadstrat(op::LocalOperator, tfs::DirectProductSpace, bfs::Space) = defaultquadstrat(op, tfs.factors[1], bfs) -# defaultquadstrat(op::LocalOperator, tfs::DirectProductSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs.factors[1], bfs.factors[1]) - const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace, BDMRefSpace} defaultquadstrat(::LocalOperator, ::LinearRefSpaceTriangle, ::LinearRefSpaceTriangle) = SingleNumQStrat(6) function quaddata(op::LocalOperator, g::LinearRefSpaceTriangle, f::LinearRefSpaceTriangle, tels, bels, diff --git a/src/integralop.jl b/src/integralop.jl index 74f9a752..15d39bf0 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -214,8 +214,8 @@ end function assembleblock_primer(biop, tfs, bfs; quadstrat=defaultquadstrat(biop, tfs, bfs)) - test_elements, tad = assemblydata(tfs) - bsis_elements, bad = assemblydata(bfs) + test_elements, tad = assemblydata(tfs; onlyactives=false) + bsis_elements, bad = assemblydata(bfs; onlyactives=false) tshapes = refspace(tfs); num_tshapes = numfunctions(tshapes) bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes) @@ -255,6 +255,12 @@ function assembleblock_body!(biop::IntegralOperator, active_test_el_ids = unique!(sort!(active_test_el_ids)) active_trial_el_ids = unique!(sort!(active_trial_el_ids)) + @assert length(active_test_el_ids) <= length(test_elements) + @assert length(active_trial_el_ids) <= length(bsis_elements) + + @assert maximum(active_test_el_ids) <= length(test_elements) "$(maximum(active_test_el_ids)), $(length(test_elements))" + @assert maximum(active_trial_el_ids) <= length(bsis_elements) "$(maximum(active_trial_el_ids)), $(length(bsis_elements))" + for p in active_test_el_ids tcell = test_elements[p] for q in active_trial_el_ids diff --git a/src/operator.jl b/src/operator.jl index 005bb554..489e397e 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -153,6 +153,13 @@ function allocatestorage(operator::LinearCombinationOfOperators, storage_policy, long_delays_policy) end +struct _OffsetStore{F} + store::F + row_offset::Int + col_offset::Int +end + +(f::_OffsetStore)(v,m,n) = f.store(v,m + f.row_offset, n + f.col_offset) function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, threading::Type{Threading{:multi}} = Threading{:multi}; @@ -166,11 +173,13 @@ function assemble!(operator::Operator, test_functions::Space, trial_functions::S splits = [round(Int,s) for s in range(0, stop=numfunctions(test_functions), length=numchunks+1)] Threads.@threads for i in 1:P + # @batch per=thread for i in 1:P lo, hi = splits[i]+1, splits[i+1] lo <= hi || continue test_functions_p = subset(test_functions, lo:hi) - store1 = (v,m,n) -> store(v,lo+m-1,n) - assemblechunk!(operator, test_functions_p, trial_functions, store1; quadstrat) + # store1 = (v,m,n) -> store(v,lo+m-1,n) + store1 = _OffsetStore(store, lo-1, 0) + assemblechunk!(operator, test_functions_p, trial_functions, store1, quadstrat=quadstrat) end end @@ -236,3 +245,31 @@ function assemble!(op::Operator, tfs::DirectProductSpace, bfs::DirectProductSpac assemble!(op, s, bfs, store1; quadstrat) end end + +# Discretisation and assembly of these operators +# will respect the direct product structure of the +# HilbertSpace/FiniteElmeentSpace +struct BlockDiagonalOperator <: AbstractOperator + op::AbstractOperator +end + +diag(op::AbstractOperator) = BlockDiagonalOperator(op) + +scalartype(op::BlockDiagonalOperator) = scalartype(op.op) +defaultquadstrat(op::BlockDiagonalOperator, U::DirectProductSpace, V::DirectProductSpace) = defaultquadstrat(op.op, U, V) + +function assemble!(op::BlockDiagonalOperator, U::DirectProductSpace, V::DirectProductSpace, + store, threading=Threading{:multi}; + quadstrat = defaultquadstrat(op, U, V)) + + @assert length(U.factors) == length(V.factors) + I = Int[0]; for u in U.factors push!(I, last(I) + numfunctions(u)) end + J = Int[0]; for v in V.factors push!(J, last(J) + numfunctions(v)) end + + k = 1 + for (u,v) in zip(U.factors, V.factors) + store1(v,m,n) = store(v, I[k] + m, J[k] + n) + assemble!(op.op, u, v, store1; quadstrat) + k += 1 + end +end diff --git a/src/solvers/lusolver.jl b/src/solvers/lusolver.jl index f32057e6..a660b0df 100644 --- a/src/solvers/lusolver.jl +++ b/src/solvers/lusolver.jl @@ -3,14 +3,14 @@ using LinearAlgebra function convert_to_dense(A::LinearMaps.LinearCombination) @info "convert matrix to dense..." - T = eltype(A) + # T = eltype(A) # M = zeros(T, size(A)) # M = PseudoBlockMatrix{T}(undef, BlockArrays.blocksizes(A)...) # fill!(M,0) M = zeros(eltype(A), axes(A)) @show typeof(M) for map in A.maps - mul!(M, 1, map, 1, 1) + mul!(M, map, 1, 1, 1) # M .+= Matrix(map) end diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 0d6e8090..3b17277f 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -19,8 +19,26 @@ struct DiscreteLinform test_space_dict end +function _expand_space_mappings(sms) + esms = [] + for sm in sms + if first(sm) isa Vector + j = first(sm) + X = last(sm) + @assert X isa BEAST.DirectProductSpace + append!(esms, [(ji => Xi) for (ji,Xi) in zip(j,X.factors)]) + else + append!(esms, [sm]) + end + end + return esms +end + function discretise(bf::BilForm, space_mappings::Pair...) + + space_mappings = _expand_space_mappings(space_mappings) + trial_space_dict = Dict() test_space_dict = Dict() for sm in space_mappings @@ -43,7 +61,7 @@ end function discretise(lf::LinForm, space_mappings::Pair...) - # trial_space_dict = Dict() + space_mappings = _expand_space_mappings(space_mappings) test_space_dict = Dict() for sm in space_mappings @@ -65,6 +83,8 @@ end function discretise(eq, space_mappings::Pair...) + space_mappings = _expand_space_mappings(space_mappings) + trial_space_dict = Dict() test_space_dict = Dict() for sm in space_mappings @@ -110,7 +130,7 @@ sysmatrix(eq::DiscreteEquation; materialize=BEAST.assemble) = assemble(eq.equation.lhs, eq.test_space_dict, eq.trial_space_dict, materialize=materialize) rhs(eq::DiscreteEquation) = assemble(eq.equation.rhs, eq.test_space_dict) -assemble(dbf::DiscreteBilform) = assemble(dbf.bilform, dbf.test_space_dict, dbf.trial_space_dict) +assemble(dbf::DiscreteBilform; materialize=BEAST.assemble) = assemble(dbf.bilform, dbf.test_space_dict, dbf.trial_space_dict; materialize) assemble(dlf::DiscreteLinform) = assemble(dlf.linform, dlf.test_space_dict) function assemble(lform::LinForm, test_space_dict) diff --git a/src/utils/variational.jl b/src/utils/variational.jl index 6818067d..4e325c96 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -1,7 +1,7 @@ module Variational using BlockArrays - +import BEAST # import Base: start, done, next export transposecalls! @@ -261,6 +261,14 @@ Return a LinForm corresponding to f[v] """ getindex(f, v::HilbertVector) = LinForm(v.space, [LinTerm(v.idx, v.opstack, 1, f)]) +function getindex(f, V::Vector{HilbertVector}) + terms = Vector{LinTerm}() + for v in V + term = LinTerm(v.idx, v.opstack, 1, f) + push!(terms, term) + end + return LinForm(first(V).space, terms) +end """ getindex(A, v::HilbertVector, u::HilbertVector) @@ -273,6 +281,29 @@ function getindex(A, v::HilbertVector, u::HilbertVector) end +function getindex(A::BEAST.BlockDiagonalOperator, V::Vector{HilbertVector}, U::Vector{HilbertVector}) + op = A.op + terms = Vector{BilTerm}() + @assert length(V) == length(U) + for (v,u) in zip(V,U) + term = BilTerm(v.idx, u.idx, v.opstack, u.opstack, 1, op) + push!(terms, term) + end + return BilForm(first(V).space, first(U).space, terms) +end + +function getindex(op::Any, V::Vector{HilbertVector}, U::Vector{HilbertVector}) + terms = Vector{BilTerm}() + for v in V + for u in U + term = BilTerm(v.idx, u.idx, v.opstack, u.opstack, 1, op) + push!(terms, term) + end + end + return BilForm(first(V).space, first(U).space, terms) +end + + "Add two BilForms together" function +(a::BilForm, b::BilForm) @assert a.test_space == b.test_space diff --git a/src/utils/zeromap.jl b/src/utils/zeromap.jl index 37e0cc11..f33390fc 100644 --- a/src/utils/zeromap.jl +++ b/src/utils/zeromap.jl @@ -11,18 +11,24 @@ LinearMaps.MulStyle(A::ZeroMap) = LinearMaps.FiveArg() Base.size(A::ZeroMap) = (length(A.range), length(A.domain),) Base.axes(A::ZeroMap) = (A.range, A.domain) -function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector, - α::Number, β::Number) - +function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector, α::Number, β::Number) y .*= β end -function LinearAlgebra.mul!(Y::PseudoBlockMatrix, c::Number, X::ZeroMap, a::Number, b::Number) - @assert b == 1 +# function LinearAlgebra.mul!(Y::PseudoBlockMatrix, c::Number, X::ZeroMap, a::Number, b::Number) +# @assert b == 1 +# return Y +# end + +function LinearAlgebra.mul!(Y::AbstractMatrix, X::ZeroMap, c::Number, a::Number, b::Number) + rmul!(Y, b) return Y end - +function LinearAlgebra.mul!(Y::AbstractMatrix, X::ZeroMap, c::Number) + fill!(Y, false) + return Y +end # function LinearAlgebra.mul!(y::AbstractVector, # Lt::LinearMaps.TransposeMap{<:Any,<:ZeroMap}, @@ -45,11 +51,11 @@ end LinearAlgebra.adjoint(A::ZeroMap{T}) where {T} = ZeroMap{T}(A.domain, A.range) LinearAlgebra.transpose(A::ZeroMap{T}) where {T} = ZeroMap{T}(A.domain, A.range) -function Base.:(*)(A::ZeroMap, x::AbstractVector) - T = eltype(A) - y = similar(A.range, T) - fill!(y, zero(T)) - LinearAlgebra.mul!(y,A,x) -end +# function Base.:(*)(A::ZeroMap, x::AbstractVector) +# T = eltype(A) +# y = similar(A.range, T) +# fill!(y, zero(T)) +# LinearAlgebra.mul!(y,A,x) +# end -Base.Matrix{T}(A::ZeroMap) where {T} = zeros(T, length(A.range), length(A.domain)) +# Base.Matrix{T}(A::ZeroMap) where {T} = zeros(T, length(A.range), length(A.domain)) diff --git a/test/runtests.jl b/test/runtests.jl index ed8935ac..1d24fb7c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -67,6 +67,8 @@ include("test_matrixconv.jl") include("test_tdop_scaling.jl") include("test_tdrhs_scaling.jl") +include("test_variational.jl") + # include("test_farfield.jl") #include("test_assemblerow.jl") diff --git a/test/test_basis.jl b/test/test_basis.jl index e4ae3b82..f4382c1e 100644 --- a/test/test_basis.jl +++ b/test/test_basis.jl @@ -17,11 +17,11 @@ identityop = Identity() doublelayer = DoubleLayer(κ) @time N = assemble(hypersingular, X, X) -@time I = assemble(identityop, X, X) +@time Ixx = assemble(identityop, X, X) @test size(N) == (numfunctions(X), numfunctions(X)) -@test size(I) == (numfunctions(X), numfunctions(X)) -@test rank(I) == numfunctions(X) +@test size(Ixx) == (numfunctions(X), numfunctions(X)) +@test rank(Ixx) == numfunctions(X) @time e = assemble(PlaneWaveNeumann(κ, point(0.0, 1.0)), X) @test length(e) == numfunctions(X) diff --git a/test/test_dipole.jl b/test/test_dipole.jl index 0742a0f7..004d7350 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -14,7 +14,7 @@ k = 2*π/λ ω = k*c η = sqrt(μ/ε) -a = 1 +a = 1.0 Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.1) Γ = translate(Γ_orig,SVector(-a/2,-a/2,-a/2)) diff --git a/test/test_mixed_blkassm.jl b/test/test_mixed_blkassm.jl index a251a747..ff74ff8a 100644 --- a/test/test_mixed_blkassm.jl +++ b/test/test_mixed_blkassm.jl @@ -33,7 +33,7 @@ k = 2π/λ ω = k*c η = sqrt(μ/ε) -a = 1 +a = 1.0 Γ = CompScienceMeshes.meshcuboid(a,a,a,0.2) 𝓣 = Maxwell3D.singlelayer(wavenumber=k) diff --git a/test/test_variational.jl b/test/test_variational.jl new file mode 100644 index 00000000..eee2b42e --- /dev/null +++ b/test/test_variational.jl @@ -0,0 +1,60 @@ +using BEAST +using CompScienceMeshes +using Test + +@hilbertspace j[1:3] +@hilbertspace k[1:3] + +n = BEAST.n + +SL = BEAST.diag(BEAST.Maxwell3D.singlelayer(wavenumber=1.0)) +EF = BEAST.Maxwell3D.planewave(polarization=point(1,0,0), direction=point(0,0,1), wavenumber=1.0) +ef = (n × EF) × n + +bf = SL[j,k] +lf = ef[j] + +@test length(bf.terms) == 3 +for term in bf.terms + @test term.test_id == term.trial_id +end + +mesh1 = Mesh( + [ + point(0,0,0), + point(1,0,0), + point(1,1,0), + point(0,1,0), + ], + [ + index(1,2,3), + index(1,3,4) + ] +) + +mesh2 = CompScienceMeshes.translate(mesh1, point(0,0,1)) +mesh3 = CompScienceMeshes.translate(mesh1, point(0,0,2)) + +X1 = raviartthomas(mesh1) +X2 = raviartthomas(mesh2) +X3 = raviartthomas(mesh3) + +X = X1 × X2 × X3 + +dbf = BEAST.discretise(bf, j=>X, k=>X) +dlf = BEAST.discretise(lf, j=>X) + +space_mappings = (j=>X, k=>X,) +for sm in space_mappings + @show sm +end + +M = assemble(dbf) +A = Matrix(M) + +@test A[1,2] == A[1,3] == A[2,1] == A[2,3] == A[3,1] == A[3,2] == 0 +@test A[1,1] ≈ A[2,2] ≈ A[3,3] + +N = assemble(SL, X, X) +B = Matrix(N) +@test A == B \ No newline at end of file From 2aed1147a3c88e51ca9de198a086643de5fcbf23 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 25 May 2022 14:40:48 +0200 Subject: [PATCH 156/528] gitignore local envs --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a18f44e6..28c533fe 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ temp/ dev/ profile/ /Manifest.toml +/envs/ \ No newline at end of file From d6386c2e7f6892a424ba29319ca9075d12bdb107 Mon Sep 17 00:00:00 2001 From: Paula Respondek Date: Wed, 23 Mar 2022 13:09:11 +0100 Subject: [PATCH 157/528] Update source files for support 32bit discretization - Not everything could be adjusted to be 32bit compatible due to problems with the CollisionDetection package. - To be effective bug fixed WiltonInts84 and CompScienceMeshes are necessary. --- src/bases/basis.jl | 4 ++-- src/bases/bcspace.jl | 8 ++++---- src/bases/lagrange.jl | 24 ++++++++++++------------ src/bases/rtspace.jl | 24 +++++++++++++----------- src/bases/subdbasis.jl | 6 +++--- src/bases/timebasis.jl | 6 +++--- src/excitation.jl | 14 +++++++------- src/helmholtz2d/helmholtzop.jl | 4 ++-- src/identityop.jl | 4 ++-- src/interpolation.jl | 7 ++++--- src/localop.jl | 10 +++++----- src/maxwell/mwops.jl | 22 ++++++++++++---------- src/maxwell/sourcefield.jl | 2 +- src/maxwell/wiltonints.jl | 5 +++-- src/postproc.jl | 8 ++++---- src/solvers/solver.jl | 10 +++++----- src/utils/specialfns.jl | 3 +-- 17 files changed, 83 insertions(+), 78 deletions(-) diff --git a/src/bases/basis.jl b/src/bases/basis.jl index 71f10f8b..e56743c0 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -113,11 +113,11 @@ end function add!(bf::Vector{Shape{T}}, cellid, refid, coeff) where T for (i,sh) in pairs(bf) if sh.cellid == cellid && sh.refid == refid - bf[i] = Shape(cellid, refid, sh.coeff + coeff) + bf[i] = Shape(cellid, refid, sh.coeff + T(coeff)) return nothing end end - push!(bf, Shape(cellid, refid, coeff)) + push!(bf, Shape(cellid, refid, T(coeff))) return nothing end diff --git a/src/bases/bcspace.jl b/src/bases/bcspace.jl index 45f55e0b..187f05a5 100644 --- a/src/bases/bcspace.jl +++ b/src/bases/bcspace.jl @@ -514,7 +514,7 @@ function buildhalfbc2(patch, port, dirichlet, prt_fluxes) end -function buffachristiansen2(Faces::CompScienceMeshes.AbstractMesh) +function buffachristiansen2(Faces::CompScienceMeshes.AbstractMesh{U,D1,T}) where {U,D1,T} faces = barycentric_refinement(Faces) Edges = skeleton(Faces,1) @@ -525,7 +525,7 @@ function buffachristiansen2(Faces::CompScienceMeshes.AbstractMesh) return true end - T = Float64 + #T = Float64 bfs = Vector{Vector{Shape{T}}}(undef, numcells(Edges)) pos = Vector{vertextype(Faces)}(undef, numcells(Edges)) dirichlet = boundary(faces) @@ -603,7 +603,7 @@ function buffachristiansen2(Faces::CompScienceMeshes.AbstractMesh) end -function buffachristiansen3(Faces::CompScienceMeshes.AbstractMesh) +function buffachristiansen3(Faces::CompScienceMeshes.AbstractMesh{U,D1,T}) where {U,D1,T} faces = barycentric_refinement(Faces) Edges = skeleton(Faces,1) @@ -614,7 +614,7 @@ function buffachristiansen3(Faces::CompScienceMeshes.AbstractMesh) return true end - T = Float64 + #T = Float64 bfs = Vector{Vector{Shape{T}}}(undef, numcells(Edges)) pos = Vector{vertextype(Faces)}(undef, numcells(Edges)) dirichlet = boundary(faces) diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index e4080f23..c9fcf3dc 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -32,15 +32,15 @@ function lagrangecxd0(mesh) U = universedimension(mesh) D1 = dimension(mesh)+1 - + T = coordtype(mesh) geometry = mesh num_cells = numcells(mesh) # create the local shapes - fns = Vector{Vector{Shape{Float64}}}(undef,num_cells) + fns = Vector{Vector{Shape{T}}}(undef,num_cells) pos = Vector{vertextype(mesh)}(undef,num_cells) for (i,cell) in enumerate(cells(mesh)) - fns[i] = [Shape(i, 1, 1.0)] + fns[i] = [Shape(i, 1, T(1.0))] pos[i] = cartesian(center(chart(mesh, cell))) end @@ -193,7 +193,7 @@ function lagrangec0d1(mesh, vertexlist::Vector, ::Type{Val{3}}) Verts = vertices(mesh) # create the local shapes - fns = Vector{Shape{Float64}}[] + fns = Vector{Shape{T}}[] pos = Vector{vertextype(mesh)}() sizehint!(fns, length(vertexlist)) @@ -203,7 +203,7 @@ function lagrangec0d1(mesh, vertexlist::Vector, ::Type{Val{3}}) numshapes = ncells[v] numshapes == 0 && continue - shapes = Vector{Shape{Float64}}(undef,numshapes) + shapes = Vector{Shape{T}}(undef,numshapes) for s in 1: numshapes c = cellids[v,s] # cell = mesh.faces[c] @@ -212,7 +212,7 @@ function lagrangec0d1(mesh, vertexlist::Vector, ::Type{Val{3}}) localid = something(findfirst(isequal(v), cell),0) @assert localid != 0 - shapes[s] = Shape(c, localid, 1.0) + shapes[s] = Shape(c, localid, T(1.0)) end push!(fns, shapes) @@ -247,14 +247,14 @@ function lagrangec0d1(mesh, vertexlist, ::Type{Val{2}}) numshapes = ncells[v] numshapes == 0 && continue # skip detached vertices - shapes = Vector{Shape{Float64}}(undef,numshapes) + shapes = Vector{Shape{T}}(undef,numshapes) for s in 1: numshapes c = cellids[v,s] cell = mesh.faces[c] if cell[1] == v - shapes[s] = Shape(c, 1, 1.0) + shapes[s] = Shape(c, 1, T(1.0)) elseif cell[2] == v - shapes[s] = Shape(c, 2, 1.0) + shapes[s] = Shape(c, 2, T(1.0)) else error("Junctions not supported") end @@ -286,7 +286,7 @@ function lagrangec0d1(mesh, nodes::CompScienceMeshes.AbstractMesh{U,1} where {U} for k in nzrange(Conn,i) cellid = rows[k] refid = vals[k] - push!(fn, Shape(cellid, refid, 1.0)) + push!(fn, Shape(cellid, refid, T(1.0))) end push!(fns,fn) push!(pos,cartesian(center(chart(nodes,node)))) @@ -401,7 +401,7 @@ This basis function creats the dual Lagrange basis function and return an object It also return a gemoetry containing the refined mesh. """ function duallagrangec0d1(mesh, mesh2, pred, ::Type{Val{2}}) - T = Float64 + T = coordtype(mesh) U = universedimension(mesh) # get the information about number of vertices, number of faces , and the maping between vertices and faces for the original mesh numverts1 = numvertices(mesh) @@ -627,7 +627,7 @@ function dual0forms_body(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}, refd, bn x3_int, _, Lg3_int = extend_0_form(supp3, dir3_edges, x3_prt, Lg3_prt) # inject in the global space - fn = BEAST.Shape{Float64}[] + fn = BEAST.Shape{T}[] addf!(fn, x1_prt, Lg1_prt, idcs1) addf!(fn, x1_int, Lg1_int, idcs1) diff --git a/src/bases/rtspace.jl b/src/bases/rtspace.jl index 1565b72d..374ccadb 100644 --- a/src/bases/rtspace.jl +++ b/src/bases/rtspace.jl @@ -29,11 +29,11 @@ Returns an object of type `RTBasis`, which comprises both the mesh and pairs of coefficients and indices to compute the exact basis functions when required by the solver. """ -function raviartthomas(mesh, cellpairs::Array{Int,2}) +function raviartthomas(mesh::Mesh{U,D1,T}, cellpairs::Array{Int,2}) where {U,D1,T} # combine now the pairs of monopolar RWGs in div-conforming RWGs numpairs = size(cellpairs,2) - functions = Vector{Vector{Shape{Float64}}}(undef,numpairs) + functions = Vector{Vector{Shape{T}}}(undef,numpairs) positions = Vector{vertextype(mesh)}(undef,numpairs) Cells = cells(mesh) for i in 1:numpairs @@ -42,8 +42,8 @@ function raviartthomas(mesh, cellpairs::Array{Int,2}) c2 = cellpairs[2,i]; cell2 = Cells[c2] #mesh.faces[c2] e1, e2 = getcommonedge(cell1, cell2) functions[i] = [ - Shape(c1, abs(e1), +1.0), - Shape(c2, abs(e2), -1.0)] + Shape{T}(c1, abs(e1), T(+1.0)), + Shape{T}(c2, abs(e2), T(-1.0))] isct = intersect(cell1, cell2) @assert length(isct) == 2 @assert !(cell1[abs(e1)] in isct) @@ -56,7 +56,7 @@ function raviartthomas(mesh, cellpairs::Array{Int,2}) c1 = cellpairs[1,i] e1 = cellpairs[2,i] functions[i] = [ - Shape(c1, abs(e1), +1.0)] + Shape(c1, abs(e1), T(+1.0))] positions[i] = cartesian(center(chart(mesh, Cells[c1]))) end end @@ -150,10 +150,11 @@ leaving or entering port defined by cellpairs cps. weight defines the total current over the port and its direction (+ve = out, -ve = in) """ function rt_cedge(cps::Array{Int,2}, weight) + T=typeof(weight) numpairs = size(cps,2) @assert numpairs > 0 weight = weight / numpairs #total current leaving and entering equal 1 - functions = Vector{Shape{Float64}}(undef,numpairs) #note: not a Vector{Vector} + functions = Vector{Shape{T}}(undef,numpairs) #note: not a Vector{Vector} for i in 1:numpairs c1 = cps[1,i] e1 = cps[2,i] @@ -172,10 +173,11 @@ weight defines the magnitude of individual current in and out the half triangles and it's polarity simply defines whether to start with in or out """ function rt_vedge(cps::Array{Int,2}, weight) + T=typeof(weight) numpairs = size(cps,2) @assert numpairs > 0 #adjacent cells are considered a pair, so we have one less numpairs - functions = Vector{Vector{Shape{Float64}}}(undef,numpairs - 1) + functions = Vector{Vector{Shape{T}}}(undef,numpairs - 1) for i in 1:numpairs if i < numpairs # stop when on last cellpair c1 = cps[1,i]; e1 = cps[2,i] @@ -215,14 +217,14 @@ function rt_ports(Γ, γ...) port1 = portcells(Γ, γ[i][1]) port2 = portcells(Γ, γ[i][2]) - ce1 = rt_cedge(port1, +1.0) - ce2 = rt_cedge(port2, -1.0) + ce1 = rt_cedge(port1, T(+1.0)) + ce2 = rt_cedge(port2, T(-1.0)) ffs = Vector{Shape{T}}(undef,length(ce1) + length(ce2)) ffs = [ce1;ce2] - ve1 = rt_vedge(port1, +1.0) - ve2 = rt_vedge(port2, -1.0) + ve1 = rt_vedge(port1, T(+1.0)) + ve2 = rt_vedge(port2, T(-1.0)) fns = [fns;[ffs];ve1;ve2] end diff --git a/src/bases/subdbasis.jl b/src/bases/subdbasis.jl index 6c3cb8b3..bda73d31 100644 --- a/src/bases/subdbasis.jl +++ b/src/bases/subdbasis.jl @@ -28,13 +28,13 @@ function (rs::subReferenceSpace)(nbhd) return s #,scurl end -function subdsurface(mesh) +function subdsurface(mesh::Mesh{U,D1,T}) where {U,D1,T} subd_mesh = GSubdMesh(mesh) vertices = mesh.vertices subd_elements = subd_mesh.elements nvertices = length(vertices) nelem = length(subd_elements) - funs=Array{Vector{BEAST.Shape{Float64}}}(undef,nvertices) + funs=Array{Vector{BEAST.Shape{T}}}(undef,nvertices) for i = 1 : nvertices funs[i] = [] end for ie = 1:nelem inodes = subd_elements[ie].RingNodes @@ -42,7 +42,7 @@ function subdsurface(mesh) for ib = 1:N nodeid = inodes[ib] coeff = 1.0 - ifun = BEAST.Shape{Float64}(ie,ib,coeff) + ifun = BEAST.Shape{T}(ie,ib,coeff) push!(funs[nodeid], ifun) end end diff --git a/src/bases/timebasis.jl b/src/bases/timebasis.jl index 172c0630..dedd754e 100644 --- a/src/bases/timebasis.jl +++ b/src/bases/timebasis.jl @@ -140,7 +140,7 @@ end Create a temporal basis based on shifted copies of the quadratic spline. The spline is the convolution of a cxd0 and a c0d1 basis function. """ -function timebasisspline2(dt, numfunctions, T::Type=Float64) +function timebasisspline2(dt, numfunctions, T::Type=typeof(dt)) i, z = one(T), zero(T) polys = SVector{4,Polynomial{3,T}}( Polynomial(SVector(i/2, i/dt, i/2/dt/dt)), @@ -159,11 +159,11 @@ function timebasisshiftedlagrange(dt, numfunctions, degree, T::Type=typeof(dt)) for k = 0:degree f = c for i in 1:k - f = (1/i) * f * (i - t) + f = T(1/i) * f * (i - t) end g = c for i in 1:(degree-k) - g = (1/i) * g * (i + t) + g = T.(1/i) * g * (i + t) end push!(polys, f*g) end diff --git a/src/excitation.jl b/src/excitation.jl index 6e774370..dd1b4c87 100644 --- a/src/excitation.jl +++ b/src/excitation.jl @@ -11,9 +11,9 @@ quadrule(fn::Functional, refs, p, cell, qd) = qd[1,p] Assemble the vector of test coefficients corresponding to functional `fn` and test functions `tfs`. """ -function assemble(field::Functional, tfs; quaddata=quaddata, quadrule=quadrule) +function assemble(field::Functional, tfs::Space{T}; quaddata=quaddata, quadrule=quadrule) where T - b = zeros(ComplexF64, numfunctions(tfs)) + b = zeros(Complex{T}, numfunctions(tfs)) store(v,m) = (b[m] += v) assemble!(field, tfs, store, quaddata=quaddata, quadrule=quadrule) return b @@ -78,10 +78,10 @@ function assemble!(field::Functional, tfs::subdBasis, store; end -function celltestvalues(tshs, tcell, field, qr) +function celltestvalues(tshs::RefSpace{T,NF}, tcell, field, qr) where {T,NF} num_tshs = numfunctions(tshs) - interactions = zeros(ComplexF64, num_tshs) + interactions = zeros(Complex{T}, num_tshs) num_oqp = length(qr) @@ -104,11 +104,11 @@ function celltestvalues(tshs, tcell, field, qr) return interactions end -function celltestvalues(tshs::subReferenceSpace, tcell, field, qr) +function celltestvalues(tshs::subReferenceSpace{T,D}, tcell, field, qr) where {T,D} num_oqp = length(qr) num_tshs = length(qr[1].value[1]) - interactions = (ComplexF64, num_tshs) + interactions = (Complex{T}, num_tshs) for p in 1 : num_oqp mp = qr[p].point @@ -117,7 +117,7 @@ function celltestvalues(tshs::subReferenceSpace, tcell, field, qr) fval = field(mp) tvals = qr[p].value - interactions = zeros(ComplexF64, num_tshs) + interactions = zeros(Complex{T}, num_tshs) for m in 1 : num_tshs tval = tvals[1][m] diff --git a/src/helmholtz2d/helmholtzop.jl b/src/helmholtz2d/helmholtzop.jl index f61631bb..efd924d0 100644 --- a/src/helmholtz2d/helmholtzop.jl +++ b/src/helmholtz2d/helmholtzop.jl @@ -128,12 +128,12 @@ mutable struct PlaneWaveNeumann{T,P} <: Functional end mutable struct ScalarTrace{F} <: Functional - f::F + field::F end strace(f, mesh::Mesh) = ScalarTrace(f) -(s::ScalarTrace)(x) = s.f(cartesian(x)) +(s::ScalarTrace)(x) = s.field(cartesian(x)) integrand(s::ScalarTrace, tx, fx) = dot(tx.value, fx) shapevals(f::Functional, ϕ, ts) = shapevals(ValOnly(), ϕ, ts) diff --git a/src/identityop.jl b/src/identityop.jl index ad362fb5..dac6bbd6 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -62,10 +62,10 @@ end defaultquadstrat(::LocalOperator, ::LagrangeRefSpace{T,D1,2}, ::LagrangeRefSpace{T,D2,2}) where {T,D1,D2} = SingleNumQStrat(6) function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,2} where {T,Deg}, f::LagrangeRefSpace, tels::Vector, bels::Vector, qs::SingleNumQStrat) - + U = typeof(tels[1].volume) u, w = legendre(qs.quad_rule, 0.0, 1.0) qd = [(w[i],u[i]) for i in eachindex(w)] - A = _alloc_workspace(qd, g, f, tels, bels) + A = _alloc_workspace(Tuple{U,U}.(qd), g, f, tels, bels) return qd, A end diff --git a/src/interpolation.jl b/src/interpolation.jl index 16c05cc3..df8c5e92 100644 --- a/src/interpolation.jl +++ b/src/interpolation.jl @@ -243,8 +243,8 @@ end function Centervalues(mesh::Mesh,f::Functional) num_cells = numcells(mesh) - - res = Vector{SVector{3,Complex{Float64}}}(undef, num_cells) + T = coordtype(mesh) + res = Vector{SVector{3,Complex{T}}}(undef, num_cells) for i in 1:num_cells cell = cells(mesh)[i] @@ -264,11 +264,12 @@ end function EvalCenter(basis::Space, coeff) mesh = basis.geo + T = coordtype(mesh) num_cells = numcells(mesh) num_bfs = numfunctions(basis) ref_space = refspace(basis) - res = Vector{SVector{3,Complex{Float64}}}(undef, num_cells) + res = Vector{SVector{3,Complex{T}}}(undef, num_cells) for i in 1:num_cells res[i] = [0,0,0] diff --git a/src/localop.jl b/src/localop.jl index 6d4852e0..44f00a41 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -238,10 +238,10 @@ end For use when basis and test functions are defined on different meshes """ -function assemble_local_mixed!(biop::LocalOperator, tfs::Space, bfs::Space, store; - quadstrat=defaultquadstrat(biop, tfs, bfs)) +function assemble_local_mixed!(biop::LocalOperator, tfs::Space{T}, bfs::Space{T}, store; + quadstrat=defaultquadstrat(biop, tfs, bfs)) where {T} - tol = sqrt(eps(Float64)) + tol = sqrt(eps(T)) trefs = refspace(tfs) brefs = refspace(bfs) @@ -331,12 +331,12 @@ function cellinteractions_matched!(zlocal, biop, trefs, brefs, cell, qr) return zlocal end -function cellinteractions(biop, trefs, brefs, cell, qr) +function cellinteractions(biop, trefs::U, brefs::V, cell, qr) where {U<:RefSpace{T},V<:RefSpace{T}} where {T} num_tshs = length(qr[1][3]) num_bshs = length(qr[1][4]) - zlocal = zeros(Float64, num_tshs, num_bshs) + zlocal = zeros(T, num_tshs, num_bshs) for q in qr w, mp, tvals, bvals = q[1], q[2], q[3], q[4] diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 6370dd10..e4de374a 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -15,13 +15,14 @@ function kernelvals(biop::MaxwellOperator3D, p, q) γ = biop.gamma r = cartesian(p) - cartesian(q) + T = eltype(r) R = norm(r) γR = γ*R inv_R = 1/R expn = exp(-γR) - green = expn * inv_R * inv_4pi + green = expn * inv_R * T(inv_4pi) gradgreen = -(γ + inv_R) * green * inv_R * r KernelValsMaxwell3D(γ, r, R, green, gradgreen) @@ -33,12 +34,12 @@ function kernelvals(kernel::MaxwellOperator3DReg, p, q) r = p.cart - q.cart R = norm(r) γR = γ*R - + P=typeof(γ) Exp = exp(-γ*R) green = (Exp - 1 + γR - 0.5*γR^2) / (4pi*R) gradgreen = ( - (γR + 1)*Exp + (1 - 0.5*γR^2) ) * (r/R^3) / (4π) - KernelValsMaxwell3D(γ, r, R, green, gradgreen) + KernelValsMaxwell3D(γ, r, R, P(green), gradgreen) end struct MWSingleLayer3D{T,U} <: MaxwellOperator3D @@ -87,13 +88,14 @@ defaultquadstrat(op::MaxwellOperator3D, tfs::RefSpace, bfs::RefSpace) = DoubleNu function quaddata(op::MaxwellOperator3D, test_local_space::RefSpace, trial_local_space::RefSpace, test_charts, trial_charts, qs::DoubleNumWiltonSauterQStrat) - + T = coordtype(test_charts[1]) tqd = quadpoints(test_local_space, test_charts, (qs.outer_rule_far,qs.outer_rule_near)) bqd = quadpoints(trial_local_space, trial_charts, (qs.inner_rule_far,qs.inner_rule_near)) + leg = ( - _legendre(qs.sauter_schwab_common_vert,0,1), - _legendre(qs.sauter_schwab_common_edge,0,1), - _legendre(qs.sauter_schwab_common_face,0,1),) + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_vert,0,1)), + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_edge,0,1)), + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_face,0,1)),) # High accuracy rules (use them e.g. in LF MFIE scenarios) @@ -154,10 +156,10 @@ end # function qrss(op, g, f, i, τ, j, σ, qd) function quadrule(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd, qs::DoubleNumWiltonSauterQStrat) - + T = eltype(eltype(τ.vertices)) hits = 0 - dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) - dmin2 = floatmax(eltype(eltype(τ.vertices))) + dtol = 1.0e3 * eps(T) + dmin2 = floatmax(T) for t in τ.vertices for s in σ.vertices d2 = LinearAlgebra.norm_sqr(t-s) diff --git a/src/maxwell/sourcefield.jl b/src/maxwell/sourcefield.jl index 792e5d31..8a6222e9 100644 --- a/src/maxwell/sourcefield.jl +++ b/src/maxwell/sourcefield.jl @@ -1,5 +1,5 @@ struct SourceField{F} <: Functional - f::F + field::F end (s::SourceField)(p) = s.f(cartesian(p)) diff --git a/src/maxwell/wiltonints.jl b/src/maxwell/wiltonints.jl index daa2407c..ccb6fb0f 100644 --- a/src/maxwell/wiltonints.jl +++ b/src/maxwell/wiltonints.jl @@ -9,6 +9,7 @@ function innerintegrals!(op::MWSingleLayer3DSng, p, g, f, t, s, z, strat::WiltonSERule, dx) γ = op.gamma + T = typeof(γ) x = cartesian(p) n = cross(s[1]-s[3],s[2]-s[3]) n /= norm(n) @@ -51,6 +52,7 @@ end function innerintegrals!(op::MWDoubleLayer3DSng, p, g, f, t, s, z, strat::WiltonSERule, dx) γ = op.gamma + T=typeof(γ) x = cartesian(p) n = cross(s[1]-s[3],s[2]-s[3]) n /= norm(n) @@ -59,8 +61,7 @@ function innerintegrals!(op::MWDoubleLayer3DSng, p, g, f, t, s, z, strat::Wilton scal, vec, grad = WiltonInts84.wiltonints(s[1], s[2], s[3], x, Val{1}) # \int \nabla G_s with G_s = \nabla (1/R + 0.5*γ^2*R) / (4\pi) - ∫∇G = (-grad[1] - 0.5*γ^2*grad[3]) / (4π) - + ∫∇G = T.((-grad[1] - 0.5*γ^2*grad[3]) / (4π)) α = 1 / volume(t) / volume(s) / 4 for i in 1 : numfunctions(g) a = t[i] diff --git a/src/postproc.jl b/src/postproc.jl index b2aec6ab..aeec9c5d 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -129,7 +129,7 @@ end function potential(op, points, coeffs, basis; type=SVector{3,ComplexF64}) # T = SVector{3,ComplexF64} - T = type + T = SVector{3,eltype(coeffs)} ff = zeros(T, size(points)) store(v,m,n) = (ff[m] += v*coeffs[n]) potential!(store, op, points, basis, type=T) @@ -137,7 +137,7 @@ function potential(op, points, coeffs, basis; type=SVector{3,ComplexF64}) end function potential(op, points,coeffs, basis::SpaceTimeBasis) - T = SVector{3,ComplexF64} + T = SVector{3,eltype(coeffs)} ff = zeros(T, length(points), size(coeffs)[2]) store(v,m,n,k,o) = (ff[m,k] += v*coeffs[n,o]) potential!(store, op, points, basis) @@ -146,7 +146,7 @@ end function potential(op, points, coeffs, space::DirectProductSpace; type=SVector{3,ComplexF64}) # T = SVector{3,ComplexF64} - T = type + T = SVector{3,eltype(coeffs)} ff = zeros(T, size(points)) @show size(ff) @@ -216,7 +216,7 @@ function potential!(store, op, points, basis::SpaceTimeBasis) Nt = numfunctions(time_basis) Δt = timestep(time_basis) - T = SVector{3,ComplexF64} + T = SVector{3,Complex{eltype(eltype(points))}} refs = refspace(space_basis) trefs = refspace(time_basis) diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 0d6e8090..23d43381 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -114,10 +114,10 @@ assemble(dbf::DiscreteBilform) = assemble(dbf.bilform, dbf.test_space_dict, dbf. assemble(dlf::DiscreteLinform) = assemble(dlf.linform, dlf.test_space_dict) function assemble(lform::LinForm, test_space_dict) - + T=ComplexF64 terms = lform.terms - T = ComplexF64 - + #T = Complex{typeof(terms[1].functional.field.wavenumber)} + #T = Complex{eltype(vertextype(test_space_dict[1].geo))} # I = Int[1] blocksizes1 = Int[] for p in 1:length(lform.test_space) @@ -189,10 +189,10 @@ function td_assemble(lform::LinForm, test_space_dict) return B end -function assemble_hide(bilform::BilForm, test_space_dict, trial_space_dict) +function assemble_hide(bilform::BilForm, test_space_dict::Space{U}, trial_space_dict) where U lhterms = bilform.terms - T = ComplexF64 # TDOD: Fix this + T = Complex{U} # TDOD: Fix this blocksizes1 = Int[] for p in 1:length(bilform.test_space) diff --git a/src/utils/specialfns.jl b/src/utils/specialfns.jl index 2605f593..03e8436c 100644 --- a/src/utils/specialfns.jl +++ b/src/utils/specialfns.jl @@ -6,8 +6,7 @@ struct Gaussian{T} delay::T end -Gaussian(;scaling=1.0, width, delay) = Gaussian(scaling, width, delay) - +Gaussian(;scaling=1.0, width, delay) = Gaussian(typeof(width)(scaling), width, delay) (g::Gaussian)(s::Real) = 4*g.scaling/(g.width*√π) * exp(-(4*(s-g.delay)/g.width)^2) From b1fb016a6212e77d039fcbdcd89ffcb3a9cc5de3 Mon Sep 17 00:00:00 2001 From: Paula Respondek Date: Wed, 23 Mar 2022 13:10:14 +0100 Subject: [PATCH 158/528] Update unit tests Introduced for loops for testing Float32 and Float64. As a side effect tests are more isolated from each other by using the local modifier. --- test/test_assemblerow.jl | 13 +++++---- test/test_basis.jl | 47 +++++++++++++++++------------- test/test_bcspace.jl | 26 +++++++++-------- test/test_curlcurlgreen.jl | 26 +++++++++-------- test/test_dipole.jl | 55 ++++++++++++++++++----------------- test/test_directproduct.jl | 12 ++++---- test/test_fourier.jl | 11 +++---- test/test_gradient.jl | 13 +++++---- test/test_hh3dexc.jl | 24 ++++++++------- test/test_hh3dtd_exc.jl | 23 ++++++++------- test/test_local_storage.jl | 11 +++---- test/test_mixed_blkassm.jl | 38 +++++++++++++----------- test/test_mult.jl | 8 +++-- test/test_ndlcd_restrict.jl | 12 ++++---- test/test_ndspace.jl | 5 +++- test/test_raviartthomas.jl | 47 ++++++++++++++++-------------- test/test_restrict.jl | 30 +++++++++++-------- test/test_rt.jl | 24 ++++++++------- test/test_rt3d.jl | 38 ++++++++++++------------ test/test_rtx.jl | 8 +++-- test/test_sauterschwabints.jl | 43 ++++++++++++++------------- test/test_specials.jl | 6 ++-- test/test_subd_basis.jl | 11 +++---- test/test_tdassembly.jl | 4 +-- test/test_ttrace.jl | 40 +++++++++++++------------ test/test_wiltonints.jl | 1 + 26 files changed, 317 insertions(+), 259 deletions(-) diff --git a/test/test_assemblerow.jl b/test/test_assemblerow.jl index 1154fcf4..9830721e 100644 --- a/test/test_assemblerow.jl +++ b/test/test_assemblerow.jl @@ -2,9 +2,11 @@ using CompScienceMeshes, BEAST using Test fn = joinpath(dirname(@__FILE__),"assets","sphere35.in") -m = readmesh(fn) -t = Maxwell3D.singlelayer(wavenumber=1.0) -X = raviartthomas(m) + +for T in [Float32, Float64] +local m = readmesh(fn,T=T) +t = Maxwell3D.singlelayer(wavenumber=T(1.0)) +local X = raviartthomas(m) numfunctions(X) ## @@ -20,10 +22,10 @@ T2 = BEAST.assemblerow(t,X1,X) # @test T1 == T2 # T2 = BEAST.assembleblock(t,X,X) -@test T1≈T2 atol=1e-8 +@test T1≈T2 atol=sqrt(eps(T)) -I = [3,2,7] +local I = [3,2,7] X1 = subset(X,I) T2 = zeros(scalartype(t,X1,X1),numfunctions(X1),numfunctions(X1)) store(v,m,n) = (T2[m,n] += v) @@ -58,3 +60,4 @@ T4 = assemble(t,X,X) # @time blkasm(I,I) # @time assemble(t,X1,X1) +end \ No newline at end of file diff --git a/test/test_basis.jl b/test/test_basis.jl index e4ae3b82..08984df4 100644 --- a/test/test_basis.jl +++ b/test/test_basis.jl @@ -5,10 +5,10 @@ using CompScienceMeshes using BEAST ## The actual tests +for T in [Float32, Float64] +κ = ω = T(1.0) -κ = ω = 1.0 - -Γ = meshsegment(1.0, 0.5) +Γ = meshsegment(T(1.0), T(0.5)) X = lagrangec0d1(Γ) @test numvertices(Γ)-2 == numfunctions(X) @@ -29,7 +29,8 @@ doublelayer = DoubleLayer(κ) x1 = N \ e; # Testing duallagrangec0d1 -Γ1 = meshcircle(1.0, 2.5,3) # creating a triangle +if T == Float64 +Γ1 = meshcircle(T(1.0), T(2.5),3) # creating a triangle Γ2 = barycentric_refinement(Γ1) # creating the refined mesh X1 =duallagrangec0d1(Γ1,Γ2) # creating the basis functions @@ -37,17 +38,18 @@ X1 = duallagrangec0d1(Γ1, Γ2, x->false, Val{2}) @test numcells(Γ1) == numfunctions(X1) # making sure it is assigned according to the coarse mesh segments @test length(X1.fns[1])== 6 # making sure each segment represent 6 shapes inside it @test length(X1.fns[numfunctions(X1)])== 6 # making sure the last segment functions contains 6 shapes as well - +end +end ## Test linear Lagrange elements on triangles and the computation of their curl -T = Float64 +for T in [Float32, Float64] Degr = 1 Dim1 = 3 NumF = 3 f = BEAST.LagrangeRefSpace{T,Degr,Dim1,NumF}() -sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in")) +sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in"),T=T) s = chart(sphere, first(cells(sphere))) -t = neighborhood(s, [1,1]/3) +t = neighborhood(s, T.([1,1]/3)) v = f(t, Val{:withcurl}) A = volume(s) @@ -58,13 +60,15 @@ A = volume(s) @test v[1][1] ≈ 1/3 @test v[2][1] ≈ 1/3 @test v[3][1] ≈ 1/3 +end ## Test the construction of continuous linear Lagrange elements on 2D surfaces using CompScienceMeshes using BEAST using Test -m = meshrectangle(1.0, 1.0, 0.5, 3) +for T in [Float32, Float64] +m = meshrectangle(T(1.0), T(1.0), T(0.5), 3) X = lagrangec0d1(m) x = refspace(X) @@ -72,16 +76,16 @@ x = refspace(X) @test numfunctions(X) == 1 @test length(X.fns[1]) == 6 - +end ## test the scalar trace for Lagrange functions using CompScienceMeshes using BEAST using Test - -p1 = point(0,0,0) -p2 = point(1,0,0) -p3 = point(0,1,0) -p4 = point(1,1,1) +for T in [Float64] +p1 = point(T,0,0,0) +p2 = point(T,1,0,0) +p3 = point(T,0,1,0) +p4 = point(T,1,1,1) c1 = index(1,2,3) m = Mesh([p1,p2,p3,p4],[c1]) @@ -107,16 +111,17 @@ cell = chart(m, first(cells(m))) face = chart(b, first(cells(b))) Q = BEAST.strace(x, cell, 3, face) @test Q == [1 0 0; 0 1 0] - +end ## test Lagrange construction on Junctions using CompScienceMeshes using BEAST using Test -m1 = meshrectangle(1.0, 0.5, 0.5) -m2 = CompScienceMeshes.rotate(m1, 0.5π*[1,0,0]) -m3 = CompScienceMeshes.rotate(m1, 1.0π*[1,0,0]) +for T in [Float64] +m1 = meshrectangle(T(1.0), T(0.5), T(0.5)) +m2 = CompScienceMeshes.rotate(m1, T(0.5π)*[1,0,0]) +m3 = CompScienceMeshes.rotate(m1, T(1.0π)*[1,0,0]) m = weld(m1, m2, m3) X = lagrangec0d1(m) @@ -124,14 +129,14 @@ x = refspace(X) @test numfunctions(X) == 1 @test length(X.fns[1]) == 9 -p = point(0.5, 0.0, 0.0) +p = point(T, 0.5, 0.0, 0.0) for _s in X.fns[1] _cell = m.faces[_s.cellid] patch = chart(m, _cell) bary = carttobary(patch, p) mp = neighborhood(patch, bary) end - +end ## Test the dual pieweise constant lagrange elemetns using CompScienceMeshes using BEAST diff --git a/test/test_bcspace.jl b/test/test_bcspace.jl index db82ffca..252a063b 100644 --- a/test/test_bcspace.jl +++ b/test/test_bcspace.jl @@ -17,14 +17,14 @@ function isdivconforming(space) geo = geometry(space) mesh = geo - + T=eltype(vertextype(mesh)) edges = skeleton(mesh,1) D = connectivity(edges, mesh, abs) rows = rowvals(D) vals = nonzeros(D) - Flux = zeros(Float64, numcells(mesh),3) - TotalFlux = zeros(Float64, numcells(edges), numfunctions(space)) + Flux = zeros(T, numcells(mesh),3) + TotalFlux = zeros(T, numcells(edges), numfunctions(space)) for i in 1 : numfunctions(space) @@ -85,30 +85,31 @@ function interior(mesh::Mesh) end #meshfile = Pkg.dir("BEAST","test","sphere2.in") +for T in [Float32, Float64] meshfile = joinpath(dirname(@__FILE__),"assets","sphere316.in") -mesh = readmesh(meshfile) +mesh = readmesh(meshfile,T=T) @test numvertices(mesh) == 160 @test numcells(mesh) == 316 rt = raviartthomas(mesh) @test numfunctions(rt) == 316 * 3 / 2 -fine = barycentric_refinement(mesh) -edges = skeleton(mesh, 1) +local fine = barycentric_refinement(mesh) +local edges = skeleton(mesh, 1) bc = buffachristiansen(mesh) @test numfunctions(bc) == 316 * 3 / 2 lc = isdivconforming(rt) -@test maximum(lc) < eps(Float64) * 1000 +@test maximum(lc) < eps(T) * 1000 println("RT space is div-conforming") lc = isdivconforming(bc); -@test maximum(lc) < eps(Float64) * 1000 +@test maximum(lc) < eps(T) * 1000 println("BC space is div-conforming") # Now repeat the exercise with an open mesh -mesh = meshrectangle(1.0, 1.0, 0.2); +mesh = meshrectangle(T(1.0), T(1.0), T(0.2)); fine = barycentric_refinement(mesh); rt = raviartthomas(mesh) @@ -134,14 +135,14 @@ leaky_edges = findall(vec(sum(abs.(isdivconforming(bc)),dims=1)) .!= 0) ## Test the charge of BC functions #meshfile = Pkg.dir("BEAST","test","sphere2.in") meshfile = joinpath(dirname(@__FILE__),"assets","sphere316.in") -mesh = readmesh(meshfile) +mesh = readmesh(meshfile,T=T) bc = buffachristiansen(mesh) fine = geometry(bc) charges = zeros(numcells(fine)) for fn in bc.fns - abs_charge = 0.0 - net_charge = 0.0 + abs_charge = T(0.0) + net_charge = T(0.0) fill!(charges,0) for _sh in fn cellid = _sh.cellid @@ -154,6 +155,7 @@ for fn in bc.fns @test net_charge + 1 ≈ 1 @test abs_charge ≈ 2 end +end # THe BC construction function should throw for non-oriented surfaces # width, height, h = 1.0, 0.5, 0.05 diff --git a/test/test_curlcurlgreen.jl b/test/test_curlcurlgreen.jl index 01e099e3..206e21e6 100644 --- a/test/test_curlcurlgreen.jl +++ b/test/test_curlcurlgreen.jl @@ -3,18 +3,18 @@ using LinearAlgebra using BEAST using CompScienceMeshes using StaticArrays - -y = point(2,0,0) -j = ẑ -κ = 1.0 +for T in [Float32, Float64] +local y = point(T,2,0,0) +local j = ẑ +local κ = T(1.0) gj(x) = exp(-im*κ*norm(x-y))/(4*π*norm(x-y)) * j ccg = BEAST.CurlCurlGreen(κ, j, y) function curlh(f,x,h) - e1 = point(1,0,0) - e2 = point(0,1,0) - e3 = point(0,0,1) + e1 = point(T,1,0,0) + e2 = point(T,0,1,0) + e3 = point(T,0,0,1) d1f = (f(x+h*e1) - f(x-h*e1))/(2*h) d2f = (f(x+h*e2) - f(x-h*e2))/(2*h) @@ -29,16 +29,18 @@ end cgh(x) = curlh(gj,x,h) ccgh(x) = curlh(cgh,x,h) -h = 0.01 -x = point(1,1,1) +local h = T(0.01) +local x = point(T,1,1,1) a = ccg(x) -b = ccgh(x) +local b = ccgh(x) @show norm(x-y) -@test norm(a-b) < 1e-5 +T == Float64 ? atol=1e-5 : atol=1e-4 +@test norm(a-b) < atol cccg = curl(ccg) cccgh(x) = curlh(ccg,x,h) # @show a = cccg(x) # @show b = cccgh(x) -@test norm(a-b) < 1e-5 +@test norm(a-b) < atol +end \ No newline at end of file diff --git a/test/test_dipole.jl b/test/test_dipole.jl index 2d2a9456..4963e698 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -5,48 +5,50 @@ using BEAST using StaticArrays using LinearAlgebra -c = 3e8 -μ0 = 4*π*1e-7 -μr = 1.0 +for U in [Float32,Float64] + +c = U(3e8) +μ0 = U(4*π*1e-7) +μr = U(1.0) μ = μ0*μr -ε0 = 8.854187812e-12 -εr = 5 +ε0 = U(8.854187812e-12) +εr = U(5) ε = ε0*εr -c = 1/sqrt(ε*μ) -f = 5e7 +c = U(1)/sqrt(ε*μ) +local f = U(5e7) λ = c/f -k = 2*π/λ -ω = k*c +k = U(2*π/λ) +local ω = k*c η = sqrt(μ/ε) -a = 1 -Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,0.1) -Γ = translate(Γ_orig,SVector(-a/2,-a/2,-a/2)) +a = U(1) +Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,U(0.1)) +local Γ = translate(Γ_orig,SVector(U(-a/2),U(-a/2),U(-a/2))) -Φ, Θ = [0.0], range(0,stop=π,length=3) -pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] +Φ, Θ = U.([0.0]), range(U(0),stop=U(π),length=100) +pts = [point(U,cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] # This is an electric dipole # The pre-factor (1/ε) is used to resemble # (9.18) in Jackson's Classical Electrodynamics -E = (1/ε) * dipolemw3d(location=SVector(0.4,0.2,0), - orientation=1e-9.*SVector(0.5,0.5,0), +local E = U(1/ε) * dipolemw3d(location=SVector(U(0.4),U(0.2),U(0)), + orientation=U(1e-9).*SVector(U(0.5),U(0.5),U(0)), wavenumber=k) -n = BEAST.NormalVector() +local n = BEAST.NormalVector() 𝒆 = (n × E) × n -H = (-1/(im*μ*ω))*curl(E) +local H = (-1/(im*μ*ω))*curl(E) 𝒉 = (n × H) × n 𝓣 = Maxwell3D.singlelayer(wavenumber=k, alpha=-im*ω*μ, beta=-1/(im*ω*ε)) 𝓝 = BEAST.NCross() 𝓚 = Maxwell3D.doublelayer(wavenumber=k) -X = raviartthomas(Γ) -Y = buffachristiansen(Γ) +local X = raviartthomas(Γ) +local Y = buffachristiansen(Γ) -T = Matrix(assemble(𝓣,X,X)) +local T = Matrix(assemble(𝓣,X,X)) e = Vector(assemble(𝒆,X)) j_EFIE = T\e @@ -63,7 +65,7 @@ ff_H_EFIE = potential(BEAST.MWDoubleLayerFarField3D(𝓚), pts, j_EFIE, X) K_bc = Matrix(assemble(𝓚,Y,X)) G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) h_bc = Vector(assemble(𝒉,Y)) -M_bc = -0.5*G_nxbc_rt + K_bc +M_bc = -U(0.5)*G_nxbc_rt + K_bc j_BCMFIE = M_bc\h_bc nf_E_BCMFIE = potential(MWSingleLayerField3D(𝓣), pts, j_BCMFIE, X) @@ -74,8 +76,8 @@ ff_E_BCMFIE = potential(MWFarField3D(𝓣), pts, j_BCMFIE, X) @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 -H = dipolemw3d(location=SVector(0.0,0.0,0.3), - orientation=1e-9.*SVector(0.5,0.5,0), +H = dipolemw3d(location=SVector(U(0.0),U(0.0),U(0.3)), + orientation=U(1e-9).*SVector(U(0.5),U(0.5),U(0)), wavenumber=k) # This time, we do not specify alpha and beta @@ -104,7 +106,7 @@ ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X) K_bc = Matrix(assemble(𝓚,Y,X)) G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) h_bc = η*Vector(assemble(𝒉,Y)) -M_bc = -0.5*G_nxbc_rt + K_bc +M_bc = -U(0.5)*G_nxbc_rt + K_bc j_BCMFIE = M_bc\h_bc nf_E_BCMFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) @@ -114,4 +116,5 @@ ff_E_BCMFIE = potential(MWFarField3D(wavenumber=k), pts, j_BCMFIE, X) @test norm(j_BCMFIE - j_EFIE)/norm(j_EFIE) ≈ 0 atol=0.02 @test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 -@test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 \ No newline at end of file +@test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 +end \ No newline at end of file diff --git a/test/test_directproduct.jl b/test/test_directproduct.jl index f9c87e66..470eda12 100644 --- a/test/test_directproduct.jl +++ b/test/test_directproduct.jl @@ -3,15 +3,16 @@ using BEAST using Test -m1 = meshrectangle(1.0, 1.0, 0.5) -m2 = CompScienceMeshes.translate!(meshrectangle(1.0, 1.0, 0.5), point(0,0,1)) -m3 = CompScienceMeshes.translate!(meshrectangle(1.0, 1.0, 0.5), point(0,0,2)) +for U in [Float32, Float64] +m1 = meshrectangle(U(1.0), U(1.0), U(0.5)) +m2 = CompScienceMeshes.translate!(meshrectangle(U(1.0), U(1.0), U(0.5)), point(U,0,0,1)) +m3 = CompScienceMeshes.translate!(meshrectangle(U(1.0), U(1.0), U(0.5)), point(U,0,0,2)) X1 = raviartthomas(m1) X2 = raviartthomas(m2) X3 = raviartthomas(m3) -X = X1 × X2 × X3 +local X = X1 × X2 × X3 n1 = numfunctions(X1) n2 = numfunctions(X2) @@ -20,7 +21,8 @@ n3 = numfunctions(X3) @test numfunctions(X) == n1 + n2 + n3 nt = numfunctions(X) -T = MWSingleLayer3D(1.0) +T = MWSingleLayer3D(U(1.0)) t = assemble(T, X, X) @test size(t) == (nt,nt) +end \ No newline at end of file diff --git a/test/test_fourier.jl b/test/test_fourier.jl index 3700b301..dcb625c8 100644 --- a/test/test_fourier.jl +++ b/test/test_fourier.jl @@ -2,13 +2,14 @@ using BEAST using Test using LinearAlgebra -width = 4.0 -delay = 6.0 +for T in [Float32, Float64] +width = T(4.0) +delay = T(6.0) g = BEAST.creategaussian(width, delay) G = BEAST.fouriertransform(g) dx = width / 15 -x0 = 0.0 +x0 = T(0.0) x = x0 : dx : 3*delay n = length(x) y = g.(x) @@ -22,5 +23,5 @@ Z = G.(ω) # scatter!(ω, real(Z)) # plot!(ω, imag(Y)) # scatter!(ω, imag(Z)) - -@test norm(Y-Z) < 1.e-10 +@test norm(Y-Z) < sqrt(eps(T)) +end \ No newline at end of file diff --git a/test/test_gradient.jl b/test/test_gradient.jl index 58e2c6c3..892bada4 100644 --- a/test/test_gradient.jl +++ b/test/test_gradient.jl @@ -2,21 +2,22 @@ using CompScienceMeshes using BEAST using Test - -o, x, y, z = euclidianbasis(3) +for T in [Float32, Float64] +local o, x, y, z = euclidianbasis(3,T) tet = simplex(x,y,z,o) ctr = center(tet) -T = Float64 + iref = BEAST.LagrangeRefSpace{T,1,4,4}() oref = BEAST.NDLCCRefSpace{T}() ishp = BEAST.Shape{T}(1, 3, 1.0) output = BEAST.gradient(iref, ishp, tet) -oval = point(0,0,0) +global oval = point(T,0,0,0) for oshp in output - global oval += oref(ctr)[oshp.refid].value * oshp.coeff + oval += oref(ctr)[oshp.refid].value * oshp.coeff end -@test oval ≈ point(0,0,1) +@test oval ≈ point(T,0,0,1) +end \ No newline at end of file diff --git a/test/test_hh3dexc.jl b/test/test_hh3dexc.jl index 0f04466a..3b7119ea 100644 --- a/test/test_hh3dexc.jl +++ b/test/test_hh3dexc.jl @@ -2,15 +2,16 @@ using CompScienceMeshes using BEAST using Test -sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in")) +for T in [Float32, Float64] +sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in"), T=T) numcells(sphere) -κ = 2π -direction = point(0,0,1) -f = BEAST.HH3DPlaneWave(direction, κ) +local κ = T(2π) +direction = point(T,0,0,1) +local f = BEAST.HH3DPlaneWave(direction, κ) -v1 = f(point(0,0,0)) -v2 = f(point(0,0,0.5)) +v1 = f(point(T,0,0,0)) +v2 = f(point(T,0,0,0.5)) @test v1 ≈ +1 @test v2 ≈ -1 @@ -18,22 +19,23 @@ v2 = f(point(0,0,0.5)) import BEAST.∂n p = ∂n(f) -s = chart(m,first(cells(m))) -c = neighborhood(s, [1,1]/3) +local s = chart(sphere,first(cells(sphere))) +local c = neighborhood(s, T.([1,1]/3)) r = cartesian(c) -n = normal(s) +local n = normal(s) w1 = p(c) w2 = -im*κ*dot(direction, n)*f(r) w1 ≈ w2 -N = BEAST.HH3DHyperSingularFDBIO(im*κ) -X = BEAST.lagrangec0d1(sphere) +local N = BEAST.HH3DHyperSingularFDBIO(im*κ) +local X = BEAST.lagrangec0d1(sphere) numfunctions(X) Nxx = assemble(N, X, X) @test size(Nxx) == (numfunctions(X), numfunctions(X)) +end \ No newline at end of file diff --git a/test/test_hh3dtd_exc.jl b/test/test_hh3dtd_exc.jl index 2b25f070..cda4f1a6 100644 --- a/test/test_hh3dtd_exc.jl +++ b/test/test_hh3dtd_exc.jl @@ -1,19 +1,19 @@ using BEAST using CompScienceMeshes using Test - -dir = point(0,0,1) -width = 1.0 -delay = 1.5 -scaling = 1.0 +for T in [Float32, Float64] +dir = point(T,0,0,1) +local width = T(1.0) +delay = T(1.5) +scaling = T(1.0) sig = creategaussian(width, delay, scaling) -pw = BEAST.planewave(dir, 1.0, sig) +pw = BEAST.planewave(dir, T(1.0), sig) CompScienceMeshes.cartesian(p::typeof(dir)) = p CompScienceMeshes.cartesian(p::Number) = p -x = point(1,0,0) -t = 1.0 +local x = point(T,1,0,0) +local t = T(1.0) @test pw(x,t) ≈ sig(t-dot(dir,x)) pw2 = BEAST.gradient(pw) @@ -26,9 +26,10 @@ dsig = derive(sig) trc = dot(BEAST.n,pw2) ch = simplex( - point(0,0,0), - point(1,0,0), - point(0,1,0)) + point(T,0,0,0), + point(T,1,0,0), + point(T,0,1,0)) ctr = center(ch) val = trc(ctr,t) @test val ≈ -dsig(t-dot(dir,x)) +end \ No newline at end of file diff --git a/test/test_local_storage.jl b/test/test_local_storage.jl index 980baab3..7edfdd55 100644 --- a/test/test_local_storage.jl +++ b/test/test_local_storage.jl @@ -2,12 +2,12 @@ using Test using BEAST using CompScienceMeshes using SparseArrays - -fn = joinpath(@__DIR__, "assets/sphere5.in") -m = readmesh(fn) +for T in [Float32, Float64] +local fn = joinpath(@__DIR__, "assets/sphere5.in") +local m = readmesh(fn, T=T) Id = BEAST.Identity() -X = BEAST.raviartthomas(m) +local X = BEAST.raviartthomas(m) Z1 = assemble(Id, X, X, storage_policy=Val{:densestorage}) Z2 = assemble(Id, X, X, storage_policy=Val{:bandedstorage}) @@ -18,4 +18,5 @@ Z3 = assemble(Id, X, X, storage_policy=Val{:sparsedicts}) @test Z3 isa SparseMatrixCSC @test Z1 ≈ Z2 atol=1e-8 -@test Z1 ≈ Z3 atol=1e-8 \ No newline at end of file +@test Z1 ≈ Z3 atol=1e-8 +end \ No newline at end of file diff --git a/test/test_mixed_blkassm.jl b/test/test_mixed_blkassm.jl index a251a747..005cff9e 100644 --- a/test/test_mixed_blkassm.jl +++ b/test/test_mixed_blkassm.jl @@ -24,35 +24,36 @@ function hassemble(operator::BEAST.AbstractOperator, return mat end -c = 3e8 -μ = 4π * 1e-7 -ε = 1/(μ*c^2) -f = 1e8 -λ = c/f -k = 2π/λ -ω = k*c -η = sqrt(μ/ε) - -a = 1 -Γ = CompScienceMeshes.meshcuboid(a,a,a,0.2) +for T in [Float32, Float64] +c = T(3e8) +μ = T(4π * 1e-7) +ε = T(1/(μ*c^2)) +local f = T(1e8) +λ = T(c/f) +k = T(2π/λ) +local ω = T(k*c) +η = T(sqrt(μ/ε)) + +a = T(1) +local Γ = CompScienceMeshes.meshcuboid(a,a,a,T(0.2)) 𝓣 = Maxwell3D.singlelayer(wavenumber=k) 𝓚 = Maxwell3D.doublelayer(wavenumber=k) -X = raviartthomas(Γ) -Y = buffachristiansen(Γ) +local X = raviartthomas(Γ) +local Y = buffachristiansen(Γ) println("Number of RWG functions: ", numfunctions(X)) T_blockassembler = hassemble(𝓣, X, X) T_standardassembler = assemble(𝓣, X, X) -@test norm(T_blockassembler - T_standardassembler)/norm(T_standardassembler) ≈ 0.0 atol=1e-14 +@test norm(T_blockassembler - T_standardassembler)/norm(T_standardassembler) ≈ 0.0 atol=100*eps(T) T_bc_blockassembler = hassemble(𝓣, Y, Y) T_bc_standardassembler = assemble(𝓣, Y, Y) -@test norm(T_bc_blockassembler - T_bc_standardassembler)/norm(T_bc_standardassembler) ≈ 0.0 atol=1e-14 +@test norm(T_bc_blockassembler - T_bc_standardassembler)/norm(T_bc_standardassembler) ≈ 0.0 atol=100*eps(T) K_mix_blockassembler = hassemble(𝓚,Y,X) K_mix_standardassembler = assemble(𝓚,Y,X) @@ -60,5 +61,8 @@ K_mix_standardassembler = assemble(𝓚,Y,X) T_mix_blockassembler = hassemble(𝓣, Y, X) T_mix_standardassembler = assemble(𝓣, Y, X) -@test norm(K_mix_blockassembler - K_mix_standardassembler)/norm(K_mix_standardassembler) ≈ 0.0 atol=1e-14 -@test norm(T_mix_blockassembler - T_mix_standardassembler)/norm(T_mix_standardassembler) ≈ 0.0 atol=1e-14 \ No newline at end of file +if T==Float64 +@test norm(K_mix_blockassembler - K_mix_standardassembler)/norm(K_mix_standardassembler) ≈ 0.0 atol=100*eps(T) +@test norm(T_mix_blockassembler - T_mix_standardassembler)/norm(T_mix_standardassembler) ≈ 0.0 atol=100*eps(T) +end +end \ No newline at end of file diff --git a/test/test_mult.jl b/test/test_mult.jl index d8b2081d..537d0e9f 100644 --- a/test/test_mult.jl +++ b/test/test_mult.jl @@ -4,9 +4,10 @@ using BEAST using Test using LinearAlgebra -faces = meshrectangle(1.0, 1.0, 0.5, 3) +for T in [Float32, Float64] +faces = meshrectangle(T(1.0), T(1.0), T(0.5), 3) srt_bnd_faces = sort.(boundary(faces)) -edges = submesh(skeleton(faces,1)) do edge +local edges = submesh(skeleton(faces,1)) do edge !(sort(edge) in srt_bnd_faces) end @@ -19,7 +20,7 @@ end Conn = connectivity(nodes, edges, sign) -X = raviartthomas(faces, cellpairs(faces,edges)) +local X = raviartthomas(faces, cellpairs(faces,edges)) @test numfunctions(X) == 8 divX = divergence(X) @@ -30,3 +31,4 @@ L = divX * Conn for sh in L.fns[1] @test isapprox(sh.coeff, 0, atol=1e-8) end +end \ No newline at end of file diff --git a/test/test_ndlcd_restrict.jl b/test/test_ndlcd_restrict.jl index 19f88e34..b2e0495c 100644 --- a/test/test_ndlcd_restrict.jl +++ b/test/test_ndlcd_restrict.jl @@ -3,16 +3,17 @@ using BEAST using Test using LinearAlgebra -o, x, y, z = CompScienceMeshes.euclidianbasis(3) +for T in [Float32, Float64] +local o, x, y, z = CompScienceMeshes.euclidianbasis(3,T) tet = simplex(x,y,z,o) -rs = BEAST.NDLCDRefSpace{Float64}() +rs = BEAST.NDLCDRefSpace{T}() Q = BEAST.restrict(rs, tet, tet) -@test Q ≈ Matrix(1.0LinearAlgebra.I, 4, 4) +@test Q ≈ Matrix(T(1.0)LinearAlgebra.I, 4, 4) -rs = BEAST.NDLCCRefSpace{Float64}() +rs = BEAST.NDLCCRefSpace{T}() Q = BEAST.restrict(rs, tet, tet) -@test Q ≈ Matrix(1.0LinearAlgebra.I, 6, 6) +@test Q ≈ Matrix(T(1.0)LinearAlgebra.I, 6, 6) c = cartesian(center(tet)) smalltet = simplex(x,y,z,c) @@ -34,3 +35,4 @@ for j in axes(Q,1) @show y @test x ≈ y end +end \ No newline at end of file diff --git a/test/test_ndspace.jl b/test/test_ndspace.jl index 64cbf85e..6b8a304e 100644 --- a/test/test_ndspace.jl +++ b/test/test_ndspace.jl @@ -2,9 +2,11 @@ using CompScienceMeshes using BEAST using Test + +for T in [Float32, Float64] fn = joinpath(dirname(@__FILE__),"assets","rect1.in") -mesh = readmesh(fn) +mesh = readmesh(fn, T=T) edgs = skeleton(mesh,1) charts = [chart(edgs,edg) for edg in cells(edgs)] @@ -32,3 +34,4 @@ fn = ND.fns[3] v1 = dot(fn[1].coeff*ndlocal(nbd1)[1][1],ut) v2 = dot(fn[2].coeff*ndlocal(nbd2)[2][1],ut) @test v1 ≈ v2 ≈ -1/√2 ≈ -1/volume(charts[3]) +end \ No newline at end of file diff --git a/test/test_raviartthomas.jl b/test/test_raviartthomas.jl index 32eacb5d..ac1acd54 100644 --- a/test/test_raviartthomas.jl +++ b/test/test_raviartthomas.jl @@ -2,10 +2,30 @@ using Test using BEAST using CompScienceMeshes -T = Float64 +function neighbortest(X) + Γ = X.geo + for (e,f) in enumerate(X.fns) + i = Γ.faces[f[1].cellid] + j = Γ.faces[f[2].cellid] + ij = intersect(i,j) + @assert length(ij) == 2 + ri = f[1].refid + rj = f[2].refid + #@assert !(i[ri] in ij) + if (j[rj] in ij) + @show e + @show f + @show i, ri + @show j, rj + @assert false + end + end +end + +for T in [Float32,Float64] tol = eps(T) * 10^3 -mesh = meshrectangle(1.0, 1.0, 0.5) +mesh = meshrectangle(T(1.0), T(1.0), T(0.5)) @test numvertices(mesh) == 9 idcs = mesh.faces[1] @@ -18,7 +38,7 @@ faces = skeleton(mesh, 2) idcs = faces.faces[1] verts = vertices(mesh, idcs) p = simplex(verts, Val{2}) -@test volume(p) == 1/8 +@test volume(p) == T(1/8) edges = skeleton(mesh,1) @test numcells(edges) == 16 @@ -35,24 +55,7 @@ cps = cps[:,I] rt = raviartthomas(mesh, cps) @test numfunctions(rt) == 8 -function neighbortest(X) - Γ = X.geo - for (e,f) in enumerate(X.fns) - i = Γ.faces[f[1].cellid] - j = Γ.faces[f[2].cellid] - ij = intersect(i,j) - @assert length(ij) == 2 - ri = f[1].refid - rj = f[2].refid - #@assert !(i[ri] in ij) - if (j[rj] in ij) - @show e - @show f - @show i, ri - @show j, rj - @assert false - end - end -end + neighbortest(rt) +end \ No newline at end of file diff --git a/test/test_restrict.jl b/test/test_restrict.jl index 224d14bd..96c4757b 100644 --- a/test/test_restrict.jl +++ b/test/test_restrict.jl @@ -8,24 +8,27 @@ const e1 = point(1.0,0.0,0.0) const e2 = point(0.0,1.0,0.0) const e3 = point(0.0,0.0,1.0) +for T in [Float32, Float64] + + p = simplex( [ - point(0.0,0.0,0.0), - point(1.0,0.0,0.0) + point(T,0.0,0.0,0.0), + point(T,1.0,0.0,0.0) ], Val{1} ) q = simplex( [ - point(0.0,0.0,0.0), - point(0.5,0.0,0.0) + point(T,0.0,0.0,0.0), + point(T,0.5,0.0,0.0) ], Val{1} ) -f = BEAST.LagrangeRefSpace{Float64,1,2,2}() -x = neighborhood(p, [0.0]) +f = BEAST.LagrangeRefSpace{T,1,2,2}() +local x = neighborhood(p, T.([0.0])) v = f(x) @test v[1].value == 0 @@ -36,8 +39,8 @@ v = f(x) Q = restrict(f, p, q) @test Q == [ - 1.0 0.5 - 0.0 0.5] + T(1.0) T(0.5) + T(0.0) T(0.5)] # Test restriction of RT elements @@ -48,17 +51,20 @@ wi = triangleGaussW[ni]; wo = triangleGaussW[no]; # universe = Universe(1.0, ui, wi, uo, wo); -p = simplex([2*e0,2*e1,2*e2], Val{2}) -x = neighborhood(p,[0.5, 0.5]) -ϕ = BEAST.RTRefSpace{Float64}() +p = simplex([T.(2*e0),T.(2*e1),T.(2*e2)], Val{2}) +x = neighborhood(p,T.([0.5, 0.5])) +ϕ = BEAST.RTRefSpace{T}() v = ϕ(x) Q = restrict(ϕ, p, p) +if T==Float64 @test Q == Matrix(LinearAlgebra.I, 3, 3) -q = simplex([e0+e1, 2*e1, e1+e2], Val{2}) +q = simplex([T.(e0+e1), T.(2*e1), T.(e1+e2)], Val{2}) Q = restrict(ϕ, p, q) @test Q == [ 2 -1 0 0 1 0 0 -1 2] // 4 +end +end \ No newline at end of file diff --git a/test/test_rt.jl b/test/test_rt.jl index aa0ac4ab..cc3e7138 100644 --- a/test/test_rt.jl +++ b/test/test_rt.jl @@ -4,7 +4,8 @@ using BEAST using Test using StaticArrays -T = Float64 +const third = one(Float64)/3 +for T in [Float32, Float64] P = SVector{3,T} tol = eps(T) * 10^3 @@ -26,8 +27,8 @@ function shapevals(ϕ, ts) return y end -const third = one(T)/3 -m = meshrectangle(1.0,1.0,0.5); + +local m = meshrectangle(T(1.0),T(1.0),T(0.5)); rwg = raviartthomas(m) ref = refspace(rwg) @@ -35,7 +36,7 @@ ref = refspace(rwg) # ptch = simplex(vrts[1], vrts[2], vrts[3]) ptch = chart(m, first(cells(m))) -ctrd = neighborhood(ptch, [1,1]/3) +ctrd = neighborhood(ptch, T.([1,1]/3)) vals = shapevals(ref, [ctrd]) # test edge detection @@ -44,15 +45,15 @@ vp = cellpairs(m,edges) # test function evaluation v = [ - point(0.0, 0.0, 0.0), - point(2.0, 0.0, 0.0), - point(0.0, 3.0, 0.0)] + point(T, 0.0, 0.0, 0.0), + point(T, 2.0, 0.0, 0.0), + point(T, 0.0, 3.0, 0.0)] cell = simplex(v[1], v[2], v[3]) -mp = neighborhood(cell,[third, third]); -ϕ = BEAST.RTRefSpace{Float64}() +mp = neighborhood(cell,[T(third), T(third)]); +ϕ = BEAST.RTRefSpace{T}() fx = ϕ(mp)[2][1] A = volume(cell) @@ -65,10 +66,10 @@ gx = (r - v[2]) / 2A @test norm((r - v[3])/2A - ϕ(mp)[3][1]) < tol # repeat the test but now on a random cell -randpoint() = point(2*rand()-1, 2*rand()-1, 2*rand()-1) +randpoint() = point(2*rand(T)-1, 2*rand(T)-1, 2*rand(T)-1) v = [randpoint(), randpoint(), randpoint()] cell = simplex(v[1], v[2], v[3]) -mp = neighborhood(cell,[third, third]); +mp = neighborhood(cell,[T(third), T(third)]); fx = ϕ(mp)[2][1] #fx = evalfun(rs, mp) @@ -81,3 +82,4 @@ gx = (r-v[2]) / 2A @test norm((r - v[1])/2A - ϕ(mp)[1][1]) < tol @test norm((r - v[2])/2A - ϕ(mp)[2][1]) < tol @test norm((r - v[3])/2A - ϕ(mp)[3][1]) < tol +end \ No newline at end of file diff --git a/test/test_rt3d.jl b/test/test_rt3d.jl index ba78a4c3..7e0bc9cc 100644 --- a/test/test_rt3d.jl +++ b/test/test_rt3d.jl @@ -3,15 +3,16 @@ using BEAST using Test -o, x, y, z = euclidianbasis(3) +for T in [Float32, Float64] +local o, x, y, z = euclidianbasis(3,T) tet = simplex(x,y,z,o) -nbd1 = neighborhood(tet, [0,1,1]/3) -nbd2 = neighborhood(tet, [1,0,1]/3) -nbd3 = neighborhood(tet, [1,1,0]/3) -nbd4 = neighborhood(tet, [1,1,1]/3) +nbd1 = neighborhood(tet, T.([0,1,1]/3)) +nbd2 = neighborhood(tet, T.([1,0,1]/3)) +nbd3 = neighborhood(tet, T.([1,1,0]/3)) +nbd4 = neighborhood(tet, T.([1,1,1]/3)) -rs = BEAST.NDLCDRefSpace{Float64}() +rs = BEAST.NDLCDRefSpace{T}() fcs = BEAST.faces(tet) @test dot(rs(nbd1)[1].value, normal(fcs[1])) > 0 @test dot(rs(nbd2)[2].value, normal(fcs[2])) > 0 @@ -23,21 +24,21 @@ dot(rs(nbd2)[2].value, normal(fcs[2])) * volume(fcs[2]) ≈ 1 dot(rs(nbd3)[3].value, normal(fcs[3])) * volume(fcs[2]) ≈ 1 dot(rs(nbd4)[4].value, normal(fcs[4])) * volume(fcs[4]) ≈ 1 -@test dot(rs(nbd1)[2].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=1e-8 -@test dot(rs(nbd1)[3].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=1e-8 -@test dot(rs(nbd1)[4].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=1e-8 +@test dot(rs(nbd1)[2].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=eps(T) +@test dot(rs(nbd1)[3].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=eps(T) +@test dot(rs(nbd1)[4].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=eps(T) -@test dot(rs(nbd2)[1].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=1e-8 -@test dot(rs(nbd2)[3].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=1e-8 -@test dot(rs(nbd2)[4].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=1e-8 +@test dot(rs(nbd2)[1].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=eps(T) +@test dot(rs(nbd2)[3].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=eps(T) +@test dot(rs(nbd2)[4].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=eps(T) -@test dot(rs(nbd3)[1].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=1e-8 -@test dot(rs(nbd3)[2].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=1e-8 -@test dot(rs(nbd3)[4].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=1e-8 +@test dot(rs(nbd3)[1].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=eps(T) +@test dot(rs(nbd3)[2].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=eps(T) +@test dot(rs(nbd3)[4].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=eps(T) -@test dot(rs(nbd4)[1].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=1e-8 -@test dot(rs(nbd4)[2].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=1e-8 -@test dot(rs(nbd4)[3].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=1e-8 +@test dot(rs(nbd4)[1].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=eps(T) +@test dot(rs(nbd4)[2].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=eps(T) +@test dot(rs(nbd4)[3].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=eps(T) ctr = cartesian(center(tet)) @@ -45,3 +46,4 @@ ctr = cartesian(center(tet)) @test dot(cartesian(nbd2)-ctr, normal(fcs[2])) > 0 @test dot(cartesian(nbd3)-ctr, normal(fcs[3])) > 0 @test dot(cartesian(nbd4)-ctr, normal(fcs[4])) > 0 +end \ No newline at end of file diff --git a/test/test_rtx.jl b/test/test_rtx.jl index d9cb4a12..c9af6e88 100644 --- a/test/test_rtx.jl +++ b/test/test_rtx.jl @@ -2,9 +2,10 @@ using CompScienceMeshes using BEAST using Test -m = meshrectangle(1.0, 1.0, 0.25) +for T in [Float32, Float64] +local m = meshrectangle(T(1.0), T(1.0), T(0.25)) -X = raviartthomas(m, BEAST.Continuity{:none}) +local X = raviartthomas(m, BEAST.Continuity{:none}) @test numfunctions(X) == 16*2*3 @test all(length.(X.fns) .== 1) @@ -17,5 +18,6 @@ ctr = cartesian(center(ch)) @test ctr ≈ p[i] for (i,f) in enumerate(X.fns) - @test f[1].coeff == 1.0 + @test f[1].coeff == T(1.0) end +end \ No newline at end of file diff --git a/test/test_sauterschwabints.jl b/test/test_sauterschwabints.jl index e026c85e..ef5d190a 100644 --- a/test/test_sauterschwabints.jl +++ b/test/test_sauterschwabints.jl @@ -3,14 +3,16 @@ using LinearAlgebra using BEAST, CompScienceMeshes, SauterSchwabQuadrature, StaticArrays +#for T in [Float64] +T=Float64 t1 = simplex( - @SVector[0.180878, -0.941848, -0.283207], - @SVector[0.0, -0.980785, -0.19509], - @SVector[0.0, -0.92388, -0.382683]) + T.(@SVector [0.180878, -0.941848, -0.283207]), + T.(@SVector [0.0, -0.980785, -0.19509]), + T.(@SVector [0.0, -0.92388, -0.382683])) t2 = simplex( - @SVector[0.373086, -0.881524, -0.289348], - @SVector[0.180878, -0.941848, -0.283207], - @SVector[0.294908, -0.944921, -0.141962]) + T.(@SVector [0.373086, -0.881524, -0.289348]), + T.(@SVector [0.180878, -0.941848, -0.283207]), + T.(@SVector [0.294908, -0.944921, -0.141962])) # s1 = simplex( @@ -23,13 +25,13 @@ t2 = simplex( # @SVector[0.294908, -0.944921, -0.141962]) # Common face case: -rt = BEAST.RTRefSpace{Float64}() +rt = BEAST.RTRefSpace{T}() tqd = BEAST.quadpoints(rt, [t1,t2], (12,)) bqd = BEAST.quadpoints(rt, [t1,t2], (13,)) -op1 = BEAST.MWSingleLayer3D(1.0, 250.0, 1.0) -op2 = BEAST.MWSingleLayer3D(1.0) -op3 = BEAST.MWDoubleLayer3D(0.0) +op1 = BEAST.MWSingleLayer3D(T(1.0), T(250.0), T(1.0)) +op2 = BEAST.MWSingleLayer3D(T(1.0)) +op3 = BEAST.MWDoubleLayer3D(T(0.0)) SE_strategy = BEAST.WiltonSERule( tqd[1,1], @@ -38,7 +40,7 @@ SE_strategy = BEAST.WiltonSERule( bqd[1,1], ), ) -SS_strategy = SauterSchwabQuadrature.CommonFace(BEAST._legendre(8,0.0,1.0)) +SS_strategy = SauterSchwabQuadrature.CommonFace(BEAST._legendre(8,T(0.0),T(1.0))) z_se = zeros(3,3) z_ss = zeros(3,3) @@ -62,7 +64,7 @@ SE_strategy = BEAST.WiltonSERule( BEAST.DoubleQuadRule( tqd[1,1], bqd[1,2])) -SS_strategy = SauterSchwabQuadrature.CommonVertex(BEAST._legendre(12,0.0,1.0)) +SS_strategy = SauterSchwabQuadrature.CommonVertex(BEAST._legendre(12,T(0.0),T(1.0))) z_cv_se_1 = zeros(3,3) z_cv_ss_1 = zeros(3,3) @@ -90,14 +92,14 @@ BEAST.momintegrals!(op3, rt, rt, t1, t2, z_cv_ss_3, SS_strategy) ## Common Edge Case: t1 = simplex( - @SVector[0.180878, -0.941848, -0.283207], - @SVector[0.0, -0.980785, -0.19509], - @SVector[0.0, -0.92388, -0.382683], + T.(@SVector [0.180878, -0.941848, -0.283207]), + T.(@SVector [0.0, -0.980785, -0.19509]), + T.(@SVector [0.0, -0.92388, -0.382683]) ) t2 = simplex( - @SVector[0.180878, -0.941848, -0.283207], - @SVector[0.158174, -0.881178, -0.44554], - @SVector[0.0, -0.92388, -0.382683], + T.(@SVector [0.180878, -0.941848, -0.283207]), + T.(@SVector [0.158174, -0.881178, -0.44554]), + T.(@SVector [0.0, -0.92388, -0.382683]) ) tqd = BEAST.quadpoints(rt, [t1,t2], (12,)) @@ -108,7 +110,7 @@ SE_strategy = BEAST.WiltonSERule( BEAST.DoubleQuadRule( tqd[1,1], bqd[1,2])) -SS_strategy = SauterSchwabQuadrature.CommonEdge(BEAST._legendre(12,0.0,1.0)) +SS_strategy = SauterSchwabQuadrature.CommonEdge(BEAST._legendre(12,T(0.0),T(1.0))) z_ce_se_1 = zeros(3,3) z_ce_ss_1 = zeros(3,3) @@ -134,8 +136,9 @@ BEAST.momintegrals!(op3, rt, rt, t1, t2, z_ce_ss_3, SS_strategy) @show norm(z_ce_se_3 - z_ce_ss_3) @test z_ce_se_3 ≈ z_ce_ss_3 atol=1e-5 -SS_strategy = SauterSchwabQuadrature.CommonEdge(BEAST._legendre(18,0.0,1.0)) +SS_strategy = SauterSchwabQuadrature.CommonEdge(BEAST._legendre(18,T(0.0),T(1.0))) z_ce_ss_3_18 = zeros(3,3) BEAST.momintegrals!(op3, rt, rt, t1, t2, z_ce_ss_3_18, SS_strategy) @show norm(z_ce_ss_3 - z_ce_ss_3_18) @test z_ce_ss_3 ≈ z_ce_ss_3_18 atol=1e-14 +#end \ No newline at end of file diff --git a/test/test_specials.jl b/test/test_specials.jl index 7f9637b3..d4f817f1 100644 --- a/test/test_specials.jl +++ b/test/test_specials.jl @@ -1,6 +1,7 @@ using BEAST -width, delay = 4.0, 6.0 +for T in [Float32, Float64] +width, delay = T(4.0), T(6.0) f = BEAST.Gaussian(width=width, delay=delay) g = BEAST.integrate(f) @@ -12,4 +13,5 @@ y1 = g.(x) y2 = cumsum(f.(x))*step using LinearAlgebra -@assert norm(y1-y2, Inf) < 1e-2 +@assert norm(y1-y2, Inf) < T(1e-2) +end \ No newline at end of file diff --git a/test/test_subd_basis.jl b/test/test_subd_basis.jl index ef54ffa2..33140011 100644 --- a/test/test_subd_basis.jl +++ b/test/test_subd_basis.jl @@ -18,20 +18,21 @@ function CompScienceMeshes.chart(Smesh::CompScienceMeshes.subdMesh,E) return chart = CompScienceMeshes.subd_chart(E,N,nodes,verticecoords) end - -G = readmesh(joinpath(dirname(@__FILE__),"assets","sphere872.in")) +for T in [Float32, Float64] +G = readmesh(joinpath(dirname(@__FILE__),"assets","sphere872.in"),T=T) # G0 = readmesh(fn) # G0 = meshsphere(1.0, 0.5) #G = readmesh("/Users/Benjamin/Documents/sphere.in") # G = Loop_subdivision(G0) -X = subdsurface(G) +local X = subdsurface(G) #els, ad = BEAST.assemblydata(X) identityop = Identity() -singlelayer = Helmholtz3D.singlelayer(gamma=1.0) +singlelayer = Helmholtz3D.singlelayer(gamma=T(1.0)) I = assemble(identityop, X, X) #S = assemble(singlelayer, X, X) ncd = cond(Matrix(I)) -@test ncd ≈ 64.50401358713235 rtol=1e-8 +@test ncd ≈ T(64.50401358713235) rtol=sqrt(eps(T)) +end \ No newline at end of file diff --git a/test/test_tdassembly.jl b/test/test_tdassembly.jl index 8baf9214..69f54877 100644 --- a/test/test_tdassembly.jl +++ b/test/test_tdassembly.jl @@ -67,7 +67,7 @@ end end end end end end # Compare results for a single monomial z1 = zeros(numfunctions(x1), numfunctions(x2), numfunctions(q)) for r in BEAST.rings(τ1, τ2, ΔR) - ι = BEAST.ring(r, ΔR) + local ι = BEAST.ring(r, ΔR) momintegrals!(z1, G, x1, x2, q, τ1, τ2, ι, DoubleQuadTimeDomainRule()) end @@ -75,7 +75,7 @@ qs = BEAST.defaultquadstrat(G,X1,X2) qd = quaddata(G, x1, x2, q, [τ1], [τ2], nothing, qs) z2 = zeros(numfunctions(x1), numfunctions(x2), numfunctions(q)) for r in BEAST.rings(τ1, τ2, ΔR) - ι = BEAST.ring(r, ΔR) + local ι = BEAST.ring(r, ΔR) quad_rule = quadrule(G, x1, x2, q, 1, τ1, 1, τ2, r, ι, qd, qs) BEAST.momintegrals!(z2, G, x1, x2, q, τ1, τ2, ι, quad_rule) end diff --git a/test/test_ttrace.jl b/test/test_ttrace.jl index cc97e7a1..2b867657 100644 --- a/test/test_ttrace.jl +++ b/test/test_ttrace.jl @@ -4,7 +4,8 @@ using LinearAlgebra using Test using StaticArrays -o, x, y, z = euclidianbasis(3) +for T in [Float32, Float64] +local o, x, y, z = euclidianbasis(3, T) p1 = 2x p2 = y @@ -14,26 +15,26 @@ tet = simplex(p1, p2, p3, p4) q = abs(CompScienceMeshes.relorientation([1,2,3],[1,2,3,4])) tri = simplex(p1, p2, p3) -n = normal(tri) +local n = normal(tri) i = 3 -j = 3 +local j = 3 edg = simplex(p1, p2) -T = Float64 + x = BEAST.NDLCDRefSpace{T}() y = BEAST.NDRefSpace{T}() -p = neighborhood(tet, [0.5, 0.5, 0.0]) -r = neighborhood(tri, [0.5, 0.5]) +p = neighborhood(tet, T.([0.5, 0.5, 0.0])) +r = neighborhood(tri, T.([0.5, 0.5])) -@test carttobary(edg, cartesian(p)) ≈ [0.5] -@test carttobary(edg, cartesian(r)) ≈ [0.5] +@test carttobary(edg, cartesian(p)) ≈ T.([0.5]) +@test carttobary(edg, cartesian(r)) ≈ T.([0.5]) xp = x(p)[j].value yr = y(r)[i].value -a, b = extrema((n × xp) ./ yr) +local a, b = extrema((n × xp) ./ yr) @test a ≈ b tgt = p2 - p1 @@ -48,8 +49,8 @@ volume(simplex(p1,p2)) Q = BEAST.ttrace(x, tet, q, tri) -p = neighborhood(tet, [1/3, 1/3, 1/3]) -r = neighborhood(tri, [1/3, 1/3]) +p = neighborhood(tet, T.([1/3, 1/3, 1/3])) +r = neighborhood(tri, T.([1/3, 1/3])) @test cartesian(p) ≈ cartesian(r) xp = n × x(p)[j].value @@ -57,8 +58,8 @@ yr = y(r)[i].value @test xp ≈ yr * Q[i,j] -x = BEAST.NDLCCRefSpace{Float64}() -y = BEAST.RTRefSpace{Float64}() +x = BEAST.NDLCCRefSpace{T}() +y = BEAST.RTRefSpace{T}() for q in 1:4 q = 3 fc = BEAST.faces(tet)[q] @@ -77,10 +78,10 @@ for q in 1:4 end # test the case where intrinsic and extrinsic orientations differ -o, x, y, z = euclidianbasis(3) +o, x, y, z = euclidianbasis(3, T) fc = simplex(z,o,y) -x = BEAST.NDLCCRefSpace{Float64}() -y = BEAST.RTRefSpace{Float64}() +x = BEAST.NDLCCRefSpace{T}() +y = BEAST.RTRefSpace{T}() Q = BEAST.ttrace(x, tet, 3000, fc) nbdi = CompScienceMeshes.center(fc) @@ -98,15 +99,15 @@ end o, x, y, z = euclidianbasis(3) -m = Mesh([x,y,z,o], [@SVector[1,2,3,4]]) +local m = Mesh([x,y,z,o], [@SVector[1,2,3,4]]) m1 = skeleton(m,1) -X = BEAST.nedelecc3d(m, m1) +local X = BEAST.nedelecc3d(m, m1) @test numfunctions(X) == 6 # m2 = skeleton(m,2) m2 = boundary(m) -Y = BEAST.ttrace(X,m2) +local Y = BEAST.ttrace(X,m2) @test numfunctions(Y) == 6 pa = Y.fns[1][1].cellid @@ -124,3 +125,4 @@ ctrb = cartesian(CompScienceMeshes.center(CompScienceMeshes.edges(trib)[rb])) # tri1 = chart(m, cells(m)[Y.fns[1][1].cellid]) # ctr1 = cartesian(center(CompScienceMeshes.edges(tri)[])) +end \ No newline at end of file diff --git a/test/test_wiltonints.jl b/test/test_wiltonints.jl index 612188e9..5ebfefbf 100644 --- a/test/test_wiltonints.jl +++ b/test/test_wiltonints.jl @@ -1,5 +1,6 @@ import BEAST; BE = BEAST; using CompScienceMeshes +using StaticArrays using Test T = Float64 From 55caaedbb29360e0fa710d61b106843754de8e96 Mon Sep 17 00:00:00 2001 From: Paula Respondek Date: Fri, 22 Apr 2022 11:33:23 +0200 Subject: [PATCH 159/528] Update unit tests - indent for-loop body --- test/test_assemblerow.jl | 112 +++++++++--------- test/test_basis.jl | 174 ++++++++++++++-------------- test/test_bcspace.jl | 136 +++++++++++----------- test/test_curlcurlgreen.jl | 78 ++++++------- test/test_dipole.jl | 220 ++++++++++++++++++------------------ test/test_directproduct.jl | 30 ++--- test/test_fourier.jl | 36 +++--- test/test_gradient.jl | 24 ++-- test/test_hh3dexc.jl | 46 ++++---- test/test_hh3dtd_exc.jl | 50 ++++---- test/test_local_storage.jl | 24 ++-- test/test_mixed_blkassm.jl | 58 +++++----- test/test_mult.jl | 44 ++++---- test/test_ndlcd_restrict.jl | 52 ++++----- test/test_ndspace.jl | 60 +++++----- test/test_raviartthomas.jl | 48 ++++---- test/test_restrict.jl | 92 +++++++-------- test/test_rt3d.jl | 84 +++++++------- test/test_rtx.jl | 26 ++--- test/test_specials.jl | 20 ++-- test/test_ttrace.jl | 168 +++++++++++++-------------- 21 files changed, 791 insertions(+), 791 deletions(-) diff --git a/test/test_assemblerow.jl b/test/test_assemblerow.jl index 9830721e..1687900c 100644 --- a/test/test_assemblerow.jl +++ b/test/test_assemblerow.jl @@ -4,60 +4,60 @@ using Test fn = joinpath(dirname(@__FILE__),"assets","sphere35.in") for T in [Float32, Float64] -local m = readmesh(fn,T=T) -t = Maxwell3D.singlelayer(wavenumber=T(1.0)) -local X = raviartthomas(m) -numfunctions(X) - -## -X1 = subset(X,1:1) -numfunctions(X1) - -T1 = assemble(t,X1,X) -T2 = BEAST.assemblerow(t,X1,X) -# -# T3 = assemble(t,X,X1) -# T4 = BEAST.assemblecol(t,X,X1) -# -# @test T1 == T2 - -# T2 = BEAST.assembleblock(t,X,X) -@test T1≈T2 atol=sqrt(eps(T)) - - -local I = [3,2,7] -X1 = subset(X,I) -T2 = zeros(scalartype(t,X1,X1),numfunctions(X1),numfunctions(X1)) -store(v,m,n) = (T2[m,n] += v) - -qs = BEAST.defaultquadstrat(t,X,X) -test_elements, test_assembly_data, - trial_elements, trial_assembly_data, - quadrature_data, zlocal = BEAST.assembleblock_primer(t,X,X, quadstrat=qs) - -BEAST.assembleblock_body!(t, - X, I, test_elements, test_assembly_data, - X, I, trial_elements, trial_assembly_data, - quadrature_data, zlocal, store, quadstrat=qs) - -T1 = assemble(t,X1,X1) -@test T1 == T2 - -# @time BEAST.assembleblock_body!(t, -# X, I, test_elements, test_assembly_data, -# X, I, trial_elements, trial_assembly_data, -# quadrature_data, zlocal, store) -# @time assemble(t,X1,X1) - -T3 = zeros(scalartype(t,X1,X1),numfunctions(X1),numfunctions(X1)) -store3(v,m,n) = (T3[m,n] += v) - -blkasm = BEAST.blockassembler(t,X,X) -blkasm(I,I,store3) - -T4 = assemble(t,X,X) -@test T3 == T4[I,I] - -# @time blkasm(I,I) -# @time assemble(t,X1,X1) + local m = readmesh(fn,T=T) + t = Maxwell3D.singlelayer(wavenumber=T(1.0)) + local X = raviartthomas(m) + numfunctions(X) + + ## + X1 = subset(X,1:1) + numfunctions(X1) + + T1 = assemble(t,X1,X) + T2 = BEAST.assemblerow(t,X1,X) + # + # T3 = assemble(t,X,X1) + # T4 = BEAST.assemblecol(t,X,X1) + # + # @test T1 == T2 + + # T2 = BEAST.assembleblock(t,X,X) + @test T1≈T2 atol=sqrt(eps(T)) + + + local I = [3,2,7] + X1 = subset(X,I) + T2 = zeros(scalartype(t,X1,X1),numfunctions(X1),numfunctions(X1)) + store(v,m,n) = (T2[m,n] += v) + + qs = BEAST.defaultquadstrat(t,X,X) + test_elements, test_assembly_data, + trial_elements, trial_assembly_data, + quadrature_data, zlocal = BEAST.assembleblock_primer(t,X,X, quadstrat=qs) + + BEAST.assembleblock_body!(t, + X, I, test_elements, test_assembly_data, + X, I, trial_elements, trial_assembly_data, + quadrature_data, zlocal, store, quadstrat=qs) + + T1 = assemble(t,X1,X1) + @test T1 == T2 + + # @time BEAST.assembleblock_body!(t, + # X, I, test_elements, test_assembly_data, + # X, I, trial_elements, trial_assembly_data, + # quadrature_data, zlocal, store) + # @time assemble(t,X1,X1) + + T3 = zeros(scalartype(t,X1,X1),numfunctions(X1),numfunctions(X1)) + store3(v,m,n) = (T3[m,n] += v) + + blkasm = BEAST.blockassembler(t,X,X) + blkasm(I,I,store3) + + T4 = assemble(t,X,X) + @test T3 == T4[I,I] + + # @time blkasm(I,I) + # @time assemble(t,X1,X1) end \ No newline at end of file diff --git a/test/test_basis.jl b/test/test_basis.jl index 08984df4..f9755335 100644 --- a/test/test_basis.jl +++ b/test/test_basis.jl @@ -6,60 +6,60 @@ using BEAST ## The actual tests for T in [Float32, Float64] -κ = ω = T(1.0) + κ = ω = T(1.0) -Γ = meshsegment(T(1.0), T(0.5)) -X = lagrangec0d1(Γ) -@test numvertices(Γ)-2 == numfunctions(X) + Γ = meshsegment(T(1.0), T(0.5)) + X = lagrangec0d1(Γ) + @test numvertices(Γ)-2 == numfunctions(X) -hypersingular = HyperSingular(κ) -identityop = Identity() -doublelayer = DoubleLayer(κ) + hypersingular = HyperSingular(κ) + identityop = Identity() + doublelayer = DoubleLayer(κ) -@time N = assemble(hypersingular, X, X) -@time I = assemble(identityop, X, X) + @time N = assemble(hypersingular, X, X) + @time I = assemble(identityop, X, X) -@test size(N) == (numfunctions(X), numfunctions(X)) -@test size(I) == (numfunctions(X), numfunctions(X)) -@test rank(I) == numfunctions(X) + @test size(N) == (numfunctions(X), numfunctions(X)) + @test size(I) == (numfunctions(X), numfunctions(X)) + @test rank(I) == numfunctions(X) -@time e = assemble(PlaneWaveNeumann(κ, point(0.0, 1.0)), X) -@test length(e) == numfunctions(X) + @time e = assemble(PlaneWaveNeumann(κ, point(0.0, 1.0)), X) + @test length(e) == numfunctions(X) -x1 = N \ e; + x1 = N \ e; -# Testing duallagrangec0d1 -if T == Float64 -Γ1 = meshcircle(T(1.0), T(2.5),3) # creating a triangle -Γ2 = barycentric_refinement(Γ1) # creating the refined mesh + # Testing duallagrangec0d1 + if T == Float64 + Γ1 = meshcircle(T(1.0), T(2.5),3) # creating a triangle + Γ2 = barycentric_refinement(Γ1) # creating the refined mesh -X1 =duallagrangec0d1(Γ1,Γ2) # creating the basis functions -X1 = duallagrangec0d1(Γ1, Γ2, x->false, Val{2}) -@test numcells(Γ1) == numfunctions(X1) # making sure it is assigned according to the coarse mesh segments -@test length(X1.fns[1])== 6 # making sure each segment represent 6 shapes inside it -@test length(X1.fns[numfunctions(X1)])== 6 # making sure the last segment functions contains 6 shapes as well -end + X1 =duallagrangec0d1(Γ1,Γ2) # creating the basis functions + X1 = duallagrangec0d1(Γ1, Γ2, x->false, Val{2}) + @test numcells(Γ1) == numfunctions(X1) # making sure it is assigned according to the coarse mesh segments + @test length(X1.fns[1])== 6 # making sure each segment represent 6 shapes inside it + @test length(X1.fns[numfunctions(X1)])== 6 # making sure the last segment functions contains 6 shapes as well + end end ## Test linear Lagrange elements on triangles and the computation of their curl for T in [Float32, Float64] -Degr = 1 -Dim1 = 3 -NumF = 3 -f = BEAST.LagrangeRefSpace{T,Degr,Dim1,NumF}() -sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in"),T=T) -s = chart(sphere, first(cells(sphere))) -t = neighborhood(s, T.([1,1]/3)) -v = f(t, Val{:withcurl}) - -A = volume(s) -@test v[1][2] == (s[3]-s[2])/2A -@test v[2][2] == (s[1]-s[3])/2A -@test v[3][2] == (s[2]-s[1])/2A - -@test v[1][1] ≈ 1/3 -@test v[2][1] ≈ 1/3 -@test v[3][1] ≈ 1/3 + Degr = 1 + Dim1 = 3 + NumF = 3 + f = BEAST.LagrangeRefSpace{T,Degr,Dim1,NumF}() + sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in"),T=T) + s = chart(sphere, first(cells(sphere))) + t = neighborhood(s, T.([1,1]/3)) + v = f(t, Val{:withcurl}) + + A = volume(s) + @test v[1][2] == (s[3]-s[2])/2A + @test v[2][2] == (s[1]-s[3])/2A + @test v[3][2] == (s[2]-s[1])/2A + + @test v[1][1] ≈ 1/3 + @test v[2][1] ≈ 1/3 + @test v[3][1] ≈ 1/3 end ## Test the construction of continuous linear Lagrange elements on 2D surfaces @@ -68,49 +68,49 @@ using BEAST using Test for T in [Float32, Float64] -m = meshrectangle(T(1.0), T(1.0), T(0.5), 3) -X = lagrangec0d1(m) -x = refspace(X) + m = meshrectangle(T(1.0), T(1.0), T(0.5), 3) + X = lagrangec0d1(m) + x = refspace(X) -@test numfunctions(x) == 3 + @test numfunctions(x) == 3 -@test numfunctions(X) == 1 -@test length(X.fns[1]) == 6 + @test numfunctions(X) == 1 + @test length(X.fns[1]) == 6 end ## test the scalar trace for Lagrange functions using CompScienceMeshes using BEAST using Test for T in [Float64] -p1 = point(T,0,0,0) -p2 = point(T,1,0,0) -p3 = point(T,0,1,0) -p4 = point(T,1,1,1) -c1 = index(1,2,3) + p1 = point(T,0,0,0) + p2 = point(T,1,0,0) + p3 = point(T,0,1,0) + p4 = point(T,1,1,1) + c1 = index(1,2,3) -m = Mesh([p1,p2,p3,p4],[c1]) -b = Mesh([p1,p2], [index(1,2)]) -X = lagrangec0d1(m, boundary(m)) -@test numfunctions(X) == 3 + m = Mesh([p1,p2,p3,p4],[c1]) + b = Mesh([p1,p2], [index(1,2)]) + X = lagrangec0d1(m, boundary(m)) + @test numfunctions(X) == 3 -Y = BEAST.strace(X, b) + Y = BEAST.strace(X, b) -@test numfunctions(X) == 3 -@test numfunctions(Y) == 3 + @test numfunctions(X) == 3 + @test numfunctions(Y) == 3 -@test length(Y.fns[1]) == 1 -@test length(Y.fns[2]) == 1 -@test length(Y.fns[3]) == 0 + @test length(Y.fns[1]) == 1 + @test length(Y.fns[2]) == 1 + @test length(Y.fns[3]) == 0 -sh = Y.fns[1][1]; @test (sh.cellid, sh.refid, sh.coeff) == (1, 1, 1.0) -sh = Y.fns[2][1]; @test (sh.cellid, sh.refid, sh.coeff) == (1, 2, 1.0) + sh = Y.fns[1][1]; @test (sh.cellid, sh.refid, sh.coeff) == (1, 1, 1.0) + sh = Y.fns[2][1]; @test (sh.cellid, sh.refid, sh.coeff) == (1, 2, 1.0) -x = refspace(X) + x = refspace(X) -cell = chart(m, first(cells(m))) -face = chart(b, first(cells(b))) -Q = BEAST.strace(x, cell, 3, face) -@test Q == [1 0 0; 0 1 0] + cell = chart(m, first(cells(m))) + face = chart(b, first(cells(b))) + Q = BEAST.strace(x, cell, 3, face) + @test Q == [1 0 0; 0 1 0] end ## test Lagrange construction on Junctions @@ -119,23 +119,23 @@ using BEAST using Test for T in [Float64] -m1 = meshrectangle(T(1.0), T(0.5), T(0.5)) -m2 = CompScienceMeshes.rotate(m1, T(0.5π)*[1,0,0]) -m3 = CompScienceMeshes.rotate(m1, T(1.0π)*[1,0,0]) -m = weld(m1, m2, m3) - -X = lagrangec0d1(m) -x = refspace(X) -@test numfunctions(X) == 1 -@test length(X.fns[1]) == 9 - -p = point(T, 0.5, 0.0, 0.0) -for _s in X.fns[1] - _cell = m.faces[_s.cellid] - patch = chart(m, _cell) - bary = carttobary(patch, p) - mp = neighborhood(patch, bary) -end + m1 = meshrectangle(T(1.0), T(0.5), T(0.5)) + m2 = CompScienceMeshes.rotate(m1, T(0.5π)*[1,0,0]) + m3 = CompScienceMeshes.rotate(m1, T(1.0π)*[1,0,0]) + m = weld(m1, m2, m3) + + X = lagrangec0d1(m) + x = refspace(X) + @test numfunctions(X) == 1 + @test length(X.fns[1]) == 9 + + p = point(T, 0.5, 0.0, 0.0) + for _s in X.fns[1] + _cell = m.faces[_s.cellid] + patch = chart(m, _cell) + bary = carttobary(patch, p) + mp = neighborhood(patch, bary) + end end ## Test the dual pieweise constant lagrange elemetns using CompScienceMeshes diff --git a/test/test_bcspace.jl b/test/test_bcspace.jl index 252a063b..4d4908a6 100644 --- a/test/test_bcspace.jl +++ b/test/test_bcspace.jl @@ -86,75 +86,75 @@ end #meshfile = Pkg.dir("BEAST","test","sphere2.in") for T in [Float32, Float64] -meshfile = joinpath(dirname(@__FILE__),"assets","sphere316.in") -mesh = readmesh(meshfile,T=T) -@test numvertices(mesh) == 160 -@test numcells(mesh) == 316 - -rt = raviartthomas(mesh) -@test numfunctions(rt) == 316 * 3 / 2 - -local fine = barycentric_refinement(mesh) -local edges = skeleton(mesh, 1) - -bc = buffachristiansen(mesh) -@test numfunctions(bc) == 316 * 3 / 2 - -lc = isdivconforming(rt) -@test maximum(lc) < eps(T) * 1000 -println("RT space is div-conforming") - -lc = isdivconforming(bc); -@test maximum(lc) < eps(T) * 1000 -println("BC space is div-conforming") - -# Now repeat the exercise with an open mesh -mesh = meshrectangle(T(1.0), T(1.0), T(0.2)); -fine = barycentric_refinement(mesh); - -rt = raviartthomas(mesh) -bc = buffachristiansen(mesh) - -@test numfunctions(rt) == 65 -@test numfunctions(bc) == 65 - -int_pred = interior_tpredicate(mesh) -bnd_pred(s) = !int_pred(s) - -leaky_edges = findall(sum(abs.(isdivconforming(rt)),dims=1) .!= 0) -@test length(leaky_edges) == 0 - -bnd = boundary(mesh) -bndtch_pred = touches_predicate(bnd) -edges = interior(mesh) -bndtch_edges = findall(bndtch_pred, cells(edges)) -leaky_edges = findall(vec(sum(abs.(isdivconforming(bc)),dims=1)) .!= 0) -@test bndtch_edges == leaky_edges - - -## Test the charge of BC functions -#meshfile = Pkg.dir("BEAST","test","sphere2.in") -meshfile = joinpath(dirname(@__FILE__),"assets","sphere316.in") -mesh = readmesh(meshfile,T=T) -bc = buffachristiansen(mesh) -fine = geometry(bc) -charges = zeros(numcells(fine)) - -for fn in bc.fns - abs_charge = T(0.0) - net_charge = T(0.0) - fill!(charges,0) - for _sh in fn - cellid = _sh.cellid - - #cell = simplex(vertices(fine, fine.faces[cellid])) - net_charge += _sh.coeff - charges[cellid] += _sh.coeff + meshfile = joinpath(dirname(@__FILE__),"assets","sphere316.in") + mesh = readmesh(meshfile,T=T) + @test numvertices(mesh) == 160 + @test numcells(mesh) == 316 + + rt = raviartthomas(mesh) + @test numfunctions(rt) == 316 * 3 / 2 + + local fine = barycentric_refinement(mesh) + local edges = skeleton(mesh, 1) + + bc = buffachristiansen(mesh) + @test numfunctions(bc) == 316 * 3 / 2 + + lc = isdivconforming(rt) + @test maximum(lc) < eps(T) * 1000 + println("RT space is div-conforming") + + lc = isdivconforming(bc); + @test maximum(lc) < eps(T) * 1000 + println("BC space is div-conforming") + + # Now repeat the exercise with an open mesh + mesh = meshrectangle(T(1.0), T(1.0), T(0.2)); + fine = barycentric_refinement(mesh); + + rt = raviartthomas(mesh) + bc = buffachristiansen(mesh) + + @test numfunctions(rt) == 65 + @test numfunctions(bc) == 65 + + int_pred = interior_tpredicate(mesh) + bnd_pred(s) = !int_pred(s) + + leaky_edges = findall(sum(abs.(isdivconforming(rt)),dims=1) .!= 0) + @test length(leaky_edges) == 0 + + bnd = boundary(mesh) + bndtch_pred = touches_predicate(bnd) + edges = interior(mesh) + bndtch_edges = findall(bndtch_pred, cells(edges)) + leaky_edges = findall(vec(sum(abs.(isdivconforming(bc)),dims=1)) .!= 0) + @test bndtch_edges == leaky_edges + + + ## Test the charge of BC functions + #meshfile = Pkg.dir("BEAST","test","sphere2.in") + meshfile = joinpath(dirname(@__FILE__),"assets","sphere316.in") + mesh = readmesh(meshfile,T=T) + bc = buffachristiansen(mesh) + fine = geometry(bc) + charges = zeros(numcells(fine)) + + for fn in bc.fns + abs_charge = T(0.0) + net_charge = T(0.0) + fill!(charges,0) + for _sh in fn + cellid = _sh.cellid + + #cell = simplex(vertices(fine, fine.faces[cellid])) + net_charge += _sh.coeff + charges[cellid] += _sh.coeff + end + abs_charge = sum(abs.(charges)) + @test net_charge + 1 ≈ 1 + @test abs_charge ≈ 2 end - abs_charge = sum(abs.(charges)) - @test net_charge + 1 ≈ 1 - @test abs_charge ≈ 2 -end end # THe BC construction function should throw for non-oriented surfaces diff --git a/test/test_curlcurlgreen.jl b/test/test_curlcurlgreen.jl index 206e21e6..3acc667a 100644 --- a/test/test_curlcurlgreen.jl +++ b/test/test_curlcurlgreen.jl @@ -4,43 +4,43 @@ using BEAST using CompScienceMeshes using StaticArrays for T in [Float32, Float64] -local y = point(T,2,0,0) -local j = ẑ -local κ = T(1.0) - -gj(x) = exp(-im*κ*norm(x-y))/(4*π*norm(x-y)) * j -ccg = BEAST.CurlCurlGreen(κ, j, y) - -function curlh(f,x,h) - e1 = point(T,1,0,0) - e2 = point(T,0,1,0) - e3 = point(T,0,0,1) - - d1f = (f(x+h*e1) - f(x-h*e1))/(2*h) - d2f = (f(x+h*e2) - f(x-h*e2))/(2*h) - d3f = (f(x+h*e3) - f(x-h*e3))/(2*h) - - return @SVector[ - d2f[3] - d3f[2], - d3f[1] - d1f[3], - d1f[2] - d2f[1]] -end - -cgh(x) = curlh(gj,x,h) -ccgh(x) = curlh(cgh,x,h) - -local h = T(0.01) -local x = point(T,1,1,1) -a = ccg(x) -local b = ccgh(x) -@show norm(x-y) -T == Float64 ? atol=1e-5 : atol=1e-4 -@test norm(a-b) < atol - -cccg = curl(ccg) -cccgh(x) = curlh(ccg,x,h) - -# @show a = cccg(x) -# @show b = cccgh(x) -@test norm(a-b) < atol + local y = point(T,2,0,0) + local j = ẑ + local κ = T(1.0) + + gj(x) = exp(-im*κ*norm(x-y))/(4*π*norm(x-y)) * j + ccg = BEAST.CurlCurlGreen(κ, j, y) + + function curlh(f,x,h) + e1 = point(T,1,0,0) + e2 = point(T,0,1,0) + e3 = point(T,0,0,1) + + d1f = (f(x+h*e1) - f(x-h*e1))/(2*h) + d2f = (f(x+h*e2) - f(x-h*e2))/(2*h) + d3f = (f(x+h*e3) - f(x-h*e3))/(2*h) + + return @SVector[ + d2f[3] - d3f[2], + d3f[1] - d1f[3], + d1f[2] - d2f[1]] + end + + cgh(x) = curlh(gj,x,h) + ccgh(x) = curlh(cgh,x,h) + + local h = T(0.01) + local x = point(T,1,1,1) + a = ccg(x) + local b = ccgh(x) + @show norm(x-y) + T == Float64 ? atol=1e-5 : atol=1e-4 + @test norm(a-b) < atol + + cccg = curl(ccg) + cccgh(x) = curlh(ccg,x,h) + + # @show a = cccg(x) + # @show b = cccgh(x) + @test norm(a-b) < atol end \ No newline at end of file diff --git a/test/test_dipole.jl b/test/test_dipole.jl index 4963e698..3f9d6dde 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -7,114 +7,114 @@ using LinearAlgebra for U in [Float32,Float64] -c = U(3e8) -μ0 = U(4*π*1e-7) -μr = U(1.0) -μ = μ0*μr -ε0 = U(8.854187812e-12) -εr = U(5) -ε = ε0*εr -c = U(1)/sqrt(ε*μ) -local f = U(5e7) -λ = c/f -k = U(2*π/λ) -local ω = k*c -η = sqrt(μ/ε) - -a = U(1) -Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,U(0.1)) -local Γ = translate(Γ_orig,SVector(U(-a/2),U(-a/2),U(-a/2))) - -Φ, Θ = U.([0.0]), range(U(0),stop=U(π),length=100) -pts = [point(U,cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] - -# This is an electric dipole -# The pre-factor (1/ε) is used to resemble -# (9.18) in Jackson's Classical Electrodynamics -local E = U(1/ε) * dipolemw3d(location=SVector(U(0.4),U(0.2),U(0)), - orientation=U(1e-9).*SVector(U(0.5),U(0.5),U(0)), - wavenumber=k) - -local n = BEAST.NormalVector() - -𝒆 = (n × E) × n -local H = (-1/(im*μ*ω))*curl(E) -𝒉 = (n × H) × n - -𝓣 = Maxwell3D.singlelayer(wavenumber=k, alpha=-im*ω*μ, beta=-1/(im*ω*ε)) -𝓝 = BEAST.NCross() -𝓚 = Maxwell3D.doublelayer(wavenumber=k) - -local X = raviartthomas(Γ) -local Y = buffachristiansen(Γ) - -local T = Matrix(assemble(𝓣,X,X)) -e = Vector(assemble(𝒆,X)) -j_EFIE = T\e - -nf_E_EFIE = potential(MWSingleLayerField3D(𝓣), pts, j_EFIE, X) -nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(𝓚), pts, j_EFIE, X) -ff_E_EFIE = potential(MWFarField3D(𝓣), pts, j_EFIE, X) -ff_H_EFIE = potential(BEAST.MWDoubleLayerFarField3D(𝓚), pts, j_EFIE, X) - -@test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 -@test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 -@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.001 -@test norm(ff_H_EFIE - H.(pts, isfarfield=true))/norm(H.(pts, isfarfield=true)) ≈ 0 atol=0.001 - -K_bc = Matrix(assemble(𝓚,Y,X)) -G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) -h_bc = Vector(assemble(𝒉,Y)) -M_bc = -U(0.5)*G_nxbc_rt + K_bc -j_BCMFIE = M_bc\h_bc - -nf_E_BCMFIE = potential(MWSingleLayerField3D(𝓣), pts, j_BCMFIE, X) -nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(𝓚), pts, j_BCMFIE, X) -ff_E_BCMFIE = potential(MWFarField3D(𝓣), pts, j_BCMFIE, X) - -@test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 -@test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 -@test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 - -H = dipolemw3d(location=SVector(U(0.0),U(0.0),U(0.3)), - orientation=U(1e-9).*SVector(U(0.5),U(0.5),U(0)), - wavenumber=k) - -# This time, we do not specify alpha and beta -# We include η in the magnetic RHS -𝓣 = Maxwell3D.singlelayer(wavenumber=k) - -𝒉 = (n × H) × n -E = (1/(im*ε*ω))*curl(H) -𝒆 = (n × E) × n - -X = raviartthomas(Γ) -Y = buffachristiansen(Γ) - -T = Matrix(assemble(𝓣,X,X)) -e = Vector(assemble(𝒆,X)) -j_EFIE = T\e - -nf_E_EFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_EFIE, X) -nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_EFIE, X) ./ η -ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X) - -@test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 -@test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 -@test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 - -K_bc = Matrix(assemble(𝓚,Y,X)) -G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) -h_bc = η*Vector(assemble(𝒉,Y)) -M_bc = -U(0.5)*G_nxbc_rt + K_bc -j_BCMFIE = M_bc\h_bc - -nf_E_BCMFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) -nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) ./ η -ff_E_BCMFIE = potential(MWFarField3D(wavenumber=k), pts, j_BCMFIE, X) - -@test norm(j_BCMFIE - j_EFIE)/norm(j_EFIE) ≈ 0 atol=0.02 -@test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 -@test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 -@test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 + c = U(3e8) + μ0 = U(4*π*1e-7) + μr = U(1.0) + μ = μ0*μr + ε0 = U(8.854187812e-12) + εr = U(5) + ε = ε0*εr + c = U(1)/sqrt(ε*μ) + local f = U(5e7) + λ = c/f + k = U(2*π/λ) + local ω = k*c + η = sqrt(μ/ε) + + a = U(1) + Γ_orig = CompScienceMeshes.meshcuboid(a,a,a,U(0.1)) + local Γ = translate(Γ_orig,SVector(U(-a/2),U(-a/2),U(-a/2))) + + Φ, Θ = U.([0.0]), range(U(0),stop=U(π),length=100) + pts = [point(U,cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] + + # This is an electric dipole + # The pre-factor (1/ε) is used to resemble + # (9.18) in Jackson's Classical Electrodynamics + local E = U(1/ε) * dipolemw3d(location=SVector(U(0.4),U(0.2),U(0)), + orientation=U(1e-9).*SVector(U(0.5),U(0.5),U(0)), + wavenumber=k) + + local n = BEAST.NormalVector() + + 𝒆 = (n × E) × n + local H = (-1/(im*μ*ω))*curl(E) + 𝒉 = (n × H) × n + + 𝓣 = Maxwell3D.singlelayer(wavenumber=k, alpha=-im*ω*μ, beta=-1/(im*ω*ε)) + 𝓝 = BEAST.NCross() + 𝓚 = Maxwell3D.doublelayer(wavenumber=k) + + local X = raviartthomas(Γ) + local Y = buffachristiansen(Γ) + + local T = Matrix(assemble(𝓣,X,X)) + e = Vector(assemble(𝒆,X)) + j_EFIE = T\e + + nf_E_EFIE = potential(MWSingleLayerField3D(𝓣), pts, j_EFIE, X) + nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(𝓚), pts, j_EFIE, X) + ff_E_EFIE = potential(MWFarField3D(𝓣), pts, j_EFIE, X) + ff_H_EFIE = potential(BEAST.MWDoubleLayerFarField3D(𝓚), pts, j_EFIE, X) + + @test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 + @test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 + @test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.001 + @test norm(ff_H_EFIE - H.(pts, isfarfield=true))/norm(H.(pts, isfarfield=true)) ≈ 0 atol=0.001 + + K_bc = Matrix(assemble(𝓚,Y,X)) + G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) + h_bc = Vector(assemble(𝒉,Y)) + M_bc = -U(0.5)*G_nxbc_rt + K_bc + j_BCMFIE = M_bc\h_bc + + nf_E_BCMFIE = potential(MWSingleLayerField3D(𝓣), pts, j_BCMFIE, X) + nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(𝓚), pts, j_BCMFIE, X) + ff_E_BCMFIE = potential(MWFarField3D(𝓣), pts, j_BCMFIE, X) + + @test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 + @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 + @test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 + + H = dipolemw3d(location=SVector(U(0.0),U(0.0),U(0.3)), + orientation=U(1e-9).*SVector(U(0.5),U(0.5),U(0)), + wavenumber=k) + + # This time, we do not specify alpha and beta + # We include η in the magnetic RHS + 𝓣 = Maxwell3D.singlelayer(wavenumber=k) + + 𝒉 = (n × H) × n + E = (1/(im*ε*ω))*curl(H) + 𝒆 = (n × E) × n + + X = raviartthomas(Γ) + Y = buffachristiansen(Γ) + + T = Matrix(assemble(𝓣,X,X)) + e = Vector(assemble(𝒆,X)) + j_EFIE = T\e + + nf_E_EFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_EFIE, X) + nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_EFIE, X) ./ η + ff_E_EFIE = potential(MWFarField3D(wavenumber=k), pts, j_EFIE, X) + + @test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 + @test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 + @test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 + + K_bc = Matrix(assemble(𝓚,Y,X)) + G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) + h_bc = η*Vector(assemble(𝒉,Y)) + M_bc = -U(0.5)*G_nxbc_rt + K_bc + j_BCMFIE = M_bc\h_bc + + nf_E_BCMFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) + nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) ./ η + ff_E_BCMFIE = potential(MWFarField3D(wavenumber=k), pts, j_BCMFIE, X) + + @test norm(j_BCMFIE - j_EFIE)/norm(j_EFIE) ≈ 0 atol=0.02 + @test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 + @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 + @test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 end \ No newline at end of file diff --git a/test/test_directproduct.jl b/test/test_directproduct.jl index 470eda12..f8619f0f 100644 --- a/test/test_directproduct.jl +++ b/test/test_directproduct.jl @@ -4,25 +4,25 @@ using BEAST using Test for U in [Float32, Float64] -m1 = meshrectangle(U(1.0), U(1.0), U(0.5)) -m2 = CompScienceMeshes.translate!(meshrectangle(U(1.0), U(1.0), U(0.5)), point(U,0,0,1)) -m3 = CompScienceMeshes.translate!(meshrectangle(U(1.0), U(1.0), U(0.5)), point(U,0,0,2)) + m1 = meshrectangle(U(1.0), U(1.0), U(0.5)) + m2 = CompScienceMeshes.translate!(meshrectangle(U(1.0), U(1.0), U(0.5)), point(U,0,0,1)) + m3 = CompScienceMeshes.translate!(meshrectangle(U(1.0), U(1.0), U(0.5)), point(U,0,0,2)) -X1 = raviartthomas(m1) -X2 = raviartthomas(m2) -X3 = raviartthomas(m3) + X1 = raviartthomas(m1) + X2 = raviartthomas(m2) + X3 = raviartthomas(m3) -local X = X1 × X2 × X3 + local X = X1 × X2 × X3 -n1 = numfunctions(X1) -n2 = numfunctions(X2) -n3 = numfunctions(X3) + n1 = numfunctions(X1) + n2 = numfunctions(X2) + n3 = numfunctions(X3) -@test numfunctions(X) == n1 + n2 + n3 -nt = numfunctions(X) + @test numfunctions(X) == n1 + n2 + n3 + nt = numfunctions(X) -T = MWSingleLayer3D(U(1.0)) -t = assemble(T, X, X) + T = MWSingleLayer3D(U(1.0)) + t = assemble(T, X, X) -@test size(t) == (nt,nt) + @test size(t) == (nt,nt) end \ No newline at end of file diff --git a/test/test_fourier.jl b/test/test_fourier.jl index dcb625c8..783b816a 100644 --- a/test/test_fourier.jl +++ b/test/test_fourier.jl @@ -3,25 +3,25 @@ using Test using LinearAlgebra for T in [Float32, Float64] -width = T(4.0) -delay = T(6.0) -g = BEAST.creategaussian(width, delay) -G = BEAST.fouriertransform(g) + width = T(4.0) + delay = T(6.0) + g = BEAST.creategaussian(width, delay) + G = BEAST.fouriertransform(g) -dx = width / 15 -x0 = T(0.0) -x = x0 : dx : 3*delay -n = length(x) -y = g.(x) + dx = width / 15 + x0 = T(0.0) + x = x0 : dx : 3*delay + n = length(x) + y = g.(x) -Y, dω, ω0 = BEAST.fouriertransform(y, dx, x0) -ω = collect(ω0 .+ (0:n-1)*dω) -Z = G.(ω) + Y, dω, ω0 = BEAST.fouriertransform(y, dx, x0) + ω = collect(ω0 .+ (0:n-1)*dω) + Z = G.(ω) -# using Plots -# plot(ω, real(Y)) -# scatter!(ω, real(Z)) -# plot!(ω, imag(Y)) -# scatter!(ω, imag(Z)) -@test norm(Y-Z) < sqrt(eps(T)) + # using Plots + # plot(ω, real(Y)) + # scatter!(ω, real(Z)) + # plot!(ω, imag(Y)) + # scatter!(ω, imag(Z)) + @test norm(Y-Z) < sqrt(eps(T)) end \ No newline at end of file diff --git a/test/test_gradient.jl b/test/test_gradient.jl index 892bada4..e3cc0a40 100644 --- a/test/test_gradient.jl +++ b/test/test_gradient.jl @@ -3,21 +3,21 @@ using BEAST using Test for T in [Float32, Float64] -local o, x, y, z = euclidianbasis(3,T) -tet = simplex(x,y,z,o) -ctr = center(tet) + local o, x, y, z = euclidianbasis(3,T) + tet = simplex(x,y,z,o) + ctr = center(tet) -iref = BEAST.LagrangeRefSpace{T,1,4,4}() -oref = BEAST.NDLCCRefSpace{T}() -ishp = BEAST.Shape{T}(1, 3, 1.0) + iref = BEAST.LagrangeRefSpace{T,1,4,4}() + oref = BEAST.NDLCCRefSpace{T}() + ishp = BEAST.Shape{T}(1, 3, 1.0) -output = BEAST.gradient(iref, ishp, tet) + output = BEAST.gradient(iref, ishp, tet) -global oval = point(T,0,0,0) -for oshp in output - oval += oref(ctr)[oshp.refid].value * oshp.coeff -end + global oval = point(T,0,0,0) + for oshp in output + oval += oref(ctr)[oshp.refid].value * oshp.coeff + end -@test oval ≈ point(T,0,0,1) + @test oval ≈ point(T,0,0,1) end \ No newline at end of file diff --git a/test/test_hh3dexc.jl b/test/test_hh3dexc.jl index 3b7119ea..c5f62325 100644 --- a/test/test_hh3dexc.jl +++ b/test/test_hh3dexc.jl @@ -3,39 +3,39 @@ using BEAST using Test for T in [Float32, Float64] -sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in"), T=T) -numcells(sphere) + sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in"), T=T) + numcells(sphere) -local κ = T(2π) -direction = point(T,0,0,1) -local f = BEAST.HH3DPlaneWave(direction, κ) + local κ = T(2π) + direction = point(T,0,0,1) + local f = BEAST.HH3DPlaneWave(direction, κ) -v1 = f(point(T,0,0,0)) -v2 = f(point(T,0,0,0.5)) + v1 = f(point(T,0,0,0)) + v2 = f(point(T,0,0,0.5)) -@test v1 ≈ +1 -@test v2 ≈ -1 + @test v1 ≈ +1 + @test v2 ≈ -1 -import BEAST.∂n -p = ∂n(f) + import BEAST.∂n + p = ∂n(f) -local s = chart(sphere,first(cells(sphere))) -local c = neighborhood(s, T.([1,1]/3)) + local s = chart(sphere,first(cells(sphere))) + local c = neighborhood(s, T.([1,1]/3)) -r = cartesian(c) -local n = normal(s) + r = cartesian(c) + local n = normal(s) -w1 = p(c) -w2 = -im*κ*dot(direction, n)*f(r) + w1 = p(c) + w2 = -im*κ*dot(direction, n)*f(r) -w1 ≈ w2 + w1 ≈ w2 -local N = BEAST.HH3DHyperSingularFDBIO(im*κ) -local X = BEAST.lagrangec0d1(sphere) + local N = BEAST.HH3DHyperSingularFDBIO(im*κ) + local X = BEAST.lagrangec0d1(sphere) -numfunctions(X) + numfunctions(X) -Nxx = assemble(N, X, X) + Nxx = assemble(N, X, X) -@test size(Nxx) == (numfunctions(X), numfunctions(X)) + @test size(Nxx) == (numfunctions(X), numfunctions(X)) end \ No newline at end of file diff --git a/test/test_hh3dtd_exc.jl b/test/test_hh3dtd_exc.jl index cda4f1a6..a400dad6 100644 --- a/test/test_hh3dtd_exc.jl +++ b/test/test_hh3dtd_exc.jl @@ -2,34 +2,34 @@ using BEAST using CompScienceMeshes using Test for T in [Float32, Float64] -dir = point(T,0,0,1) -local width = T(1.0) -delay = T(1.5) -scaling = T(1.0) -sig = creategaussian(width, delay, scaling) + dir = point(T,0,0,1) + local width = T(1.0) + delay = T(1.5) + scaling = T(1.0) + sig = creategaussian(width, delay, scaling) -pw = BEAST.planewave(dir, T(1.0), sig) + pw = BEAST.planewave(dir, T(1.0), sig) -CompScienceMeshes.cartesian(p::typeof(dir)) = p -CompScienceMeshes.cartesian(p::Number) = p -local x = point(T,1,0,0) -local t = T(1.0) -@test pw(x,t) ≈ sig(t-dot(dir,x)) + CompScienceMeshes.cartesian(p::typeof(dir)) = p + CompScienceMeshes.cartesian(p::Number) = p + local x = point(T,1,0,0) + local t = T(1.0) + @test pw(x,t) ≈ sig(t-dot(dir,x)) -pw2 = BEAST.gradient(pw) -@test pw2.direction ≈ dir -@test pw2.polarisation ≈ -dir -@test pw2.speedoflight ≈ pw.speed_of_light + pw2 = BEAST.gradient(pw) + @test pw2.direction ≈ dir + @test pw2.polarisation ≈ -dir + @test pw2.speedoflight ≈ pw.speed_of_light -dsig = derive(sig) -@test pw2(x,t) ≈ -dir*dsig(t-dot(dir,x)) + dsig = derive(sig) + @test pw2(x,t) ≈ -dir*dsig(t-dot(dir,x)) -trc = dot(BEAST.n,pw2) -ch = simplex( - point(T,0,0,0), - point(T,1,0,0), - point(T,0,1,0)) -ctr = center(ch) -val = trc(ctr,t) -@test val ≈ -dsig(t-dot(dir,x)) + trc = dot(BEAST.n,pw2) + ch = simplex( + point(T,0,0,0), + point(T,1,0,0), + point(T,0,1,0)) + ctr = center(ch) + val = trc(ctr,t) + @test val ≈ -dsig(t-dot(dir,x)) end \ No newline at end of file diff --git a/test/test_local_storage.jl b/test/test_local_storage.jl index 7edfdd55..7e186ae1 100644 --- a/test/test_local_storage.jl +++ b/test/test_local_storage.jl @@ -3,20 +3,20 @@ using BEAST using CompScienceMeshes using SparseArrays for T in [Float32, Float64] -local fn = joinpath(@__DIR__, "assets/sphere5.in") -local m = readmesh(fn, T=T) + local fn = joinpath(@__DIR__, "assets/sphere5.in") + local m = readmesh(fn, T=T) -Id = BEAST.Identity() -local X = BEAST.raviartthomas(m) + Id = BEAST.Identity() + local X = BEAST.raviartthomas(m) -Z1 = assemble(Id, X, X, storage_policy=Val{:densestorage}) -Z2 = assemble(Id, X, X, storage_policy=Val{:bandedstorage}) -Z3 = assemble(Id, X, X, storage_policy=Val{:sparsedicts}) + Z1 = assemble(Id, X, X, storage_policy=Val{:densestorage}) + Z2 = assemble(Id, X, X, storage_policy=Val{:bandedstorage}) + Z3 = assemble(Id, X, X, storage_policy=Val{:sparsedicts}) -@test Z1 isa DenseMatrix -@test Z2 isa SparseMatrixCSC -@test Z3 isa SparseMatrixCSC + @test Z1 isa DenseMatrix + @test Z2 isa SparseMatrixCSC + @test Z3 isa SparseMatrixCSC -@test Z1 ≈ Z2 atol=1e-8 -@test Z1 ≈ Z3 atol=1e-8 + @test Z1 ≈ Z2 atol=1e-8 + @test Z1 ≈ Z3 atol=1e-8 end \ No newline at end of file diff --git a/test/test_mixed_blkassm.jl b/test/test_mixed_blkassm.jl index 005cff9e..d49d485c 100644 --- a/test/test_mixed_blkassm.jl +++ b/test/test_mixed_blkassm.jl @@ -25,44 +25,44 @@ function hassemble(operator::BEAST.AbstractOperator, end for T in [Float32, Float64] -c = T(3e8) -μ = T(4π * 1e-7) -ε = T(1/(μ*c^2)) -local f = T(1e8) -λ = T(c/f) -k = T(2π/λ) -local ω = T(k*c) -η = T(sqrt(μ/ε)) + c = T(3e8) + μ = T(4π * 1e-7) + ε = T(1/(μ*c^2)) + local f = T(1e8) + λ = T(c/f) + k = T(2π/λ) + local ω = T(k*c) + η = T(sqrt(μ/ε)) -a = T(1) -local Γ = CompScienceMeshes.meshcuboid(a,a,a,T(0.2)) + a = T(1) + local Γ = CompScienceMeshes.meshcuboid(a,a,a,T(0.2)) -𝓣 = Maxwell3D.singlelayer(wavenumber=k) -𝓚 = Maxwell3D.doublelayer(wavenumber=k) + 𝓣 = Maxwell3D.singlelayer(wavenumber=k) + 𝓚 = Maxwell3D.doublelayer(wavenumber=k) -local X = raviartthomas(Γ) -local Y = buffachristiansen(Γ) + local X = raviartthomas(Γ) + local Y = buffachristiansen(Γ) -println("Number of RWG functions: ", numfunctions(X)) + println("Number of RWG functions: ", numfunctions(X)) -T_blockassembler = hassemble(𝓣, X, X) -T_standardassembler = assemble(𝓣, X, X) + T_blockassembler = hassemble(𝓣, X, X) + T_standardassembler = assemble(𝓣, X, X) -@test norm(T_blockassembler - T_standardassembler)/norm(T_standardassembler) ≈ 0.0 atol=100*eps(T) + @test norm(T_blockassembler - T_standardassembler)/norm(T_standardassembler) ≈ 0.0 atol=100*eps(T) -T_bc_blockassembler = hassemble(𝓣, Y, Y) -T_bc_standardassembler = assemble(𝓣, Y, Y) + T_bc_blockassembler = hassemble(𝓣, Y, Y) + T_bc_standardassembler = assemble(𝓣, Y, Y) -@test norm(T_bc_blockassembler - T_bc_standardassembler)/norm(T_bc_standardassembler) ≈ 0.0 atol=100*eps(T) + @test norm(T_bc_blockassembler - T_bc_standardassembler)/norm(T_bc_standardassembler) ≈ 0.0 atol=100*eps(T) -K_mix_blockassembler = hassemble(𝓚,Y,X) -K_mix_standardassembler = assemble(𝓚,Y,X) + K_mix_blockassembler = hassemble(𝓚,Y,X) + K_mix_standardassembler = assemble(𝓚,Y,X) -T_mix_blockassembler = hassemble(𝓣, Y, X) -T_mix_standardassembler = assemble(𝓣, Y, X) + T_mix_blockassembler = hassemble(𝓣, Y, X) + T_mix_standardassembler = assemble(𝓣, Y, X) -if T==Float64 -@test norm(K_mix_blockassembler - K_mix_standardassembler)/norm(K_mix_standardassembler) ≈ 0.0 atol=100*eps(T) -@test norm(T_mix_blockassembler - T_mix_standardassembler)/norm(T_mix_standardassembler) ≈ 0.0 atol=100*eps(T) -end + if T==Float64 + @test norm(K_mix_blockassembler - K_mix_standardassembler)/norm(K_mix_standardassembler) ≈ 0.0 atol=100*eps(T) + @test norm(T_mix_blockassembler - T_mix_standardassembler)/norm(T_mix_standardassembler) ≈ 0.0 atol=100*eps(T) + end end \ No newline at end of file diff --git a/test/test_mult.jl b/test/test_mult.jl index 537d0e9f..a8d01f52 100644 --- a/test/test_mult.jl +++ b/test/test_mult.jl @@ -5,30 +5,30 @@ using Test using LinearAlgebra for T in [Float32, Float64] -faces = meshrectangle(T(1.0), T(1.0), T(0.5), 3) -srt_bnd_faces = sort.(boundary(faces)) -local edges = submesh(skeleton(faces,1)) do edge - !(sort(edge) in srt_bnd_faces) -end + faces = meshrectangle(T(1.0), T(1.0), T(0.5), 3) + srt_bnd_faces = sort.(boundary(faces)) + local edges = submesh(skeleton(faces,1)) do edge + !(sort(edge) in srt_bnd_faces) + end -srt_bnd_nodes = sort.(skeleton(boundary(faces),0)) -@test length(srt_bnd_nodes) == 8 -nodes = submesh(skeleton(faces,0)) do node - !(sort(node) in srt_bnd_nodes) -end -@test length(nodes) == 1 + srt_bnd_nodes = sort.(skeleton(boundary(faces),0)) + @test length(srt_bnd_nodes) == 8 + nodes = submesh(skeleton(faces,0)) do node + !(sort(node) in srt_bnd_nodes) + end + @test length(nodes) == 1 -Conn = connectivity(nodes, edges, sign) + Conn = connectivity(nodes, edges, sign) -local X = raviartthomas(faces, cellpairs(faces,edges)) -@test numfunctions(X) == 8 + local X = raviartthomas(faces, cellpairs(faces,edges)) + @test numfunctions(X) == 8 -divX = divergence(X) -Id = BEAST.Identity() -DD = assemble(Id, divX, divX) -@test rank(DD) == 7 -L = divX * Conn -for sh in L.fns[1] - @test isapprox(sh.coeff, 0, atol=1e-8) -end + divX = divergence(X) + Id = BEAST.Identity() + DD = assemble(Id, divX, divX) + @test rank(DD) == 7 + L = divX * Conn + for sh in L.fns[1] + @test isapprox(sh.coeff, 0, atol=1e-8) + end end \ No newline at end of file diff --git a/test/test_ndlcd_restrict.jl b/test/test_ndlcd_restrict.jl index b2e0495c..fc88e1e0 100644 --- a/test/test_ndlcd_restrict.jl +++ b/test/test_ndlcd_restrict.jl @@ -4,35 +4,35 @@ using Test using LinearAlgebra for T in [Float32, Float64] -local o, x, y, z = CompScienceMeshes.euclidianbasis(3,T) -tet = simplex(x,y,z,o) + local o, x, y, z = CompScienceMeshes.euclidianbasis(3,T) + tet = simplex(x,y,z,o) -rs = BEAST.NDLCDRefSpace{T}() -Q = BEAST.restrict(rs, tet, tet) -@test Q ≈ Matrix(T(1.0)LinearAlgebra.I, 4, 4) + rs = BEAST.NDLCDRefSpace{T}() + Q = BEAST.restrict(rs, tet, tet) + @test Q ≈ Matrix(T(1.0)LinearAlgebra.I, 4, 4) -rs = BEAST.NDLCCRefSpace{T}() -Q = BEAST.restrict(rs, tet, tet) -@test Q ≈ Matrix(T(1.0)LinearAlgebra.I, 6, 6) + rs = BEAST.NDLCCRefSpace{T}() + Q = BEAST.restrict(rs, tet, tet) + @test Q ≈ Matrix(T(1.0)LinearAlgebra.I, 6, 6) -c = cartesian(center(tet)) -smalltet = simplex(x,y,z,c) -p_smalltet = center(smalltet) -p_tet = neighborhood(tet, carttobary(tet, cartesian(p_smalltet))) -@show cartesian(p_smalltet) -@show cartesian(p_tet) -@assert cartesian(p_tet) ≈ cartesian(p_smalltet) -@assert volume(tet) / volume(smalltet) ≈ 4 + c = cartesian(center(tet)) + smalltet = simplex(x,y,z,c) + p_smalltet = center(smalltet) + p_tet = neighborhood(tet, carttobary(tet, cartesian(p_smalltet))) + @show cartesian(p_smalltet) + @show cartesian(p_tet) + @assert cartesian(p_tet) ≈ cartesian(p_smalltet) + @assert volume(tet) / volume(smalltet) ≈ 4 -Q = BEAST.restrict(rs, tet, smalltet) -Fp = rs(p_tet) -fp = rs(p_smalltet) + Q = BEAST.restrict(rs, tet, smalltet) + Fp = rs(p_tet) + fp = rs(p_smalltet) -for j in axes(Q,1) - x = Fp[j].value - y = sum(Q[j,i]*fp[i].value for i in axes(Q,2)) - @show x - @show y - @test x ≈ y -end + for j in axes(Q,1) + x = Fp[j].value + y = sum(Q[j,i]*fp[i].value for i in axes(Q,2)) + @show x + @show y + @test x ≈ y + end end \ No newline at end of file diff --git a/test/test_ndspace.jl b/test/test_ndspace.jl index 6b8a304e..7755120c 100644 --- a/test/test_ndspace.jl +++ b/test/test_ndspace.jl @@ -4,34 +4,34 @@ using Test for T in [Float32, Float64] -fn = joinpath(dirname(@__FILE__),"assets","rect1.in") - -mesh = readmesh(fn, T=T) -edgs = skeleton(mesh,1) - -charts = [chart(edgs,edg) for edg in cells(edgs)] -ctrs = [cartesian(center(cht)) for cht in charts] - -ND = BEAST.nedelec(mesh, edgs) - -@test numfunctions(ND) == numcells(edgs) -@test length(ND.fns[1]) == 1 -@test length(ND.fns[2]) == 1 -@test length(ND.fns[3]) == 2 -@test length(ND.fns[4]) == 1 -@test length(ND.fns[5]) == 1 - -# center of the diagonal -ctr = center(charts[3]) -face_charts = [chart(mesh,fce) for fce in cells(mesh)] -nbd1 = neighborhood(face_charts[1], carttobary(face_charts[1], ctr)) -nbd2 = neighborhood(face_charts[2], carttobary(face_charts[2], ctr)) -t = tangents(ctr,1) -ut = t / norm(t) - -ndlocal = refspace(ND) -fn = ND.fns[3] -v1 = dot(fn[1].coeff*ndlocal(nbd1)[1][1],ut) -v2 = dot(fn[2].coeff*ndlocal(nbd2)[2][1],ut) -@test v1 ≈ v2 ≈ -1/√2 ≈ -1/volume(charts[3]) + fn = joinpath(dirname(@__FILE__),"assets","rect1.in") + + mesh = readmesh(fn, T=T) + edgs = skeleton(mesh,1) + + charts = [chart(edgs,edg) for edg in cells(edgs)] + ctrs = [cartesian(center(cht)) for cht in charts] + + ND = BEAST.nedelec(mesh, edgs) + + @test numfunctions(ND) == numcells(edgs) + @test length(ND.fns[1]) == 1 + @test length(ND.fns[2]) == 1 + @test length(ND.fns[3]) == 2 + @test length(ND.fns[4]) == 1 + @test length(ND.fns[5]) == 1 + + # center of the diagonal + ctr = center(charts[3]) + face_charts = [chart(mesh,fce) for fce in cells(mesh)] + nbd1 = neighborhood(face_charts[1], carttobary(face_charts[1], ctr)) + nbd2 = neighborhood(face_charts[2], carttobary(face_charts[2], ctr)) + t = tangents(ctr,1) + ut = t / norm(t) + + ndlocal = refspace(ND) + fn = ND.fns[3] + v1 = dot(fn[1].coeff*ndlocal(nbd1)[1][1],ut) + v2 = dot(fn[2].coeff*ndlocal(nbd2)[2][1],ut) + @test v1 ≈ v2 ≈ -1/√2 ≈ -1/volume(charts[3]) end \ No newline at end of file diff --git a/test/test_raviartthomas.jl b/test/test_raviartthomas.jl index ac1acd54..d2a38fa6 100644 --- a/test/test_raviartthomas.jl +++ b/test/test_raviartthomas.jl @@ -23,39 +23,39 @@ function neighbortest(X) end for T in [Float32,Float64] -tol = eps(T) * 10^3 + tol = eps(T) * 10^3 -mesh = meshrectangle(T(1.0), T(1.0), T(0.5)) -@test numvertices(mesh) == 9 + mesh = meshrectangle(T(1.0), T(1.0), T(0.5)) + @test numvertices(mesh) == 9 -idcs = mesh.faces[1] -@test size(idcs) == (3,) + idcs = mesh.faces[1] + @test size(idcs) == (3,) -verts = vertices(mesh, idcs) -@test size(verts) == (3,) + verts = vertices(mesh, idcs) + @test size(verts) == (3,) -faces = skeleton(mesh, 2) -idcs = faces.faces[1] -verts = vertices(mesh, idcs) -p = simplex(verts, Val{2}) -@test volume(p) == T(1/8) + faces = skeleton(mesh, 2) + idcs = faces.faces[1] + verts = vertices(mesh, idcs) + p = simplex(verts, Val{2}) + @test volume(p) == T(1/8) -edges = skeleton(mesh,1) -@test numcells(edges) == 16 + edges = skeleton(mesh,1) + @test numcells(edges) == 16 -cps = cellpairs(mesh, edges) -@test size(cps) == (2,16) + cps = cellpairs(mesh, edges) + @test size(cps) == (2,16) -# select only inner edges -I = findall(x->(x>0), cps[2,:]) -cps = cps[:,I] -@test size(cps) == (2,8) + # select only inner edges + I = findall(x->(x>0), cps[2,:]) + cps = cps[:,I] + @test size(cps) == (2,8) -# build the Raviart-Thomas elements -rt = raviartthomas(mesh, cps) -@test numfunctions(rt) == 8 + # build the Raviart-Thomas elements + rt = raviartthomas(mesh, cps) + @test numfunctions(rt) == 8 -neighbortest(rt) + neighbortest(rt) end \ No newline at end of file diff --git a/test/test_restrict.jl b/test/test_restrict.jl index 96c4757b..5a7d267d 100644 --- a/test/test_restrict.jl +++ b/test/test_restrict.jl @@ -11,60 +11,60 @@ const e3 = point(0.0,0.0,1.0) for T in [Float32, Float64] -p = simplex( - [ - point(T,0.0,0.0,0.0), - point(T,1.0,0.0,0.0) - ], - Val{1} -) + p = simplex( + [ + point(T,0.0,0.0,0.0), + point(T,1.0,0.0,0.0) + ], + Val{1} + ) -q = simplex( - [ - point(T,0.0,0.0,0.0), - point(T,0.5,0.0,0.0) - ], - Val{1} -) + q = simplex( + [ + point(T,0.0,0.0,0.0), + point(T,0.5,0.0,0.0) + ], + Val{1} + ) -f = BEAST.LagrangeRefSpace{T,1,2,2}() -local x = neighborhood(p, T.([0.0])) -v = f(x) + f = BEAST.LagrangeRefSpace{T,1,2,2}() + local x = neighborhood(p, T.([0.0])) + v = f(x) -@test v[1].value == 0 -@test v[2].value == 1 + @test v[1].value == 0 + @test v[2].value == 1 -@test v[1].derivative == -1 -@test v[2].derivative == +1 + @test v[1].derivative == -1 + @test v[2].derivative == +1 -Q = restrict(f, p, q) -@test Q == [ - T(1.0) T(0.5) - T(0.0) T(0.5)] + Q = restrict(f, p, q) + @test Q == [ + T(1.0) T(0.5) + T(0.0) T(0.5)] -# Test restriction of RT elements -ni, no = 6, 7; -ui = transpose([triangleGaussA[ni] triangleGaussB[ni] ]); -uo = transpose([triangleGaussA[no] triangleGaussB[no] ]); -wi = triangleGaussW[ni]; -wo = triangleGaussW[no]; -# universe = Universe(1.0, ui, wi, uo, wo); + # Test restriction of RT elements + ni, no = 6, 7; + ui = transpose([triangleGaussA[ni] triangleGaussB[ni] ]); + uo = transpose([triangleGaussA[no] triangleGaussB[no] ]); + wi = triangleGaussW[ni]; + wo = triangleGaussW[no]; + # universe = Universe(1.0, ui, wi, uo, wo); -p = simplex([T.(2*e0),T.(2*e1),T.(2*e2)], Val{2}) -x = neighborhood(p,T.([0.5, 0.5])) -ϕ = BEAST.RTRefSpace{T}() -v = ϕ(x) + p = simplex([T.(2*e0),T.(2*e1),T.(2*e2)], Val{2}) + x = neighborhood(p,T.([0.5, 0.5])) + ϕ = BEAST.RTRefSpace{T}() + v = ϕ(x) -Q = restrict(ϕ, p, p) -if T==Float64 -@test Q == Matrix(LinearAlgebra.I, 3, 3) + Q = restrict(ϕ, p, p) + if T==Float64 + @test Q == Matrix(LinearAlgebra.I, 3, 3) -q = simplex([T.(e0+e1), T.(2*e1), T.(e1+e2)], Val{2}) -Q = restrict(ϕ, p, q) -@test Q == [ - 2 -1 0 - 0 1 0 - 0 -1 2] // 4 -end + q = simplex([T.(e0+e1), T.(2*e1), T.(e1+e2)], Val{2}) + Q = restrict(ϕ, p, q) + @test Q == [ + 2 -1 0 + 0 1 0 + 0 -1 2] // 4 + end end \ No newline at end of file diff --git a/test/test_rt3d.jl b/test/test_rt3d.jl index 7e0bc9cc..27cdf8c6 100644 --- a/test/test_rt3d.jl +++ b/test/test_rt3d.jl @@ -4,46 +4,46 @@ using BEAST using Test for T in [Float32, Float64] -local o, x, y, z = euclidianbasis(3,T) -tet = simplex(x,y,z,o) - -nbd1 = neighborhood(tet, T.([0,1,1]/3)) -nbd2 = neighborhood(tet, T.([1,0,1]/3)) -nbd3 = neighborhood(tet, T.([1,1,0]/3)) -nbd4 = neighborhood(tet, T.([1,1,1]/3)) - -rs = BEAST.NDLCDRefSpace{T}() -fcs = BEAST.faces(tet) -@test dot(rs(nbd1)[1].value, normal(fcs[1])) > 0 -@test dot(rs(nbd2)[2].value, normal(fcs[2])) > 0 -@test dot(rs(nbd3)[3].value, normal(fcs[3])) > 0 -@test dot(rs(nbd4)[4].value, normal(fcs[4])) > 0 - -dot(rs(nbd1)[1].value, normal(fcs[1])) * volume(fcs[1]) ≈ 1 -dot(rs(nbd2)[2].value, normal(fcs[2])) * volume(fcs[2]) ≈ 1 -dot(rs(nbd3)[3].value, normal(fcs[3])) * volume(fcs[2]) ≈ 1 -dot(rs(nbd4)[4].value, normal(fcs[4])) * volume(fcs[4]) ≈ 1 - -@test dot(rs(nbd1)[2].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=eps(T) -@test dot(rs(nbd1)[3].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=eps(T) -@test dot(rs(nbd1)[4].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=eps(T) - -@test dot(rs(nbd2)[1].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=eps(T) -@test dot(rs(nbd2)[3].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=eps(T) -@test dot(rs(nbd2)[4].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=eps(T) - -@test dot(rs(nbd3)[1].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=eps(T) -@test dot(rs(nbd3)[2].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=eps(T) -@test dot(rs(nbd3)[4].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=eps(T) - -@test dot(rs(nbd4)[1].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=eps(T) -@test dot(rs(nbd4)[2].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=eps(T) -@test dot(rs(nbd4)[3].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=eps(T) - - -ctr = cartesian(center(tet)) -@test dot(cartesian(nbd1)-ctr, normal(fcs[1])) > 0 -@test dot(cartesian(nbd2)-ctr, normal(fcs[2])) > 0 -@test dot(cartesian(nbd3)-ctr, normal(fcs[3])) > 0 -@test dot(cartesian(nbd4)-ctr, normal(fcs[4])) > 0 + local o, x, y, z = euclidianbasis(3,T) + tet = simplex(x,y,z,o) + + nbd1 = neighborhood(tet, T.([0,1,1]/3)) + nbd2 = neighborhood(tet, T.([1,0,1]/3)) + nbd3 = neighborhood(tet, T.([1,1,0]/3)) + nbd4 = neighborhood(tet, T.([1,1,1]/3)) + + rs = BEAST.NDLCDRefSpace{T}() + fcs = BEAST.faces(tet) + @test dot(rs(nbd1)[1].value, normal(fcs[1])) > 0 + @test dot(rs(nbd2)[2].value, normal(fcs[2])) > 0 + @test dot(rs(nbd3)[3].value, normal(fcs[3])) > 0 + @test dot(rs(nbd4)[4].value, normal(fcs[4])) > 0 + + dot(rs(nbd1)[1].value, normal(fcs[1])) * volume(fcs[1]) ≈ 1 + dot(rs(nbd2)[2].value, normal(fcs[2])) * volume(fcs[2]) ≈ 1 + dot(rs(nbd3)[3].value, normal(fcs[3])) * volume(fcs[2]) ≈ 1 + dot(rs(nbd4)[4].value, normal(fcs[4])) * volume(fcs[4]) ≈ 1 + + @test dot(rs(nbd1)[2].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=eps(T) + @test dot(rs(nbd1)[3].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=eps(T) + @test dot(rs(nbd1)[4].value, normal(fcs[1])) * volume(fcs[1]) ≈ 0 atol=eps(T) + + @test dot(rs(nbd2)[1].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=eps(T) + @test dot(rs(nbd2)[3].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=eps(T) + @test dot(rs(nbd2)[4].value, normal(fcs[2])) * volume(fcs[2]) ≈ 0 atol=eps(T) + + @test dot(rs(nbd3)[1].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=eps(T) + @test dot(rs(nbd3)[2].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=eps(T) + @test dot(rs(nbd3)[4].value, normal(fcs[3])) * volume(fcs[3]) ≈ 0 atol=eps(T) + + @test dot(rs(nbd4)[1].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=eps(T) + @test dot(rs(nbd4)[2].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=eps(T) + @test dot(rs(nbd4)[3].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=eps(T) + + + ctr = cartesian(center(tet)) + @test dot(cartesian(nbd1)-ctr, normal(fcs[1])) > 0 + @test dot(cartesian(nbd2)-ctr, normal(fcs[2])) > 0 + @test dot(cartesian(nbd3)-ctr, normal(fcs[3])) > 0 + @test dot(cartesian(nbd4)-ctr, normal(fcs[4])) > 0 end \ No newline at end of file diff --git a/test/test_rtx.jl b/test/test_rtx.jl index c9af6e88..11b53bc9 100644 --- a/test/test_rtx.jl +++ b/test/test_rtx.jl @@ -3,21 +3,21 @@ using BEAST using Test for T in [Float32, Float64] -local m = meshrectangle(T(1.0), T(1.0), T(0.25)) + local m = meshrectangle(T(1.0), T(1.0), T(0.25)) -local X = raviartthomas(m, BEAST.Continuity{:none}) -@test numfunctions(X) == 16*2*3 -@test all(length.(X.fns) .== 1) + local X = raviartthomas(m, BEAST.Continuity{:none}) + @test numfunctions(X) == 16*2*3 + @test all(length.(X.fns) .== 1) -p = positions(X) + p = positions(X) -i = 12 -c = X.fns[i][1].cellid -ch = chart(m, cells(m)[c]) -ctr = cartesian(center(ch)) -@test ctr ≈ p[i] + i = 12 + c = X.fns[i][1].cellid + ch = chart(m, cells(m)[c]) + ctr = cartesian(center(ch)) + @test ctr ≈ p[i] -for (i,f) in enumerate(X.fns) - @test f[1].coeff == T(1.0) -end + for (i,f) in enumerate(X.fns) + @test f[1].coeff == T(1.0) + end end \ No newline at end of file diff --git a/test/test_specials.jl b/test/test_specials.jl index d4f817f1..63bc1d0d 100644 --- a/test/test_specials.jl +++ b/test/test_specials.jl @@ -1,17 +1,17 @@ using BEAST for T in [Float32, Float64] -width, delay = T(4.0), T(6.0) -f = BEAST.Gaussian(width=width, delay=delay) -g = BEAST.integrate(f) + width, delay = T(4.0), T(6.0) + f = BEAST.Gaussian(width=width, delay=delay) + g = BEAST.integrate(f) -step = width/150 -x = range(delay-2*width, stop=delay+2*width, step=step) -# xc = 0.5*(x[1:end-1] + x[2:end]) + step = width/150 + x = range(delay-2*width, stop=delay+2*width, step=step) + # xc = 0.5*(x[1:end-1] + x[2:end]) -y1 = g.(x) -y2 = cumsum(f.(x))*step + y1 = g.(x) + y2 = cumsum(f.(x))*step -using LinearAlgebra -@assert norm(y1-y2, Inf) < T(1e-2) + using LinearAlgebra + @assert norm(y1-y2, Inf) < T(1e-2) end \ No newline at end of file diff --git a/test/test_ttrace.jl b/test/test_ttrace.jl index 2b867657..096a4b25 100644 --- a/test/test_ttrace.jl +++ b/test/test_ttrace.jl @@ -5,124 +5,124 @@ using Test using StaticArrays for T in [Float32, Float64] -local o, x, y, z = euclidianbasis(3, T) + local o, x, y, z = euclidianbasis(3, T) -p1 = 2x -p2 = y -p3 = 3z -p4 = o -tet = simplex(p1, p2, p3, p4) + p1 = 2x + p2 = y + p3 = 3z + p4 = o + tet = simplex(p1, p2, p3, p4) -q = abs(CompScienceMeshes.relorientation([1,2,3],[1,2,3,4])) -tri = simplex(p1, p2, p3) -local n = normal(tri) + q = abs(CompScienceMeshes.relorientation([1,2,3],[1,2,3,4])) + tri = simplex(p1, p2, p3) + local n = normal(tri) -i = 3 -local j = 3 -edg = simplex(p1, p2) + i = 3 + local j = 3 + edg = simplex(p1, p2) -x = BEAST.NDLCDRefSpace{T}() -y = BEAST.NDRefSpace{T}() + x = BEAST.NDLCDRefSpace{T}() + y = BEAST.NDRefSpace{T}() -p = neighborhood(tet, T.([0.5, 0.5, 0.0])) -r = neighborhood(tri, T.([0.5, 0.5])) + p = neighborhood(tet, T.([0.5, 0.5, 0.0])) + r = neighborhood(tri, T.([0.5, 0.5])) -@test carttobary(edg, cartesian(p)) ≈ T.([0.5]) -@test carttobary(edg, cartesian(r)) ≈ T.([0.5]) + @test carttobary(edg, cartesian(p)) ≈ T.([0.5]) + @test carttobary(edg, cartesian(r)) ≈ T.([0.5]) -xp = x(p)[j].value -yr = y(r)[i].value + xp = x(p)[j].value + yr = y(r)[i].value -local a, b = extrema((n × xp) ./ yr) -@test a ≈ b + local a, b = extrema((n × xp) ./ yr) + @test a ≈ b -tgt = p2 - p1 -a2 = dot(n × xp, tgt) / dot(yr, tgt) -@test a ≈ a2 + tgt = p2 - p1 + a2 = dot(n × xp, tgt) / dot(yr, tgt) + @test a ≈ a2 -z = a * yr -@test z ≈ n × xp + z = a * yr + @test z ≈ n × xp -volume(simplex(p1,p2,p4)) -volume(simplex(p1,p2)) + volume(simplex(p1,p2,p4)) + volume(simplex(p1,p2)) -Q = BEAST.ttrace(x, tet, q, tri) + Q = BEAST.ttrace(x, tet, q, tri) -p = neighborhood(tet, T.([1/3, 1/3, 1/3])) -r = neighborhood(tri, T.([1/3, 1/3])) -@test cartesian(p) ≈ cartesian(r) + p = neighborhood(tet, T.([1/3, 1/3, 1/3])) + r = neighborhood(tri, T.([1/3, 1/3])) + @test cartesian(p) ≈ cartesian(r) -xp = n × x(p)[j].value -yr = y(r)[i].value -@test xp ≈ yr * Q[i,j] + xp = n × x(p)[j].value + yr = y(r)[i].value + @test xp ≈ yr * Q[i,j] -x = BEAST.NDLCCRefSpace{T}() -y = BEAST.RTRefSpace{T}() -for q in 1:4 - q = 3 - fc = BEAST.faces(tet)[q] - Q = BEAST.ttrace(x, tet, q, fc) + x = BEAST.NDLCCRefSpace{T}() + y = BEAST.RTRefSpace{T}() + for q in 1:4 + q = 3 + fc = BEAST.faces(tet)[q] + Q = BEAST.ttrace(x, tet, q, fc) + + nbdi = CompScienceMeshes.center(fc) + nbdj = neighborhood(tet, carttobary(tet, cartesian(nbdi))) + + xvals = x(nbdj) + yvals = y(nbdi) + + for j in 1:6 + trc = sum(Q[i,j]*yvals[i].value for i in 1:3) + @test isapprox(trc, normal(fc) × xvals[j].value, atol=1e-4) + end + end + + # test the case where intrinsic and extrinsic orientations differ + o, x, y, z = euclidianbasis(3, T) + fc = simplex(z,o,y) + x = BEAST.NDLCCRefSpace{T}() + y = BEAST.RTRefSpace{T}() + Q = BEAST.ttrace(x, tet, 3000, fc) nbdi = CompScienceMeshes.center(fc) nbdj = neighborhood(tet, carttobary(tet, cartesian(nbdi))) + @test cartesian(nbdi) ≈ cartesian(nbdj) + xvals = x(nbdj) yvals = y(nbdi) for j in 1:6 trc = sum(Q[i,j]*yvals[i].value for i in 1:3) - @test isapprox(trc, normal(fc) × xvals[j].value, atol=1e-4) + @test isapprox(trc, -normal(fc) × xvals[j].value, atol=1e-4) end -end - -# test the case where intrinsic and extrinsic orientations differ -o, x, y, z = euclidianbasis(3, T) -fc = simplex(z,o,y) -x = BEAST.NDLCCRefSpace{T}() -y = BEAST.RTRefSpace{T}() -Q = BEAST.ttrace(x, tet, 3000, fc) - -nbdi = CompScienceMeshes.center(fc) -nbdj = neighborhood(tet, carttobary(tet, cartesian(nbdi))) - -@test cartesian(nbdi) ≈ cartesian(nbdj) - -xvals = x(nbdj) -yvals = y(nbdi) - -for j in 1:6 - trc = sum(Q[i,j]*yvals[i].value for i in 1:3) - @test isapprox(trc, -normal(fc) × xvals[j].value, atol=1e-4) -end -o, x, y, z = euclidianbasis(3) -local m = Mesh([x,y,z,o], [@SVector[1,2,3,4]]) + o, x, y, z = euclidianbasis(3) + local m = Mesh([x,y,z,o], [@SVector[1,2,3,4]]) -m1 = skeleton(m,1) -local X = BEAST.nedelecc3d(m, m1) -@test numfunctions(X) == 6 + m1 = skeleton(m,1) + local X = BEAST.nedelecc3d(m, m1) + @test numfunctions(X) == 6 -# m2 = skeleton(m,2) -m2 = boundary(m) -local Y = BEAST.ttrace(X,m2) -@test numfunctions(Y) == 6 + # m2 = skeleton(m,2) + m2 = boundary(m) + local Y = BEAST.ttrace(X,m2) + @test numfunctions(Y) == 6 -pa = Y.fns[1][1].cellid -pb = Y.fns[1][2].cellid + pa = Y.fns[1][1].cellid + pb = Y.fns[1][2].cellid -tria = chart(m2, cells(m2)[pa]) -trib = chart(m2, cells(m2)[pb]) + tria = chart(m2, cells(m2)[pa]) + trib = chart(m2, cells(m2)[pb]) -ra = Y.fns[1][1].refid -rb = Y.fns[1][2].refid + ra = Y.fns[1][1].refid + rb = Y.fns[1][2].refid -ctra = cartesian(CompScienceMeshes.center(CompScienceMeshes.edges(tria)[ra])) -ctrb = cartesian(CompScienceMeshes.center(CompScienceMeshes.edges(trib)[rb])) -@test ctra ≈ ctrb -# tri1 = chart(m, cells(m)[Y.fns[1][1].cellid]) + ctra = cartesian(CompScienceMeshes.center(CompScienceMeshes.edges(tria)[ra])) + ctrb = cartesian(CompScienceMeshes.center(CompScienceMeshes.edges(trib)[rb])) + @test ctra ≈ ctrb + # tri1 = chart(m, cells(m)[Y.fns[1][1].cellid]) -# ctr1 = cartesian(center(CompScienceMeshes.edges(tri)[])) + # ctr1 = cartesian(center(CompScienceMeshes.edges(tri)[])) end \ No newline at end of file From 0fc8d8967825113b76543478013c687acb8665ce Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 21 Jun 2022 15:32:31 +0200 Subject: [PATCH 160/528] first step towards generic sauterschwab routine --- src/bases/local/bdmlocal.jl | 29 +++++++++++++ src/bases/local/rtlocal.jl | 23 ++++++++++ src/maxwell/sauterschwabints_bdm.jl | 66 ++++++++++++++++++++--------- 3 files changed, 99 insertions(+), 19 deletions(-) diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index 8991e587..a0b62eda 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -18,3 +18,32 @@ function (f::BDMRefSpace)(p) (value=u*tu/j, divergence=d), (value=v*tv/j, divergence=d),] end + + + +const _vert_perms_bdm = [ + (1,2,3), + (2,3,1), + (3,1,2), + (2,1,3), + (1,3,2), + (3,2,1), +] +const _dof_perms_bdm = [ + (1,2,3,4,5,6), + (5,6,1,2,3,4), + (3,4,5,6,1,2), + (4,3,2,1,6,5), + (2,1,6,5,4,3), + (6,5,4,3,2,1), +] + +function dof_permutation(::BDMRefSpace, vert_permutation) + i = findfirst(==(tuple(vert_permutation...)), _vert_perms_bdm) + if !(i != nothing) + @show vert_permutation + @show i + error() + end + return _dof_perms_bdm[i] +end \ No newline at end of file diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index a54ea44e..c0c310ba 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -79,3 +79,26 @@ function restrict(ϕ::RTRefSpace{T}, dom1, dom2) where T return Q end + + +const _vert_perms_rt = [ + (1,2,3), + (2,3,1), + (3,1,2), + (2,1,3), + (1,3,2), + (3,2,1), +] +const _dof_perms_rt = [ + (1,2,3), + (3,1,2), + (2,3,1), + (2,1,3), + (1,3,2), + (3,2,1), +] + +function dof_permutation(vert_permutation) + i = findfirst(==(vert_permutation), _vert_perms_rt) + return _dof_perms_rt[i] +end \ No newline at end of file diff --git a/src/maxwell/sauterschwabints_bdm.jl b/src/maxwell/sauterschwabints_bdm.jl index 49034734..dd3c2b72 100644 --- a/src/maxwell/sauterschwabints_bdm.jl +++ b/src/maxwell/sauterschwabints_bdm.jl @@ -29,14 +29,51 @@ function (igd::MWSL3DIntegrand2)(u,v) G = @SVector[αjG*g[i].value for i in 1:6] H = @SVector[βjG*g[i].divergence for i in 1:6] - @SMatrix[dot(f[i].value,G[j])+f[i].divergence*H[j] for i in 1:6, j in 1:6] + SMatrix{6,6}(tuple( + dot(f[1].value,G[1])+f[1].divergence*H[1], + dot(f[2].value,G[1])+f[2].divergence*H[1], + dot(f[3].value,G[1])+f[3].divergence*H[1], + dot(f[4].value,G[1])+f[4].divergence*H[1], + dot(f[5].value,G[1])+f[5].divergence*H[1], + dot(f[6].value,G[1])+f[6].divergence*H[1], + dot(f[1].value,G[2])+f[1].divergence*H[2], + dot(f[2].value,G[2])+f[2].divergence*H[2], + dot(f[3].value,G[2])+f[3].divergence*H[2], + dot(f[4].value,G[2])+f[4].divergence*H[2], + dot(f[5].value,G[2])+f[5].divergence*H[2], + dot(f[6].value,G[2])+f[6].divergence*H[2], + dot(f[1].value,G[3])+f[1].divergence*H[3], + dot(f[2].value,G[3])+f[2].divergence*H[3], + dot(f[3].value,G[3])+f[3].divergence*H[3], + dot(f[4].value,G[3])+f[4].divergence*H[3], + dot(f[5].value,G[3])+f[5].divergence*H[3], + dot(f[6].value,G[3])+f[6].divergence*H[3], + dot(f[1].value,G[4])+f[1].divergence*H[4], + dot(f[2].value,G[4])+f[2].divergence*H[4], + dot(f[3].value,G[4])+f[3].divergence*H[4], + dot(f[4].value,G[4])+f[4].divergence*H[4], + dot(f[5].value,G[4])+f[5].divergence*H[4], + dot(f[6].value,G[4])+f[6].divergence*H[4], + dot(f[1].value,G[5])+f[1].divergence*H[5], + dot(f[2].value,G[5])+f[2].divergence*H[5], + dot(f[3].value,G[5])+f[3].divergence*H[5], + dot(f[4].value,G[5])+f[4].divergence*H[5], + dot(f[5].value,G[5])+f[5].divergence*H[5], + dot(f[6].value,G[5])+f[6].divergence*H[5], + dot(f[1].value,G[6])+f[1].divergence*H[6], + dot(f[2].value,G[6])+f[2].divergence*H[6], + dot(f[3].value,G[6])+f[3].divergence*H[6], + dot(f[4].value,G[6])+f[4].divergence*H[6], + dot(f[5].value,G[6])+f[5].divergence*H[6], + dot(f[6].value,G[6])+f[6].divergence*H[6], + )) end function momintegrals!(op::MWSingleLayer3D, test_local_space::BDMRefSpace, trial_local_space::BDMRefSpace, test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) - I, J, K, L = SauterSchwabQuadrature.reorder( + I, J, _, _ = SauterSchwabQuadrature.reorder( test_triangular_element.vertices, trial_triangular_element.vertices, strat) @@ -52,23 +89,14 @@ function momintegrals!(op::MWSingleLayer3D, igd = MWSL3DIntegrand2(test_triangular_element, trial_triangular_element, op, test_local_space, trial_local_space) - G = SauterSchwabQuadrature.Sautersauterschwab_parameterized(igd, strat) - - A = levicivita(K) == 1 ? @SVector[1,2] : @SVector[2,1] - B = levicivita(L) == 1 ? @SVector[1,2] : @SVector[2,1] - for j ∈ 1:3 - q = L[j] - for i ∈ 1:3 - p = K[i] - for n in 1:2 - b = B[n] - for m in 1:2 - a = A[m] - out[2*(i-1)+m,2*(j-1)+n] += G[2*(p-1)+a,2*(q-1)+b] - end - end - end - end + G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) + + K = dof_permutation(test_local_space, I) + L = dof_permutation(trial_local_space, J) + for i in 1:numfunctions(test_local_space) + for j in 1:numfunctions(trial_local_space) + out[i,j] = G[K[i],L[j]] + end end nothing end From 36b5f14dbbd91082525b05967fc0004fcc6ab40e Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 21 Jun 2022 17:20:51 +0200 Subject: [PATCH 161/528] remove Polyester from deps --- Project.toml | 5 ++--- src/BEAST.jl | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Project.toml b/Project.toml index 06b2c196..c9c992b3 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.4.0" +version = "1.5.0-dev" [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" @@ -17,7 +17,6 @@ IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LiftedMaps = "d22a30c1-52ac-4762-a8c9-5838452405e0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" -Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" SauterSchwab3D = "0a13313b-1c00-422e-8263-562364ed9544" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" @@ -38,7 +37,7 @@ FastGaussQuadrature = "0.3, 0.4" FillArrays = "0.11, 0.12, 0.13" IterativeSolvers = "0.9" LiftedMaps = "0.3" -LinearMaps = "3.5" +LinearMaps = "3.7" SauterSchwab3D = "0.1.0" SauterSchwabQuadrature = "2.1.3" SparseMatrixDicts = "0.2" diff --git a/src/BEAST.jl b/src/BEAST.jl index 25b37c90..ca634a3f 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -7,7 +7,6 @@ using SparseArrays using FillArrays using BlockArrays using SparseMatrixDicts -using Polyester using SauterSchwabQuadrature using SauterSchwab3D From a1254289f356d8ec45c6240b230af514ed92c1db Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 22 Jun 2022 19:37:58 +0200 Subject: [PATCH 162/528] support new FIllArrays 0.13 --- Project.toml | 2 +- src/solvers/lusolver.jl | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Project.toml b/Project.toml index 973262f7..a7de95f6 100644 --- a/Project.toml +++ b/Project.toml @@ -34,7 +34,7 @@ CompScienceMeshes = "0.3.2" Compat = "2, 3" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4" -FillArrays = "0.11, 0.12" +FillArrays = "0.11, 0.12, 0.13" IterativeSolvers = "0.9" LiftedMaps = "0.3" LinearMaps = "3.5" diff --git a/src/solvers/lusolver.jl b/src/solvers/lusolver.jl index f32057e6..ce728402 100644 --- a/src/solvers/lusolver.jl +++ b/src/solvers/lusolver.jl @@ -2,20 +2,20 @@ using LinearAlgebra function convert_to_dense(A::LinearMaps.LinearCombination) - @info "convert matrix to dense..." - T = eltype(A) - # M = zeros(T, size(A)) - # M = PseudoBlockMatrix{T}(undef, BlockArrays.blocksizes(A)...) - # fill!(M,0) - M = zeros(eltype(A), axes(A)) - @show typeof(M) - for map in A.maps - mul!(M, 1, map, 1, 1) - # M .+= Matrix(map) - end - - @info "matrix converted to dense" - return M + # @info "convert matrix to dense..." + # T = eltype(A) + # # M = zeros(T, size(A)) + # # M = PseudoBlockMatrix{T}(undef, BlockArrays.blocksizes(A)...) + # # fill!(M,0) + # M = zeros(eltype(A), axes(A)) + # @show typeof(M) + # for map in A.maps + # mul!(M, 1, map, 1, 1) + # end + + # @info "matrix converted to dense" + # return M + return Matrix(A) end """ From d9ca511ec4c7228e518a40a9261aeebbcd4640ba Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 22 Jun 2022 19:39:55 +0200 Subject: [PATCH 163/528] Require LiftedMaps 0.4 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c9c992b3..fb1eeaa6 100644 --- a/Project.toml +++ b/Project.toml @@ -36,7 +36,7 @@ FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4" FillArrays = "0.11, 0.12, 0.13" IterativeSolvers = "0.9" -LiftedMaps = "0.3" +LiftedMaps = "0.4" LinearMaps = "3.7" SauterSchwab3D = "0.1.0" SauterSchwabQuadrature = "2.1.3" From 1c7e80c13aaa2bbd529e256e3e4193e38c039d7f Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 28 Jun 2022 14:11:00 +0200 Subject: [PATCH 164/528] Increased verision to 0.5.0 tightened compat reqs for LiftedMaps, WiltonInts84, and CompScienceMeshes --- Project.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Project.toml b/Project.toml index cf76c90c..b86f636d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.5.0-dev" +version = "1.5.0" [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" @@ -30,20 +30,20 @@ WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" CollisionDetection = "0.1" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.3.2" +CompScienceMeshes = "0.3.3" Compat = "2, 3, 4" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4" FillArrays = "0.11, 0.12, 0.13" IterativeSolvers = "0.9" -LiftedMaps = "0.4" +LiftedMaps = "0.4.1" LinearMaps = "3.7" SauterSchwab3D = "0.1.0" SauterSchwabQuadrature = "2.1.3" SparseMatrixDicts = "0.2" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" -WiltonInts84 = "0.2" +WiltonInts84 = "0.2.3" julia = "1.6" [extras] From 4d3a3dc9cf402468904fa32a0b97da2bf32964b5 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 29 Jun 2022 17:21:04 +0200 Subject: [PATCH 165/528] First steps in factoring out SauterSchwab repetitive code --- examples/efie.jl | 5 ++- examples/efie_bdm.jl | 5 ++- examples/mfie_bdm.jl | 6 ++- examples/tdefie.jl | 8 ++-- examples/tdpmchwt.jl | 3 +- src/BEAST.jl | 2 +- src/maxwell/nxdbllayer.jl | 2 +- src/maxwell/sauterschwabints_bdm.jl | 65 ++++++++++++++++++++++++++++- 8 files changed, 83 insertions(+), 13 deletions(-) diff --git a/examples/efie.jl b/examples/efie.jl index 5292f83f..19bb9dac 100644 --- a/examples/efie.jl +++ b/examples/efie.jl @@ -1,10 +1,11 @@ using CompScienceMeshes using BEAST -Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +Γ = meshsphere(radius=1.0, h=0.1) X = raviartthomas(Γ) -κ, η = 1.0, 1.0 +κ, η = 3.0, 1.0 t = Maxwell3D.singlelayer(wavenumber=κ) E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) # E = -η/(im*κ)*BEAST.CurlCurlGreen(κ, ẑ, point(2,0,0)) diff --git a/examples/efie_bdm.jl b/examples/efie_bdm.jl index 5b83042b..bcda043e 100644 --- a/examples/efie_bdm.jl +++ b/examples/efie_bdm.jl @@ -1,10 +1,11 @@ using CompScienceMeshes using BEAST -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +# Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = meshsphere(radius=1.0, h=0.2) X = brezzidouglasmarini(Γ) -κ = 1.0 +κ = 3.0 t = Maxwell3D.singlelayer(wavenumber=κ) E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) e = (n × E) × n diff --git a/examples/mfie_bdm.jl b/examples/mfie_bdm.jl index 388d2d70..4fbdc419 100644 --- a/examples/mfie_bdm.jl +++ b/examples/mfie_bdm.jl @@ -1,13 +1,15 @@ using CompScienceMeshes, BEAST -Γ = readmesh(joinpath(dirname(@__FILE__),"sphere2.in")) +# Γ = readmesh(joinpath(dirname(@__FILE__),"sphere2.in")) +Γ = meshsphere(radius=1.0, h=0.1) # X, Y = raviartthomas(Γ), buffachristiansen(Γ) X = brezzidouglasmarini(Γ) Y = brezzidouglasmarini(Γ) # X = raviartthomas(Γ) # Y = raviartthomas(Γ) -ϵ, μ, ω = 1.0, 1.0, 1.0; κ = ω * √(ϵ*μ) +ϵ, μ, ω = 1.0, 1.0, 3.0; κ = ω * √(ϵ*μ) +# κ = 3.0 NK, Id = BEAST.DoubleLayerRotatedMW3D(im*κ), Identity() E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) H = -1/(im*μ*ω)*curl(E) diff --git a/examples/tdefie.jl b/examples/tdefie.jl index 37228e5c..b58e4dcd 100644 --- a/examples/tdefie.jl +++ b/examples/tdefie.jl @@ -1,9 +1,11 @@ using CompScienceMeshes, BEAST, LinearAlgebra -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +# Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = meshsphere(radius=1.0, h=0.1) X = raviartthomas(Γ) -Δt, Nt = 0.3, 200 +Δt = 0.1 +Nt = 200 T = timebasisshiftedlagrange(Δt, Nt, 3) U = timebasisdelta(Δt, Nt) @@ -28,7 +30,7 @@ import Plots Plots.plot(xefie[1,:]) import Plotly -fcr, geo = facecurrents(xefie[:,60], X) +fcr, geo = facecurrents(xefie[:,125], X) Plotly.plot(patch(geo, norm.(fcr))) diff --git a/examples/tdpmchwt.jl b/examples/tdpmchwt.jl index 004228a2..69a3e671 100644 --- a/examples/tdpmchwt.jl +++ b/examples/tdpmchwt.jl @@ -6,7 +6,8 @@ using CompScienceMeshes, BEAST using LinearAlgebra # Γ = meshcuboid(1.0, 1.0, 1.0, 0.25) -Γ = meshsphere(1.0, 0.3) +h = 0.3 +Γ = meshsphere(1.0, h) X = raviartthomas(Γ) Y = buffachristiansen(Γ, ibscaled=true) diff --git a/src/BEAST.jl b/src/BEAST.jl index 8b5556da..798dd340 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -186,10 +186,10 @@ include("timedomain/zdomain.jl") # Support for Maxwell equations include("maxwell/mwexc.jl") include("maxwell/mwops.jl") +include("maxwell/nxdbllayer.jl") include("maxwell/wiltonints.jl") include("maxwell/sauterschwabints_rt.jl") include("maxwell/sauterschwabints_bdm.jl") -include("maxwell/nxdbllayer.jl") include("maxwell/nitsche.jl") include("maxwell/farfield.jl") include("maxwell/nearfield.jl") diff --git a/src/maxwell/nxdbllayer.jl b/src/maxwell/nxdbllayer.jl index e081d4d9..8c691499 100644 --- a/src/maxwell/nxdbllayer.jl +++ b/src/maxwell/nxdbllayer.jl @@ -7,7 +7,7 @@ end LinearAlgebra.cross(::NormalVector, a::MWDoubleLayer3D) = DoubleLayerRotatedMW3D(a.gamma) -defaultquadstrat(::DoubleLayerRotatedMW3D, tfs, bfs) = DoubleNumQStrat(2,3) +# defaultquadstrat(::DoubleLayerRotatedMW3D, tfs, bfs) = DoubleNumQStrat(2,3) function quaddata(operator::DoubleLayerRotatedMW3D, local_test_basis::LinearRefSpaceTriangle, local_trial_basis::LinearRefSpaceTriangle, diff --git a/src/maxwell/sauterschwabints_bdm.jl b/src/maxwell/sauterschwabints_bdm.jl index dd3c2b72..71da46e3 100644 --- a/src/maxwell/sauterschwabints_bdm.jl +++ b/src/maxwell/sauterschwabints_bdm.jl @@ -1,3 +1,66 @@ +struct Integrand{Op,LSt,LSb,Elt,Elb} + operator::Op + local_test_space::LSt + local_trial_space::LSb + test_chart::Elt + trial_chart::Elb +end + + +function momintegrals!(op::Operator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_chart, trial_chart, out, strat::SauterSchwabStrategy) + + I, J, _, _ = SauterSchwabQuadrature.reorder( + test_chart.vertices, + trial_chart.vertices, strat) + + test_chart = simplex( + test_chart.vertices[I[1]], + test_chart.vertices[I[2]], + test_chart.vertices[I[3]]) + + trial_chart = simplex( + trial_chart.vertices[J[1]], + trial_chart.vertices[J[2]], + trial_chart.vertices[J[3]]) + + igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) + G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) + + K = dof_permutation(test_local_space, I) + L = dof_permutation(trial_local_space, J) + for i in 1:numfunctions(test_local_space) + for j in 1:numfunctions(trial_local_space) + out[i,j] = G[K[i],L[j]] + end end + + nothing +end + + +function (igd::Integrand{<:DoubleLayerRotatedMW3D})(u,v) + + x = neighborhood(igd.test_chart,u) + y = neighborhood(igd.trial_chart,v) + j = jacobian(x) * jacobian(y) + nx = normal(x) + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1/R + γ = igd.operator.gamma + G = exp(-γ*R)/(4π*R) + K = -(γ + iR) * G * (iR * r) + + f = igd.local_test_space(x) + gq = igd.local_trial_space(y) + + Kg = [cross(K, j*gi.value) for gi in gq] + return [dot(fj.value, cross(nx, Kgi)) for fj in f, Kgi in Kg] +end + + struct MWSL3DIntegrand2{C,O,L} test_triangular_element::C trial_triangular_element::C @@ -182,4 +245,4 @@ function momintegrals!(op::MWDoubleLayer3D, end end nothing -end +end \ No newline at end of file From 0ba5161a56e75cf141f353b632aca29bf585e782 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sun, 17 Jul 2022 21:44:23 +0200 Subject: [PATCH 166/528] fixed type bug in postproc --- src/bases/local/bdmlocal.jl | 9 +++------ src/bases/rtspace.jl | 2 +- src/maxwell/sauterschwabints_bdm.jl | 4 ++-- src/postproc.jl | 3 ++- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index a0b62eda..cc6ff83a 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -40,10 +40,7 @@ const _dof_perms_bdm = [ function dof_permutation(::BDMRefSpace, vert_permutation) i = findfirst(==(tuple(vert_permutation...)), _vert_perms_bdm) - if !(i != nothing) - @show vert_permutation - @show i - error() - end return _dof_perms_bdm[i] -end \ No newline at end of file +end + +dimtype(::BDMRefSpace, ::CompScienceMeshes.Simplex{U,2}) where {U} = Val{6} \ No newline at end of file diff --git a/src/bases/rtspace.jl b/src/bases/rtspace.jl index 94b80817..3be142b7 100644 --- a/src/bases/rtspace.jl +++ b/src/bases/rtspace.jl @@ -29,7 +29,7 @@ Returns an object of type `RTBasis`, which comprises both the mesh and pairs of coefficients and indices to compute the exact basis functions when required by the solver. """ -function raviartthomas(mesh::Mesh{U,D1,T}, cellpairs::Array{Int,2}) where {U,D1,T} +function raviartthomas(mesh::CompScienceMeshes.AbstractMesh{U,D1,T}, cellpairs::Array{Int,2}) where {U,D1,T} # combine now the pairs of monopolar RWGs in div-conforming RWGs numpairs = size(cellpairs,2) diff --git a/src/maxwell/sauterschwabints_bdm.jl b/src/maxwell/sauterschwabints_bdm.jl index 71da46e3..c61da235 100644 --- a/src/maxwell/sauterschwabints_bdm.jl +++ b/src/maxwell/sauterschwabints_bdm.jl @@ -54,9 +54,9 @@ function (igd::Integrand{<:DoubleLayerRotatedMW3D})(u,v) K = -(γ + iR) * G * (iR * r) f = igd.local_test_space(x) - gq = igd.local_trial_space(y) + g = igd.local_trial_space(y) - Kg = [cross(K, j*gi.value) for gi in gq] + Kg = [cross(K, j*gi.value) for gi in g] return [dot(fj.value, cross(nx, Kgi)) for fj in f, Kgi in Kg] end diff --git a/src/postproc.jl b/src/postproc.jl index aeec9c5d..fbc3d94a 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -146,7 +146,8 @@ end function potential(op, points, coeffs, space::DirectProductSpace; type=SVector{3,ComplexF64}) # T = SVector{3,ComplexF64} - T = SVector{3,eltype(coeffs)} + # T = SVector{3,eltype(coeffs)} + T = type ff = zeros(T, size(points)) @show size(ff) From eae1530cdb6170ce49b6fdc763aa2969147dee4e Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 29 Jul 2022 13:06:19 +0200 Subject: [PATCH 167/528] Matrices can act like operators --- examples/Manifest.toml | 739 +++++++++++++++++++++++++-------------- examples/efie.jl | 4 +- src/maxwell/maxwell.jl | 3 + src/operator.jl | 7 + src/utils/variational.jl | 5 + 5 files changed, 491 insertions(+), 267 deletions(-) diff --git a/examples/Manifest.toml b/examples/Manifest.toml index d3ccd565..72cd5d73 100644 --- a/examples/Manifest.toml +++ b/examples/Manifest.toml @@ -1,28 +1,28 @@ # This file is machine-generated - editing it directly is not advised [[AbstractFFTs]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "485ee0867925449198280d4af84bdb46a2a404d0" +deps = ["ChainRulesCore", "LinearAlgebra"] +git-tree-sha1 = "69f7020bd72f069c219b5e8c236c1fa90d2cb409" uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" -version = "1.0.1" +version = "1.2.1" [[Adapt]] deps = ["LinearAlgebra"] -git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7" +git-tree-sha1 = "af92965fb30777147966f58acb05da51c5616b5f" uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "3.3.1" +version = "3.3.3" + +[[ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" [[ArrayLayouts]] deps = ["FillArrays", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "d68733034d1d5d2cd3ea68e2b4cb11f456f6d015" +git-tree-sha1 = "ebe4bbfc4de38ef88323f67d60a4e848fb550f0e" uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "0.6.5" +version = "0.8.9" [[Artifacts]] -deps = ["Pkg"] -git-tree-sha1 = "c30985d8821e0cd73870b17b0ed0ce6dc44cb744" uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" -version = "1.3.0" [[AssetRegistry]] deps = ["Distributed", "JSON", "Pidfile", "SHA", "Test"] @@ -31,10 +31,10 @@ uuid = "bf4720bc-e11a-5d0c-854e-bdca1663c893" version = "0.1.0" [[BEAST]] -deps = ["BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "Distributed", "FFTW", "FastGaussQuadrature", "IterativeSolvers", "LinearAlgebra", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SparseMatrixDicts", "SpecialFunctions", "StaticArrays", "WiltonInts84"] -git-tree-sha1 = "7754b0c61dac72ec7ea524f2bfa831966edd2d97" +deps = ["BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "Distributed", "FFTW", "FastGaussQuadrature", "FillArrays", "InteractiveUtils", "IterativeSolvers", "LiftedMaps", "LinearAlgebra", "LinearMaps", "SauterSchwab3D", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SparseMatrixDicts", "SpecialFunctions", "StaticArrays", "WiltonInts84"] +git-tree-sha1 = "979d068d25c0e4775828c2b5e329216a9aedaf77" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.3.0" +version = "1.5.0" [[Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" @@ -53,27 +53,33 @@ version = "0.12.5" [[BlockArrays]] deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] -git-tree-sha1 = "a048ffafcf6eb52a1f59e32ea7d9e74419736a17" +git-tree-sha1 = "43b09ac794ed8347592dd90539756d1c3416e5f2" uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" -version = "0.14.5" +version = "0.16.19" [[Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c3598e525718abcc440f69cc6d5f60dda0a1b61e" +git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.6+5" +version = "1.0.8+0" [[Cairo_jll]] deps = ["Artifacts", "Bzip2_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "e2f47f6d8337369411569fd45ae5753ca10394c6" +git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2" uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.16.0+6" +version = "1.16.1+1" [[ChainRulesCore]] deps = ["Compat", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "d659e42240c2162300b321f05173cab5cc40a5ba" +git-tree-sha1 = "80ca332f6dcb2508adba68f22f551adb2d00a624" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "0.10.4" +version = "1.15.3" + +[[ChangesOfVariables]] +deps = ["ChainRulesCore", "LinearAlgebra", "Test"] +git-tree-sha1 = "38f7a08f19d8810338d4f5085211c7dfa5d5bdd8" +uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" +version = "0.1.4" [[ClusterTrees]] deps = ["DelimitedFiles", "LinearAlgebra", "Pkg"] @@ -83,21 +89,27 @@ version = "0.2.1" [[CollisionDetection]] deps = ["Compat", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "8edb46db045228884ac57fc4a0d45ce3ce1ec399" +git-tree-sha1 = "a936b212eea4c2552a1fdff8f07aa5767fdad4ba" uuid = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" -version = "0.1.3" +version = "0.1.4" [[ColorSchemes]] -deps = ["ColorTypes", "Colors", "FixedPointNumbers", "Random", "StaticArrays"] -git-tree-sha1 = "c8fd01e4b736013bc61b704871d20503b33ea402" +deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "Random"] +git-tree-sha1 = "1fd869cc3875b57347f7027521f561cf46d1fcd8" uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.12.1" +version = "3.19.0" [[ColorTypes]] deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597" +git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4" uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.11.0" +version = "0.11.4" + +[[ColorVectorSpace]] +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"] +git-tree-sha1 = "d08c20eef1f2cbc6e60fd3612ac4340b89fea322" +uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" +version = "0.9.9" [[Colors]] deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] @@ -111,39 +123,36 @@ uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" version = "1.0.2" [[CompScienceMeshes]] -deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays"] -git-tree-sha1 = "8e54028f066cc4559e3a6305747d7d7866cbc58a" +deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays"] +git-tree-sha1 = "f40ebed348c8bb7c3085e00f3936f90564fd0f23" uuid = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" -version = "0.2.8" +version = "0.3.3" [[Compat]] deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "e4e2b39db08f967cc1360951f01e8a75ec441cab" +git-tree-sha1 = "9be8be1d8a6f44b96482c8af52238ea7987da3e3" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.30.0" +version = "3.45.0" [[CompilerSupportLibraries_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "8e695f735fca77e9708e795eda62afdb869cbb70" +deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "0.3.4+0" [[Contour]] -deps = ["StaticArrays"] -git-tree-sha1 = "9f02045d934dc030edad45944ea80dbd1f0ebea7" +git-tree-sha1 = "d05d9e7b7aedff4e5b51a029dced05cfb6125781" uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" -version = "0.5.7" +version = "0.6.2" [[DataAPI]] -git-tree-sha1 = "dfb3b7e89e395be1e25c2ad6d7690dc29cc53b1d" +git-tree-sha1 = "fb5f5316dd3fd4c5e7c30a24d50643b73e37cd40" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.6.0" +version = "1.10.0" [[DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "4437b64df1e0adccc3e5d1adbc3ac741095e4677" +git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.9" +version = "0.18.13" [[DataValueInterfaces]] git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" @@ -164,21 +173,25 @@ uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" [[DocStringExtensions]] deps = ["LibGit2"] -git-tree-sha1 = "a32185f5428d3986f47c2ab78b1f216d5e6cc96f" +git-tree-sha1 = "b19534d1895d702889b219c382a6e18010797f0b" uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.8.5" +version = "0.8.6" + +[[Downloads]] +deps = ["ArgTools", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" [[EarCut_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "92d8f9f208637e8d2d28c664051a00569c01493d" +git-tree-sha1 = "3f3a2501fa7236e9b911e0f7a588c657e822bb6d" uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" -version = "2.1.5+1" +version = "2.2.3+0" [[Expat_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "1402e52fcda25064f51c77a9655ce8680b76acf0" +git-tree-sha1 = "bad72f730e9e91c08d9427d5e8db95478a3c323d" uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.2.7+6" +version = "2.4.8+0" [[FFMPEG]] deps = ["FFMPEG_jll"] @@ -187,37 +200,43 @@ uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" version = "0.4.1" [[FFMPEG_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "LibVPX_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "Pkg", "Zlib_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] -git-tree-sha1 = "3cc57ad0a213808473eafef4845a74766242e05f" +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "Pkg", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] +git-tree-sha1 = "ccd479984c7838684b3ac204b716c89955c76623" uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" -version = "4.3.1+4" +version = "4.4.2+0" [[FFTW]] -deps = ["AbstractFFTs", "FFTW_jll", "IntelOpenMP_jll", "Libdl", "LinearAlgebra", "MKL_jll", "Reexport"] -git-tree-sha1 = "1b48dbde42f307e48685fa9213d8b9f8c0d87594" +deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] +git-tree-sha1 = "90630efff0894f8142308e334473eba54c433549" uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" -version = "1.3.2" +version = "1.5.0" [[FFTW_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "5a0d4b6a22a34d17d53543bd124f4b08ed78e8b0" +git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea" uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" -version = "3.3.9+7" +version = "3.3.10+0" + +[[FLTK_jll]] +deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "72a4842f93e734f378cf381dae2ca4542f019d23" +uuid = "4fce6fc7-ba6a-5f4c-898f-77e99806d6f8" +version = "1.3.8+0" [[FastGaussQuadrature]] deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "5829b25887e53fb6730a9df2ff89ed24baa6abf6" +git-tree-sha1 = "58d83dd5a78a36205bdfddb82b1bb67682e64487" uuid = "442a2c76-b920-505d-bb47-c5924d526838" -version = "0.4.7" +version = "0.4.9" [[FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" [[FillArrays]] -deps = ["LinearAlgebra", "Random", "SparseArrays"] -git-tree-sha1 = "31939159aeb8ffad1d4d8ee44d07f8558273120a" +deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"] +git-tree-sha1 = "246621d23d1f43e3b9c368bf3b72b2331a27c286" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "0.11.7" +version = "0.13.2" [[FixedPointNumbers]] deps = ["Statistics"] @@ -227,9 +246,9 @@ version = "0.8.4" [[Fontconfig_jll]] deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "35895cf184ceaab11fd778b4590144034a167a2f" +git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.1+14" +version = "2.13.93+0" [[Formatting]] deps = ["Printf"] @@ -239,15 +258,15 @@ version = "0.4.2" [[FreeType2_jll]] deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "cbd58c9deb1d304f5a245a0b7eb841a2560cfec6" +git-tree-sha1 = "87eb71354d8ec1a96d4a7636bd57a7347dde3ef9" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.10.1+5" +version = "2.10.4+0" [[FriBidi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "0d20aed5b14dd4c9a2453c1b601d08e1149679cc" +git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" uuid = "559328eb-81f9-559d-9380-de523a88c83c" -version = "1.0.5+6" +version = "1.0.10+0" [[FunctionalCollections]] deps = ["Test"] @@ -257,50 +276,90 @@ version = "0.5.0" [[GLFW_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll"] -git-tree-sha1 = "a199aefead29c3c2638c3571a9993b564109d45a" +git-tree-sha1 = "51d2dfe8e590fbd74e7a842cf6d13d8a2f45dc01" uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" -version = "3.3.4+0" +version = "3.3.6+0" + +[[GLU_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] +git-tree-sha1 = "65af046f4221e27fb79b28b6ca89dd1d12bc5ec7" +uuid = "bd17208b-e95e-5925-bf81-e2f59b3e5c61" +version = "9.0.1+0" + +[[GMP_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" [[GR]] -deps = ["Base64", "DelimitedFiles", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Printf", "Random", "Serialization", "Sockets", "Test", "UUIDs"] -git-tree-sha1 = "b83e3125048a9c3158cbb7ca423790c7b1b57bea" +deps = ["Base64", "DelimitedFiles", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Printf", "Random", "RelocatableFolders", "Serialization", "Sockets", "Test", "UUIDs"] +git-tree-sha1 = "037a1ca47e8a5989cc07d19729567bb71bfabd0c" uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" -version = "0.57.5" +version = "0.66.0" [[GR_jll]] deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Pkg", "Qt5Base_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "e14907859a1d3aee73a019e7b3c98e9e7b8b5b3e" +git-tree-sha1 = "c8ab731c9127cd931c93221f65d6a1008dad7256" uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" -version = "0.57.3+0" +version = "0.66.0+0" [[GeometryBasics]] deps = ["EarCut_jll", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] -git-tree-sha1 = "4136b8a5668341e58398bb472754bff4ba0456ff" +git-tree-sha1 = "83ea630384a13fc4f002b77690bc0afeb4255ac9" uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" -version = "0.3.12" +version = "0.4.2" [[Gettext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "8c14294a079216000a0bdca5ec5a447f073ddc9d" +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" -version = "0.20.1+7" +version = "0.21.0+0" [[Glib_jll]] deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "04690cc5008b38ecbdfede949220bc7d9ba26397" +git-tree-sha1 = "a32d672ac2c967f3deb8a81d828afc739c838a06" uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.59.0+4" +version = "2.68.3+2" + +[[GmshTools]] +deps = ["Libdl", "gmsh_jll"] +git-tree-sha1 = "299aa66053646db77f8aa7fafcebe0f9e5c0d1dc" +uuid = "82e2f556-b1bd-5f1a-9576-f93c0da5f0ee" +version = "0.5.2" + +[[Graphite2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" +uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" +version = "1.3.14+0" [[Grisu]] git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" version = "1.0.2" +[[GrundmannMoeller]] +deps = ["LinearAlgebra", "StaticArrays", "Test"] +git-tree-sha1 = "e264cf5f081091e4af712a911d3b620567c565e3" +uuid = "36aa67b7-9d79-4e90-bbc0-05abd90a007e" +version = "0.1.2" + +[[HDF5_jll]] +deps = ["Artifacts", "JLLWrappers", "LibCURL_jll", "Libdl", "OpenSSL_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "bab67c0d1c4662d2c4be8c6007751b0b6111de5c" +uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" +version = "1.12.1+0" + [[HTTP]] -deps = ["Base64", "Dates", "IniFile", "MbedTLS", "NetworkOptions", "Sockets", "URIs"] -git-tree-sha1 = "86ed84701fbfd1142c9786f8e53c595ff5a4def9" +deps = ["Base64", "Dates", "IniFile", "Logging", "MbedTLS", "NetworkOptions", "Sockets", "URIs"] +git-tree-sha1 = "0fa77022fe4b511826b39c894c90daf5fce3334a" uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "0.9.10" +version = "0.9.17" + +[[HarfBuzz_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] +git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3" +uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" +version = "2.8.1+1" [[Hiccup]] deps = ["MacroTools", "Test"] @@ -309,10 +368,9 @@ uuid = "9fb69e20-1954-56bb-a84f-559cc56a8ff7" version = "0.2.2" [[IniFile]] -deps = ["Test"] -git-tree-sha1 = "098e4d2c533924c921f9f9847274f2ad89e018b8" +git-tree-sha1 = "f550e6e32074c939295eb5ea6de31849ac2c9625" uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f" -version = "0.5.0" +version = "0.5.1" [[IntelOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -324,16 +382,27 @@ version = "2018.0.3+2" deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +[[InverseFunctions]] +deps = ["Test"] +git-tree-sha1 = "b3364212fb5d870f724876ffcd34dd8ec6d98918" +uuid = "3587e190-3f89-42d0-90ee-14403ec27112" +version = "0.1.7" + +[[IrrationalConstants]] +git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.1.1" + [[IterTools]] -git-tree-sha1 = "05110a2ab1fc5f932622ffea2a003221f4782c18" +git-tree-sha1 = "fa6287a4469f5e048d763df38279ee729fbd44e5" uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" -version = "1.3.0" +version = "1.4.0" [[IterativeSolvers]] deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] -git-tree-sha1 = "1a8c6237e78b714e901e406c096fc8a65528af7d" +git-tree-sha1 = "1169632f425f79429f245113b775a0e3d121457c" uuid = "42fd0dbc-a981-5370-80f2-aaf504508153" -version = "0.9.1" +version = "0.9.2" [[IteratorInterfaceExtensions]] git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" @@ -342,56 +411,68 @@ version = "1.0.0" [[JLLWrappers]] deps = ["Preferences"] -git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e" +git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.3.0" +version = "1.4.1" [[JSExpr]] deps = ["JSON", "MacroTools", "Observables", "WebIO"] -git-tree-sha1 = "bd6c034156b1e7295450a219c4340e32e50b08b1" +git-tree-sha1 = "b413a73785b98474d8af24fd4c8a975e31df3658" uuid = "97c1335a-c9c5-57fe-bc5d-ec35cebe8660" -version = "0.5.3" +version = "0.5.4" [[JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "81690084b6198a2e1da36fcfda16eeca9f9f24e4" +git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.1" +version = "0.21.3" [[JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9aff0587d9603ea0de2c6f6300d9f9492bbefbd3" +git-tree-sha1 = "b53380851c6e6664204efb2e62cd24fa5c47e4ba" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "2.0.1+3" +version = "2.1.2+0" [[Kaleido_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "2ef87eeaa28713cb010f9fb0be288b6c1a4ecd53" +git-tree-sha1 = "43032da5832754f58d14a91ffbe86d5f176acda9" uuid = "f7e6163d-2fa5-5f23-b69c-1db539e41963" -version = "0.1.0+0" +version = "0.2.1+0" [[LAME_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "df381151e871f41ee86cee4f5f6fd598b8a68826" +git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c" uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" -version = "3.100.0+3" +version = "3.100.1+0" + +[[LERC_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "bf36f528eec6634efc60d7ec062008f171071434" +uuid = "88015f11-f218-50d7-93a8-a6af411a945d" +version = "3.0.0+1" + +[[LLVMOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "ad927676766e6529a2d5152f12040620447c0c9b" +uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" +version = "14.0.4+0" [[LZO_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f128cd6cd05ffd6d3df0523ed99b90ff6f9b349a" +git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.0+3" +version = "2.10.1+0" [[LaTeXStrings]] -git-tree-sha1 = "c7f1c695e06c01b95a67f0cd1d34994f3e7db104" +git-tree-sha1 = "f2355693d6778a178ade15952b7ac47a4ff97996" uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.2.1" +version = "1.3.0" [[Latexify]] deps = ["Formatting", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "Printf", "Requires"] -git-tree-sha1 = "a4b12a1bd2ebade87891ab7e36fdbce582301a92" +git-tree-sha1 = "1a43be956d433b5d0321197150c2f94e16c0aaa0" uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" -version = "0.15.6" +version = "0.15.16" [[Lazy]] deps = ["MacroTools"] @@ -400,35 +481,39 @@ uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" version = "0.15.1" [[LazyArtifacts]] -deps = ["Pkg"] -git-tree-sha1 = "4bb5499a1fc437342ea9ab7e319ede5a457c0968" +deps = ["Artifacts", "Pkg"] uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" -version = "1.3.0" + +[[LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" + +[[LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" [[LibGit2]] -deps = ["Printf"] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" -[[LibVPX_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "85fcc80c3052be96619affa2fe2e6d2da3908e11" -uuid = "dd192d2f-8180-539f-9fb4-cc70b1dcf69a" -version = "1.9.0+1" +[[LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" [[Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" [[Libffi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "a2cd088a88c0d37eef7d209fd3d8712febce0d90" +git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.2.1+4" +version = "3.2.2+1" [[Libgcrypt_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] -git-tree-sha1 = "b391a18ab1170a2e568f9fb8d83bc7c780cb9999" +git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.5+4" +version = "1.8.7+0" [[Libglvnd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] @@ -438,74 +523,102 @@ version = "1.3.0+3" [[Libgpg_error_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "ec7f2e8ad5c9fa99fc773376cdbc86d9a5a23cb7" +git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.36.0+3" +version = "1.42.0+0" [[Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "8e924324b2e9275a51407a4e06deb3455b1e359f" +git-tree-sha1 = "42b62845d70a619f063a7da093d995ec8e15e778" uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.16.0+7" +version = "1.16.1+1" [[Libmount_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "51ad0c01c94c1ce48d5cad629425035ad030bfd5" +git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73" uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.34.0+3" +version = "2.35.0+0" [[Libtiff_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "291dd857901f94d683973cdf679984cdf73b56d0" +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"] +git-tree-sha1 = "3eb79b0ca5764d4799c06699573fd8f533259713" uuid = "89763e89-9b03-5906-acba-b20f662cd828" -version = "4.1.0+2" +version = "4.4.0+0" [[Libuuid_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f879ae9edbaa2c74c922e8b85bb83cc84ea1450b" +git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066" uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.34.0+7" +version = "2.36.0+0" + +[[LiftedMaps]] +deps = ["LinearAlgebra", "LinearMaps"] +git-tree-sha1 = "9e417fe8b11edb183ee990c31722757cf7def62c" +uuid = "d22a30c1-52ac-4762-a8c9-5838452405e0" +version = "0.4.1" [[LinearAlgebra]] deps = ["Libdl"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +[[LinearElasticity_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "71e8ee0f9fe0e86a8f8c7f28361e5118eab2f93f" +uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" +version = "5.0.0+0" + +[[LinearMaps]] +deps = ["LinearAlgebra", "SparseArrays", "Statistics"] +git-tree-sha1 = "d1b46faefb7c2f48fdec69e6f3cc34857769bc15" +uuid = "7a12625a-238d-50fd-b39a-03d52299707e" +version = "3.8.0" + [[LogExpFunctions]] -deps = ["DocStringExtensions", "LinearAlgebra"] -git-tree-sha1 = "1ba664552f1ef15325e68dc4c05c3ef8c2d5d885" +deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "7c88f63f9f0eb5929f15695af9a4d7d3ed278a91" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.2.4" +version = "0.3.16" [[Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" +[[METIS_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "1d31872bb9c5e7ec1f618e8c4a56c8b0d9bddc7e" +uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" +version = "5.1.1+0" + [[MKL_jll]] deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"] -git-tree-sha1 = "c253236b0ed414624b083e6b72bfe891fbd2c7af" +git-tree-sha1 = "e595b205efd49508358f7dc670a940c790204629" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2021.1.1+1" +version = "2022.0.0+0" + +[[MMG_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] +git-tree-sha1 = "70a59df96945782bb0d43b56d0fbfdf1ce2e4729" +uuid = "86086c02-e288-5929-a127-40944b0018b7" +version = "5.6.0+0" [[MacroTools]] deps = ["Markdown", "Random"] -git-tree-sha1 = "6a8a2a625ab0dea913aba95c11370589e0239ff0" +git-tree-sha1 = "3d3e902b31198a27340d0bf00d6ac452866021cf" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.6" +version = "0.5.9" [[Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[MbedTLS]] -deps = ["Dates", "MbedTLS_jll", "Random", "Sockets"] -git-tree-sha1 = "1c38e51c3d08ef2278062ebceade0e46cefc96fe" +deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "Random", "Sockets"] +git-tree-sha1 = "9f4f5a42de3300439cb8300236925670f844a555" uuid = "739be429-bea8-5141-9913-cc70e7f3736d" -version = "1.0.3" +version = "1.1.1" [[MbedTLS_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "0eef589dd1c26a3ac9d753fe1a8bcad63f956fa6" +deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.16.8+1" [[Measures]] git-tree-sha1 = "e498ddeee6f9fdb4551ce855a46f54dbd900245f" @@ -514,18 +627,21 @@ version = "0.3.1" [[Missings]] deps = ["DataAPI"] -git-tree-sha1 = "4ea90bd5d3985ae1f9a908bd4500ae88921c5ce7" +git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f" uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "1.0.0" +version = "1.0.2" [[Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" +[[MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" + [[Mustache]] deps = ["Printf", "Tables"] -git-tree-sha1 = "36995ef0d532fe08119d70b2365b7b03d4e00f48" +git-tree-sha1 = "1e566ae913a57d0062ff1af54d2697b9344b99cd" uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" -version = "1.0.10" +version = "1.0.14" [[Mux]] deps = ["AssetRegistry", "Base64", "HTTP", "Hiccup", "Pkg", "Sockets", "WebSockets"] @@ -534,43 +650,52 @@ uuid = "a975b10e-0019-58db-a62f-e48ff68538c9" version = "0.7.6" [[NaNMath]] -git-tree-sha1 = "bfe47e760d60b82b66b61d2d44128b62e3a369fb" +deps = ["OpenLibm_jll"] +git-tree-sha1 = "a7c3d1da1189a1c2fe843a3bfa04d18d20eb3211" uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" -version = "0.3.5" +version = "1.0.1" [[NetworkOptions]] -git-tree-sha1 = "ed3157f48a05543cce9b241e1f2815f7e843d96e" uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" -version = "1.2.0" + +[[OCCT_jll]] +deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "acc8099ae8ed10226dc8424fb256ec9fe367a1f0" +uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" +version = "7.6.2+2" [[Observables]] -git-tree-sha1 = "3469ef96607a6b9a1e89e54e6f23401073ed3126" +git-tree-sha1 = "dfd8d34871bc3ad08cd16026c1828e271d554db9" uuid = "510215fc-4207-5dde-b226-833fc4488ee2" -version = "0.3.3" +version = "0.5.1" [[Ogg_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "a42c0f138b9ebe8b58eba2271c5053773bde52d0" +git-tree-sha1 = "887579a3eb005446d514ab7aeac5d1d027658b8f" uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" -version = "1.3.4+2" +version = "1.3.5+1" + +[[OpenLibm_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "05823500-19ac-5b8b-9628-191a04bc5112" [[OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "71bbbc616a1d710879f5a1021bcba65ffba6ce58" +git-tree-sha1 = "e60321e3f2616584ff98f0a4f18d98ae6f89bbb3" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "1.1.1+6" +version = "1.1.17+0" [[OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9db77584158d0ab52307f8c04f8e7c08ca76b5b3" +git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.3+4" +version = "0.5.5+0" [[Opus_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f9d57f4126c39565e05a2b0264df99f497fc6f37" +git-tree-sha1 = "51a08fb14ec28da2ec7a927c4337e4332c2a4720" uuid = "91d4177d-7536-5919-b921-800302f37372" -version = "1.3.1+3" +version = "1.3.2+0" [[OrderedCollections]] git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" @@ -579,73 +704,79 @@ version = "1.4.1" [[PCRE_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "1b556ad51dceefdbf30e86ffa8f528b73c7df2bb" +git-tree-sha1 = "b2a7af664e098055a7529ad1a900ded962bca488" uuid = "2f80f16e-611a-54ab-bc61-aa92de5b98fc" -version = "8.42.0+4" +version = "8.44.0+0" + +[[Parameters]] +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.3" [[Parsers]] deps = ["Dates"] -git-tree-sha1 = "c8abc88faa3f7a3950832ac5d6e690881590d6dc" +git-tree-sha1 = "0044b23da09b5608b4ecacb4e5e6c6332f833a7e" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "1.1.0" +version = "2.3.2" [[Pidfile]] deps = ["FileWatching", "Test"] -git-tree-sha1 = "1be8660b2064893cd2dae4bd004b589278e4440d" +git-tree-sha1 = "2d8aaf8ee10df53d0dfb9b8ee44ae7c04ced2b03" uuid = "fa939f87-e72e-5be4-a000-7fc836dbe307" -version = "1.2.0" +version = "1.3.0" [[Pixman_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "6a20a83c1ae86416f0a5de605eaea08a552844a3" +git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29" uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.40.0+0" +version = "0.40.1+0" [[Pkg]] -deps = ["Dates", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" [[PlotThemes]] -deps = ["PlotUtils", "Requires", "Statistics"] -git-tree-sha1 = "a3a964ce9dc7898193536002a6dd892b1b5a6f1d" +deps = ["PlotUtils", "Statistics"] +git-tree-sha1 = "8162b2f8547bc23876edd0c5181b27702ae58dce" uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" -version = "2.0.1" +version = "3.0.0" [[PlotUtils]] deps = ["ColorSchemes", "Colors", "Dates", "Printf", "Random", "Reexport", "Statistics"] -git-tree-sha1 = "ae9a295ac761f64d8c2ec7f9f24d21eb4ffba34d" +git-tree-sha1 = "9888e59493658e476d3073f1ce24348bdc086660" uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" -version = "1.0.10" +version = "1.3.0" [[Plotly]] deps = ["Base64", "DelimitedFiles", "HTTP", "JSON", "PlotlyJS", "Reexport"] -git-tree-sha1 = "ad5dc3a3bc18246d552aad6c78268102becd9d59" +git-tree-sha1 = "044a9194ae38a50cbdb34a05dc63bf68e4db95df" uuid = "58dd65bb-95f3-509e-9936-c39a10fdeae7" -version = "0.4.0" +version = "0.4.1" [[PlotlyBase]] -deps = ["Base64", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "Kaleido_jll", "LaTeXStrings", "Logging", "Pkg", "Requires", "Statistics", "UUIDs"] -git-tree-sha1 = "f20d4669281187c9e9d75820f8ec95e88131fc1e" +deps = ["ColorSchemes", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "LaTeXStrings", "Logging", "Parameters", "Pkg", "REPL", "Requires", "Statistics", "UUIDs"] +git-tree-sha1 = "180d744848ba316a3d0fdf4dbd34b77c7242963a" uuid = "a03496cd-edff-5a9b-9e67-9cda94a718b5" -version = "0.5.3" +version = "0.8.18" [[PlotlyJS]] -deps = ["Blink", "DelimitedFiles", "JSExpr", "JSON", "Markdown", "Pkg", "PlotlyBase", "REPL", "Reexport", "Requires", "WebIO"] -git-tree-sha1 = "b859551864853bb2c317d0937e94bd19d749396b" +deps = ["Base64", "Blink", "DelimitedFiles", "JSExpr", "JSON", "Kaleido_jll", "Markdown", "Pkg", "PlotlyBase", "REPL", "Reexport", "Requires", "WebIO"] +git-tree-sha1 = "53d6325e14d3bdb85fd387a085075f36082f35a3" uuid = "f0f68f2c-4968-5e81-91da-67840de0976a" -version = "0.14.1" +version = "0.18.8" [[Plots]] -deps = ["Base64", "Contour", "Dates", "FFMPEG", "FixedPointNumbers", "GR", "GeometryBasics", "JSON", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "PlotThemes", "PlotUtils", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs"] -git-tree-sha1 = "e995fa1821b6daff8b107a8eafbec234ae2263d0" +deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "GeometryBasics", "JSON", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs", "UnicodeFun", "Unzip"] +git-tree-sha1 = "0a0da27969e8b6b2ee67c112dcf7001a659049a0" uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -version = "1.16.5" +version = "1.31.4" [[Preferences]] deps = ["TOML"] -git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a" +git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d" uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.2.2" +version = "1.3.0" [[Printf]] deps = ["Unicode"] @@ -653,12 +784,12 @@ uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" [[Qt5Base_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"] -git-tree-sha1 = "16626cfabbf7206d60d84f2bf4725af7b37d4a77" +git-tree-sha1 = "c6c0f690d0cc7caddb74cef7aa847b824a16b256" uuid = "ea2cea3b-5b76-57ae-a6ef-0a8af62496e1" -version = "5.15.2+0" +version = "5.15.3+1" [[REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets"] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[Random]] @@ -666,41 +797,59 @@ deps = ["Serialization"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [[RecipesBase]] -git-tree-sha1 = "b3fb709f3c97bfc6e948be68beeecb55a0b340ae" +git-tree-sha1 = "6bf3f380ff52ce0832ddd3a2a7b9538ed1bcca7d" uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "1.1.1" +version = "1.2.1" [[RecipesPipeline]] deps = ["Dates", "NaNMath", "PlotUtils", "RecipesBase"] -git-tree-sha1 = "7a5026a6741c14147d1cb6daf2528a77ca28eb51" +git-tree-sha1 = "2690681814016887462cf5ac37102b51cd9ec781" uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c" -version = "0.3.2" +version = "0.6.2" [[Reexport]] -git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220" +git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.1.0" +version = "1.2.2" + +[[RelocatableFolders]] +deps = ["SHA", "Scratch"] +git-tree-sha1 = "22c5201127d7b243b9ee1de3b43c408879dff60f" +uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" +version = "0.3.0" [[Requires]] deps = ["UUIDs"] -git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621" +git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.1.3" +version = "1.3.0" + +[[SCOTCH_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "7110b749766853054ce8a2afaa73325d72d32129" +uuid = "a8d0f55d-b80e-548d-aff6-1a04c175f0f9" +version = "6.1.3+0" [[SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +[[SauterSchwab3D]] +deps = ["CompScienceMeshes", "FastGaussQuadrature", "GrundmannMoeller", "LinearAlgebra", "ShunnHamQuadrature", "StaticArrays"] +git-tree-sha1 = "e5c1dce465dd31be2e21761922ceab26ec28db7c" +uuid = "0a13313b-1c00-422e-8263-562364ed9544" +version = "0.1.1" + [[SauterSchwabQuadrature]] deps = ["CompScienceMeshes", "FastGaussQuadrature", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "8218588065871ebba62938e43b00e6b1c67fa0a5" +git-tree-sha1 = "8d7eed829815a48c042589dd11d8526a0d81bf1c" uuid = "535c7bfe-2023-5c1d-b712-654ef9d93a38" -version = "2.1.1" +version = "2.1.3" [[Scratch]] deps = ["Dates"] -git-tree-sha1 = "0b4b7f1393cff97c33891da2a0bf69c6ed241fda" +git-tree-sha1 = "f94f779c94e58bf9ea243e77a37e16d9de9126bd" uuid = "6c6a2e73-6563-6170-7368-637461726353" -version = "1.1.0" +version = "1.1.1" [[Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" @@ -715,14 +864,20 @@ git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" version = "1.0.3" +[[ShunnHamQuadrature]] +deps = ["LinearAlgebra", "StaticArrays"] +git-tree-sha1 = "dfa53166b13cd6f352d54c99a24124321ef95282" +uuid = "164309f2-5039-4884-b6c7-6da8aa5c66ad" +version = "0.1.0" + [[Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" [[SortingAlgorithms]] deps = ["DataStructures"] -git-tree-sha1 = "2ec1962eba973f383239da22e75218565c390a96" +git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508" uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.0.0" +version = "1.0.1" [[SparseArrays]] deps = ["LinearAlgebra", "Random"] @@ -735,43 +890,47 @@ uuid = "5cb6c4b0-9b79-11e8-24c9-f9621d252589" version = "0.2.4" [[SpecialFunctions]] -deps = ["ChainRulesCore", "LogExpFunctions", "OpenSpecFun_jll"] -git-tree-sha1 = "a50550fa3164a8c46747e62063b4d774ac1bcf49" +deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "d75bda01f8c31ebb72df80a46c88b25d1c79c56d" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "1.5.1" +version = "2.1.7" [[StaticArrays]] -deps = ["LinearAlgebra", "Random", "Statistics"] -git-tree-sha1 = "42378d3bab8b4f57aa1ca443821b752850592668" +deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] +git-tree-sha1 = "23368a3313d12a2326ad0035f0db0c0966f438ef" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.2.2" +version = "1.5.2" + +[[StaticArraysCore]] +git-tree-sha1 = "66fe9eb253f910fe8cf161953880cfdaef01cdf0" +uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" +version = "1.0.1" [[Statistics]] deps = ["LinearAlgebra", "SparseArrays"] uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [[StatsAPI]] -git-tree-sha1 = "1958272568dc176a1d881acb797beb909c785510" +deps = ["LinearAlgebra"] +git-tree-sha1 = "2c11d7290036fe7aac9038ff312d3b3a2a5bf89e" uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" -version = "1.0.0" +version = "1.4.0" [[StatsBase]] -deps = ["DataAPI", "DataStructures", "LinearAlgebra", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "2f6792d523d7448bbe2fec99eca9218f06cc746d" +deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] +git-tree-sha1 = "472d044a1c8df2b062b23f222573ad6837a615ba" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.33.8" +version = "0.33.19" [[StructArrays]] -deps = ["Adapt", "DataAPI", "Tables"] -git-tree-sha1 = "44b3afd37b17422a62aea25f04c1f7e09ce6b07f" +deps = ["Adapt", "DataAPI", "StaticArrays", "Tables"] +git-tree-sha1 = "ec47fb6069c57f1cee2f67541bf8f23415146de7" uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" -version = "0.5.1" +version = "0.6.11" [[TOML]] deps = ["Dates"] -git-tree-sha1 = "44aaac2d2aec4a850302f9aa69127c74f0c3787e" uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.3" [[TableTraits]] deps = ["IteratorInterfaceExtensions"] @@ -780,13 +939,23 @@ uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" version = "1.0.1" [[Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "TableTraits", "Test"] -git-tree-sha1 = "aa30f8bb63f9ff3f8303a06c604c8500a69aa791" +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits", "Test"] +git-tree-sha1 = "5ce79ce186cc678bbb5c5681ca3379d1ddae11a1" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.4.3" +version = "1.7.0" + +[[Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" + +[[TensorCore]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" +uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" +version = "0.1.1" [[Test]] -deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [[URIParser]] @@ -796,34 +965,50 @@ uuid = "30578b45-9adc-5946-b283-645ec420af67" version = "0.4.1" [[URIs]] -git-tree-sha1 = "97bbe755a53fe859669cd907f2d96aee8d2c1355" +git-tree-sha1 = "e59ecc5a41b000fa94423a578d29290c7266fc10" uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.3.0" +version = "1.4.0" [[UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" +[[UnPack]] +git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.2" + [[Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" +[[UnicodeFun]] +deps = ["REPL"] +git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" +uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" +version = "0.4.1" + +[[Unzip]] +git-tree-sha1 = "34db80951901073501137bdbc3d5a8e7bbd06670" +uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d" +version = "0.1.2" + [[Wayland_jll]] deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "dc643a9b774da1c2781413fd7b6dcd2c56bb8056" +git-tree-sha1 = "3e61f0b86f90dacb0bc0e73a0c5a83f6a8636e23" uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" -version = "1.17.0+4" +version = "1.19.0+0" [[Wayland_protocols_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll"] -git-tree-sha1 = "2839f1c1296940218e35df0bbb220f2a79686670" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4528479aa01ee1b3b4cd0e6faef0e04cf16466da" uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91" -version = "1.18.0+4" +version = "1.25.0+0" [[WebIO]] deps = ["AssetRegistry", "Base64", "Distributed", "FunctionalCollections", "JSON", "Logging", "Observables", "Pkg", "Random", "Requires", "Sockets", "UUIDs", "WebSockets", "Widgets"] -git-tree-sha1 = "adc25e225bc334c7df6eec3b39496edfc451cc38" +git-tree-sha1 = "a8bbcd0b08061bba794c56fb78426e96e114ae7f" uuid = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" -version = "0.8.15" +version = "0.8.18" [[WebSockets]] deps = ["Base64", "Dates", "HTTP", "Logging", "Sockets"] @@ -833,27 +1018,27 @@ version = "1.5.9" [[Widgets]] deps = ["Colors", "Dates", "Observables", "OrderedCollections"] -git-tree-sha1 = "eae2fbbc34a79ffd57fb4c972b08ce50b8f6a00d" +git-tree-sha1 = "fcdae142c1cfc7d89de2d11e08721d0f2f86c98a" uuid = "cc8bc4a8-27d6-5769-a93b-9d913e69aa62" -version = "0.6.3" +version = "0.6.6" [[WiltonInts84]] deps = ["LinearAlgebra"] -git-tree-sha1 = "b7374c784a208b377157146e3bdd94287e41f95a" +git-tree-sha1 = "d412771d65e9760b6a7fdb86754c237ee2a92dfd" uuid = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" -version = "0.2.2" +version = "0.2.3" [[XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "be0db24f70aae7e2b89f2f3092e93b8606d659a6" +git-tree-sha1 = "58443b63fb7e465a8a7210828c91c08b92132dff" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.9.10+3" +version = "2.9.14+0" [[XSLT_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "2b3eac39df218762d2d005702d601cd44c997497" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.33+4" +version = "1.1.34+0" [[Xorg_libX11_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] @@ -891,6 +1076,12 @@ git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" version = "5.0.3+4" +[[Xorg_libXft_jll]] +deps = ["Fontconfig_jll", "Libdl", "Pkg", "Xorg_libXrender_jll"] +git-tree-sha1 = "754b542cdc1057e0a2f1888ec5414ee17a4ca2a1" +uuid = "2c808117-e144-5220-80d1-69d4eaa9352c" +version = "2.3.3+1" + [[Xorg_libXi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246" @@ -982,52 +1173,70 @@ uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" version = "1.4.0+3" [[Zlib_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "320228915c8debb12cb434c59057290f0834dbf6" +deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.2.11+18" [[Zstd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "2c1332c54931e83f8f94d310fa447fd743e8d600" +git-tree-sha1 = "e45044cd873ded54b6a5bac0eb5c971392cf1927" uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" -version = "1.4.8+0" +version = "1.5.2+0" + +[[gmsh_jll]] +deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "9774ebf68348b3b56c74a78b829051310163fd76" +uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" +version = "4.10.2+0" + +[[libaom_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "3a2ea60308f0996d26f1e5354e10c24e9ef905d4" +uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" +version = "3.4.0+0" [[libass_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "acc685bcf777b2202a904cdcb49ad34c2fa1880c" +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "5982a94fcba20f02f42ace44b9894ee2b140fe47" uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" -version = "0.14.0+4" +version = "0.15.1+0" [[libfdk_aac_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "7a5780a0d9c6864184b3a2eeeb833a0c871f00ab" +git-tree-sha1 = "daacc84a041563f965be61859a36e17c4e4fcd55" uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" -version = "0.1.6+4" +version = "2.0.2+0" [[libpng_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "6abbc424248097d69c0c87ba50fcb0753f93e0ee" +git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.37+6" +version = "1.6.38+0" [[libvorbis_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] -git-tree-sha1 = "fa14ac25af7a4b8a7f61b287a124df7aab601bcd" +git-tree-sha1 = "b910cb81ef3fe6e78bf6acee440bda86fd6ae00c" uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" -version = "1.3.6+6" +version = "1.3.7+1" + +[[nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" + +[[p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" [[x264_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "d713c1ce4deac133e3334ee12f4adff07f81778f" +git-tree-sha1 = "4fea590b89e6ec504593146bf8b988b2c00922b2" uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" -version = "2020.7.14+2" +version = "2021.5.5+0" [[x265_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "487da2f8f2f0c8ee0e83f39d13037d6bbf0a45ab" +git-tree-sha1 = "ee567a171cce03570d77ad3a43e90218e38937a9" uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" -version = "3.0.0+3" +version = "3.5.0+0" [[xkbcommon_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] diff --git a/examples/efie.jl b/examples/efie.jl index 19bb9dac..16e7a3ef 100644 --- a/examples/efie.jl +++ b/examples/efie.jl @@ -1,8 +1,8 @@ using CompScienceMeshes using BEAST -# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -Γ = meshsphere(radius=1.0, h=0.1) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +# Γ = meshsphere(radius=1.0, h=0.1) X = raviartthomas(Γ) κ, η = 3.0, 1.0 diff --git a/src/maxwell/maxwell.jl b/src/maxwell/maxwell.jl index a3695b3f..9ab88702 100644 --- a/src/maxwell/maxwell.jl +++ b/src/maxwell/maxwell.jl @@ -45,6 +45,9 @@ module Maxwell3D Mod.MWSingleLayer3D(gamma, alpha, beta) end + weaklysingular(;wavenumber) = singlelayer(;wavenumber, alpha=-im*wavenumber, beta=zero(im*wavenumber)) + hypersingular(;wavenumber) = singlelayer(; wavenumber, alpha=zero(im*wavenumber), beta=-1/(im*wavenumber)) + """ doublelayer(;gamma) doublelaher(;wavenumber) diff --git a/src/operator.jl b/src/operator.jl index 489e397e..6878f730 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -90,6 +90,13 @@ function assemble(operator::AbstractOperator, test_functions, trial_functions; return Z() end + +function assemble(A::Matrix, testfns, trialfns) + @assert numfunctions(testfns) == size(A,1) + @assert numfunctions(trialfns) == size(A,2) + return A +end + function assemblerow(operator::AbstractOperator, test_functions, trial_functions, storage_policy = Val{:bandedstorage}, long_delays_policy = LongDelays{:ignore}; diff --git a/src/utils/variational.jl b/src/utils/variational.jl index 4e325c96..365a1586 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -303,6 +303,11 @@ function getindex(op::Any, V::Vector{HilbertVector}, U::Vector{HilbertVector}) return BilForm(first(V).space, first(U).space, terms) end +function getindex(A::Matrix, v::HilbertVector, u::HilbertVector) + terms = [ BilTerm(v.idx, u.idx, v.opstack, u.opstack, 1, A) ] + BilForm(v.space, u.space, terms) +end + "Add two BilForms together" function +(a::BilForm, b::BilForm) From 5642a0ad897fc4112a5350a9e616ffc775b157d7 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 29 Jul 2022 16:27:59 +0200 Subject: [PATCH 168/528] mixed assembly allows refinement in both directions --- src/integralop.jl | 57 +++++++++++++++++++++++++++--- src/maxwell/sauterschwabints_rt.jl | 53 +++++++++++++++++++++++++++ src/operator.jl | 2 +- test/runtests.jl | 1 + test/test_assemble_refinements.jl | 32 +++++++++++++++++ 5 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 test/test_assemble_refinements.jl diff --git a/src/integralop.jl b/src/integralop.jl index 15d39bf0..f4c769c4 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -74,17 +74,32 @@ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; tshapes = refspace(tfs); num_tshapes = numfunctions(tshapes) bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes) + tgeo = geometry(tfs) + bgeo = geometry(bfs) + qd = quaddata(biop, tshapes, bshapes, test_elements, bsis_elements, quadstrat) zlocal = zeros(scalartype(biop, tfs, bfs), 2num_tshapes, 2num_bshapes) - if !CompScienceMeshes.refines(tfs.geo, bfs.geo) - assemblechunk_body!(biop, + # if !CompScienceMeshes.refines(tfs.geo, bfs.geo) + # assemblechunk_body!(biop, + # tshapes, test_elements, tad, + # bshapes, bsis_elements, bad, + # qd, zlocal, store; quadstrat) + # else + if CompScienceMeshes.refines(tgeo, bgeo) + @info "assemblechunk: test refines trial mesh" + assemblechunk_body_nested_meshes!(biop, + tshapes, test_elements, tad, + bshapes, bsis_elements, bad, + qd, zlocal, store; quadstrat) + elseif CompScienceMeshes.refines(bgeo, tgeo) + @info "assemblechunk: trial refines test mesh" + assemblechunk_body_trial_refines_test!(biop, tshapes, test_elements, tad, bshapes, bsis_elements, bad, qd, zlocal, store; quadstrat) else - @info "assemblechunk for nested meshes" - assemblechunk_body_nested_meshes!(biop, + assemblechunk_body!(biop, tshapes, test_elements, tad, bshapes, bsis_elements, bad, qd, zlocal, store; quadstrat) @@ -160,6 +175,40 @@ function assemblechunk_body_nested_meshes!(biop, end +function assemblechunk_body_trial_refines_test!(biop, + test_shapes, test_elements, test_assembly_data, + trial_shapes, trial_elements, trial_assembly_data, + qd, zlocal, store; quadstrat) + +myid = Threads.threadid() +myid == 1 && print("dots out of 10: ") +todo, done, pctg = length(test_elements), 0, 0 +for (p,tcell) in enumerate(test_elements) + for (q,bcell) in enumerate(trial_elements) + + fill!(zlocal, 0) + qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, qd, quadstrat) + momintegrals_trial_refines_test!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule) + I = length(test_assembly_data[p]) + J = length(trial_assembly_data[q]) + for j in 1 : J, i in 1 : I + zij = zlocal[i,j] + for (n,b) in trial_assembly_data[q][j] + zb = zij*b + for (m,a) in test_assembly_data[p][i] + store(a*zb, m, n) + end end end end + + done += 1 + new_pctg = round(Int, done / todo * 100) + if new_pctg > pctg + 9 + myid == 1 && print(".") + pctg = new_pctg + end end +myid == 1 && println("") +end + + function blockassembler(biop::IntegralOperator, tfs::Space, bfs::Space; quadstrat=defaultquadstrat(biop, tfs, bfs)) diff --git a/src/maxwell/sauterschwabints_rt.jl b/src/maxwell/sauterschwabints_rt.jl index 79655c74..282f1275 100644 --- a/src/maxwell/sauterschwabints_rt.jl +++ b/src/maxwell/sauterschwabints_rt.jl @@ -175,3 +175,56 @@ function momintegrals_nested!(op::IntegralOperator, momintegrals!(op, test_local_space, trial_local_space, test_chart, trial_chart, out, strat) end + +function momintegrals_trial_refines_test!(op::IntegralOperator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_chart, trial_chart, out, strat) + + momintegrals!(op, test_local_space, trial_local_space, + test_chart, trial_chart, out, strat) +end + + +function momintegrals_trial_refines_test!(op::MWOperator3D, + test_local_space::RTRefSpace, trial_local_space::RTRefSpace, + test_chart, trial_chart, out, strat::SauterSchwabStrategy) + + # 1. Refine the test_chart + p1, p2, p3 = test_chart.vertices + + # TODO: generalise this to include more general refinements + e1 = cartesian(neighborhood(test_chart, (0,1/2))) + e2 = cartesian(neighborhood(test_chart, (1/2,0))) + e3 = cartesian(neighborhood(test_chart, (1/2,1/2))) + + ct = cartesian(center(test_chart)) + + refined_test_chart = [ + simplex(ct, p1, e3), + simplex(ct, e3, p2), + simplex(ct, p2, e1), + simplex(ct, e1, p3), + simplex(ct, p3, e2), + simplex(ct, e2, p1)] + + qs = defaultquadstrat(op, test_local_space, trial_local_space) + qd = quaddata(op, test_local_space, trial_local_space, + refined_test_chart, [trial_chart], qs) + + for (p,chart) in enumerate(refined_test_chart) + qr = quadrule(op, test_local_space, trial_local_space, + p, chart, 1, trial_chart, qd, qs) + + Q = restrict(test_local_space, test_chart, chart) + zlocal = zero(out) + momintegrals!(op, test_local_space, trial_local_space, + chart, trial_chart, zlocal, qr) + + for j in 1:3 + for i in 1:3 + for k in 1:3 + # out[i,j] += zlocal[i,k] * Q[j,k] + out[i,j] += Q[i,k] * zlocal[k,j] + end end end + end +end \ No newline at end of file diff --git a/src/operator.jl b/src/operator.jl index 6878f730..4f88c62a 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -195,7 +195,7 @@ function assemble!(operator::Operator, test_functions::Space, trial_functions::S threading::Type{Threading{:single}}; quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) - @info "Single-threaded assembly" + # @info "Single-threaded assembly" assemblechunk!(operator, test_functions, trial_functions, store; quadstrat) end diff --git a/test/runtests.jl b/test/runtests.jl index 1d24fb7c..22775757 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -46,6 +46,7 @@ include("test_laminated.jl") include("test_assemblerow.jl") include("test_mixed_blkassm.jl") include("test_local_assembly.jl") +include("test_assemble_refinements.jl") include("test_dipole.jl") diff --git a/test/test_assemble_refinements.jl b/test/test_assemble_refinements.jl new file mode 100644 index 00000000..a24de30c --- /dev/null +++ b/test/test_assemble_refinements.jl @@ -0,0 +1,32 @@ +using StaticArrays +using CompScienceMeshes +using BEAST +# using SauterSchwabQuadrature +using LinearAlgebra +using Test + +function generate_refpair(;angle=180) + vertices = [ + SVector(0.0, 1.0, 0.0), + SVector(-0.5, 0.0, 0.0), + SVector(+0.5, 0.0, 0.0), + SVector(0.0, 1.0*cos(angle/180*π), 1.0*sin(angle/180*π)) + ] + + triangles = [ + SVector(1, 2, 3), + SVector(2, 4, 3) + ] + return Mesh(vertices, triangles) +end +Γ = generate_refpair(angle=45) + +X = raviartthomas(Γ) +Y = buffachristiansen(Γ) + +Kop = Maxwell3D.doublelayer(wavenumber=0.0) + +K1 = assemble(Kop, X, Y) +K2 = assemble(Kop, Y, X) + +@test K1[1,1] ≈ K2[1,1] rtol=1e-3 \ No newline at end of file From 554f6de310991126a0fbb0f75a8fd558fe3ffcd2 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 9 Aug 2022 12:30:31 +0200 Subject: [PATCH 169/528] update examples manifest --- examples/Manifest.toml | 751 ++++++++++++++++++++++++++--------------- 1 file changed, 484 insertions(+), 267 deletions(-) diff --git a/examples/Manifest.toml b/examples/Manifest.toml index d3ccd565..e3ef7d18 100644 --- a/examples/Manifest.toml +++ b/examples/Manifest.toml @@ -1,28 +1,28 @@ # This file is machine-generated - editing it directly is not advised [[AbstractFFTs]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "485ee0867925449198280d4af84bdb46a2a404d0" +deps = ["ChainRulesCore", "LinearAlgebra"] +git-tree-sha1 = "69f7020bd72f069c219b5e8c236c1fa90d2cb409" uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" -version = "1.0.1" +version = "1.2.1" [[Adapt]] deps = ["LinearAlgebra"] -git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7" +git-tree-sha1 = "af92965fb30777147966f58acb05da51c5616b5f" uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "3.3.1" +version = "3.3.3" + +[[ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" [[ArrayLayouts]] deps = ["FillArrays", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "d68733034d1d5d2cd3ea68e2b4cb11f456f6d015" +git-tree-sha1 = "ebe4bbfc4de38ef88323f67d60a4e848fb550f0e" uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "0.6.5" +version = "0.8.9" [[Artifacts]] -deps = ["Pkg"] -git-tree-sha1 = "c30985d8821e0cd73870b17b0ed0ce6dc44cb744" uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" -version = "1.3.0" [[AssetRegistry]] deps = ["Distributed", "JSON", "Pidfile", "SHA", "Test"] @@ -31,10 +31,10 @@ uuid = "bf4720bc-e11a-5d0c-854e-bdca1663c893" version = "0.1.0" [[BEAST]] -deps = ["BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "Distributed", "FFTW", "FastGaussQuadrature", "IterativeSolvers", "LinearAlgebra", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SparseMatrixDicts", "SpecialFunctions", "StaticArrays", "WiltonInts84"] -git-tree-sha1 = "7754b0c61dac72ec7ea524f2bfa831966edd2d97" +deps = ["BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "Distributed", "FFTW", "FastGaussQuadrature", "FillArrays", "InteractiveUtils", "IterativeSolvers", "LiftedMaps", "LinearAlgebra", "LinearMaps", "SauterSchwab3D", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SparseMatrixDicts", "SpecialFunctions", "StaticArrays", "WiltonInts84"] +git-tree-sha1 = "979d068d25c0e4775828c2b5e329216a9aedaf77" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.3.0" +version = "1.5.0" [[Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" @@ -53,27 +53,33 @@ version = "0.12.5" [[BlockArrays]] deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] -git-tree-sha1 = "a048ffafcf6eb52a1f59e32ea7d9e74419736a17" +git-tree-sha1 = "43b09ac794ed8347592dd90539756d1c3416e5f2" uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" -version = "0.14.5" +version = "0.16.19" [[Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c3598e525718abcc440f69cc6d5f60dda0a1b61e" +git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.6+5" +version = "1.0.8+0" [[Cairo_jll]] deps = ["Artifacts", "Bzip2_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "e2f47f6d8337369411569fd45ae5753ca10394c6" +git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2" uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.16.0+6" +version = "1.16.1+1" [[ChainRulesCore]] deps = ["Compat", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "d659e42240c2162300b321f05173cab5cc40a5ba" +git-tree-sha1 = "80ca332f6dcb2508adba68f22f551adb2d00a624" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "0.10.4" +version = "1.15.3" + +[[ChangesOfVariables]] +deps = ["ChainRulesCore", "LinearAlgebra", "Test"] +git-tree-sha1 = "38f7a08f19d8810338d4f5085211c7dfa5d5bdd8" +uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" +version = "0.1.4" [[ClusterTrees]] deps = ["DelimitedFiles", "LinearAlgebra", "Pkg"] @@ -83,21 +89,27 @@ version = "0.2.1" [[CollisionDetection]] deps = ["Compat", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "8edb46db045228884ac57fc4a0d45ce3ce1ec399" +git-tree-sha1 = "a936b212eea4c2552a1fdff8f07aa5767fdad4ba" uuid = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" -version = "0.1.3" +version = "0.1.4" [[ColorSchemes]] -deps = ["ColorTypes", "Colors", "FixedPointNumbers", "Random", "StaticArrays"] -git-tree-sha1 = "c8fd01e4b736013bc61b704871d20503b33ea402" +deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "Random"] +git-tree-sha1 = "1fd869cc3875b57347f7027521f561cf46d1fcd8" uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.12.1" +version = "3.19.0" [[ColorTypes]] deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597" +git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4" uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.11.0" +version = "0.11.4" + +[[ColorVectorSpace]] +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"] +git-tree-sha1 = "d08c20eef1f2cbc6e60fd3612ac4340b89fea322" +uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" +version = "0.9.9" [[Colors]] deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] @@ -111,39 +123,36 @@ uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" version = "1.0.2" [[CompScienceMeshes]] -deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays"] -git-tree-sha1 = "8e54028f066cc4559e3a6305747d7d7866cbc58a" +deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays"] +git-tree-sha1 = "f40ebed348c8bb7c3085e00f3936f90564fd0f23" uuid = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" -version = "0.2.8" +version = "0.3.3" [[Compat]] deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "e4e2b39db08f967cc1360951f01e8a75ec441cab" +git-tree-sha1 = "9be8be1d8a6f44b96482c8af52238ea7987da3e3" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.30.0" +version = "3.45.0" [[CompilerSupportLibraries_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "8e695f735fca77e9708e795eda62afdb869cbb70" +deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "0.3.4+0" [[Contour]] -deps = ["StaticArrays"] -git-tree-sha1 = "9f02045d934dc030edad45944ea80dbd1f0ebea7" +git-tree-sha1 = "d05d9e7b7aedff4e5b51a029dced05cfb6125781" uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" -version = "0.5.7" +version = "0.6.2" [[DataAPI]] -git-tree-sha1 = "dfb3b7e89e395be1e25c2ad6d7690dc29cc53b1d" +git-tree-sha1 = "fb5f5316dd3fd4c5e7c30a24d50643b73e37cd40" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.6.0" +version = "1.10.0" [[DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "4437b64df1e0adccc3e5d1adbc3ac741095e4677" +git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.9" +version = "0.18.13" [[DataValueInterfaces]] git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" @@ -164,21 +173,25 @@ uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" [[DocStringExtensions]] deps = ["LibGit2"] -git-tree-sha1 = "a32185f5428d3986f47c2ab78b1f216d5e6cc96f" +git-tree-sha1 = "b19534d1895d702889b219c382a6e18010797f0b" uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.8.5" +version = "0.8.6" + +[[Downloads]] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" [[EarCut_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "92d8f9f208637e8d2d28c664051a00569c01493d" +git-tree-sha1 = "3f3a2501fa7236e9b911e0f7a588c657e822bb6d" uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" -version = "2.1.5+1" +version = "2.2.3+0" [[Expat_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "1402e52fcda25064f51c77a9655ce8680b76acf0" +git-tree-sha1 = "bad72f730e9e91c08d9427d5e8db95478a3c323d" uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.2.7+6" +version = "2.4.8+0" [[FFMPEG]] deps = ["FFMPEG_jll"] @@ -187,37 +200,43 @@ uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" version = "0.4.1" [[FFMPEG_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "LibVPX_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "Pkg", "Zlib_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] -git-tree-sha1 = "3cc57ad0a213808473eafef4845a74766242e05f" +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "Pkg", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] +git-tree-sha1 = "ccd479984c7838684b3ac204b716c89955c76623" uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" -version = "4.3.1+4" +version = "4.4.2+0" [[FFTW]] -deps = ["AbstractFFTs", "FFTW_jll", "IntelOpenMP_jll", "Libdl", "LinearAlgebra", "MKL_jll", "Reexport"] -git-tree-sha1 = "1b48dbde42f307e48685fa9213d8b9f8c0d87594" +deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] +git-tree-sha1 = "90630efff0894f8142308e334473eba54c433549" uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" -version = "1.3.2" +version = "1.5.0" [[FFTW_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "5a0d4b6a22a34d17d53543bd124f4b08ed78e8b0" +git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea" uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" -version = "3.3.9+7" +version = "3.3.10+0" + +[[FLTK_jll]] +deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "72a4842f93e734f378cf381dae2ca4542f019d23" +uuid = "4fce6fc7-ba6a-5f4c-898f-77e99806d6f8" +version = "1.3.8+0" [[FastGaussQuadrature]] deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "5829b25887e53fb6730a9df2ff89ed24baa6abf6" +git-tree-sha1 = "58d83dd5a78a36205bdfddb82b1bb67682e64487" uuid = "442a2c76-b920-505d-bb47-c5924d526838" -version = "0.4.7" +version = "0.4.9" [[FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" [[FillArrays]] -deps = ["LinearAlgebra", "Random", "SparseArrays"] -git-tree-sha1 = "31939159aeb8ffad1d4d8ee44d07f8558273120a" +deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"] +git-tree-sha1 = "246621d23d1f43e3b9c368bf3b72b2331a27c286" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "0.11.7" +version = "0.13.2" [[FixedPointNumbers]] deps = ["Statistics"] @@ -227,9 +246,9 @@ version = "0.8.4" [[Fontconfig_jll]] deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "35895cf184ceaab11fd778b4590144034a167a2f" +git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.1+14" +version = "2.13.93+0" [[Formatting]] deps = ["Printf"] @@ -239,15 +258,15 @@ version = "0.4.2" [[FreeType2_jll]] deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "cbd58c9deb1d304f5a245a0b7eb841a2560cfec6" +git-tree-sha1 = "87eb71354d8ec1a96d4a7636bd57a7347dde3ef9" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.10.1+5" +version = "2.10.4+0" [[FriBidi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "0d20aed5b14dd4c9a2453c1b601d08e1149679cc" +git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" uuid = "559328eb-81f9-559d-9380-de523a88c83c" -version = "1.0.5+6" +version = "1.0.10+0" [[FunctionalCollections]] deps = ["Test"] @@ -257,50 +276,90 @@ version = "0.5.0" [[GLFW_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll"] -git-tree-sha1 = "a199aefead29c3c2638c3571a9993b564109d45a" +git-tree-sha1 = "51d2dfe8e590fbd74e7a842cf6d13d8a2f45dc01" uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" -version = "3.3.4+0" +version = "3.3.6+0" + +[[GLU_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] +git-tree-sha1 = "65af046f4221e27fb79b28b6ca89dd1d12bc5ec7" +uuid = "bd17208b-e95e-5925-bf81-e2f59b3e5c61" +version = "9.0.1+0" + +[[GMP_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" [[GR]] -deps = ["Base64", "DelimitedFiles", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Printf", "Random", "Serialization", "Sockets", "Test", "UUIDs"] -git-tree-sha1 = "b83e3125048a9c3158cbb7ca423790c7b1b57bea" +deps = ["Base64", "DelimitedFiles", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Printf", "Random", "RelocatableFolders", "Serialization", "Sockets", "Test", "UUIDs"] +git-tree-sha1 = "037a1ca47e8a5989cc07d19729567bb71bfabd0c" uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" -version = "0.57.5" +version = "0.66.0" [[GR_jll]] deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Pkg", "Qt5Base_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "e14907859a1d3aee73a019e7b3c98e9e7b8b5b3e" +git-tree-sha1 = "c8ab731c9127cd931c93221f65d6a1008dad7256" uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" -version = "0.57.3+0" +version = "0.66.0+0" [[GeometryBasics]] deps = ["EarCut_jll", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] -git-tree-sha1 = "4136b8a5668341e58398bb472754bff4ba0456ff" +git-tree-sha1 = "83ea630384a13fc4f002b77690bc0afeb4255ac9" uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" -version = "0.3.12" +version = "0.4.2" [[Gettext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "8c14294a079216000a0bdca5ec5a447f073ddc9d" +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" -version = "0.20.1+7" +version = "0.21.0+0" [[Glib_jll]] deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "04690cc5008b38ecbdfede949220bc7d9ba26397" +git-tree-sha1 = "a32d672ac2c967f3deb8a81d828afc739c838a06" uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.59.0+4" +version = "2.68.3+2" + +[[GmshTools]] +deps = ["Libdl", "gmsh_jll"] +git-tree-sha1 = "299aa66053646db77f8aa7fafcebe0f9e5c0d1dc" +uuid = "82e2f556-b1bd-5f1a-9576-f93c0da5f0ee" +version = "0.5.2" + +[[Graphite2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" +uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" +version = "1.3.14+0" [[Grisu]] git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" version = "1.0.2" +[[GrundmannMoeller]] +deps = ["LinearAlgebra", "StaticArrays", "Test"] +git-tree-sha1 = "e264cf5f081091e4af712a911d3b620567c565e3" +uuid = "36aa67b7-9d79-4e90-bbc0-05abd90a007e" +version = "0.1.2" + +[[HDF5_jll]] +deps = ["Artifacts", "JLLWrappers", "LibCURL_jll", "Libdl", "OpenSSL_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "bab67c0d1c4662d2c4be8c6007751b0b6111de5c" +uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" +version = "1.12.1+0" + [[HTTP]] -deps = ["Base64", "Dates", "IniFile", "MbedTLS", "NetworkOptions", "Sockets", "URIs"] -git-tree-sha1 = "86ed84701fbfd1142c9786f8e53c595ff5a4def9" +deps = ["Base64", "Dates", "IniFile", "Logging", "MbedTLS", "NetworkOptions", "Sockets", "URIs"] +git-tree-sha1 = "0fa77022fe4b511826b39c894c90daf5fce3334a" uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "0.9.10" +version = "0.9.17" + +[[HarfBuzz_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] +git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3" +uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" +version = "2.8.1+1" [[Hiccup]] deps = ["MacroTools", "Test"] @@ -309,10 +368,9 @@ uuid = "9fb69e20-1954-56bb-a84f-559cc56a8ff7" version = "0.2.2" [[IniFile]] -deps = ["Test"] -git-tree-sha1 = "098e4d2c533924c921f9f9847274f2ad89e018b8" +git-tree-sha1 = "f550e6e32074c939295eb5ea6de31849ac2c9625" uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f" -version = "0.5.0" +version = "0.5.1" [[IntelOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -324,16 +382,27 @@ version = "2018.0.3+2" deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +[[InverseFunctions]] +deps = ["Test"] +git-tree-sha1 = "b3364212fb5d870f724876ffcd34dd8ec6d98918" +uuid = "3587e190-3f89-42d0-90ee-14403ec27112" +version = "0.1.7" + +[[IrrationalConstants]] +git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.1.1" + [[IterTools]] -git-tree-sha1 = "05110a2ab1fc5f932622ffea2a003221f4782c18" +git-tree-sha1 = "fa6287a4469f5e048d763df38279ee729fbd44e5" uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" -version = "1.3.0" +version = "1.4.0" [[IterativeSolvers]] deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] -git-tree-sha1 = "1a8c6237e78b714e901e406c096fc8a65528af7d" +git-tree-sha1 = "1169632f425f79429f245113b775a0e3d121457c" uuid = "42fd0dbc-a981-5370-80f2-aaf504508153" -version = "0.9.1" +version = "0.9.2" [[IteratorInterfaceExtensions]] git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" @@ -342,56 +411,68 @@ version = "1.0.0" [[JLLWrappers]] deps = ["Preferences"] -git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e" +git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.3.0" +version = "1.4.1" [[JSExpr]] deps = ["JSON", "MacroTools", "Observables", "WebIO"] -git-tree-sha1 = "bd6c034156b1e7295450a219c4340e32e50b08b1" +git-tree-sha1 = "b413a73785b98474d8af24fd4c8a975e31df3658" uuid = "97c1335a-c9c5-57fe-bc5d-ec35cebe8660" -version = "0.5.3" +version = "0.5.4" [[JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "81690084b6198a2e1da36fcfda16eeca9f9f24e4" +git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.1" +version = "0.21.3" [[JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9aff0587d9603ea0de2c6f6300d9f9492bbefbd3" +git-tree-sha1 = "b53380851c6e6664204efb2e62cd24fa5c47e4ba" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "2.0.1+3" +version = "2.1.2+0" [[Kaleido_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "2ef87eeaa28713cb010f9fb0be288b6c1a4ecd53" +git-tree-sha1 = "43032da5832754f58d14a91ffbe86d5f176acda9" uuid = "f7e6163d-2fa5-5f23-b69c-1db539e41963" -version = "0.1.0+0" +version = "0.2.1+0" [[LAME_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "df381151e871f41ee86cee4f5f6fd598b8a68826" +git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c" uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" -version = "3.100.0+3" +version = "3.100.1+0" + +[[LERC_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "bf36f528eec6634efc60d7ec062008f171071434" +uuid = "88015f11-f218-50d7-93a8-a6af411a945d" +version = "3.0.0+1" + +[[LLVMOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "ad927676766e6529a2d5152f12040620447c0c9b" +uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" +version = "14.0.4+0" [[LZO_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f128cd6cd05ffd6d3df0523ed99b90ff6f9b349a" +git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.0+3" +version = "2.10.1+0" [[LaTeXStrings]] -git-tree-sha1 = "c7f1c695e06c01b95a67f0cd1d34994f3e7db104" +git-tree-sha1 = "f2355693d6778a178ade15952b7ac47a4ff97996" uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.2.1" +version = "1.3.0" [[Latexify]] deps = ["Formatting", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "Printf", "Requires"] -git-tree-sha1 = "a4b12a1bd2ebade87891ab7e36fdbce582301a92" +git-tree-sha1 = "1a43be956d433b5d0321197150c2f94e16c0aaa0" uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" -version = "0.15.6" +version = "0.15.16" [[Lazy]] deps = ["MacroTools"] @@ -400,35 +481,39 @@ uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" version = "0.15.1" [[LazyArtifacts]] -deps = ["Pkg"] -git-tree-sha1 = "4bb5499a1fc437342ea9ab7e319ede5a457c0968" +deps = ["Artifacts", "Pkg"] uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" -version = "1.3.0" + +[[LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" + +[[LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" [[LibGit2]] -deps = ["Printf"] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" -[[LibVPX_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "85fcc80c3052be96619affa2fe2e6d2da3908e11" -uuid = "dd192d2f-8180-539f-9fb4-cc70b1dcf69a" -version = "1.9.0+1" +[[LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" [[Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" [[Libffi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "a2cd088a88c0d37eef7d209fd3d8712febce0d90" +git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.2.1+4" +version = "3.2.2+1" [[Libgcrypt_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] -git-tree-sha1 = "b391a18ab1170a2e568f9fb8d83bc7c780cb9999" +git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.5+4" +version = "1.8.7+0" [[Libglvnd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] @@ -438,74 +523,102 @@ version = "1.3.0+3" [[Libgpg_error_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "ec7f2e8ad5c9fa99fc773376cdbc86d9a5a23cb7" +git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.36.0+3" +version = "1.42.0+0" [[Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "8e924324b2e9275a51407a4e06deb3455b1e359f" +git-tree-sha1 = "42b62845d70a619f063a7da093d995ec8e15e778" uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.16.0+7" +version = "1.16.1+1" [[Libmount_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "51ad0c01c94c1ce48d5cad629425035ad030bfd5" +git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73" uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.34.0+3" +version = "2.35.0+0" [[Libtiff_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "291dd857901f94d683973cdf679984cdf73b56d0" +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"] +git-tree-sha1 = "3eb79b0ca5764d4799c06699573fd8f533259713" uuid = "89763e89-9b03-5906-acba-b20f662cd828" -version = "4.1.0+2" +version = "4.4.0+0" [[Libuuid_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f879ae9edbaa2c74c922e8b85bb83cc84ea1450b" +git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066" uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.34.0+7" +version = "2.36.0+0" + +[[LiftedMaps]] +deps = ["LinearAlgebra", "LinearMaps"] +git-tree-sha1 = "9e417fe8b11edb183ee990c31722757cf7def62c" +uuid = "d22a30c1-52ac-4762-a8c9-5838452405e0" +version = "0.4.1" [[LinearAlgebra]] -deps = ["Libdl"] +deps = ["Libdl", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +[[LinearElasticity_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "71e8ee0f9fe0e86a8f8c7f28361e5118eab2f93f" +uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" +version = "5.0.0+0" + +[[LinearMaps]] +deps = ["LinearAlgebra", "SparseArrays", "Statistics"] +git-tree-sha1 = "d1b46faefb7c2f48fdec69e6f3cc34857769bc15" +uuid = "7a12625a-238d-50fd-b39a-03d52299707e" +version = "3.8.0" + [[LogExpFunctions]] -deps = ["DocStringExtensions", "LinearAlgebra"] -git-tree-sha1 = "1ba664552f1ef15325e68dc4c05c3ef8c2d5d885" +deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "7c88f63f9f0eb5929f15695af9a4d7d3ed278a91" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.2.4" +version = "0.3.16" [[Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" +[[METIS_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "1d31872bb9c5e7ec1f618e8c4a56c8b0d9bddc7e" +uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" +version = "5.1.1+0" + [[MKL_jll]] deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"] -git-tree-sha1 = "c253236b0ed414624b083e6b72bfe891fbd2c7af" +git-tree-sha1 = "e595b205efd49508358f7dc670a940c790204629" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2021.1.1+1" +version = "2022.0.0+0" + +[[MMG_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] +git-tree-sha1 = "70a59df96945782bb0d43b56d0fbfdf1ce2e4729" +uuid = "86086c02-e288-5929-a127-40944b0018b7" +version = "5.6.0+0" [[MacroTools]] deps = ["Markdown", "Random"] -git-tree-sha1 = "6a8a2a625ab0dea913aba95c11370589e0239ff0" +git-tree-sha1 = "3d3e902b31198a27340d0bf00d6ac452866021cf" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.6" +version = "0.5.9" [[Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[MbedTLS]] -deps = ["Dates", "MbedTLS_jll", "Random", "Sockets"] -git-tree-sha1 = "1c38e51c3d08ef2278062ebceade0e46cefc96fe" +deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "Random", "Sockets"] +git-tree-sha1 = "9f4f5a42de3300439cb8300236925670f844a555" uuid = "739be429-bea8-5141-9913-cc70e7f3736d" -version = "1.0.3" +version = "1.1.1" [[MbedTLS_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "0eef589dd1c26a3ac9d753fe1a8bcad63f956fa6" +deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.16.8+1" [[Measures]] git-tree-sha1 = "e498ddeee6f9fdb4551ce855a46f54dbd900245f" @@ -514,18 +627,21 @@ version = "0.3.1" [[Missings]] deps = ["DataAPI"] -git-tree-sha1 = "4ea90bd5d3985ae1f9a908bd4500ae88921c5ce7" +git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f" uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "1.0.0" +version = "1.0.2" [[Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" +[[MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" + [[Mustache]] deps = ["Printf", "Tables"] -git-tree-sha1 = "36995ef0d532fe08119d70b2365b7b03d4e00f48" +git-tree-sha1 = "1e566ae913a57d0062ff1af54d2697b9344b99cd" uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" -version = "1.0.10" +version = "1.0.14" [[Mux]] deps = ["AssetRegistry", "Base64", "HTTP", "Hiccup", "Pkg", "Sockets", "WebSockets"] @@ -534,43 +650,56 @@ uuid = "a975b10e-0019-58db-a62f-e48ff68538c9" version = "0.7.6" [[NaNMath]] -git-tree-sha1 = "bfe47e760d60b82b66b61d2d44128b62e3a369fb" +deps = ["OpenLibm_jll"] +git-tree-sha1 = "a7c3d1da1189a1c2fe843a3bfa04d18d20eb3211" uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" -version = "0.3.5" +version = "1.0.1" [[NetworkOptions]] -git-tree-sha1 = "ed3157f48a05543cce9b241e1f2815f7e843d96e" uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" -version = "1.2.0" + +[[OCCT_jll]] +deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "acc8099ae8ed10226dc8424fb256ec9fe367a1f0" +uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" +version = "7.6.2+2" [[Observables]] -git-tree-sha1 = "3469ef96607a6b9a1e89e54e6f23401073ed3126" +git-tree-sha1 = "dfd8d34871bc3ad08cd16026c1828e271d554db9" uuid = "510215fc-4207-5dde-b226-833fc4488ee2" -version = "0.3.3" +version = "0.5.1" [[Ogg_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "a42c0f138b9ebe8b58eba2271c5053773bde52d0" +git-tree-sha1 = "887579a3eb005446d514ab7aeac5d1d027658b8f" uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" -version = "1.3.4+2" +version = "1.3.5+1" + +[[OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" + +[[OpenLibm_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "05823500-19ac-5b8b-9628-191a04bc5112" [[OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "71bbbc616a1d710879f5a1021bcba65ffba6ce58" +git-tree-sha1 = "e60321e3f2616584ff98f0a4f18d98ae6f89bbb3" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "1.1.1+6" +version = "1.1.17+0" [[OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9db77584158d0ab52307f8c04f8e7c08ca76b5b3" +git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.3+4" +version = "0.5.5+0" [[Opus_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f9d57f4126c39565e05a2b0264df99f497fc6f37" +git-tree-sha1 = "51a08fb14ec28da2ec7a927c4337e4332c2a4720" uuid = "91d4177d-7536-5919-b921-800302f37372" -version = "1.3.1+3" +version = "1.3.2+0" [[OrderedCollections]] git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" @@ -579,73 +708,79 @@ version = "1.4.1" [[PCRE_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "1b556ad51dceefdbf30e86ffa8f528b73c7df2bb" +git-tree-sha1 = "b2a7af664e098055a7529ad1a900ded962bca488" uuid = "2f80f16e-611a-54ab-bc61-aa92de5b98fc" -version = "8.42.0+4" +version = "8.44.0+0" + +[[Parameters]] +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.3" [[Parsers]] deps = ["Dates"] -git-tree-sha1 = "c8abc88faa3f7a3950832ac5d6e690881590d6dc" +git-tree-sha1 = "0044b23da09b5608b4ecacb4e5e6c6332f833a7e" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "1.1.0" +version = "2.3.2" [[Pidfile]] deps = ["FileWatching", "Test"] -git-tree-sha1 = "1be8660b2064893cd2dae4bd004b589278e4440d" +git-tree-sha1 = "2d8aaf8ee10df53d0dfb9b8ee44ae7c04ced2b03" uuid = "fa939f87-e72e-5be4-a000-7fc836dbe307" -version = "1.2.0" +version = "1.3.0" [[Pixman_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "6a20a83c1ae86416f0a5de605eaea08a552844a3" +git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29" uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.40.0+0" +version = "0.40.1+0" [[Pkg]] -deps = ["Dates", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] +deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" [[PlotThemes]] -deps = ["PlotUtils", "Requires", "Statistics"] -git-tree-sha1 = "a3a964ce9dc7898193536002a6dd892b1b5a6f1d" +deps = ["PlotUtils", "Statistics"] +git-tree-sha1 = "8162b2f8547bc23876edd0c5181b27702ae58dce" uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" -version = "2.0.1" +version = "3.0.0" [[PlotUtils]] deps = ["ColorSchemes", "Colors", "Dates", "Printf", "Random", "Reexport", "Statistics"] -git-tree-sha1 = "ae9a295ac761f64d8c2ec7f9f24d21eb4ffba34d" +git-tree-sha1 = "9888e59493658e476d3073f1ce24348bdc086660" uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" -version = "1.0.10" +version = "1.3.0" [[Plotly]] deps = ["Base64", "DelimitedFiles", "HTTP", "JSON", "PlotlyJS", "Reexport"] -git-tree-sha1 = "ad5dc3a3bc18246d552aad6c78268102becd9d59" +git-tree-sha1 = "044a9194ae38a50cbdb34a05dc63bf68e4db95df" uuid = "58dd65bb-95f3-509e-9936-c39a10fdeae7" -version = "0.4.0" +version = "0.4.1" [[PlotlyBase]] -deps = ["Base64", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "Kaleido_jll", "LaTeXStrings", "Logging", "Pkg", "Requires", "Statistics", "UUIDs"] -git-tree-sha1 = "f20d4669281187c9e9d75820f8ec95e88131fc1e" +deps = ["ColorSchemes", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "LaTeXStrings", "Logging", "Parameters", "Pkg", "REPL", "Requires", "Statistics", "UUIDs"] +git-tree-sha1 = "180d744848ba316a3d0fdf4dbd34b77c7242963a" uuid = "a03496cd-edff-5a9b-9e67-9cda94a718b5" -version = "0.5.3" +version = "0.8.18" [[PlotlyJS]] -deps = ["Blink", "DelimitedFiles", "JSExpr", "JSON", "Markdown", "Pkg", "PlotlyBase", "REPL", "Reexport", "Requires", "WebIO"] -git-tree-sha1 = "b859551864853bb2c317d0937e94bd19d749396b" +deps = ["Base64", "Blink", "DelimitedFiles", "JSExpr", "JSON", "Kaleido_jll", "Markdown", "Pkg", "PlotlyBase", "REPL", "Reexport", "Requires", "WebIO"] +git-tree-sha1 = "53d6325e14d3bdb85fd387a085075f36082f35a3" uuid = "f0f68f2c-4968-5e81-91da-67840de0976a" -version = "0.14.1" +version = "0.18.8" [[Plots]] -deps = ["Base64", "Contour", "Dates", "FFMPEG", "FixedPointNumbers", "GR", "GeometryBasics", "JSON", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "PlotThemes", "PlotUtils", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs"] -git-tree-sha1 = "e995fa1821b6daff8b107a8eafbec234ae2263d0" +deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "GeometryBasics", "JSON", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs", "UnicodeFun", "Unzip"] +git-tree-sha1 = "0a0da27969e8b6b2ee67c112dcf7001a659049a0" uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -version = "1.16.5" +version = "1.31.4" [[Preferences]] deps = ["TOML"] -git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a" +git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d" uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.2.2" +version = "1.3.0" [[Printf]] deps = ["Unicode"] @@ -653,54 +788,72 @@ uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" [[Qt5Base_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"] -git-tree-sha1 = "16626cfabbf7206d60d84f2bf4725af7b37d4a77" +git-tree-sha1 = "c6c0f690d0cc7caddb74cef7aa847b824a16b256" uuid = "ea2cea3b-5b76-57ae-a6ef-0a8af62496e1" -version = "5.15.2+0" +version = "5.15.3+1" [[REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets"] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[Random]] -deps = ["Serialization"] +deps = ["SHA", "Serialization"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [[RecipesBase]] -git-tree-sha1 = "b3fb709f3c97bfc6e948be68beeecb55a0b340ae" +git-tree-sha1 = "6bf3f380ff52ce0832ddd3a2a7b9538ed1bcca7d" uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "1.1.1" +version = "1.2.1" [[RecipesPipeline]] deps = ["Dates", "NaNMath", "PlotUtils", "RecipesBase"] -git-tree-sha1 = "7a5026a6741c14147d1cb6daf2528a77ca28eb51" +git-tree-sha1 = "2690681814016887462cf5ac37102b51cd9ec781" uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c" -version = "0.3.2" +version = "0.6.2" [[Reexport]] -git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220" +git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.1.0" +version = "1.2.2" + +[[RelocatableFolders]] +deps = ["SHA", "Scratch"] +git-tree-sha1 = "22c5201127d7b243b9ee1de3b43c408879dff60f" +uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" +version = "0.3.0" [[Requires]] deps = ["UUIDs"] -git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621" +git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.1.3" +version = "1.3.0" + +[[SCOTCH_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "7110b749766853054ce8a2afaa73325d72d32129" +uuid = "a8d0f55d-b80e-548d-aff6-1a04c175f0f9" +version = "6.1.3+0" [[SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +[[SauterSchwab3D]] +deps = ["CompScienceMeshes", "FastGaussQuadrature", "GrundmannMoeller", "LinearAlgebra", "ShunnHamQuadrature", "StaticArrays"] +git-tree-sha1 = "e5c1dce465dd31be2e21761922ceab26ec28db7c" +uuid = "0a13313b-1c00-422e-8263-562364ed9544" +version = "0.1.1" + [[SauterSchwabQuadrature]] deps = ["CompScienceMeshes", "FastGaussQuadrature", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "8218588065871ebba62938e43b00e6b1c67fa0a5" +git-tree-sha1 = "8d7eed829815a48c042589dd11d8526a0d81bf1c" uuid = "535c7bfe-2023-5c1d-b712-654ef9d93a38" -version = "2.1.1" +version = "2.1.3" [[Scratch]] deps = ["Dates"] -git-tree-sha1 = "0b4b7f1393cff97c33891da2a0bf69c6ed241fda" +git-tree-sha1 = "f94f779c94e58bf9ea243e77a37e16d9de9126bd" uuid = "6c6a2e73-6563-6170-7368-637461726353" -version = "1.1.0" +version = "1.1.1" [[Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" @@ -715,14 +868,20 @@ git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" version = "1.0.3" +[[ShunnHamQuadrature]] +deps = ["LinearAlgebra", "StaticArrays"] +git-tree-sha1 = "dfa53166b13cd6f352d54c99a24124321ef95282" +uuid = "164309f2-5039-4884-b6c7-6da8aa5c66ad" +version = "0.1.0" + [[Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" [[SortingAlgorithms]] deps = ["DataStructures"] -git-tree-sha1 = "2ec1962eba973f383239da22e75218565c390a96" +git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508" uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.0.0" +version = "1.0.1" [[SparseArrays]] deps = ["LinearAlgebra", "Random"] @@ -735,43 +894,47 @@ uuid = "5cb6c4b0-9b79-11e8-24c9-f9621d252589" version = "0.2.4" [[SpecialFunctions]] -deps = ["ChainRulesCore", "LogExpFunctions", "OpenSpecFun_jll"] -git-tree-sha1 = "a50550fa3164a8c46747e62063b4d774ac1bcf49" +deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "d75bda01f8c31ebb72df80a46c88b25d1c79c56d" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "1.5.1" +version = "2.1.7" [[StaticArrays]] -deps = ["LinearAlgebra", "Random", "Statistics"] -git-tree-sha1 = "42378d3bab8b4f57aa1ca443821b752850592668" +deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] +git-tree-sha1 = "23368a3313d12a2326ad0035f0db0c0966f438ef" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.2.2" +version = "1.5.2" + +[[StaticArraysCore]] +git-tree-sha1 = "66fe9eb253f910fe8cf161953880cfdaef01cdf0" +uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" +version = "1.0.1" [[Statistics]] deps = ["LinearAlgebra", "SparseArrays"] uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [[StatsAPI]] -git-tree-sha1 = "1958272568dc176a1d881acb797beb909c785510" +deps = ["LinearAlgebra"] +git-tree-sha1 = "2c11d7290036fe7aac9038ff312d3b3a2a5bf89e" uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" -version = "1.0.0" +version = "1.4.0" [[StatsBase]] -deps = ["DataAPI", "DataStructures", "LinearAlgebra", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "2f6792d523d7448bbe2fec99eca9218f06cc746d" +deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] +git-tree-sha1 = "472d044a1c8df2b062b23f222573ad6837a615ba" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.33.8" +version = "0.33.19" [[StructArrays]] -deps = ["Adapt", "DataAPI", "Tables"] -git-tree-sha1 = "44b3afd37b17422a62aea25f04c1f7e09ce6b07f" +deps = ["Adapt", "DataAPI", "StaticArrays", "Tables"] +git-tree-sha1 = "ec47fb6069c57f1cee2f67541bf8f23415146de7" uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" -version = "0.5.1" +version = "0.6.11" [[TOML]] deps = ["Dates"] -git-tree-sha1 = "44aaac2d2aec4a850302f9aa69127c74f0c3787e" uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.3" [[TableTraits]] deps = ["IteratorInterfaceExtensions"] @@ -780,13 +943,23 @@ uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" version = "1.0.1" [[Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "TableTraits", "Test"] -git-tree-sha1 = "aa30f8bb63f9ff3f8303a06c604c8500a69aa791" +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits", "Test"] +git-tree-sha1 = "5ce79ce186cc678bbb5c5681ca3379d1ddae11a1" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.4.3" +version = "1.7.0" + +[[Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" + +[[TensorCore]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" +uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" +version = "0.1.1" [[Test]] -deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [[URIParser]] @@ -796,34 +969,50 @@ uuid = "30578b45-9adc-5946-b283-645ec420af67" version = "0.4.1" [[URIs]] -git-tree-sha1 = "97bbe755a53fe859669cd907f2d96aee8d2c1355" +git-tree-sha1 = "e59ecc5a41b000fa94423a578d29290c7266fc10" uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.3.0" +version = "1.4.0" [[UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" +[[UnPack]] +git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.2" + [[Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" +[[UnicodeFun]] +deps = ["REPL"] +git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" +uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" +version = "0.4.1" + +[[Unzip]] +git-tree-sha1 = "34db80951901073501137bdbc3d5a8e7bbd06670" +uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d" +version = "0.1.2" + [[Wayland_jll]] deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "dc643a9b774da1c2781413fd7b6dcd2c56bb8056" +git-tree-sha1 = "3e61f0b86f90dacb0bc0e73a0c5a83f6a8636e23" uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" -version = "1.17.0+4" +version = "1.19.0+0" [[Wayland_protocols_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll"] -git-tree-sha1 = "2839f1c1296940218e35df0bbb220f2a79686670" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4528479aa01ee1b3b4cd0e6faef0e04cf16466da" uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91" -version = "1.18.0+4" +version = "1.25.0+0" [[WebIO]] deps = ["AssetRegistry", "Base64", "Distributed", "FunctionalCollections", "JSON", "Logging", "Observables", "Pkg", "Random", "Requires", "Sockets", "UUIDs", "WebSockets", "Widgets"] -git-tree-sha1 = "adc25e225bc334c7df6eec3b39496edfc451cc38" +git-tree-sha1 = "a8bbcd0b08061bba794c56fb78426e96e114ae7f" uuid = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" -version = "0.8.15" +version = "0.8.18" [[WebSockets]] deps = ["Base64", "Dates", "HTTP", "Logging", "Sockets"] @@ -833,27 +1022,27 @@ version = "1.5.9" [[Widgets]] deps = ["Colors", "Dates", "Observables", "OrderedCollections"] -git-tree-sha1 = "eae2fbbc34a79ffd57fb4c972b08ce50b8f6a00d" +git-tree-sha1 = "fcdae142c1cfc7d89de2d11e08721d0f2f86c98a" uuid = "cc8bc4a8-27d6-5769-a93b-9d913e69aa62" -version = "0.6.3" +version = "0.6.6" [[WiltonInts84]] deps = ["LinearAlgebra"] -git-tree-sha1 = "b7374c784a208b377157146e3bdd94287e41f95a" +git-tree-sha1 = "d412771d65e9760b6a7fdb86754c237ee2a92dfd" uuid = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" -version = "0.2.2" +version = "0.2.3" [[XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "be0db24f70aae7e2b89f2f3092e93b8606d659a6" +git-tree-sha1 = "58443b63fb7e465a8a7210828c91c08b92132dff" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.9.10+3" +version = "2.9.14+0" [[XSLT_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "2b3eac39df218762d2d005702d601cd44c997497" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.33+4" +version = "1.1.34+0" [[Xorg_libX11_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] @@ -891,6 +1080,12 @@ git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" version = "5.0.3+4" +[[Xorg_libXft_jll]] +deps = ["Fontconfig_jll", "Libdl", "Pkg", "Xorg_libXrender_jll"] +git-tree-sha1 = "754b542cdc1057e0a2f1888ec5414ee17a4ca2a1" +uuid = "2c808117-e144-5220-80d1-69d4eaa9352c" +version = "2.3.3+1" + [[Xorg_libXi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246" @@ -982,52 +1177,74 @@ uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" version = "1.4.0+3" [[Zlib_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "320228915c8debb12cb434c59057290f0834dbf6" +deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.2.11+18" [[Zstd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "2c1332c54931e83f8f94d310fa447fd743e8d600" +git-tree-sha1 = "e45044cd873ded54b6a5bac0eb5c971392cf1927" uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" -version = "1.4.8+0" +version = "1.5.2+0" + +[[gmsh_jll]] +deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "9774ebf68348b3b56c74a78b829051310163fd76" +uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" +version = "4.10.2+0" + +[[libaom_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "3a2ea60308f0996d26f1e5354e10c24e9ef905d4" +uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" +version = "3.4.0+0" [[libass_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "acc685bcf777b2202a904cdcb49ad34c2fa1880c" +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "5982a94fcba20f02f42ace44b9894ee2b140fe47" uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" -version = "0.14.0+4" +version = "0.15.1+0" + +[[libblastrampoline_jll]] +deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" [[libfdk_aac_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "7a5780a0d9c6864184b3a2eeeb833a0c871f00ab" +git-tree-sha1 = "daacc84a041563f965be61859a36e17c4e4fcd55" uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" -version = "0.1.6+4" +version = "2.0.2+0" [[libpng_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "6abbc424248097d69c0c87ba50fcb0753f93e0ee" +git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.37+6" +version = "1.6.38+0" [[libvorbis_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] -git-tree-sha1 = "fa14ac25af7a4b8a7f61b287a124df7aab601bcd" +git-tree-sha1 = "b910cb81ef3fe6e78bf6acee440bda86fd6ae00c" uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" -version = "1.3.6+6" +version = "1.3.7+1" + +[[nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" + +[[p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" [[x264_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "d713c1ce4deac133e3334ee12f4adff07f81778f" +git-tree-sha1 = "4fea590b89e6ec504593146bf8b988b2c00922b2" uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" -version = "2020.7.14+2" +version = "2021.5.5+0" [[x265_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "487da2f8f2f0c8ee0e83f39d13037d6bbf0a45ab" +git-tree-sha1 = "ee567a171cce03570d77ad3a43e90218e38937a9" uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" -version = "3.0.0+3" +version = "3.5.0+0" [[xkbcommon_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] From dd50409d45bb8e2fef10fb24b6259aa555e85939 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 17 Aug 2022 15:33:16 +0200 Subject: [PATCH 170/528] Continued work on unifying momintegral kernels --- examples/efie.jl | 2 +- examples/efie_bdm.jl | 2 +- examples/mfie_bdm.jl | 2 +- src/BEAST.jl | 2 +- src/bases/local/rtlocal.jl | 5 +- src/maxwell/sauterschwabints_bdm.jl | 399 +++++++++++++++------------- src/maxwell/sauterschwabints_rt.jl | 150 +++-------- 7 files changed, 265 insertions(+), 297 deletions(-) diff --git a/examples/efie.jl b/examples/efie.jl index 16e7a3ef..cac0c599 100644 --- a/examples/efie.jl +++ b/examples/efie.jl @@ -5,7 +5,7 @@ using BEAST # Γ = meshsphere(radius=1.0, h=0.1) X = raviartthomas(Γ) -κ, η = 3.0, 1.0 +κ, η = 1.0, 1.0 t = Maxwell3D.singlelayer(wavenumber=κ) E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) # E = -η/(im*κ)*BEAST.CurlCurlGreen(κ, ẑ, point(2,0,0)) diff --git a/examples/efie_bdm.jl b/examples/efie_bdm.jl index bcda043e..226c7aed 100644 --- a/examples/efie_bdm.jl +++ b/examples/efie_bdm.jl @@ -5,7 +5,7 @@ using BEAST Γ = meshsphere(radius=1.0, h=0.2) X = brezzidouglasmarini(Γ) -κ = 3.0 +κ = 1.0 t = Maxwell3D.singlelayer(wavenumber=κ) E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) e = (n × E) × n diff --git a/examples/mfie_bdm.jl b/examples/mfie_bdm.jl index 4fbdc419..7fa869fe 100644 --- a/examples/mfie_bdm.jl +++ b/examples/mfie_bdm.jl @@ -8,7 +8,7 @@ Y = brezzidouglasmarini(Γ) # X = raviartthomas(Γ) # Y = raviartthomas(Γ) -ϵ, μ, ω = 1.0, 1.0, 3.0; κ = ω * √(ϵ*μ) +ϵ, μ, ω = 1.0, 1.0, 1.0; κ = ω * √(ϵ*μ) # κ = 3.0 NK, Id = BEAST.DoubleLayerRotatedMW3D(im*κ), Identity() E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) diff --git a/src/BEAST.jl b/src/BEAST.jl index 798dd340..caec9dbe 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -188,8 +188,8 @@ include("maxwell/mwexc.jl") include("maxwell/mwops.jl") include("maxwell/nxdbllayer.jl") include("maxwell/wiltonints.jl") -include("maxwell/sauterschwabints_rt.jl") include("maxwell/sauterschwabints_bdm.jl") +include("maxwell/sauterschwabints_rt.jl") include("maxwell/nitsche.jl") include("maxwell/farfield.jl") include("maxwell/nearfield.jl") diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index c0c310ba..f883a3bf 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -98,7 +98,8 @@ const _dof_perms_rt = [ (3,2,1), ] -function dof_permutation(vert_permutation) - i = findfirst(==(vert_permutation), _vert_perms_rt) +function dof_permutation(::RTRefSpace, vert_permutation) + i = findfirst(==(tuple(vert_permutation...)), _vert_perms_rt) + @assert i != nothing return _dof_perms_rt[i] end \ No newline at end of file diff --git a/src/maxwell/sauterschwabints_bdm.jl b/src/maxwell/sauterschwabints_bdm.jl index c61da235..0330ddd9 100644 --- a/src/maxwell/sauterschwabints_bdm.jl +++ b/src/maxwell/sauterschwabints_bdm.jl @@ -6,6 +6,28 @@ struct Integrand{Op,LSt,LSb,Elt,Elb} trial_chart::Elb end +getvalue(a::SVector{N}) where {N} = SVector{N}(getvalue(a.data)) +getvalue(a::NTuple{1}) = (a[1].value,) +getvalue(a::NTuple{N}) where {N} = tuple(a[1].value, getvalue(Base.tail(a))...) + +getdivergence(a::SVector{N}) where {N} = SVector{N}(getdivergence(a.data)) +getdivergence(a::NTuple{1}) = (a[1].divergence,) +getdivergence(a::NTuple{N}) where {N} = tuple(a[1].divergence, getdivergence(Base.tail(a))...) + +function _krondot_gen(a::Type{U}, b::Type{V}) where {U<:SVector{N}, V<:SVector{M}} where {M,N} + ex = :(SMatrix{N,M}(())) + for m in 1:M + for n in 1:N + push!(ex.args[2].args, :(dot(a[$n], b[$m]))) + end + end + return ex +end + +@generated function _krondot(a::SVector{N}, b::SVector{M}) where {M,N} + ex = _krondot_gen(a,b) + return ex +end function momintegrals!(op::Operator, test_local_space::RefSpace, trial_local_space::RefSpace, @@ -56,193 +78,200 @@ function (igd::Integrand{<:DoubleLayerRotatedMW3D})(u,v) f = igd.local_test_space(x) g = igd.local_trial_space(y) - Kg = [cross(K, j*gi.value) for gi in g] - return [dot(fj.value, cross(nx, Kgi)) for fj in f, Kgi in Kg] -end - - -struct MWSL3DIntegrand2{C,O,L} - test_triangular_element::C - trial_triangular_element::C - op::O - test_local_space::L - trial_local_space::L -end - -function (igd::MWSL3DIntegrand2)(u,v) - α = igd.op.α - β = igd.op.β - γ = igd.op.gamma - - x = neighborhood(igd.test_triangular_element,u) - y = neighborhood(igd.trial_triangular_element,v) - - r = cartesian(x) - cartesian(y) - R = norm(r) - G = exp(-γ*R)/(4π*R) - - f = igd.test_local_space(x) - g = igd.trial_local_space(y) - - j = jacobian(x) * jacobian(y) - - αjG = α*j*G - βjG = β*j*G - - G = @SVector[αjG*g[i].value for i in 1:6] - H = @SVector[βjG*g[i].divergence for i in 1:6] - - SMatrix{6,6}(tuple( - dot(f[1].value,G[1])+f[1].divergence*H[1], - dot(f[2].value,G[1])+f[2].divergence*H[1], - dot(f[3].value,G[1])+f[3].divergence*H[1], - dot(f[4].value,G[1])+f[4].divergence*H[1], - dot(f[5].value,G[1])+f[5].divergence*H[1], - dot(f[6].value,G[1])+f[6].divergence*H[1], - dot(f[1].value,G[2])+f[1].divergence*H[2], - dot(f[2].value,G[2])+f[2].divergence*H[2], - dot(f[3].value,G[2])+f[3].divergence*H[2], - dot(f[4].value,G[2])+f[4].divergence*H[2], - dot(f[5].value,G[2])+f[5].divergence*H[2], - dot(f[6].value,G[2])+f[6].divergence*H[2], - dot(f[1].value,G[3])+f[1].divergence*H[3], - dot(f[2].value,G[3])+f[2].divergence*H[3], - dot(f[3].value,G[3])+f[3].divergence*H[3], - dot(f[4].value,G[3])+f[4].divergence*H[3], - dot(f[5].value,G[3])+f[5].divergence*H[3], - dot(f[6].value,G[3])+f[6].divergence*H[3], - dot(f[1].value,G[4])+f[1].divergence*H[4], - dot(f[2].value,G[4])+f[2].divergence*H[4], - dot(f[3].value,G[4])+f[3].divergence*H[4], - dot(f[4].value,G[4])+f[4].divergence*H[4], - dot(f[5].value,G[4])+f[5].divergence*H[4], - dot(f[6].value,G[4])+f[6].divergence*H[4], - dot(f[1].value,G[5])+f[1].divergence*H[5], - dot(f[2].value,G[5])+f[2].divergence*H[5], - dot(f[3].value,G[5])+f[3].divergence*H[5], - dot(f[4].value,G[5])+f[4].divergence*H[5], - dot(f[5].value,G[5])+f[5].divergence*H[5], - dot(f[6].value,G[5])+f[6].divergence*H[5], - dot(f[1].value,G[6])+f[1].divergence*H[6], - dot(f[2].value,G[6])+f[2].divergence*H[6], - dot(f[3].value,G[6])+f[3].divergence*H[6], - dot(f[4].value,G[6])+f[4].divergence*H[6], - dot(f[5].value,G[6])+f[5].divergence*H[6], - dot(f[6].value,G[6])+f[6].divergence*H[6], - )) -end - -function momintegrals!(op::MWSingleLayer3D, - test_local_space::BDMRefSpace, trial_local_space::BDMRefSpace, - test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) - - I, J, _, _ = SauterSchwabQuadrature.reorder( - test_triangular_element.vertices, - trial_triangular_element.vertices, strat) - - test_triangular_element = simplex( - test_triangular_element.vertices[I[1]], - test_triangular_element.vertices[I[2]], - test_triangular_element.vertices[I[3]]) - - trial_triangular_element = simplex( - trial_triangular_element.vertices[J[1]], - trial_triangular_element.vertices[J[2]], - trial_triangular_element.vertices[J[3]]) - - igd = MWSL3DIntegrand2(test_triangular_element, trial_triangular_element, - op, test_local_space, trial_local_space) - G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) - - K = dof_permutation(test_local_space, I) - L = dof_permutation(trial_local_space, J) - for i in 1:numfunctions(test_local_space) - for j in 1:numfunctions(trial_local_space) - out[i,j] = G[K[i],L[j]] - end end - - nothing -end + fvalue = getvalue(f) + gvalue = getvalue(g) + jKg = cross.(Ref(K), j*gvalue) + jnxKg = cross.(Ref(nx), jKg) + return _krondot(fvalue, jnxKg) -struct MWDL3DIntegrand2{C,O,L} - test_triangular_element::C - trial_triangular_element::C - op::O - test_local_space::L - trial_local_space::L + # Kg = [cross(K, j*gi.value) for gi in g] + # return [dot(fj.value, cross(nx, Kgi)) for fj in f, Kgi in Kg] end -function (igd::MWDL3DIntegrand2)(u,v) - - γ = igd.op.gamma - - x = neighborhood(igd.test_triangular_element,u) - y = neighborhood(igd.trial_triangular_element,v) - - r = cartesian(x) - cartesian(y) - R = norm(r) - G = exp(-γ*R)/(4π*R) - - GG = -(γ + 1/R) * G / R * r - T = @SMatrix [ - 0 -GG[3] GG[2] - GG[3] 0 -GG[1] - -GG[2] GG[1] 0 ] - - f = igd.test_local_space(x) - g = igd.trial_local_space(y) - - jx = jacobian(x) - jy = jacobian(y) - j = jx*jy - - G1 = j*T*g[1][1] - G2 = j*T*g[2][1] - G3 = j*T*g[3][1] - f1 = f[1][1] - f2 = f[2][1] - f3 = f[3][1] - - SMatrix{3,3}( - dot(f1,G1), - dot(f2,G1), - dot(f3,G1), - dot(f1,G2), - dot(f2,G2), - dot(f3,G2), - dot(f1,G3), - dot(f2,G3), - dot(f3,G3),) -end - - -function momintegrals!(op::MWDoubleLayer3D, - test_local_space::BDMRefSpace, trial_local_space::BDMRefSpace, - test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) - - I, J, K, L = SauterSchwabQuadrature.reorder( - test_triangular_element.vertices, - trial_triangular_element.vertices, strat) - - test_triangular_element = simplex( - test_triangular_element.vertices[I[1]], - test_triangular_element.vertices[I[2]], - test_triangular_element.vertices[I[3]]) - - trial_triangular_element = simplex( - trial_triangular_element.vertices[J[1]], - trial_triangular_element.vertices[J[2]], - trial_triangular_element.vertices[J[3]]) - - igd = MWDL3DIntegrand2(test_triangular_element, trial_triangular_element, - op, test_local_space, trial_local_space) - Q = sauterschwab_parameterized(igd, strat) - for j ∈ 1:3 - for i ∈ 1:3 - out[i,j] += Q[K[i],L[j]] - end - end - nothing -end \ No newline at end of file +# struct MWSL3DIntegrand2{C,O,L} +# test_triangular_element::C +# trial_triangular_element::C +# op::O +# test_local_space::L +# trial_local_space::L +# end + +# function (igd::MWSL3DIntegrand2)(u,v) +# α = igd.op.α +# β = igd.op.β +# γ = igd.op.gamma + +# x = neighborhood(igd.test_triangular_element,u) +# y = neighborhood(igd.trial_triangular_element,v) + +# r = cartesian(x) - cartesian(y) +# R = norm(r) +# G = exp(-γ*R)/(4π*R) + +# f = igd.test_local_space(x) +# g = igd.trial_local_space(y) + +# j = jacobian(x) * jacobian(y) + +# αjG = α*j*G +# βjG = β*j*G + +# G = @SVector[αjG*g[i].value for i in 1:6] +# H = @SVector[βjG*g[i].divergence for i in 1:6] + +# SMatrix{6,6}(tuple( +# dot(f[1].value,G[1])+f[1].divergence*H[1], +# dot(f[2].value,G[1])+f[2].divergence*H[1], +# dot(f[3].value,G[1])+f[3].divergence*H[1], +# dot(f[4].value,G[1])+f[4].divergence*H[1], +# dot(f[5].value,G[1])+f[5].divergence*H[1], +# dot(f[6].value,G[1])+f[6].divergence*H[1], +# dot(f[1].value,G[2])+f[1].divergence*H[2], +# dot(f[2].value,G[2])+f[2].divergence*H[2], +# dot(f[3].value,G[2])+f[3].divergence*H[2], +# dot(f[4].value,G[2])+f[4].divergence*H[2], +# dot(f[5].value,G[2])+f[5].divergence*H[2], +# dot(f[6].value,G[2])+f[6].divergence*H[2], +# dot(f[1].value,G[3])+f[1].divergence*H[3], +# dot(f[2].value,G[3])+f[2].divergence*H[3], +# dot(f[3].value,G[3])+f[3].divergence*H[3], +# dot(f[4].value,G[3])+f[4].divergence*H[3], +# dot(f[5].value,G[3])+f[5].divergence*H[3], +# dot(f[6].value,G[3])+f[6].divergence*H[3], +# dot(f[1].value,G[4])+f[1].divergence*H[4], +# dot(f[2].value,G[4])+f[2].divergence*H[4], +# dot(f[3].value,G[4])+f[3].divergence*H[4], +# dot(f[4].value,G[4])+f[4].divergence*H[4], +# dot(f[5].value,G[4])+f[5].divergence*H[4], +# dot(f[6].value,G[4])+f[6].divergence*H[4], +# dot(f[1].value,G[5])+f[1].divergence*H[5], +# dot(f[2].value,G[5])+f[2].divergence*H[5], +# dot(f[3].value,G[5])+f[3].divergence*H[5], +# dot(f[4].value,G[5])+f[4].divergence*H[5], +# dot(f[5].value,G[5])+f[5].divergence*H[5], +# dot(f[6].value,G[5])+f[6].divergence*H[5], +# dot(f[1].value,G[6])+f[1].divergence*H[6], +# dot(f[2].value,G[6])+f[2].divergence*H[6], +# dot(f[3].value,G[6])+f[3].divergence*H[6], +# dot(f[4].value,G[6])+f[4].divergence*H[6], +# dot(f[5].value,G[6])+f[5].divergence*H[6], +# dot(f[6].value,G[6])+f[6].divergence*H[6], +# )) +# end + +# function momintegrals!(op::MWSingleLayer3D, +# test_local_space::BDMRefSpace, trial_local_space::BDMRefSpace, +# test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) + +# I, J, _, _ = SauterSchwabQuadrature.reorder( +# test_triangular_element.vertices, +# trial_triangular_element.vertices, strat) + +# test_triangular_element = simplex( +# test_triangular_element.vertices[I[1]], +# test_triangular_element.vertices[I[2]], +# test_triangular_element.vertices[I[3]]) + +# trial_triangular_element = simplex( +# trial_triangular_element.vertices[J[1]], +# trial_triangular_element.vertices[J[2]], +# trial_triangular_element.vertices[J[3]]) + +# igd = MWSL3DIntegrand2(test_triangular_element, trial_triangular_element, +# op, test_local_space, trial_local_space) +# G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) + +# K = dof_permutation(test_local_space, I) +# L = dof_permutation(trial_local_space, J) +# for i in 1:numfunctions(test_local_space) +# for j in 1:numfunctions(trial_local_space) +# out[i,j] = G[K[i],L[j]] +# end end + +# nothing +# end + + +# struct MWDL3DIntegrand2{C,O,L} +# test_triangular_element::C +# trial_triangular_element::C +# op::O +# test_local_space::L +# trial_local_space::L +# end + +# function (igd::MWDL3DIntegrand2)(u,v) + +# γ = igd.op.gamma + +# x = neighborhood(igd.test_triangular_element,u) +# y = neighborhood(igd.trial_triangular_element,v) + +# r = cartesian(x) - cartesian(y) +# R = norm(r) +# G = exp(-γ*R)/(4π*R) + +# GG = -(γ + 1/R) * G / R * r +# T = @SMatrix [ +# 0 -GG[3] GG[2] +# GG[3] 0 -GG[1] +# -GG[2] GG[1] 0 ] + +# f = igd.test_local_space(x) +# g = igd.trial_local_space(y) + +# jx = jacobian(x) +# jy = jacobian(y) +# j = jx*jy + +# G1 = j*T*g[1][1] +# G2 = j*T*g[2][1] +# G3 = j*T*g[3][1] + +# f1 = f[1][1] +# f2 = f[2][1] +# f3 = f[3][1] + +# SMatrix{3,3}( +# dot(f1,G1), +# dot(f2,G1), +# dot(f3,G1), +# dot(f1,G2), +# dot(f2,G2), +# dot(f3,G2), +# dot(f1,G3), +# dot(f2,G3), +# dot(f3,G3),) +# end + + +# function momintegrals!(op::MWDoubleLayer3D, +# test_local_space::BDMRefSpace, trial_local_space::BDMRefSpace, +# test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) + +# I, J, K, L = SauterSchwabQuadrature.reorder( +# test_triangular_element.vertices, +# trial_triangular_element.vertices, strat) + +# test_triangular_element = simplex( +# test_triangular_element.vertices[I[1]], +# test_triangular_element.vertices[I[2]], +# test_triangular_element.vertices[I[3]]) + +# trial_triangular_element = simplex( +# trial_triangular_element.vertices[J[1]], +# trial_triangular_element.vertices[J[2]], +# trial_triangular_element.vertices[J[3]]) + +# igd = MWDL3DIntegrand2(test_triangular_element, trial_triangular_element, +# op, test_local_space, trial_local_space) +# Q = sauterschwab_parameterized(igd, strat) +# for j ∈ 1:3 +# for i ∈ 1:3 +# out[i,j] += Q[K[i],L[j]] +# end +# end +# nothing +# end \ No newline at end of file diff --git a/src/maxwell/sauterschwabints_rt.jl b/src/maxwell/sauterschwabints_rt.jl index 282f1275..52183b34 100644 --- a/src/maxwell/sauterschwabints_rt.jl +++ b/src/maxwell/sauterschwabints_rt.jl @@ -1,130 +1,68 @@ -struct MWSL3DIntegrand{C,O,L} - test_triangular_element::C - trial_triangular_element::C - op::O - test_local_space::L - trial_local_space::L -end + + const i4pi = 1 / (4pi) -function (igd::MWSL3DIntegrand)(u,v) - α = igd.op.α - β = igd.op.β - γ = igd.op.gamma +function (igd::Integrand{<:MWSingleLayer3D})(u,v) + + x = neighborhood(igd.test_chart,u) + y = neighborhood(igd.trial_chart,v) + + f = igd.local_test_space(x) + g = igd.local_trial_space(y) + + return jacobian(x) * jacobian(y) * igd(x,y,f,g) +end - x = neighborhood(igd.test_triangular_element,u) - y = neighborhood(igd.trial_triangular_element,v) - r = cartesian(x) - cartesian(y) - R = norm(r) - iR = 1 / R - G = exp(-γ*R)*(i4pi*iR) +function (igd::Integrand{<:MWSingleLayer3D})(x,y,f,g) + α = igd.operator.α + β = igd.operator.β + γ = igd.operator.gamma - f = igd.test_local_space(x) - g = igd.trial_local_space(y) + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1 / R + green = exp(-γ*R)*(i4pi*iR) - j = jacobian(x) * jacobian(y) + αG = α * green + βG = β * green - jG = j*G - αjG = α*jG - βjG = β*jG - - G = @SVector [αjG*g[1].value, αjG*g[2].value, αjG*g[3].value] - H = @SVector [βjG*g[1].divergence, βjG*g[2].divergence, βjG*g[3].divergence] - - SMatrix{3,3}(( - dot(f[1].value,G[1]) + f[1].divergence*H[1], - dot(f[2].value,G[1]) + f[2].divergence*H[1], - dot(f[3].value,G[1]) + f[3].divergence*H[1], - dot(f[1].value,G[2]) + f[1].divergence*H[2], - dot(f[2].value,G[2]) + f[2].divergence*H[2], - dot(f[3].value,G[2]) + f[3].divergence*H[2], - dot(f[1].value,G[3]) + f[1].divergence*H[3], - dot(f[2].value,G[3]) + f[2].divergence*H[3], - dot(f[3].value,G[3]) + f[3].divergence*H[3])) -end + G = αG * getvalue(g) + H = βG * getdivergence(g) -struct MWDL3DIntegrand{C,O,L} - test_triangular_element::C - trial_triangular_element::C - op::O - test_local_space::L - trial_local_space::L + fvalue = getvalue(f) + fdivergence = getdivergence(f) + + # 5 pct speedup can be achieved I think by combining into a single call + return _krondot(fvalue, G) + _krondot(fdivergence, H) end -function (igd::MWDL3DIntegrand)(u,v) +function (igd::Integrand{<:MWDoubleLayer3D})(u,v) - γ = igd.op.gamma + γ = igd.operator.gamma - x = neighborhood(igd.test_triangular_element,u) - y = neighborhood(igd.trial_triangular_element,v) + x = neighborhood(igd.test_chart,u) + y = neighborhood(igd.trial_chart,v) r = cartesian(x) - cartesian(y) R = norm(r) iR = 1/R - G = exp(-γ*R)*(iR*i4pi) + green = exp(-γ*R)*(iR*i4pi) - f = igd.test_local_space(x) - g = igd.trial_local_space(y) + f = igd.local_test_space(x) + g = igd.local_trial_space(y) j = jacobian(x) * jacobian(y) - GG = -(γ + iR) * G * (iR * r) - - G = @SVector [cross(GG,(j*g[1].value)), cross(GG,(j*g[2].value)), cross(GG,(j*g[3].value))] - SMatrix{3,3}(( - dot(f[1].value, G[1]), - dot(f[2].value, G[1]), - dot(f[3].value, G[1]), - dot(f[1].value, G[2]), - dot(f[2].value, G[2]), - dot(f[3].value, G[2]), - dot(f[1].value, G[3]), - dot(f[2].value, G[3]), - dot(f[3].value, G[3]))) -end + gradgreen = -(γ + iR) * green * (iR * r) -kernel_in_bary(op::MWSingleLayer3D, - test_local_space::RTRefSpace, trial_local_space::RTRefSpace, - test_chart, trial_chart) = MWSL3DIntegrand( - test_chart, trial_chart, op, test_local_space, trial_local_space) - -kernel_in_bary(op::MWDoubleLayer3D, - test_local_space::RTRefSpace, trial_local_space::RTRefSpace, - test_chart, trial_chart) = MWDL3DIntegrand( - test_chart, trial_chart, op, test_local_space, trial_local_space) - -const MWOperator3D = Union{MWSingleLayer3D, MWDoubleLayer3D} -function momintegrals!(op::MWOperator3D, - test_local_space::RTRefSpace, trial_local_space::RTRefSpace, - test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) - - I, J, K, L = SauterSchwabQuadrature.reorder( - test_triangular_element.vertices, - trial_triangular_element.vertices, strat) - - test_triangular_element = simplex( - test_triangular_element.vertices[I[1]], - test_triangular_element.vertices[I[2]], - test_triangular_element.vertices[I[3]]) - - trial_triangular_element = simplex( - trial_triangular_element.vertices[J[1]], - trial_triangular_element.vertices[J[2]], - trial_triangular_element.vertices[J[3]]) - - # igd = MWSL3DIntegrand(test_triangular_element, trial_triangular_element, - # op, test_local_space, trial_local_space) - igd = kernel_in_bary(op, test_local_space, trial_local_space, - test_triangular_element, trial_triangular_element) - G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) - for j ∈ 1:3, i ∈ 1:3 - out[i,j] += G[K[i],L[j]] - end - - nothing + fvalue = getvalue(f) + jgvalue = j * getvalue(g) + G = cross.(Ref(gradgreen), jgvalue) + return _krondot(fvalue, G) end +const MWOperator3D = Union{MWSingleLayer3D, MWDoubleLayer3D} function momintegrals_nested!(op::MWOperator3D, test_local_space::RTRefSpace, trial_local_space::RTRefSpace, test_chart, trial_chart, out, strat::SauterSchwabStrategy) @@ -139,7 +77,7 @@ function momintegrals_nested!(op::MWOperator3D, ct = cartesian(center(trial_chart)) - refined_trial_chart = [ + refined_trial_chart = SA[ simplex(ct, p1, e3), simplex(ct, e3, p2), simplex(ct, p2, e1), @@ -199,7 +137,7 @@ function momintegrals_trial_refines_test!(op::MWOperator3D, ct = cartesian(center(test_chart)) - refined_test_chart = [ + refined_test_chart = SA[ simplex(ct, p1, e3), simplex(ct, e3, p2), simplex(ct, p2, e1), From a554de62f596b3d3dd9e57f7467304bf6e24849e Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 17 Aug 2022 22:02:33 +0200 Subject: [PATCH 171/528] LF stabilisation through projectors example --- examples/projectors.jl | 181 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 examples/projectors.jl diff --git a/examples/projectors.jl b/examples/projectors.jl new file mode 100644 index 00000000..3af0bee6 --- /dev/null +++ b/examples/projectors.jl @@ -0,0 +1,181 @@ +using BEAST, CompScienceMeshes, LinearAlgebra +using Plots +# using JLD2 + +setminus(A,B) = submesh(!in(B), A) + +radius = 1.0 + +nearstrat = BEAST.DoubleNumWiltonSauterQStrat(6, 7, 6, 7, 7, 7, 7, 7) +farstrat = BEAST.DoubleNumQStrat(1,2) + +dmat(op,tfs,bfs) = BEAST.assemble(op,tfs,bfs; quadstrat=nearstrat) +# hmat(op,tfs,bfs) = AdaptiveCrossApproximation.h1compress(op,tfs,bfs; nearstrat=nearstrat,farstrat=farstrat) +mat = dmat + +h = [0.1, 0.05, 0.025, 0.0125] +κ = [1.0, 10.0] + +h = 0.3 +κ = 0.00001 +γ = im*κ + +# function runsim(;h, κ) + +SL = Maxwell3D.singlelayer(wavenumber=κ) +WS = Maxwell3D.weaklysingular(wavenumber=κ) +HS = Maxwell3D.hypersingular(wavenumber=κ) +N = NCross() + +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +e = (n × E) × n; + +Γ = meshsphere(;radius, h) +∂Γ = boundary(Γ) + +edges = setminus(skeleton(Γ,1), ∂Γ) +verts = setminus(skeleton(Γ,0), skeleton(∂Γ,0)) + +Σ = Matrix(connectivity(Γ, edges, sign)) +Λ = Matrix(connectivity(Γ, edges, sign)) + +PΣ = Σ * pinv(Σ'*Σ) * Σ' +PΛH = I - PΣ + +ℙΛ = Λ * pinv(Λ'*Λ) * Λ' +ℙHΣ = I - ℙΛ + +MR = γ * PΣ + PΛH +ML = PΣ + 1/γ * PΛH + +MRΣ = γ * PΣ +MRΛH = PΛH +MLΣ = PΣ +MLΛH = 1/γ * PΛH + +𝕄R = γ * ℙΛ + ℙHΣ +𝕄L = ℙΛ + 1/γ * ℙHΣ + +X = raviartthomas(Γ) +Y = buffachristiansen(Γ) + +@hilbertspace p +@hilbertspace q + +SLxx = assemble(@discretise(SL[p,q], p∈X, q∈X), materialize=mat) +WSxx = assemble(@discretise(WS[p,q], p∈X, q∈X), materialize=mat) + +ex = BEAST.assemble(@discretise e[p] p∈X) + +sys0 = SLxx +sys1 = MLΣ * SLxx * MRΣ + MLΛH * WSxx * MRΣ + MLΣ * WSxx * MRΛH + MLΛH * WSxx * MRΛH + +rhs0 = ex +rhs1 = ML * ex + +u0, ch0 = solve(BEAST.GMRESSolver(sys0, tol=2e-5, restart=250), rhs0) +v1, ch1 = solve(BEAST.GMRESSolver(sys1, tol=2e-5, restart=250), rhs1) + +u1 = MR * v1 +# u2 = MR * v2 + +# error() +# @show ch1.iters +# @show ch2.iters + +Φ, Θ = [0.0], range(0,stop=π,length=40) +pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] + +near0 = potential(MWFarField3D(wavenumber=κ), pts, u0, X) +near1 = potential(MWFarField3D(wavenumber=κ), pts, u1, X) + # near2 = potential(MWFarField3D(wavenumber=κ), pts, u2, X) + + # u1, ch1.iters, u2, ch2.iters, near1, near2, X +# end + +plot(); +plot!(Θ, norm.(near0)); +scatter!(Θ, norm.(near1)) +# scatter!(Θ, norm.(near2)) + +error() + +using LinearAlgebra +using Plots +plotly() +w0 = eigvals(Matrix(Sxx)) +w1 = eigvals(Matrix(sys)) +w2 = eigvals(Matrix(P * sys)) +plot(exp.(2pi*im*range(0,1,length=200))) +scatter!(w0) +scatter!(w1) +scatter!(w2) + + +function makesim(d::Dict) + @unpack h, κ = d + u1, ch1, u2, ch2, near1, near2, X = runsim(;h, κ) + fulld = merge(d, Dict( + "u1" => u1, + "u2" => u2, + "ch1" => ch1, + "ch2" => ch2, + "near1" => near1, + "near2" => near2 + )) +end + +method = splitext(basename(@__FILE__))[1] + + +params = @strdict h κ +dicts = dict_list(params) +for (i,d) in enumerate(dicts) + @show d + f = makesim(d) + @tagsave(datadir("simulations", method, savename(d,"jld2")), f) +end + +#' Visualise the spectrum + +# mSxx = BEAST.convert_to_dense(Sxx) +# mSyy = BEAST.convert_to_dense(Syy) + +# Z = mSxx +# W = iN' * mSyy * iN * mSxx; + +# wZ = eigvals(Matrix(Z)) +# wW = eigvals(Matrix(W)) + +# plot(exp.(im*range(0,2pi,length=200))) +# scatter!(wZ) +# scatter!(wW) + + +# Study the various kernels +# HS = Maxwell3D.singlelayer(gamma=0.0, alpha=0.0, beta=1.0) +# Id = BEAST.Identity() + +# Z12 = BEAST.lagrangecxd0(G12) +# Z23 = BEAST.lagrangecxd0(Ĝ23) +# Z = Z12 × Z23 + +# W12 = BEAST.duallagrangecxd0(G12) +# W23 = BEAST.duallagrangecxd0(Ĝ23) +# W = W12 × W23 + +# DX = assemble(Id, Z, divergence(X)) +# HX = assemble(HS, X, X) + +# DY = assemble(Id, W, divergence(Y)) +# HY = assemble(HS, Y, Y) + +# Nx = BEAST.NCross() +# NYX = assemble(Nx, Y, X) + +# Q = HY * iN * HX + +using AlgebraicMultigrid +A = poisson(100) +b = rand(100); +solve(A, b, RugeStubenAMG(), maxiter=1, abstol=1e-6) From 7db0a62d985065b42db563a64c2667edb16d556d Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 18 Aug 2022 08:38:53 +0200 Subject: [PATCH 172/528] projector example added --- examples/projectors.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/projectors.jl b/examples/projectors.jl index 3af0bee6..30164b91 100644 --- a/examples/projectors.jl +++ b/examples/projectors.jl @@ -17,7 +17,7 @@ h = [0.1, 0.05, 0.025, 0.0125] κ = [1.0, 10.0] h = 0.3 -κ = 0.00001 +κ = 0.1 γ = im*κ # function runsim(;h, κ) From ed014b6f259b7b8c725477c0497eeaa03a25d01c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 18 Aug 2022 14:37:12 +0200 Subject: [PATCH 173/528] DoubleQR and SauterSchwabQR use the same kernels --- src/maxwell/mwops.jl | 3 ++- src/maxwell/sauterschwabints_rt.jl | 32 ++++++++++++++++++++++++----- src/quadrature/double_quadrature.jl | 22 ++++++++------------ 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index e4de374a..2b936476 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -109,7 +109,8 @@ end # use Union type so this code can be shared between the operator # and its regular part. -MWSL3DGen = Union{MWSingleLayer3D,MWSingleLayer3DReg} +# MWSL3DGen = Union{MWSingleLayer3D,MWSingleLayer3DReg} +MWSL3DGen = Union{MWSingleLayer3DReg} function integrand(biop::MWSL3DGen, kerneldata, tvals, tgeo, bvals, bgeo) gx = tvals[1] diff --git a/src/maxwell/sauterschwabints_rt.jl b/src/maxwell/sauterschwabints_rt.jl index 52183b34..b04e4780 100644 --- a/src/maxwell/sauterschwabints_rt.jl +++ b/src/maxwell/sauterschwabints_rt.jl @@ -14,6 +14,27 @@ function (igd::Integrand{<:MWSingleLayer3D})(u,v) end +function _integrands_gen(f::Type{U}, g::Type{V}) where {U<:SVector{N}, V<:SVector{M}} where {M,N} + ex = :(SMatrix{N,M}(())) + for m in 1:M + for n in 1:N + push!(ex.args[2].args, :(integrand(op, kervals, f[$n], x, g[$m], y))) + end + end + return ex +end + +@generated _integrands(op, kervals, f::SVector{N}, x, g::SVector{M}, y) where {M,N} = _integrands_gen(f, g) + +function (igd::Integrand)(x,y,f,g) + + op = igd.operator + kervals = kernelvals(op, x, y) + _integrands(op, kervals, f, x, g, y) + +end + + function (igd::Integrand{<:MWSingleLayer3D})(x,y,f,g) α = igd.operator.α β = igd.operator.β @@ -27,14 +48,15 @@ function (igd::Integrand{<:MWSingleLayer3D})(x,y,f,g) αG = α * green βG = β * green - G = αG * getvalue(g) - H = βG * getdivergence(g) - + gvalue = getvalue(g) fvalue = getvalue(f) + ws = _krondot(fvalue, gvalue) + + gdivergence = getdivergence(g) fdivergence = getdivergence(f) + hs = _krondot(fdivergence, gdivergence) - # 5 pct speedup can be achieved I think by combining into a single call - return _krondot(fvalue, G) + _krondot(fdivergence, H) + return αG * ws + βG * hs end function (igd::Integrand{<:MWDoubleLayer3D})(u,v) diff --git a/src/quadrature/double_quadrature.jl b/src/quadrature/double_quadrature.jl index ea14832f..67d207f5 100644 --- a/src/quadrature/double_quadrature.jl +++ b/src/quadrature/double_quadrature.jl @@ -11,12 +11,10 @@ Function for the computation of moment integrals using simple double quadrature. """ function momintegrals!(biop, tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) - # memory allocation here is a result from the type instability on strat - # which is on purpose, i.e. the momintegrals! method is chosen based - # on dynamic polymorphism. + igd = Integrand(biop, tshs, bshs, tcell, bcell) + womps = strat.outer_quad_points wimps = strat.inner_quad_points - for womp in womps tgeo = womp.point @@ -31,16 +29,12 @@ function momintegrals!(biop, tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) jy = wimp.weight j = jx * jy - kernel = kernelvals(biop, tgeo, bgeo) - - for n in 1 : N - bval = bvals[n] - for m in 1 : M - tval = tvals[m] - igd = integrand(biop, kernel, tval, tgeo, bval, bgeo) - z[m,n] += j * igd - end - end + + z1 = j * igd(tgeo, bgeo, tvals, bvals) + for n in 1:N + for m in 1:M + z[m,n] += z1[m,n] + end end end end From cbf1241d3e3dc339674b9bb5f730d5258720a701 Mon Sep 17 00:00:00 2001 From: Simon Adrian Date: Wed, 3 Aug 2022 17:03:47 +0200 Subject: [PATCH 174/528] Enable passthru of quadstrat for discretizations involving BC functions Currently, the quadstrat order is not passed through. Instead, the default quadstrat is used. This commit enables the correct passthrough. --- src/integralop.jl | 6 +++--- src/maxwell/sauterschwabints_rt.jl | 18 ++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/integralop.jl b/src/integralop.jl index f4c769c4..271c36f6 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -154,7 +154,7 @@ function assemblechunk_body_nested_meshes!(biop, fill!(zlocal, 0) qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, qd, quadstrat) - momintegrals_nested!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule) + momintegrals_nested!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule, quadstrat) I = length(test_assembly_data[p]) J = length(trial_assembly_data[q]) for j in 1 : J, i in 1 : I @@ -188,7 +188,7 @@ for (p,tcell) in enumerate(test_elements) fill!(zlocal, 0) qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, qd, quadstrat) - momintegrals_trial_refines_test!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule) + momintegrals_trial_refines_test!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule, quadstrat) I = length(test_assembly_data[p]) J = length(trial_assembly_data[q]) for j in 1 : J, i in 1 : I @@ -362,7 +362,7 @@ function assembleblock_body_nested!(biop::IntegralOperator, fill!(zlocals[Threads.threadid()], 0) qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) - momintegrals_nested!(biop, test_shapes, trial_shapes, tcell, bcell, zlocals[Threads.threadid()], qrule) + momintegrals_nested!(biop, test_shapes, trial_shapes, tcell, bcell, zlocals[Threads.threadid()], qrule, quadstrat) for j in 1 : size(zlocals[Threads.threadid()],2) for i in 1 : size(zlocals[Threads.threadid()],1) diff --git a/src/maxwell/sauterschwabints_rt.jl b/src/maxwell/sauterschwabints_rt.jl index b04e4780..8a378f65 100644 --- a/src/maxwell/sauterschwabints_rt.jl +++ b/src/maxwell/sauterschwabints_rt.jl @@ -87,7 +87,7 @@ end const MWOperator3D = Union{MWSingleLayer3D, MWDoubleLayer3D} function momintegrals_nested!(op::MWOperator3D, test_local_space::RTRefSpace, trial_local_space::RTRefSpace, - test_chart, trial_chart, out, strat::SauterSchwabStrategy) + test_chart, trial_chart, out, strat::SauterSchwabStrategy, quadstrat) # 1. Refine the trial_chart p1, p2, p3 = trial_chart.vertices @@ -107,13 +107,12 @@ function momintegrals_nested!(op::MWOperator3D, simplex(ct, p3, e2), simplex(ct, e2, p1)] - qs = defaultquadstrat(op, test_local_space, trial_local_space) qd = quaddata(op, test_local_space, trial_local_space, - [test_chart], refined_trial_chart, qs) + [test_chart], refined_trial_chart, quadstrat) for (q,chart) in enumerate(refined_trial_chart) qr = quadrule(op, test_local_space, trial_local_space, - 1, test_chart, q ,chart, qd, qs) + 1, test_chart, q ,chart, qd, quadstrat) Q = restrict(trial_local_space, trial_chart, chart) zlocal = zero(out) @@ -130,7 +129,7 @@ end function momintegrals_nested!(op::IntegralOperator, test_local_space::RefSpace, trial_local_space::RefSpace, - test_chart, trial_chart, out, strat) + test_chart, trial_chart, out, strat, quadstrat) momintegrals!(op, test_local_space, trial_local_space, test_chart, trial_chart, out, strat) @@ -138,7 +137,7 @@ end function momintegrals_trial_refines_test!(op::IntegralOperator, test_local_space::RefSpace, trial_local_space::RefSpace, - test_chart, trial_chart, out, strat) + test_chart, trial_chart, out, strat, quadstrat) momintegrals!(op, test_local_space, trial_local_space, test_chart, trial_chart, out, strat) @@ -147,7 +146,7 @@ end function momintegrals_trial_refines_test!(op::MWOperator3D, test_local_space::RTRefSpace, trial_local_space::RTRefSpace, - test_chart, trial_chart, out, strat::SauterSchwabStrategy) + test_chart, trial_chart, out, strat::SauterSchwabStrategy, quadstrat) # 1. Refine the test_chart p1, p2, p3 = test_chart.vertices @@ -167,13 +166,12 @@ function momintegrals_trial_refines_test!(op::MWOperator3D, simplex(ct, p3, e2), simplex(ct, e2, p1)] - qs = defaultquadstrat(op, test_local_space, trial_local_space) qd = quaddata(op, test_local_space, trial_local_space, - refined_test_chart, [trial_chart], qs) + refined_test_chart, [trial_chart], quadstrat) for (p,chart) in enumerate(refined_test_chart) qr = quadrule(op, test_local_space, trial_local_space, - p, chart, 1, trial_chart, qd, qs) + p, chart, 1, trial_chart, qd, quadstrat) Q = restrict(test_local_space, test_chart, chart) zlocal = zero(out) From afc2354787f01076a4ccbc7e98a6d9997466f947 Mon Sep 17 00:00:00 2001 From: Simon Adrian Date: Wed, 24 Aug 2022 17:31:05 +0200 Subject: [PATCH 175/528] Adapt unit test In test_assemble_refinements.jl, it is ensured that the quadstrat is passed on. test_ss_nested_meshes.jl needed adaption due to function interface change. --- test/test_assemble_refinements.jl | 38 ++++++++++++++++++++++++++++++- test/test_ss_nested_meshes.jl | 6 +++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/test/test_assemble_refinements.jl b/test/test_assemble_refinements.jl index a24de30c..b737c962 100644 --- a/test/test_assemble_refinements.jl +++ b/test/test_assemble_refinements.jl @@ -29,4 +29,40 @@ Kop = Maxwell3D.doublelayer(wavenumber=0.0) K1 = assemble(Kop, X, Y) K2 = assemble(Kop, Y, X) -@test K1[1,1] ≈ K2[1,1] rtol=1e-3 \ No newline at end of file +@test K1[1,1] ≈ K2[1,1] rtol=1e-3 + +# Verify that quadrature strategy is passed true +qds(i) = BEAST.DoubleNumWiltonSauterQStrat(1,1,6,7,i,i,i,i) +Kxy_1 = (assemble(Kop, X, Y, quadstrat=qds(1)))[1,1] +Kxy_2 = (assemble(Kop, X, Y, quadstrat=qds(5)))[1,1] +Kxy_3 = (assemble(Kop, X, Y, quadstrat=qds(10)))[1,1] + +@test 0.1 < abs(Kxy_1 - Kxy_3)/abs(Kxy_3) < 0.2 +@test 0.0008 < abs(Kxy_2 - Kxy_3)/abs(Kxy_3) < 0.0009 + +Kyx_1 = (assemble(Kop, Y, X, quadstrat=qds(1)))[1,1] +Kyx_2 = (assemble(Kop, Y, X, quadstrat=qds(5)))[1,1] +Kyx_3 = (assemble(Kop, Y, X, quadstrat=qds(10)))[1,1] + +@test 0.2 < abs(Kyx_1 - Kyx_3)/abs(Kyx_3) < 0.3 +@test 0.0006 < abs(Kyx_2 - Kyx_3)/abs(Kyx_3) < 0.0007 + +Γtr = translate(Γ, SVector(2.0, 0.0, 0.0)) + +Γ2 = weld(Γ, Γtr) + +X2 = raviartthomas(Γ2) +Y2 = buffachristiansen(Γ2) + +qds2(i) = BEAST.DoubleNumWiltonSauterQStrat(i,i,i,i,10,10,10,10) +K2xy_1 = (assemble(Kop, X2, Y2, quadstrat=qds2(1)))[1,2] +K2xy_2 = (assemble(Kop, X2, Y2, quadstrat=qds2(5)))[1,2] +K2xy_3 = (assemble(Kop, X2, Y2, quadstrat=qds2(10)))[1,2] +@test 0.06 < abs(K2xy_1 - K2xy_3)/abs(K2xy_3) < 0.07 +@test 1e-6 < abs(K2xy_2 - K2xy_3)/abs(K2xy_3) < 1e-5 + +K2yx_1 = (assemble(Kop, Y2, X2, quadstrat=qds2(1)))[1,2] +K2yx_2 = (assemble(Kop, Y2, X2, quadstrat=qds2(5)))[1,2] +K2yx_3 = (assemble(Kop, Y2, X2, quadstrat=qds2(10)))[1,2] +@test 0.06 < abs(K2yx_1 - K2yx_3)/abs(K2yx_3) < 0.07 +@test 1e-6 < abs(K2yx_2 - K2yx_3)/abs(K2yx_3) < 1e-5 \ No newline at end of file diff --git a/test/test_ss_nested_meshes.jl b/test/test_ss_nested_meshes.jl index 31d75bcd..2fea234b 100644 --- a/test/test_ss_nested_meshes.jl +++ b/test/test_ss_nested_meshes.jl @@ -37,13 +37,15 @@ function BEAST.quaddata(op::BEAST.MaxwellOperator3D, return (tpoints=tqd, bpoints=bqd, gausslegendre=leg) end +qs_strat = BEAST.DoubleNumWiltonSauterQStrat(1,1,6,7,10,10,10,10) + sauterschwab = BEAST.SauterSchwabQuadrature.CommonFace(nothing) out_ss = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) -BEAST.momintegrals_nested!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss,sauterschwab) +BEAST.momintegrals_nested!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss,sauterschwab,qs_strat) wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) out_dw = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) -BEAST.momintegrals_nested!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw,wiltonsingext) +BEAST.momintegrals_nested!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw,wiltonsingext,qs_strat) @show norm(out_ss-out_dw) / norm(out_dw) From a910615cd0c132786145808aa10282b528f1e18a Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 25 Aug 2022 12:52:33 +0200 Subject: [PATCH 176/528] Double layer uses unified quadrature --- examples/projectors.jl | 4 +- src/maxwell/mwops.jl | 3 +- src/maxwell/sauterschwabints_rt.jl | 59 +++++++++++++++++++++--------- 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/examples/projectors.jl b/examples/projectors.jl index 30164b91..4809cf3c 100644 --- a/examples/projectors.jl +++ b/examples/projectors.jl @@ -17,7 +17,7 @@ h = [0.1, 0.05, 0.025, 0.0125] κ = [1.0, 10.0] h = 0.3 -κ = 0.1 +κ = 0.000001 γ = im*κ # function runsim(;h, κ) @@ -37,7 +37,7 @@ edges = setminus(skeleton(Γ,1), ∂Γ) verts = setminus(skeleton(Γ,0), skeleton(∂Γ,0)) Σ = Matrix(connectivity(Γ, edges, sign)) -Λ = Matrix(connectivity(Γ, edges, sign)) +Λ = Matrix(connectivity(Γ, verts, sign)) PΣ = Σ * pinv(Σ'*Σ) * Σ' PΛH = I - PΣ diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 2b936476..13802725 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -143,7 +143,8 @@ end regularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DReg(op.gamma) singularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DSng(op.gamma) -const MWDL3DGen = Union{MWDoubleLayer3D,MWDoubleLayer3DReg} +# const MWDL3DGen = Union{MWDoubleLayer3D,MWDoubleLayer3DReg} +const MWDL3DGen = Union{MWDoubleLayer3DReg} function integrand(biop::MWDL3DGen, kerneldata, tvals, tgeo, bvals, bgeo) g = tvals[1] f = bvals[1] diff --git a/src/maxwell/sauterschwabints_rt.jl b/src/maxwell/sauterschwabints_rt.jl index b04e4780..79fdac47 100644 --- a/src/maxwell/sauterschwabints_rt.jl +++ b/src/maxwell/sauterschwabints_rt.jl @@ -2,7 +2,7 @@ const i4pi = 1 / (4pi) -function (igd::Integrand{<:MWSingleLayer3D})(u,v) +function (igd::Integrand)(u,v) x = neighborhood(igd.test_chart,u) y = neighborhood(igd.trial_chart,v) @@ -59,29 +59,54 @@ function (igd::Integrand{<:MWSingleLayer3D})(x,y,f,g) return αG * ws + βG * hs end -function (igd::Integrand{<:MWDoubleLayer3D})(u,v) +# function (igd::Integrand{<:MWDoubleLayer3D})(u,v) - γ = igd.operator.gamma +# γ = igd.operator.gamma - x = neighborhood(igd.test_chart,u) - y = neighborhood(igd.trial_chart,v) +# x = neighborhood(igd.test_chart,u) +# y = neighborhood(igd.trial_chart,v) - r = cartesian(x) - cartesian(y) - R = norm(r) - iR = 1/R - green = exp(-γ*R)*(iR*i4pi) +# r = cartesian(x) - cartesian(y) +# R = norm(r) +# iR = 1/R +# green = exp(-γ*R)*(iR*i4pi) - f = igd.local_test_space(x) - g = igd.local_trial_space(y) +# f = igd.local_test_space(x) +# g = igd.local_trial_space(y) - j = jacobian(x) * jacobian(y) +# j = jacobian(x) * jacobian(y) - gradgreen = -(γ + iR) * green * (iR * r) +# gradgreen = -(γ + iR) * green * (iR * r) - fvalue = getvalue(f) - jgvalue = j * getvalue(g) - G = cross.(Ref(gradgreen), jgvalue) - return _krondot(fvalue, G) +# fvalue = getvalue(f) +# jgvalue = j * getvalue(g) +# G = cross.(Ref(gradgreen), jgvalue) +# return _krondot(fvalue, G) +# end + +function (igd::Integrand{<:MWDoubleLayer3D})(x,y,f,g) + + # x = neighborhood(igd.test_chart,u) + # y = neighborhood(igd.trial_chart,v) + + # f = igd.local_test_space(x) + # g = igd.local_trial_space(y) + + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1/R + green = exp(-γ*R)*(iR*i4pi) + + # j = jacobian(x) * jacobian(y) + + gradgreen = -(γ + iR) * green * (iR * r) + + fvalue = getvalue(f) + gvalue = getvalue(g) + G = cross.(Ref(gradgreen), gvalue) + return _krondot(fvalue, G) end const MWOperator3D = Union{MWSingleLayer3D, MWDoubleLayer3D} From 751315f0a365ba62ae948d419b3d32fcf9e3ea17 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 22 Sep 2022 13:55:51 +0200 Subject: [PATCH 177/528] Dimension agnostic gc-free integrand generation --- src/bases/dual3d.jl | 8 -- src/bases/lagrange.jl | 2 - src/integralop.jl | 4 +- src/maxwell/mwops.jl | 60 ++++---- src/maxwell/sauterschwabints_bdm.jl | 207 +++------------------------- src/maxwell/sauterschwabints_rt.jl | 80 +++++------ 6 files changed, 89 insertions(+), 272 deletions(-) diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 8014a011..725c1bf0 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -165,14 +165,6 @@ function dual2forms(Tetrs, Faces, Dir) dual2forms_body(Faces, tetrs, bnd, dir, v2t, v2n) end -# function dualforms_init(Tetrs, Dir) -# tetrs = barycentric_refinement(Tetrs) -# v2t, v2n = CompScienceMeshes.vertextocellmap(tetrs) -# bnd = boundary(tetrs) -# gpred = CompScienceMeshes.overlap_gpredicate(Dir) -# dir = submesh(face -> gpred(chart(bnd,face)), bnd) -# return tetrs, bnd, dir, v2t, v2n -# end function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index bf51eb7a..8622db99 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -408,8 +408,6 @@ function duallagrangec0d1(mesh, mesh2, pred, ::Type{Val{2}}) numverts1 = numvertices(mesh) num_cells1 = numcells(mesh) cellids1, ncells1=vertextocellmap(mesh) - # obtain the refined mesh from the original mesh - #mesh2 = barycentric_refinement(mesh) # get the information about number of vertices, number of faces , and the maping between vertices and faces for the refined mesh num_cells2 = numcells(mesh2) numverts2 = numvertices(mesh2) diff --git a/src/integralop.jl b/src/integralop.jl index 271c36f6..24f28dc7 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -87,13 +87,13 @@ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; # qd, zlocal, store; quadstrat) # else if CompScienceMeshes.refines(tgeo, bgeo) - @info "assemblechunk: test refines trial mesh" + # @info "assemblechunk: test refines trial mesh" assemblechunk_body_nested_meshes!(biop, tshapes, test_elements, tad, bshapes, bsis_elements, bad, qd, zlocal, store; quadstrat) elseif CompScienceMeshes.refines(bgeo, tgeo) - @info "assemblechunk: trial refines test mesh" + # @info "assemblechunk: trial refines test mesh" assemblechunk_body_trial_refines_test!(biop, tshapes, test_elements, tad, bshapes, bsis_elements, bad, diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 13802725..7df6e2d0 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -28,19 +28,19 @@ function kernelvals(biop::MaxwellOperator3D, p, q) KernelValsMaxwell3D(γ, r, R, green, gradgreen) end -function kernelvals(kernel::MaxwellOperator3DReg, p, q) +# function kernelvals(kernel::MaxwellOperator3DReg, p, q) - γ = kernel.gamma - r = p.cart - q.cart - R = norm(r) - γR = γ*R - P=typeof(γ) - Exp = exp(-γ*R) - green = (Exp - 1 + γR - 0.5*γR^2) / (4pi*R) - gradgreen = ( - (γR + 1)*Exp + (1 - 0.5*γR^2) ) * (r/R^3) / (4π) +# γ = kernel.gamma +# r = p.cart - q.cart +# R = norm(r) +# γR = γ*R +# P=typeof(γ) +# Exp = exp(-γ*R) +# green = (Exp - 1 + γR - 0.5*γR^2) / (4pi*R) +# gradgreen = ( - (γR + 1)*Exp + (1 - 0.5*γR^2) ) * (r/R^3) / (4π) - KernelValsMaxwell3D(γ, r, R, P(green), gradgreen) -end +# KernelValsMaxwell3D(γ, r, R, P(green), gradgreen) +# end struct MWSingleLayer3D{T,U} <: MaxwellOperator3D gamma::T @@ -110,23 +110,23 @@ end # use Union type so this code can be shared between the operator # and its regular part. # MWSL3DGen = Union{MWSingleLayer3D,MWSingleLayer3DReg} -MWSL3DGen = Union{MWSingleLayer3DReg} -function integrand(biop::MWSL3DGen, kerneldata, tvals, tgeo, bvals, bgeo) +# MWSL3DGen = Union{MWSingleLayer3DReg} +# function integrand(biop::MWSL3DGen, kerneldata, tvals, tgeo, bvals, bgeo) - gx = tvals[1] - fy = bvals[1] +# gx = tvals[1] +# fy = bvals[1] - dgx = tvals[2] - dfy = bvals[2] +# dgx = tvals[2] +# dfy = bvals[2] - G = kerneldata.green - γ = kerneldata.gamma +# G = kerneldata.green +# γ = kerneldata.gamma - α = biop.α - β = biop.β +# α = biop.α +# β = biop.β - t = (α * dot(gx, fy) + β * (dgx*dfy)) * G -end +# t = (α * dot(gx, fy) + β * (dgx*dfy)) * G +# end struct MWDoubleLayer3D{T} <: MaxwellOperator3D gamma::T @@ -144,13 +144,13 @@ regularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DReg(op.gamma) singularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DSng(op.gamma) # const MWDL3DGen = Union{MWDoubleLayer3D,MWDoubleLayer3DReg} -const MWDL3DGen = Union{MWDoubleLayer3DReg} -function integrand(biop::MWDL3DGen, kerneldata, tvals, tgeo, bvals, bgeo) - g = tvals[1] - f = bvals[1] - ∇G = kerneldata.gradgreen - (f × g) ⋅ ∇G -end +# const MWDL3DGen = Union{MWDoubleLayer3DReg} +# function integrand(biop::MWDL3DGen, kerneldata, tvals, tgeo, bvals, bgeo) +# g = tvals[1] +# f = bvals[1] +# ∇G = kerneldata.gradgreen +# (f × g) ⋅ ∇G +# end # quadrule(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd) = qrss(op, g, f, i, τ, j, σ, qd) diff --git a/src/maxwell/sauterschwabints_bdm.jl b/src/maxwell/sauterschwabints_bdm.jl index 0330ddd9..0294b835 100644 --- a/src/maxwell/sauterschwabints_bdm.jl +++ b/src/maxwell/sauterschwabints_bdm.jl @@ -29,6 +29,23 @@ end return ex end +function _integrands_gen(::Type{U}, ::Type{V}) where {U<:SVector{N}, V<:SVector{M}} where {M,N} + ex = :(SMatrix{N,M}(())) + for m in 1:M + for n in 1:N + # push!(ex.args[2].args, :(dot(a[$n], b[$m]))) + push!(ex.args[2].args, :(f(a[$n], b[$m]))) + end + end + return ex +end + +@generated function _integrands(f, a::SVector{N}, b::SVector{M}) where {M,N} + ex = _integrands_gen(a,b) + # println(ex) + return ex +end + function momintegrals!(op::Operator, test_local_space::RefSpace, trial_local_space::RefSpace, test_chart, trial_chart, out, strat::SauterSchwabStrategy) @@ -84,194 +101,4 @@ function (igd::Integrand{<:DoubleLayerRotatedMW3D})(u,v) jKg = cross.(Ref(K), j*gvalue) jnxKg = cross.(Ref(nx), jKg) return _krondot(fvalue, jnxKg) - - # Kg = [cross(K, j*gi.value) for gi in g] - # return [dot(fj.value, cross(nx, Kgi)) for fj in f, Kgi in Kg] end - - -# struct MWSL3DIntegrand2{C,O,L} -# test_triangular_element::C -# trial_triangular_element::C -# op::O -# test_local_space::L -# trial_local_space::L -# end - -# function (igd::MWSL3DIntegrand2)(u,v) -# α = igd.op.α -# β = igd.op.β -# γ = igd.op.gamma - -# x = neighborhood(igd.test_triangular_element,u) -# y = neighborhood(igd.trial_triangular_element,v) - -# r = cartesian(x) - cartesian(y) -# R = norm(r) -# G = exp(-γ*R)/(4π*R) - -# f = igd.test_local_space(x) -# g = igd.trial_local_space(y) - -# j = jacobian(x) * jacobian(y) - -# αjG = α*j*G -# βjG = β*j*G - -# G = @SVector[αjG*g[i].value for i in 1:6] -# H = @SVector[βjG*g[i].divergence for i in 1:6] - -# SMatrix{6,6}(tuple( -# dot(f[1].value,G[1])+f[1].divergence*H[1], -# dot(f[2].value,G[1])+f[2].divergence*H[1], -# dot(f[3].value,G[1])+f[3].divergence*H[1], -# dot(f[4].value,G[1])+f[4].divergence*H[1], -# dot(f[5].value,G[1])+f[5].divergence*H[1], -# dot(f[6].value,G[1])+f[6].divergence*H[1], -# dot(f[1].value,G[2])+f[1].divergence*H[2], -# dot(f[2].value,G[2])+f[2].divergence*H[2], -# dot(f[3].value,G[2])+f[3].divergence*H[2], -# dot(f[4].value,G[2])+f[4].divergence*H[2], -# dot(f[5].value,G[2])+f[5].divergence*H[2], -# dot(f[6].value,G[2])+f[6].divergence*H[2], -# dot(f[1].value,G[3])+f[1].divergence*H[3], -# dot(f[2].value,G[3])+f[2].divergence*H[3], -# dot(f[3].value,G[3])+f[3].divergence*H[3], -# dot(f[4].value,G[3])+f[4].divergence*H[3], -# dot(f[5].value,G[3])+f[5].divergence*H[3], -# dot(f[6].value,G[3])+f[6].divergence*H[3], -# dot(f[1].value,G[4])+f[1].divergence*H[4], -# dot(f[2].value,G[4])+f[2].divergence*H[4], -# dot(f[3].value,G[4])+f[3].divergence*H[4], -# dot(f[4].value,G[4])+f[4].divergence*H[4], -# dot(f[5].value,G[4])+f[5].divergence*H[4], -# dot(f[6].value,G[4])+f[6].divergence*H[4], -# dot(f[1].value,G[5])+f[1].divergence*H[5], -# dot(f[2].value,G[5])+f[2].divergence*H[5], -# dot(f[3].value,G[5])+f[3].divergence*H[5], -# dot(f[4].value,G[5])+f[4].divergence*H[5], -# dot(f[5].value,G[5])+f[5].divergence*H[5], -# dot(f[6].value,G[5])+f[6].divergence*H[5], -# dot(f[1].value,G[6])+f[1].divergence*H[6], -# dot(f[2].value,G[6])+f[2].divergence*H[6], -# dot(f[3].value,G[6])+f[3].divergence*H[6], -# dot(f[4].value,G[6])+f[4].divergence*H[6], -# dot(f[5].value,G[6])+f[5].divergence*H[6], -# dot(f[6].value,G[6])+f[6].divergence*H[6], -# )) -# end - -# function momintegrals!(op::MWSingleLayer3D, -# test_local_space::BDMRefSpace, trial_local_space::BDMRefSpace, -# test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) - -# I, J, _, _ = SauterSchwabQuadrature.reorder( -# test_triangular_element.vertices, -# trial_triangular_element.vertices, strat) - -# test_triangular_element = simplex( -# test_triangular_element.vertices[I[1]], -# test_triangular_element.vertices[I[2]], -# test_triangular_element.vertices[I[3]]) - -# trial_triangular_element = simplex( -# trial_triangular_element.vertices[J[1]], -# trial_triangular_element.vertices[J[2]], -# trial_triangular_element.vertices[J[3]]) - -# igd = MWSL3DIntegrand2(test_triangular_element, trial_triangular_element, -# op, test_local_space, trial_local_space) -# G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) - -# K = dof_permutation(test_local_space, I) -# L = dof_permutation(trial_local_space, J) -# for i in 1:numfunctions(test_local_space) -# for j in 1:numfunctions(trial_local_space) -# out[i,j] = G[K[i],L[j]] -# end end - -# nothing -# end - - -# struct MWDL3DIntegrand2{C,O,L} -# test_triangular_element::C -# trial_triangular_element::C -# op::O -# test_local_space::L -# trial_local_space::L -# end - -# function (igd::MWDL3DIntegrand2)(u,v) - -# γ = igd.op.gamma - -# x = neighborhood(igd.test_triangular_element,u) -# y = neighborhood(igd.trial_triangular_element,v) - -# r = cartesian(x) - cartesian(y) -# R = norm(r) -# G = exp(-γ*R)/(4π*R) - -# GG = -(γ + 1/R) * G / R * r -# T = @SMatrix [ -# 0 -GG[3] GG[2] -# GG[3] 0 -GG[1] -# -GG[2] GG[1] 0 ] - -# f = igd.test_local_space(x) -# g = igd.trial_local_space(y) - -# jx = jacobian(x) -# jy = jacobian(y) -# j = jx*jy - -# G1 = j*T*g[1][1] -# G2 = j*T*g[2][1] -# G3 = j*T*g[3][1] - -# f1 = f[1][1] -# f2 = f[2][1] -# f3 = f[3][1] - -# SMatrix{3,3}( -# dot(f1,G1), -# dot(f2,G1), -# dot(f3,G1), -# dot(f1,G2), -# dot(f2,G2), -# dot(f3,G2), -# dot(f1,G3), -# dot(f2,G3), -# dot(f3,G3),) -# end - - -# function momintegrals!(op::MWDoubleLayer3D, -# test_local_space::BDMRefSpace, trial_local_space::BDMRefSpace, -# test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) - -# I, J, K, L = SauterSchwabQuadrature.reorder( -# test_triangular_element.vertices, -# trial_triangular_element.vertices, strat) - -# test_triangular_element = simplex( -# test_triangular_element.vertices[I[1]], -# test_triangular_element.vertices[I[2]], -# test_triangular_element.vertices[I[3]]) - -# trial_triangular_element = simplex( -# trial_triangular_element.vertices[J[1]], -# trial_triangular_element.vertices[J[2]], -# trial_triangular_element.vertices[J[3]]) - -# igd = MWDL3DIntegrand2(test_triangular_element, trial_triangular_element, -# op, test_local_space, trial_local_space) -# Q = sauterschwab_parameterized(igd, strat) -# for j ∈ 1:3 -# for i ∈ 1:3 -# out[i,j] += Q[K[i],L[j]] -# end -# end -# nothing -# end \ No newline at end of file diff --git a/src/maxwell/sauterschwabints_rt.jl b/src/maxwell/sauterschwabints_rt.jl index 85097abc..b842554c 100644 --- a/src/maxwell/sauterschwabints_rt.jl +++ b/src/maxwell/sauterschwabints_rt.jl @@ -14,7 +14,7 @@ function (igd::Integrand)(u,v) end -function _integrands_gen(f::Type{U}, g::Type{V}) where {U<:SVector{N}, V<:SVector{M}} where {M,N} +function _integrands_leg_gen(f::Type{U}, g::Type{V}) where {U<:SVector{N}, V<:SVector{M}} where {M,N} ex = :(SMatrix{N,M}(())) for m in 1:M for n in 1:N @@ -24,13 +24,14 @@ function _integrands_gen(f::Type{U}, g::Type{V}) where {U<:SVector{N}, V<:SVecto return ex end -@generated _integrands(op, kervals, f::SVector{N}, x, g::SVector{M}, y) where {M,N} = _integrands_gen(f, g) +@generated _integrands_leg(op, kervals, f::SVector{N}, x, g::SVector{M}, y) where {M,N} = _integrands_leg_gen(f, g) +# Support for legacy kernels function (igd::Integrand)(x,y,f,g) op = igd.operator kervals = kernelvals(op, x, y) - _integrands(op, kervals, f, x, g, y) + _integrands_leg(op, kervals, f, x, g, y) end @@ -48,60 +49,59 @@ function (igd::Integrand{<:MWSingleLayer3D})(x,y,f,g) αG = α * green βG = β * green - gvalue = getvalue(g) - fvalue = getvalue(f) - ws = _krondot(fvalue, gvalue) - - gdivergence = getdivergence(g) - fdivergence = getdivergence(f) - hs = _krondot(fdivergence, gdivergence) - - return αG * ws + βG * hs + _integrands(f,g) do fi,gj + αG * dot(fi.value, gj.value) + βG * dot(fi.divergence, gj.divergence) + end end -# function (igd::Integrand{<:MWDoubleLayer3D})(u,v) - -# γ = igd.operator.gamma - -# x = neighborhood(igd.test_chart,u) -# y = neighborhood(igd.trial_chart,v) - -# r = cartesian(x) - cartesian(y) -# R = norm(r) -# iR = 1/R -# green = exp(-γ*R)*(iR*i4pi) +function (igd::Integrand{<:MWSingleLayer3DReg})(x,y,f,g) + α = igd.operator.α + β = igd.operator.β + γ = igd.operator.gamma -# f = igd.local_test_space(x) -# g = igd.local_trial_space(y) + r = cartesian(x) - cartesian(y) + R = norm(r) + γR = γ*R + # iR = 1 / R + green = (expm1(-γR) + γR - 0.5*γR^2) / (4pi*R) -# j = jacobian(x) * jacobian(y) + αG = α * green + βG = β * green -# gradgreen = -(γ + iR) * green * (iR * r) + _integrands(f,g) do fi,gj + αG * dot(fi.value, gj.value) + βG * dot(fi.divergence, gj.divergence) + end +end -# fvalue = getvalue(f) -# jgvalue = j * getvalue(g) -# G = cross.(Ref(gradgreen), jgvalue) -# return _krondot(fvalue, G) -# end function (igd::Integrand{<:MWDoubleLayer3D})(x,y,f,g) - # x = neighborhood(igd.test_chart,u) - # y = neighborhood(igd.trial_chart,v) - - # f = igd.local_test_space(x) - # g = igd.local_trial_space(y) - γ = igd.operator.gamma r = cartesian(x) - cartesian(y) R = norm(r) iR = 1/R green = exp(-γ*R)*(iR*i4pi) + gradgreen = -(γ + iR) * green * (iR * r) + + fvalue = getvalue(f) + gvalue = getvalue(g) + G = cross.(Ref(gradgreen), gvalue) + return _krondot(fvalue, G) +end - # j = jacobian(x) * jacobian(y) - gradgreen = -(γ + iR) * green * (iR * r) +function (igd::Integrand{<:MWDoubleLayer3DReg})(x,y,f,g) + + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + γR = γ*R + iR = 1/R + expo = exp(-γR) + green = (expo - 1 + γR - 0.5*γR^2) * (i4pi*iR) + gradgreen = ( -(γR + 1)*expo + (1 - 0.5*γR^2) ) * (i4pi*iR^3) * r fvalue = getvalue(f) gvalue = getvalue(g) From 00171936d9b6f97bd43c2430df37ea83f3fa0e5c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 27 Sep 2022 09:40:17 +0200 Subject: [PATCH 178/528] convert to dense before inverting --- examples/calderon_open.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/calderon_open.jl b/examples/calderon_open.jl index 88a370a1..02b0a5eb 100644 --- a/examples/calderon_open.jl +++ b/examples/calderon_open.jl @@ -18,9 +18,10 @@ Txx = assemble(T,X,X); println("primal discretisation assembled.") Tyy = assemble(T,Y,Y); println("dual discretisation assembled.") Nxy = assemble(N,X,Y); println("duality form assembled.") -iNxy = inv(Nxy); println("duality form inverted.") +iNxy = inv(Matrix(Nxy)); println("duality form inverted.") A = iNxy' * Tyy * iNxy * Txx +@show cond(Matrix(Nxy)) @show cond(Txx) @show cond(Tyy) @show cond(A) From 5f10577eae78b0242d05af3acccb5ae374907275 Mon Sep 17 00:00:00 2001 From: Yaraslau Tamashevich Date: Wed, 28 Sep 2022 08:15:26 +0300 Subject: [PATCH 179/528] Update of path in examples. Added plotly --- Project.toml | 1 + examples/calderon.jl | 2 +- examples/dot_tdmfie.jl | 2 +- examples/dpie.jl | 2 +- examples/helmholtz3d_dirichlet.jl | 7 +++---- examples/mfie.jl | 2 +- examples/mfie_bdm.jl | 2 +- examples/mfie_monopolar.jl | 2 +- examples/planewave.jl | 2 +- examples/scomplex_efie.jl | 2 +- examples/stgalerkin.jl | 2 +- examples/stmfie.jl | 2 +- examples/tdemfie.jl | 2 +- examples/tdmfie.jl | 2 +- examples/tdmfie_sym.jl | 2 +- 15 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Project.toml b/Project.toml index b86f636d..1b5338f8 100644 --- a/Project.toml +++ b/Project.toml @@ -17,6 +17,7 @@ IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LiftedMaps = "d22a30c1-52ac-4762-a8c9-5838452405e0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" +Plotly = "58dd65bb-95f3-509e-9936-c39a10fdeae7" SauterSchwab3D = "0a13313b-1c00-422e-8263-562364ed9544" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" diff --git a/examples/calderon.jl b/examples/calderon.jl index ef52241f..4e2d6084 100644 --- a/examples/calderon.jl +++ b/examples/calderon.jl @@ -4,7 +4,7 @@ using CompScienceMeshes, BEAST using LinearAlgebra -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) println("Mesh with $(numvertices(Γ)) vertices and $(numcells(Γ)) cells.") X = raviartthomas(Γ) Y = BEAST.buffachristiansen2(Γ) diff --git a/examples/dot_tdmfie.jl b/examples/dot_tdmfie.jl index 82267942..4a8555a5 100644 --- a/examples/dot_tdmfie.jl +++ b/examples/dot_tdmfie.jl @@ -3,7 +3,7 @@ o, x, y, z = euclidianbasis(3) # D, Δx = 1.0, 0.35 -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) # Γ = meshsphere(1.0, 0.08) @show numcells(Γ) X, Y = raviartthomas(Γ), buffachristiansen(Γ) diff --git a/examples/dpie.jl b/examples/dpie.jl index 89c1a251..91cc75f2 100644 --- a/examples/dpie.jl +++ b/examples/dpie.jl @@ -1,7 +1,7 @@ using CompScienceMeshes using BEAST -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) X = raviartthomas(Γ) d = BEAST.CurlSingleLayerDP3D(1.0im, 1.0) diff --git a/examples/helmholtz3d_dirichlet.jl b/examples/helmholtz3d_dirichlet.jl index d5a8db0e..107c6089 100644 --- a/examples/helmholtz3d_dirichlet.jl +++ b/examples/helmholtz3d_dirichlet.jl @@ -2,10 +2,9 @@ using CompScienceMeshes, BEAST o, x, y, z = euclidianbasis(3) -# Γ = meshsphere(1.0, 0.11) -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) -# Γ = readmesh("/Users/Benjamin/Documents/sphere.in") -# Γ = readmesh(joinpath(@__DIR__,"sphere_subd1.in")) + +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) + X = lagrangecxd0(Γ) # X = subdsurface(Γ) # X = raviartthomas(Γ) diff --git a/examples/mfie.jl b/examples/mfie.jl index d49f424f..c5f14469 100644 --- a/examples/mfie.jl +++ b/examples/mfie.jl @@ -1,6 +1,6 @@ using CompScienceMeshes, BEAST -Γ = readmesh(joinpath(dirname(@__FILE__),"sphere2.in")) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) X, Y = raviartthomas(Γ), buffachristiansen(Γ) ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) diff --git a/examples/mfie_bdm.jl b/examples/mfie_bdm.jl index 7fa869fe..cb7e36ee 100644 --- a/examples/mfie_bdm.jl +++ b/examples/mfie_bdm.jl @@ -1,6 +1,6 @@ using CompScienceMeshes, BEAST -# Γ = readmesh(joinpath(dirname(@__FILE__),"sphere2.in")) +# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) Γ = meshsphere(radius=1.0, h=0.1) # X, Y = raviartthomas(Γ), buffachristiansen(Γ) X = brezzidouglasmarini(Γ) diff --git a/examples/mfie_monopolar.jl b/examples/mfie_monopolar.jl index ea2c428d..3452ce4b 100644 --- a/examples/mfie_monopolar.jl +++ b/examples/mfie_monopolar.jl @@ -1,6 +1,6 @@ using CompScienceMeshes, BEAST -Γ = readmesh(joinpath(dirname(@__FILE__),"sphere2.in")) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) X = raviartthomas(Γ) Y = buffachristiansen(Γ) Z = raviartthomas(Γ, BEAST.Continuity{:none}) diff --git a/examples/planewave.jl b/examples/planewave.jl index d31905c1..d2c5071a 100644 --- a/examples/planewave.jl +++ b/examples/planewave.jl @@ -1,7 +1,7 @@ using CompScienceMeshes using BEAST -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) X = raviartthomas(Γ) Y = buffachristiansen(Γ, ibscaled=true) diff --git a/examples/scomplex_efie.jl b/examples/scomplex_efie.jl index d35e5a3c..2f7fc27a 100644 --- a/examples/scomplex_efie.jl +++ b/examples/scomplex_efie.jl @@ -1,7 +1,7 @@ using CompScienceMeshes using BEAST -Γ1 = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ1 = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) Γ2 = CompScienceMeshes.SComplex2D(Γ1) # Y = raviartthomas(Γ1) Nd1 = BEAST.nedelec(Γ1) diff --git a/examples/stgalerkin.jl b/examples/stgalerkin.jl index d4079493..3d6f822d 100644 --- a/examples/stgalerkin.jl +++ b/examples/stgalerkin.jl @@ -4,7 +4,7 @@ o, x, y, z = euclidianbasis(3) # D, Δx = 1.0, 0.35 # Γ = meshsphere(D, Δx) -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) Γ = meshsphere(1.0, 0.45) X = raviartthomas(Γ) #Δt, Nt = 0.08, 400 diff --git a/examples/stmfie.jl b/examples/stmfie.jl index cf4bad1a..cd5a8912 100644 --- a/examples/stmfie.jl +++ b/examples/stmfie.jl @@ -3,7 +3,7 @@ o, x, y, z = euclidianbasis(3) # D, Δx = 1.0, 0.35 -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) X, Y = raviartthomas(Γ), buffachristiansen(Γ) # Δt, Nt = 0.11, 200 diff --git a/examples/tdemfie.jl b/examples/tdemfie.jl index 85ba0746..dde00b0f 100644 --- a/examples/tdemfie.jl +++ b/examples/tdemfie.jl @@ -2,7 +2,7 @@ # the code dealing with systems of time domain equations in the special case # of a block diagonal system. using CompScienceMeshes, BEAST -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) X = raviartthomas(Γ) Y = buffachristiansen(Γ) diff --git a/examples/tdmfie.jl b/examples/tdmfie.jl index ed3dd7dc..94d4221a 100644 --- a/examples/tdmfie.jl +++ b/examples/tdmfie.jl @@ -1,5 +1,5 @@ using CompScienceMeshes, BEAST -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) # Γ = meshsphere(1.0, 0.4) X = raviartthomas(Γ) diff --git a/examples/tdmfie_sym.jl b/examples/tdmfie_sym.jl index 8605223d..e9e87bdc 100644 --- a/examples/tdmfie_sym.jl +++ b/examples/tdmfie_sym.jl @@ -1,5 +1,5 @@ using CompScienceMeshes, BEAST -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) # Γ = meshsphere(1.0, 0.4) X = raviartthomas(Γ) From 65faea932d3c3e701bc57767c81d15604655a9fb Mon Sep 17 00:00:00 2001 From: Yaraslau <44506630+Yaraslaut@users.noreply.github.com> Date: Wed, 28 Sep 2022 13:48:17 +0100 Subject: [PATCH 180/528] removed unnecessary entry --- Project.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Project.toml b/Project.toml index 1b5338f8..b86f636d 100644 --- a/Project.toml +++ b/Project.toml @@ -17,7 +17,6 @@ IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LiftedMaps = "d22a30c1-52ac-4762-a8c9-5838452405e0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" -Plotly = "58dd65bb-95f3-509e-9936-c39a10fdeae7" SauterSchwab3D = "0a13313b-1c00-422e-8263-562364ed9544" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" From 1a66e7f42f32f9274cd1c6870dbf2bbb13cb6e45 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 27 Sep 2022 09:30:43 +0200 Subject: [PATCH 181/528] new CSM API compatibility - tests pass --- examples/pmchwt.jl | 18 ++--- src/bases/basis.jl | 7 +- src/bases/bcspace.jl | 98 +++++++++++++++++---------- src/bases/lagrange.jl | 21 +++--- src/bases/ndlccspace.jl | 4 +- src/bases/ndspace.jl | 2 +- src/bases/rtspace.jl | 23 ++++--- src/bases/trace.jl | 20 +++--- src/integralop.jl | 2 +- test/runtests.jl | 5 -- test/test_basis.jl | 20 +++--- test/test_dvg.jl | 10 ++- test/test_hh3dexc.jl | 2 +- test/test_local_assembly.jl | 9 +-- test/test_mult.jl | 15 ++-- test/test_ndspace.jl | 4 +- test/test_nitsche.jl | 14 ++-- test/test_nitschehh3d.jl | 4 +- test/test_rt.jl | 2 +- test/test_rtports.jl | 14 ++-- test/test_rtx.jl | 2 +- test/test_tdassembly.jl | 4 +- test/test_tdmwdbl.jl | 2 +- test/test_trace.jl | 2 +- test/test_ttrace.jl | 4 +- test/test_wiltonints.jl | 4 +- test/{ => untested}/test_laminated.jl | 2 +- 27 files changed, 177 insertions(+), 137 deletions(-) rename test/{ => untested}/test_laminated.jl (97%) diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index f77bbef4..0c78f7fb 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -54,18 +54,18 @@ pmchwt = @discretise( u = gmres(pmchwt) -Z = BEAST.sysmatrix(pmchwt) -u = zeros(ComplexF64, axes(Z,2)) -y = zeros(ComplexF64, axes(Z,1)) +# Z = BEAST.sysmatrix(pmchwt) +# u = zeros(ComplexF64, axes(Z,2)) +# y = zeros(ComplexF64, axes(Z,1)) -@time for i in 1:300; BEAST.LinearMaps.mul!(y, Z, u); end +# @time for i in 1:300; BEAST.LinearMaps.mul!(y, Z, u); end Θ, Φ = range(0.0,stop=2π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] # Don't forget the far field comprises two contributions -ffm = potential(MWFarField3D(κ*im), ffpoints, u[m], X) -ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], X) +ffm = potential(MWFarField3D(κ*im, η), ffpoints, u[m], X) +ffj = potential(MWFarField3D(κ*im, η), ffpoints, u[j], X) ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) using Plots @@ -118,10 +118,10 @@ contour(real.(getindex.(E_tot,1))) contour(real.(getindex.(H_tot,2))) heatmap(Z, Y, real.(getindex.(E_tot,1))) -#heatmap(Z, Y, real.(getindex.(H_tot,2))) +heatmap(Z, Y, real.(getindex.(H_tot,2))) -#plot(real.(getindex.(E_tot[:,51],1))) -#plot!(real.(getindex.(H_tot[:,51],2))) +plot(real.(getindex.(E_tot[:,51],1))) +plot!(real.(getindex.(H_tot[:,51],2))) # Compare the far field and the field far diff --git a/src/bases/basis.jl b/src/bases/basis.jl index 91724264..03b96b48 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -230,12 +230,13 @@ end function instantiate_charts(geo, num_active_cells, active) - E = typeof(chart(geo, first(cells(geo)))) + @assert length(geo) != 0 + E = typeof(chart(geo, first(geo))) elements = Vector{E}(undef,num_active_cells) j = 1 - for (i,cell) in enumerate(cells(geo)) + for (i,p) in enumerate(geo) active[i] || continue - elements[j] = chart(geo, cell) + elements[j] = chart(geo, p) j += 1 end return elements diff --git a/src/bases/bcspace.jl b/src/bases/bcspace.jl index 187f05a5..83d76b3a 100644 --- a/src/bases/bcspace.jl +++ b/src/bases/bcspace.jl @@ -101,9 +101,9 @@ function buffachristiansen(Γ, γ=mesh(coordtype(Γ),1,3); ibscaled=false, sort= if edges == :all edges = skeleton(Γ, 1; sort) - edges = submesh(edges) do edge + edges = submesh(edges) do edges, edge ch = chart(edges, edge) - !in_interior(edge) && !on_junction(ch) && return false + !in_interior(edges, edge) && !on_junction(ch) && return false return true end end @@ -129,9 +129,10 @@ function buffachristiansen(Γ, γ=mesh(coordtype(Γ),1,3); ibscaled=false, sort= jct_pred = overlap_gpredicate(γ) bcs, k = Vector{Vector{Shape{T}}}(undef,length(edges)), 1 pos = Vector{P}(undef,length(edges)) - for (i,edge) in enumerate(edges) + for (i,e) in enumerate(edges) - ch = chart(edges, edge) + edge = CompScienceMeshes.indices(edges,e) + ch = chart(edges, e) ln = volume(ch) # !in_interior(edge) && !on_junction(ch) && continue @@ -143,7 +144,8 @@ function buffachristiansen(Γ, γ=mesh(coordtype(Γ),1,3); ibscaled=false, sort= bnd = boundary(supp) bnd_nodes = skeleton(bnd,0) p = 0 - for node in bnd_nodes + for bn in bnd_nodes + node = CompScienceMeshes.indices(bnd_nodes, bn) vert1 = vertices(Γ)[edge[1]] vert2 = vertices(Γ)[edge[2]] vert = vertices(fine)[node[1]] @@ -412,10 +414,12 @@ function buildhalfbc2(patch, port, dirichlet, prt_fluxes) verts = skeleton(patch,0) bndry = boundary(patch) - dirbnd = submesh(dirichlet) do edge - edge in cells(bndry) && return true - reverse(edge) in cells(bndry) && return true - return false + in_bndry = in(bndry) + dirbnd = submesh(dirichlet) do m,edge + in_bndry(m,edge) + # edge in cells(bndry) && return true + # reverse(edge) in cells(bndry) && return true + # return false end # @show numcells(patch) # @show numcells(dirbnd) @@ -423,9 +427,11 @@ function buildhalfbc2(patch, port, dirichlet, prt_fluxes) bnd_dirbnd = boundary(dirbnd) nodes_dirbnd = skeleton(dirbnd,0) - int_nodes_dirbnd = submesh(nodes_dirbnd) do node - node in cells(bnd_dirbnd) && return false - return true + in_bnd_dirbnd = in(bnd_dirbnd) + int_nodes_dirbnd = submesh(nodes_dirbnd) do m,node + # node in cells(bnd_dirbnd) && return false + # return true + return !in_bnd_dirbnd(m, node) end # @show numcells(int_nodes_dirbnd) @assert numcells(int_nodes_dirbnd) ≤ 2 @@ -447,13 +453,21 @@ function buildhalfbc2(patch, port, dirichlet, prt_fluxes) # println(edge) # end - int_edges = submesh(edges) do edge - (edge in cells(port)) && (num_edges_on_port+=1 ; return false) - (reverse(edge) in cells(port)) && (num_edges_on_port+=1 ; return false) - edge in cells(dirbnd) && (num_edges_on_dirc+=1; return true) - reverse(edge) in cells(dirbnd) && (num_edges_on_dirc+=1; return true) - (!int_pred(edge)) && return false + in_port = in(port) + in_dirbnd = in(dirbnd) + int_edges = submesh(edges) do m,edge + + in_port(m,edge) && (num_edges_on_port+=1; return false) + in_dirbnd(m,edge) && (num_edges_on_dirc+=1; return true) + !int_pred(m,edge) && return false return true + + # (edge in cells(port)) && (num_edges_on_port+=1 ; return false) + # (reverse(edge) in cells(port)) && (num_edges_on_port+=1 ; return false) + # edge in cells(dirbnd) && (num_edges_on_dirc+=1; return true) + # reverse(edge) in cells(dirbnd) && (num_edges_on_dirc+=1; return true) + # (!int_pred(edge)) && return false + # return true end # println() # for edge in cells(int_edges) @@ -470,11 +484,18 @@ function buildhalfbc2(patch, port, dirichlet, prt_fluxes) # end # int_verts = submesh(!isonboundary, verts) # dirichlet_nodes = skeleton(dirichlet,0) - int_verts = submesh(verts) do node - node in cells(int_nodes_dirbnd) && return true - node in cells(bnd_verts) && return false - node in cells(prt_verts) && return false + in_int_nodes_dirbnd = in(int_nodes_dirbnd) + in_bnd_verts = in(bnd_verts) + in_prt_verts = in(prt_verts) + int_verts = submesh(verts) do m,node + in_int_nodes_dirbnd(m,node) && return true + in_bnd_verts(m,node) && return false + in_prt_verts(m,node) && return false return true + # node in cells(int_nodes_dirbnd) && return true + # node in cells(bnd_verts) && return false + # node in cells(prt_verts) && return false + # return true end # @show numcells(int_verts) @@ -519,17 +540,20 @@ function buffachristiansen2(Faces::CompScienceMeshes.AbstractMesh{U,D1,T}) where faces = barycentric_refinement(Faces) Edges = skeleton(Faces,1) Bndry = boundary(Faces) - Edges = submesh(Edges) do Edge - Edge in cells(Bndry) && return false - reverse(Edge) in cells(Bndry) && return false - return true + inBndry = in(Bndry) + Edges = submesh(Edges) do m,Edge + return !inBndry(m,Edge) + # Edge in cells(Bndry) && return false + # reverse(Edge) in cells(Bndry) && return false + # return true end #T = Float64 bfs = Vector{Vector{Shape{T}}}(undef, numcells(Edges)) pos = Vector{vertextype(Faces)}(undef, numcells(Edges)) dirichlet = boundary(faces) - for (E,Edge) in enumerate(cells(Edges)) + for (E,Edge) in enumerate(Edges) + EdgeInds = CompScienceMeshes.indices(Edges, Edge) # @show Edge bfs[E] = Vector{Shape{T}}() @@ -542,7 +566,7 @@ function buffachristiansen2(Faces::CompScienceMeshes.AbstractMesh{U,D1,T}) where # @show (Faces.vertices[Edge[1]]+Faces.vertices[Edge[2]])/2 # Build the plus-patch - ptch_vert_idx = Edge[1] + ptch_vert_idx = EdgeInds[1] ptch_face_idcs = [i for (i,face) in enumerate(cells(faces)) if ptch_vert_idx in face] patch = Mesh(vertices(faces), cells(faces)[ptch_face_idcs]) patch_bnd = boundary(patch) @@ -573,8 +597,8 @@ function buffachristiansen2(Faces::CompScienceMeshes.AbstractMesh{U,D1,T}) where end # Build the minus-patch - ptch_vert_idx = Edge[2] - ptch_face_idcs = [i for (i,face) in enumerate(cells(faces)) if ptch_vert_idx in face] + ptch_vert_idx = EdgeInds[2] + ptch_face_idcs = [i for (i,face) in enumerate((faces)) if ptch_vert_idx in CompScienceMeshes.indices(faces, face)] patch = Mesh(vertices(faces), cells(faces)[ptch_face_idcs]) port = Mesh(vertices(faces), filter(c->port_vertex_idx in c, cells(boundary(patch)))) # @assert numcells(patch) >= 6 @@ -608,9 +632,10 @@ function buffachristiansen3(Faces::CompScienceMeshes.AbstractMesh{U,D1,T}) where faces = barycentric_refinement(Faces) Edges = skeleton(Faces,1) Bndry = boundary(Faces) - Edges = submesh(Edges) do Edge - Edge in cells(Bndry) && return false - reverse(Edge) in cells(Bndry) && return false + inBndry = in(Bndry) + Edges = submesh(Edges) do m,Edge + inBndry(m,Edge) && return false + # reverse(Edge) in cells(Bndry) && return false return true end @@ -618,7 +643,7 @@ function buffachristiansen3(Faces::CompScienceMeshes.AbstractMesh{U,D1,T}) where bfs = Vector{Vector{Shape{T}}}(undef, numcells(Edges)) pos = Vector{vertextype(Faces)}(undef, numcells(Edges)) dirichlet = boundary(faces) - for (E,Edge) in enumerate(cells(Edges)) + for (E,Edge) in enumerate(Edges) bfs[E] = Vector{Shape{T}}() # port_vertex_idx = numvertices(Faces) + E @@ -628,8 +653,9 @@ function buffachristiansen3(Faces::CompScienceMeshes.AbstractMesh{U,D1,T}) where port_vertex_idx = argmin(norm.(vertices(faces) .- Ref(pos[E]))) # Build the dual support - ptch_face_idcs1 = [i for (i,face) in enumerate(cells(faces)) if Edge[1] in face] - ptch_face_idcs2 = [i for (i,face) in enumerate(cells(faces)) if Edge[2] in face] + EdgeInds = CompScienceMeshes.indices(Edges, Edge) + ptch_face_idcs1 = [i for (i,face) in enumerate(faces) if EdgeInds[1] in CompScienceMeshes.indices(faces,face)] + ptch_face_idcs2 = [i for (i,face) in enumerate(faces) if EdgeInds[2] in CompScienceMeshes.indices(faces,face)] patch1 = Mesh(vertices(faces), cells(faces)[ptch_face_idcs1]) patch2 = Mesh(vertices(faces), cells(faces)[ptch_face_idcs2]) patch_bnd = boundary(patch1) diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 8622db99..6759d2b3 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -39,7 +39,7 @@ function lagrangecxd0(mesh) # create the local shapes fns = Vector{Vector{Shape{T}}}(undef,num_cells) pos = Vector{vertextype(mesh)}(undef,num_cells) - for (i,cell) in enumerate(cells(mesh)) + for (i,cell) in enumerate(mesh) fns[i] = [Shape(i, 1, T(1.0))] pos[i] = cartesian(center(chart(mesh, cell))) end @@ -153,8 +153,8 @@ function singleduallagd0(fine, F, v) T = coordtype(fine) fn = Shape{T}[] for cellid in F - cell = cells(fine)[cellid] - ptch = chart(fine, cell) + # cell = cells(fine)[cellid] + ptch = chart(fine, cellid) coeff = 1 / volume(ptch) / length(F) refid = 1 push!(fn, Shape(cellid, refid, coeff)) @@ -320,16 +320,19 @@ function duallagrangec0d1(mesh, refined, jct_pred, ::Type{Val{3}}) uv_ctr = ones(dimension(mesh))/(dimension(mesh)+1) vtoc, vton = vertextocellmap(refined) - for (i,coarse_idcs) in enumerate(cells(mesh)) + for (i,p) in enumerate(mesh) + coarse_idcs = CompScienceMeshes.indices(mesh, p) + coarse_chart = chart(mesh,p) + fns[i] = Vector{Shape{T}}() - push!(pos, cartesian(center(chart(mesh, coarse_idcs)))) + push!(pos, cartesian(center(coarse_chart))) # It is assumed the vertices of this cell have the same index # mesh and its refinement. - coarse_cell = chart(mesh, coarse_idcs) + # coarse_cell = chart(mesh, coarse_idcs) # get the index in fine.vertices of the centroid of coarse_cell - centroid = barytocart(coarse_cell, uv_ctr) + centroid = barytocart(coarse_chart, uv_ctr) I = CollisionDetection.find(fine_vertices, centroid) @assert length(I) == 1 centroid_id = I[1] @@ -343,7 +346,7 @@ function duallagrangec0d1(mesh, refined, jct_pred, ::Type{Val{3}}) uv_face_ctr[f] = 0 uv_face_ctr = uv_face_ctr[1:end-1] - face_ctr = barytocart(coarse_cell, uv_face_ctr) + face_ctr = barytocart(coarse_chart, uv_face_ctr) I = CollisionDetection.find(fine_vertices, face_ctr) @assert length(I) == 1 face_center_ids[f] = I[1] @@ -468,7 +471,7 @@ function duallagrangec0d1(mesh, mesh2, pred, ::Type{Val{2}}) end # Now assign all of these shapes to the relevent segment in the coarse mesh fns[segment_coarse]=shapes - push!(pos, cartesian(center(chart(mesh, mesh.faces[segment_coarse])))) + push!(pos, cartesian(center(chart(mesh, segment_coarse)))) end NF = 2 diff --git a/src/bases/ndlccspace.jl b/src/bases/ndlccspace.jl index 32cfeb4e..aabea3e6 100644 --- a/src/bases/ndlccspace.jl +++ b/src/bases/ndlccspace.jl @@ -26,10 +26,10 @@ function nedelecc3d(mesh, edges) fns = Vector{Vector{Shape{T}}}(undef,num_edges) pos = Vector{P}(undef,num_edges) - for (i,edge) in enumerate(cells(edges)) + for (i,e) in enumerate(edges) fns[i] = Vector{Shape{T}}() - pos[i] = cartesian(center(chart(edges,edge))) + pos[i] = cartesian(center(chart(edges,e))) for k in nzrange(C,i) diff --git a/src/bases/ndspace.jl b/src/bases/ndspace.jl index cfb7403f..0393e5bb 100644 --- a/src/bases/ndspace.jl +++ b/src/bases/ndspace.jl @@ -22,7 +22,7 @@ function nedelec(surface, edges=skeleton(surface,1)) fns = Vector{Vector{Shape{T}}}(undef,num_edges) pos = Vector{P}(undef,num_edges) - for (i,edge) in enumerate(cells(edges)) + for (i,edge) in enumerate(edges) fns[i] = Vector{Shape{T}}() pos[i] = cartesian(center(chart(edges,edge))) diff --git a/src/bases/rtspace.jl b/src/bases/rtspace.jl index 3be142b7..b6a280a6 100644 --- a/src/bases/rtspace.jl +++ b/src/bases/rtspace.jl @@ -35,11 +35,15 @@ function raviartthomas(mesh::CompScienceMeshes.AbstractMesh{U,D1,T}, cellpairs:: numpairs = size(cellpairs,2) functions = Vector{Vector{Shape{T}}}(undef,numpairs) positions = Vector{vertextype(mesh)}(undef,numpairs) - Cells = cells(mesh) + # Cells = cells(mesh) for i in 1:numpairs if cellpairs[2,i] > 0 - c1 = cellpairs[1,i]; cell1 = Cells[c1] #mesh.faces[c1] - c2 = cellpairs[2,i]; cell2 = Cells[c2] #mesh.faces[c2] + c1 = cellpairs[1,i]; # cell1 = Cells[c1] #mesh.faces[c1] + c2 = cellpairs[2,i]; # cell2 = Cells[c2] #mesh.faces[c2] + + cell1 = CompScienceMeshes.indices(mesh, c1) + cell2 = CompScienceMeshes.indices(mesh, c2) + e1, e2 = getcommonedge(cell1, cell2) functions[i] = [ Shape{T}(c1, abs(e1), T(+1.0)), @@ -49,15 +53,15 @@ function raviartthomas(mesh::CompScienceMeshes.AbstractMesh{U,D1,T}, cellpairs:: @assert !(cell1[abs(e1)] in isct) @assert !(cell2[abs(e2)] in isct) - ctr1 = cartesian(center(chart(mesh, cell1))) - ctr2 = cartesian(center(chart(mesh, cell2))) + ctr1 = cartesian(center(chart(mesh, c1))) + ctr2 = cartesian(center(chart(mesh, c2))) positions[i] = (ctr1 + ctr2) / 2 else c1 = cellpairs[1,i] e1 = cellpairs[2,i] functions[i] = [ Shape(c1, abs(e1), T(+1.0))] - positions[i] = cartesian(center(chart(mesh, Cells[c1]))) + positions[i] = cartesian(center(chart(mesh, c1))) end end @@ -134,11 +138,12 @@ function portcells(Γ, γ) in_interior = interior_tpredicate(Γ) overlaps = overlap_gpredicate(γ) - on_junction = c -> overlaps(simplex(vertices(Γ,c))) + on_junction = (m,c) -> overlaps(chart(m,c)) - pred = x -> (!in_interior(x) && on_junction(x)) #check only for exterior overlapping edges + pred = (m,x) -> (!in_interior(m,x) && on_junction(m,x)) #check only for exterior overlapping edges # - if γ is defined to overlap within the structure Γ, this isn't considered a port - - edges = skeleton(pred, Γ, 1) #Take only exterior edges overlapping γ segment + # edges = skeleton(pred, Γ, 1) #Take only exterior edges overlapping γ segment + edges = submesh(pred, skeleton(Γ,1)) cps = cellpairs(Γ, edges, dropjunctionpair=true) return cps end diff --git a/src/bases/trace.jl b/src/bases/trace.jl index 90e88f92..ea52a307 100644 --- a/src/bases/trace.jl +++ b/src/bases/trace.jl @@ -33,8 +33,9 @@ function ntrace(X::Space, γ) ogeo = boundary(igeo) on_target = overlap_gpredicate(γ) - ogeo = submesh(ogeo) do face - on_target(chart(ogeo,face)) + ogeo = submesh(ogeo) do m,f + ch = chart(m,f) + on_target(ch) end # D = copy(transpose(connectivity(ogeo, igeo, abs))) @@ -59,7 +60,7 @@ function ntrace(X::Space, γ) end @assert r != 0 - fc1 = chart(ogeo, cells(ogeo)[r]) + fc1 = chart(ogeo, r) Q = ntrace(x, el, q, fc1) for i in 1:size(Q,1) @@ -116,8 +117,8 @@ function strace(X::Space, γ, dim1::Type{Val{2}}) @assert e != 0 # make sure we use fc as oriented in Σ - cell = Σ.faces[e] - fc = chart(Σ, cell) + cell = CompScienceMeshes.indices(Σ,e) #Σ.faces[e] + fc = chart(Σ, e) on_target(fc) || continue Q = strace(x,el,q,fc) @@ -201,14 +202,13 @@ function ttrace(X::Space, γ) igeo = geometry(X) @assert dimension(γ) == dimension(igeo)-1 - # ogeo = skeleton(igeo, dimension(γ)) ogeo = boundary(igeo) on_target = overlap_gpredicate(γ) - ogeo = submesh(ogeo) do face - on_target(chart(ogeo, face)) + ogeo = submesh(ogeo) do m,face + ch = chart(m,face) + on_target(ch) end - # D = copy(transpose(connectivity(ogeo, igeo, abs))) D = connectivity(igeo, ogeo, abs) rows, vals = rowvals(D), nonzeros(D) @@ -227,7 +227,7 @@ function ttrace(X::Space, γ) end @assert r != 0 - fc1 = chart(ogeo, cells(ogeo)[r]) + fc1 = chart(ogeo, r) @assert cartesian(center(fc)) ≈ cartesian(center(fc1)) Q = ttrace(x, el, q, fc1) diff --git a/src/integralop.jl b/src/integralop.jl index 24f28dc7..33d075e4 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -56,7 +56,7 @@ Create an iterable collection of the elements stored in `geo`. The order in whic this collection produces the elements determines the index used for lookup in the data structures returned by `assemblydata` and `quaddata`. """ -elements(geo) = [chart(geo,cl) for cl in cells(geo)] +elements(geo) = [chart(geo,cl) for cl in geo] elements(sp::Space) = elements(geometry(sp)) diff --git a/test/runtests.jl b/test/runtests.jl index 22775757..7477ff6d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,7 +14,6 @@ import BEAST include("test_fourier.jl") include("test_specials.jl") -# include("test_sparsend.jl") include("test_basis.jl") include("test_directproduct.jl") @@ -41,7 +40,6 @@ include("test_gram.jl") include("test_vector_gram.jl") include("test_local_storage.jl") include("test_embedding.jl") -include("test_laminated.jl") include("test_assemblerow.jl") include("test_mixed_blkassm.jl") @@ -70,9 +68,6 @@ include("test_tdrhs_scaling.jl") include("test_variational.jl") -# include("test_farfield.jl") - -#include("test_assemblerow.jl") try Pkg.installed("BogaertInts10") diff --git a/test/test_basis.jl b/test/test_basis.jl index c3fd6a19..672ed1ed 100644 --- a/test/test_basis.jl +++ b/test/test_basis.jl @@ -49,7 +49,7 @@ for T in [Float32, Float64] NumF = 3 f = BEAST.LagrangeRefSpace{T,Degr,Dim1,NumF}() sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in"),T=T) - s = chart(sphere, first(cells(sphere))) + s = chart(sphere, first(sphere)) t = neighborhood(s, T.([1,1]/3)) v = f(t, Val{:withcurl}) @@ -108,8 +108,8 @@ for T in [Float64] x = refspace(X) - cell = chart(m, first(cells(m))) - face = chart(b, first(cells(b))) + cell = chart(m, first(m)) + face = chart(b, first(b)) Q = BEAST.strace(x, cell, 3, face) @test Q == [1 0 0; 0 1 0] end @@ -132,8 +132,8 @@ for T in [Float64] p = point(T, 0.5, 0.0, 0.0) for _s in X.fns[1] - _cell = m.faces[_s.cellid] - patch = chart(m, _cell) + # _cell = m.faces[_s.cellid] + patch = chart(m, _s.cellid) bary = carttobary(patch, p) mp = neighborhood(patch, bary) end @@ -162,8 +162,8 @@ for _fn in X.fns for _sh in _fn @test _sh.refid == 1 cellid = _sh.cellid - _cell = cells(fine)[cellid] - ptch = chart(fine, _cell) + # _cell = cells(fine)[cellid] + ptch = chart(fine, cellid) @test _sh.coeff * volume(ptch) ≈ 1/_n end end @@ -203,8 +203,10 @@ lag = lagrangec0d1(m, int_nodes) @test numfunctions(lag) == 1 rs = refspace(lag) -cl = cells(m)[2] -ch = chart(m, cl) +# cl = cells(m)[2] +p = 2 +cl = CompScienceMeshes.indices(m,p) +ch = chart(m, p) nbd = neighborhood(ch, carttobary(ch, [0.5, 0.5, 0])) vals = getfield.(rs(nbd), :value) @test vals ≈ [0, 1, 0] diff --git a/test/test_dvg.jl b/test/test_dvg.jl index 259bdcb8..28a33505 100644 --- a/test/test_dvg.jl +++ b/test/test_dvg.jl @@ -9,9 +9,13 @@ using Test in_interior = CompScienceMeshes.interior_tpredicate(Γ) on_junction = CompScienceMeshes.overlap_gpredicate(γ) -edges = skeleton(Γ,1) do edge - in_interior(edge) ||on_junction(chart(Γ,edge)) +edges = submesh(skeleton(Γ,1)) do m,e + in_interior(m, e) ||on_junction(chart(m, e)) end + +# edges = skeleton(Γ,1) do m, e +# in_interior(m, e) ||on_junction(chart(m, e)) +# end length(edges) X = raviartthomas(Γ, edges) @@ -23,7 +27,7 @@ for (_f,_g) in zip(x.fns, X.fns) @test length(_f) == length(_g) if length(_f) == 1 c = _f[1].cellid - _s = chart(Γ, Γ.faces[c]) + _s = chart(Γ, c) @test _f[1].coeff == 1 / volume(_s) else @test _f[1].coeff + _f[2].coeff == 0 diff --git a/test/test_hh3dexc.jl b/test/test_hh3dexc.jl index c5f62325..fd5434f1 100644 --- a/test/test_hh3dexc.jl +++ b/test/test_hh3dexc.jl @@ -19,7 +19,7 @@ for T in [Float32, Float64] import BEAST.∂n p = ∂n(f) - local s = chart(sphere,first(cells(sphere))) + local s = chart(sphere,first(sphere)) local c = neighborhood(s, T.([1,1]/3)) r = cartesian(c) diff --git a/test/test_local_assembly.jl b/test/test_local_assembly.jl index 702eeb3e..ef015c1e 100644 --- a/test/test_local_assembly.jl +++ b/test/test_local_assembly.jl @@ -6,10 +6,11 @@ using Test m = meshrectangle(1.0, 1.0, 0.5, 3) nodes = skeleton(m,0) -srt_bnd_nodes = sort.(skeleton(boundary(m),0)) -int_nodes = submesh(nodes) do node - !(sort(node) in srt_bnd_nodes) -end +int_nodes = submesh(!in(skeleton(boundary(m),0)), nodes) +# srt_bnd_nodes = sort.(skeleton(boundary(m),0)) +# int_nodes = submesh(nodes) do node +# !(sort(node) in srt_bnd_nodes) +# end @test length(int_nodes) == 1 diff --git a/test/test_mult.jl b/test/test_mult.jl index a8d01f52..16927330 100644 --- a/test/test_mult.jl +++ b/test/test_mult.jl @@ -6,16 +6,13 @@ using LinearAlgebra for T in [Float32, Float64] faces = meshrectangle(T(1.0), T(1.0), T(0.5), 3) - srt_bnd_faces = sort.(boundary(faces)) - local edges = submesh(skeleton(faces,1)) do edge - !(sort(edge) in srt_bnd_faces) - end - srt_bnd_nodes = sort.(skeleton(boundary(faces),0)) - @test length(srt_bnd_nodes) == 8 - nodes = submesh(skeleton(faces,0)) do node - !(sort(node) in srt_bnd_nodes) - end + local bnd = boundary(faces) + local edges = submesh(!in(bnd), skeleton(faces,1)) + + local bnd_nodes = skeleton(bnd, 0) + @test length(bnd_nodes) == 8 + local nodes = submesh(!in(bnd_nodes), skeleton(faces,0)) @test length(nodes) == 1 Conn = connectivity(nodes, edges, sign) diff --git a/test/test_ndspace.jl b/test/test_ndspace.jl index 25d2c1b2..4a326c42 100644 --- a/test/test_ndspace.jl +++ b/test/test_ndspace.jl @@ -11,7 +11,7 @@ for T in [Float32, Float64] mesh = readmesh(fn, T=T) edgs = skeleton(mesh,1) - charts = [chart(edgs,edg) for edg in cells(edgs)] + charts = [chart(edgs,edg) for edg in edgs] ctrs = [cartesian(center(cht)) for cht in charts] ND = BEAST.nedelec(mesh, edgs) @@ -25,7 +25,7 @@ for T in [Float32, Float64] # center of the diagonal ctr = center(charts[3]) - face_charts = [chart(mesh,fce) for fce in cells(mesh)] + face_charts = [chart(mesh,fce) for fce in mesh] nbd1 = neighborhood(face_charts[1], carttobary(face_charts[1], ctr)) nbd2 = neighborhood(face_charts[2], carttobary(face_charts[2], ctr)) t = tangents(ctr,1) diff --git a/test/test_nitsche.jl b/test/test_nitsche.jl index d7fa4779..3d620e8f 100644 --- a/test/test_nitsche.jl +++ b/test/test_nitsche.jl @@ -18,9 +18,13 @@ h = 0.25 in_interior = CompScienceMeshes.interior_tpredicate(Γ) on_junction = CompScienceMeshes.overlap_gpredicate(γ) -edges = skeleton(Γ,1) do edge - in_interior(edge) ||on_junction(chart(Γ,edge)) +edges = submesh(skeleton(Γ,1)) do m, edge + in_interior(m,edge) || on_junction(chart(m,edge)) end + +# edges = skeleton(Γ,1) do edge +# in_interior(edge) ||on_junction(chart(Γ,edge)) +# end X = raviartthomas(Γ, edges) x = divergence(X) @@ -63,8 +67,8 @@ Nyx = assemble(N,Y,X) @test size(Nyx) == (1,1) -sx = chart(m, first(cells(m))) -sy = chart(n, first(cells(n))) +sx = chart(m, first(m)) +sy = chart(n, first(n)) cx = neighborhood(sx, [1,1]/3) cy = neighborhood(sy, [1]/2) @@ -98,7 +102,7 @@ end for _f in Y.fns @test length(_f) == 1 @test 0 < _f[1].cellid < 4 - seg = chart(Σ, cells(Σ)[_f[1].cellid]) + seg = chart(Σ, _f[1].cellid) @test _f[1].refid == 1 @test _f[1].coeff ≈ (1 / volume(seg)) end diff --git a/test/test_nitschehh3d.jl b/test/test_nitschehh3d.jl index 6b252dae..4f1fd210 100644 --- a/test/test_nitschehh3d.jl +++ b/test/test_nitschehh3d.jl @@ -17,7 +17,7 @@ X = lagrangec0d1(m, boundary(m)) @test numfunctions(X) == 3 x = refspace(X) -s = chart(m, m.faces[X.fns[1][1].cellid]) +s = chart(m, X.fns[1][1].cellid) c = neighborhood(s, [1,1]/3) # get the barycenter of that patch v = x(c, Val{:withcurl}) # evaluate the Lagrange elements in c, together with their curls @@ -29,7 +29,7 @@ Y = lagrangec0d1(n, boundary(n)) @test numfunctions(Y) == 2 y = refspace(Y) -t = chart(n, n.faces[Y.fns[1][1].cellid]) +t = chart(n, Y.fns[1][1].cellid) d = neighborhood(t, [1]/2) tg = normalize(tangents(d,1)) w = y(d) diff --git a/test/test_rt.jl b/test/test_rt.jl index cc3e7138..f213df15 100644 --- a/test/test_rt.jl +++ b/test/test_rt.jl @@ -34,7 +34,7 @@ ref = refspace(rwg) # vrts = vertices(m, first(cells(m))) # ptch = simplex(vrts[1], vrts[2], vrts[3]) -ptch = chart(m, first(cells(m))) +ptch = chart(m, first(m)) ctrd = neighborhood(ptch, T.([1,1]/3)) vals = shapevals(ref, [ctrd]) diff --git a/test/test_rtports.jl b/test/test_rtports.jl index 339ddef7..8e563c92 100644 --- a/test/test_rtports.jl +++ b/test/test_rtports.jl @@ -40,12 +40,14 @@ cps = cellpairs(sum_mesh, edges) # select only outer edges overlaps = overlap_gpredicate(Γ) -on_junction = c -> overlaps(simplex(vertices(meshZ,c))) - -pred = x -> on_junction(x) -edges = skeleton(pred, meshZ, 1) #Take only exterio -@test numcells(edges) == 2 -@test size(edges.faces[1]) == (2,) +on_junction = (m,c) -> overlaps(chart(m,c)) + +# pred = x -> on_junction(x) +edges = submesh(on_junction, skeleton(meshZ,1)) +# edges = skeleton(pred, meshZ, 1) +@test length(edges) == 2 +# @test size(edges.faces[1]) == (2,) +@test dimension(edges) == 1 cps = cellpairs(sum_mesh, edges) @test size(cps) == (2,2) diff --git a/test/test_rtx.jl b/test/test_rtx.jl index 11b53bc9..cc99d0e5 100644 --- a/test/test_rtx.jl +++ b/test/test_rtx.jl @@ -13,7 +13,7 @@ for T in [Float32, Float64] i = 12 c = X.fns[i][1].cellid - ch = chart(m, cells(m)[c]) + ch = chart(m, c) ctr = cartesian(center(ch)) @test ctr ≈ p[i] diff --git a/test/test_tdassembly.jl b/test/test_tdassembly.jl index 69f54877..feac5229 100644 --- a/test/test_tdassembly.jl +++ b/test/test_tdassembly.jl @@ -31,8 +31,8 @@ q = refspace(Q) # Overwrite the default quadrature strategy for # this operator/space combination -τ1 = chart(S1, first(Iterators.drop(cells(S1),0))) -τ2 = chart(S2, first(Iterators.drop(cells(S2),0))) +τ1 = chart(S1, first(Iterators.drop(S1,0))) +τ2 = chart(S2, first(Iterators.drop(S2,0))) ι = BEAST.ring(first(BEAST.rings(τ1, τ2, ΔR)), ΔR) struct DoubleQuadTimeDomainRule end diff --git a/test/test_tdmwdbl.jl b/test/test_tdmwdbl.jl index a10ce658..1dc7df79 100644 --- a/test/test_tdmwdbl.jl +++ b/test/test_tdmwdbl.jl @@ -41,7 +41,7 @@ Z2 = fr2() @test all(==(0), Z2[:,:,1]) γ = geometry(Y) -ch1 = chart(G, first(cells(G))) +ch1 = chart(G, first(G)) qps = quadpoints(ch1, 3) x = qps[1][1] diff --git a/test/test_trace.jl b/test/test_trace.jl index 43d65b11..f963d78c 100644 --- a/test/test_trace.jl +++ b/test/test_trace.jl @@ -21,7 +21,7 @@ for i in eachindex(rwg.fns) length(nt.fns[i]) == 1 || continue global n += 1 c = nt.fns[i][1].cellid - edge = chart(Σ, cells(Σ)[c]) + edge = chart(Σ, c) @test on_bnd(edge) @test nt.fns[i][1].refid == 1 @test nt.fns[i][1].coeff == 2.0 diff --git a/test/test_ttrace.jl b/test/test_ttrace.jl index 096a4b25..6e66e70d 100644 --- a/test/test_ttrace.jl +++ b/test/test_ttrace.jl @@ -113,8 +113,8 @@ for T in [Float32, Float64] pa = Y.fns[1][1].cellid pb = Y.fns[1][2].cellid - tria = chart(m2, cells(m2)[pa]) - trib = chart(m2, cells(m2)[pb]) + tria = chart(m2, pa) + trib = chart(m2, pb) ra = Y.fns[1][1].refid rb = Y.fns[1][2].refid diff --git a/test/test_wiltonints.jl b/test/test_wiltonints.jl index 5ebfefbf..aba1fd27 100644 --- a/test/test_wiltonints.jl +++ b/test/test_wiltonints.jl @@ -324,8 +324,8 @@ end Γ = readmesh(joinpath(dirname(@__FILE__),"assets","sphere2.in")) nc = numcells(Γ) -t = chart(Γ, first(cells(Γ))) -s = chart(Γ, last(cells(Γ))) +t = chart(Γ, first(Γ)) +s = chart(Γ, nc) X = BEAST.raviartthomas(Γ) x = BEAST.refspace(X) diff --git a/test/test_laminated.jl b/test/untested/test_laminated.jl similarity index 97% rename from test/test_laminated.jl rename to test/untested/test_laminated.jl index ee1cd672..1db6191d 100644 --- a/test/test_laminated.jl +++ b/test/untested/test_laminated.jl @@ -27,7 +27,7 @@ G1 = weld(-G01, -G12, seam=b) G2 = weld(-G02, G12, seam=b) @test CompScienceMeshes.isoriented(G2) -isclosed(m) = (numcells(boundary(m)) == 0) +isclosed(m) = (length(boundary(m)) == 0) @test isclosed(G1) @test isclosed(G2) From 0c13c76becc697ccd35621e1f6fb598aaf45cda8 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 29 Sep 2022 16:43:01 +0200 Subject: [PATCH 182/528] Compatible with new CSM API --- examples/capacitor_new.jl | 10 ++-- examples/dpie.jl | 2 +- examples/dvie.jl | 2 +- examples/evie.jl | 2 +- examples/ex_dual1forms.jl | 86 +++++++++++++++++++---------- examples/ex_dual1forms_bis.jl | 26 ++++----- examples/ex_dual2forms.jl | 98 ++++++++++++++++++++++----------- examples/ex_duals.jl | 4 +- examples/ex_fem.jl | 22 ++++---- examples/ex_restrict_space.jl | 2 +- examples/helmholtz3d_neumann.jl | 12 ++-- examples/junction_classic.jl | 4 +- examples/mfie_monopolar.jl | 8 ++- examples/mthelmholtz.jl | 6 +- examples/mtjunction.jl | 4 +- examples/nitsche.jl | 12 ++-- examples/planewave.jl | 2 +- examples/pmchwt.jl | 22 ++++---- examples/projectors.jl | 3 +- examples/tdefie_neumann.jl | 4 -- examples/utils/edge_values.jl | 7 +-- examples/utils/plotresults.jl | 10 ++-- src/bases/basis.jl | 2 +- src/bases/bcspace.jl | 8 +-- src/bases/dual3d.jl | 9 +-- src/bases/lagrange.jl | 2 +- src/bases/local/laglocal.jl | 4 +- src/bases/ndlcdspace.jl | 2 +- src/excitation.jl | 5 +- src/maxwell/mwops.jl | 96 +++++++------------------------- src/maxwell/nxdbllayer.jl | 27 +++++++++ src/operator.jl | 2 +- src/postproc.jl | 3 +- src/solvers/lusolver.jl | 14 +++-- 34 files changed, 280 insertions(+), 242 deletions(-) diff --git a/examples/capacitor_new.jl b/examples/capacitor_new.jl index 75326a4b..ca6ac5fd 100644 --- a/examples/capacitor_new.jl +++ b/examples/capacitor_new.jl @@ -32,14 +32,14 @@ edges_all = skeleton(Γ, 1) edges_int = submesh(!in(boundary(Γ)), edges_all) on_γ₀ = overlap_gpredicate(γ₀) -edges_pt0 = submesh(edges_all) do edge - ch = chart(edges_all, edge) +edges_pt0 = submesh(edges_all) do m, edge + ch = chart(m, edge) return on_γ₀(ch) end on_γ₁ = overlap_gpredicate(γ₁) -edges_pt1 = submesh(edges_all) do edge - ch = chart(edges_all, edge) +edges_pt1 = submesh(edges_all) do m, edge + ch = chart(m, edge) return on_γ₁(ch) end @@ -92,7 +92,7 @@ Plotly.plot(patch(geo, norm.(fcr))) |> display # gap of 1V comes out as a 'natural' condition on the solution. zs = range(-0.5, stop=0.5, length=100) pts = [point(0.5,0.5,z) for z ∈ zs] -Φ = potential(MWSingleLayerPotential3D(κ), pts, u, S, type=ComplexF64) +Φ = potential(MWSingleLayerPotential3D(κ), pts, u, S; type=ComplexF64) Plots.plot(zs, real(Φ), xlabel="height",ylabel="scalar potential",label=false) |> display # Plot current along γ₀ and γ₁. Note: in this symmetric situation, we diff --git a/examples/dpie.jl b/examples/dpie.jl index 91cc75f2..8db88497 100644 --- a/examples/dpie.jl +++ b/examples/dpie.jl @@ -12,7 +12,7 @@ Y = lagrangec0d1(Γ) x = refspace(X) y = refspace(Y) -charts = [chart(Γ,c) for c in cells(Γ)] +charts = [chart(Γ,c) for c in Γ] qs = BEAST.defaultquadstrat(s, x, x) qd = quaddata(s, x, x, charts, charts, qs) diff --git a/examples/dvie.jl b/examples/dvie.jl index 05ffa0db..cd5f1868 100644 --- a/examples/dvie.jl +++ b/examples/dvie.jl @@ -9,7 +9,7 @@ end ntrc = X->ntrace(X,y) -T = tetmeshsphere(1.0,0.25) +T = CompScienceMeshes.tetmeshsphere(1.0,0.25) X = nedelecd3d(T) y = boundary(T) @show numfunctions(X) diff --git a/examples/evie.jl b/examples/evie.jl index e2bc80b8..2ef54e01 100644 --- a/examples/evie.jl +++ b/examples/evie.jl @@ -9,7 +9,7 @@ end ttrc = X->ttrace(X,y) -T= tetmeshsphere(1.0,0.25) +T = CompScienceMeshes.tetmeshsphere(1.0,0.25) X = nedelecc3d(T) y = boundary(T) @show numfunctions(X) diff --git a/examples/ex_dual1forms.jl b/examples/ex_dual1forms.jl index 5a2e7f6b..c94614db 100644 --- a/examples/ex_dual1forms.jl +++ b/examples/ex_dual1forms.jl @@ -66,45 +66,63 @@ tetrs = barycentric_refinement(Tetrs) bnd_Tetrs = boundary(Tetrs) bnd_tetrs = boundary(tetrs) -srt_bnd_Tetrs = sort.(bnd_Tetrs) -srt_bnd_tetrs = sort.(bnd_tetrs) +# srt_bnd_Tetrs = sort.(bnd_Tetrs) +# srt_bnd_tetrs = sort.(bnd_tetrs) -Faces = submesh(Face -> !(sort(Face) in srt_bnd_Tetrs), skeleton(Tetrs,2)) +Faces = submesh(!in(bnd_Tetrs), skeleton(Tetrs,2)) +# Faces = submesh(Face -> !(sort(Face) in srt_bnd_Tetrs), skeleton(Tetrs,2)) @show length(Faces) F = 396 Face = cells(Faces)[F] -pos = cartesian(center(chart(Faces, Face))) +pos = cartesian(center(chart(Faces, F))) -supp1 = submesh(tet -> Face[1] in tet, tetrs.mesh) -supp2 = submesh(tet -> Face[2] in tet, tetrs.mesh) -supp3 = submesh(tet -> Face[3] in tet, tetrs.mesh) +supp1 = submesh((m,tet) -> Face[1] in CompScienceMeshes.indices(m,tet), tetrs.mesh) +supp2 = submesh((m,tet) -> Face[2] in CompScienceMeshes.indices(m,tet), tetrs.mesh) +supp3 = submesh((m,tet) -> Face[3] in CompScienceMeshes.indices(m,tet), tetrs.mesh) supp = CompScienceMeshes.union(supp1, supp2) supp = CompScienceMeshes.union(supp, supp3) # PlotlyJS.plot(patch(boundary(supp))) port = skeleton(supp1, 1) -port = submesh(edge -> sort(edge) in sort.(skeleton(supp2,1)), port) -port = submesh(edge -> sort(edge) in sort.(skeleton(supp3,1)), port) +port = submesh(in(skeleton(supp2,1)), port) +port = submesh(in(skeleton(supp3,1)), port) +# port = submesh(edge -> sort(edge) in sort.(skeleton(supp2,1)), port) +# port = submesh(edge -> sort(edge) in sort.(skeleton(supp3,1)), port) @show length(port) @assert 1 ≤ length(port) ≤ 2 -dir = submesh(face -> sort(face) in srt_bnd_tetrs, boundary(supp)) +dir = submesh(in(bnd_tetrs), boundary(supp)) +# dir = submesh(face -> sort(face) in srt_bnd_tetrs, boundary(supp)) rim = boundary(dir) @show length(dir) -srt_port = sort.(port) -srt_dir_edges = sort.(skeleton(dir,1)) -srt_bnd_edges = sort.(skeleton(boundary(supp),1)) -srt_rim = sort.(rim) -int_edges = submesh(skeleton(supp,1)) do edge - sort(edge) in srt_port && return false - sort(edge) in srt_rim && return false - sort(edge) in srt_dir_edges && return true - sort(edge) in srt_bnd_edges && return false +# srt_port = sort.(port) +# srt_dir_edges = sort.(skeleton(dir,1)) +# srt_bnd_edges = sort.(skeleton(boundary(supp),1)) +# srt_rim = sort.(rim) + +in_port = in(port) +in_dir_edges = in(skeleton(dir,1)) +in_bnd_edges = in(skeleton(boundary(supp),1)) +in_rim = in(rim) + +int_edges = submesh(skeleton(supp,1)) do m,edge + in_port(m,edge) && return false + in_rim(m,edge) && return false + in_dir_edges(m,edge) && return true + in_bnd_edges(m,edge) && return false return true end + +# int_edges = submesh(skeleton(supp,1)) do edge +# sort(edge) in srt_port && return false +# sort(edge) in srt_rim && return false +# sort(edge) in srt_dir_edges && return true +# sort(edge) in srt_bnd_edges && return false +# return true +# end @show length(int_edges) @assert length(skeleton(supp,0)) - length(skeleton(supp,1)) + length(skeleton(supp,2)) - length(supp) == 1 @@ -116,13 +134,13 @@ Nd_int = BEAST.nedelecc3d(supp, int_edges) x0 = ones(length(port)) / length(port) for (i,edge) in enumerate(port) tgt = tangents(center(chart(port,edge)),1) - if dot(normal(chart(Faces,Face)), tgt) < 0 + if dot(normal(chart(Faces,F)), tgt) < 0 x0[i] *= -1 end end curl_Nd_int = curl(Nd_int) -A = assemble(Id, curl_Nd_int, curl_Nd_int) +A = Matrix(assemble(Id, curl_Nd_int, curl_Nd_int)) N = nullspace(A) @show size(N,2) a = -assemble(Id, curl_Nd_int, curl(Nd_prt)) * x0 @@ -131,11 +149,19 @@ x1 = pinv(A) * a @show norm(N'*a) @show norm(A*x1-a) -srt_bnd_verts = sort.(skeleton(boundary(supp),0)) -srt_prt_verts = sort.(skeleton(port,0)) -int_verts = submesh(skeleton(supp,0)) do vert - sort(vert) in srt_bnd_verts && return false - sort(vert) in srt_prt_verts && return false +# srt_bnd_verts = sort.(skeleton(boundary(supp),0)) +# srt_prt_verts = sort.(skeleton(port,0)) +# int_verts = submesh(skeleton(supp,0)) do vert +# sort(vert) in srt_bnd_verts && return false +# sort(vert) in srt_prt_verts && return false +# return true +# end + +in_bnd_verts = in(skeleton(boundary(supp),0)) +in_prt_verts = in(skeleton(port,0)) +int_verts = submesh(skeleton(supp,0)) do m,vert + in_bnd_verts(m,vert) && return false + in_prt_verts(m,vert) && return false return true end @show length(int_verts) @@ -146,9 +172,10 @@ grad_L0_int = BEAST.gradient(L0_int) @assert numfunctions(grad_L0_int) == numfunctions(L0_int) B = assemble(Id, grad_L0_int, Nd_int) -B2, store = BEAST.allocatestorage(Id, grad_L0_int, Nd_int, +freeze, store = BEAST.allocatestorage(Id, grad_L0_int, Nd_int, Val{:bandedstorage}, BEAST.LongDelays{:ignore}) BEAST.assemble_local_mixed!(Id, grad_L0_int, Nd_int, store) +B2 = freeze() @assert isapprox(B, B2, atol=1e-8) @show rank(B2) @@ -172,8 +199,9 @@ check_edge_values(EV) error("stop") -Y = BEAST.dual1forms(Tetrs, Faces) +Dir = boundary(Tetrs) +Y = BEAST.dual1forms(Tetrs, Faces, Dir) curlY = curl(Y) CC = assemble(Id, curlY, curlY) -Plotly.plot(svdvals(CC)) +Plotly.plot(svdvals(Matrix(CC))) diff --git a/examples/ex_dual1forms_bis.jl b/examples/ex_dual1forms_bis.jl index 2012a16f..fdafc17c 100644 --- a/examples/ex_dual1forms_bis.jl +++ b/examples/ex_dual1forms_bis.jl @@ -5,12 +5,12 @@ using LinearAlgebra const Id = BEAST.Identity() const Nx = BEAST.NCross() -function Base.in(mesh::CompScienceMeshes.AbstractMesh) - cells_mesh = sort.(mesh) - function f(cell) - sort(cell) in cells_mesh - end -end +# function Base.in(mesh::CompScienceMeshes.AbstractMesh) +# cells_mesh = sort.(mesh) +# function f(cell) +# sort(cell) in cells_mesh +# end +# end function add!(fn, x, space, idcs) for (m,bf) in enumerate(space.fns) @@ -150,7 +150,7 @@ Faces = submesh(!in(Bnd), CompScienceMeshes.skeleton_fast(Tetrs,2)) F = 396 Face = cells(Faces)[F] -pos = cartesian(center(chart(Faces, Face))) +pos = cartesian(center(chart(Faces, F))) idcs1 = v2t[Face[1],1:v2n[Face[1]]] idcs2 = v2t[Face[2],1:v2n[Face[2]]] @@ -180,7 +180,7 @@ port_edges = submesh(in(boundary(supp12)), port_edges) x0 = ones(length(port_edges)) / length(port_edges) for (i,edge) in enumerate(port_edges) tgt = tangents(center(chart(port_edges, edge)),1) - if dot(normal(chart(Faces, Face)), tgt) < 0 + if dot(normal(chart(Faces, F)), tgt) < 0 x0[i] *= -1 end end @@ -221,11 +221,11 @@ add!(fn, x2_int, Nd2_int, idcs2) add!(fn, x3_prt, Nd3_prt, idcs3) add!(fn, x3_int, Nd3_int, idcs3) -pos = cartesian(CompScienceMeshes.center(chart(Faces, Face))) +pos = cartesian(CompScienceMeshes.center(chart(Faces, F))) space = BEAST.NDLCCBasis(tetrs, [fn], [pos]) -tetrs, bnd, dir, v2t, v2n = BEAST.dual1forms_init(Tetrs, Dir) +tetrs, bnd, dir, v2t, v2n = BEAST.dualforms_init(Tetrs, Dir) D1 = BEAST.dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) P2 = BEAST.nedelecd3d(Tetrs, Faces) # S = BEAST.dual1forms(Tetrs, Faces[collect(396:405)], Dir) @@ -246,7 +246,7 @@ CDP = assemble(Id, D1, curl_P1) CPD = CDP' GDD = assemble(Id, D1, D1) -iM = inv(M) -A1 = CPD * inv(GDD) * CDP; +iM = inv(Matrix(M)) +A1 = CPD * inv(Matrix(GDD)) * CDP; A2 = assemble(Id, curl_P1, curl_P1) -A3 = CPD * iM' * inv(GDD) * iM * CDP; +A3 = CPD * iM' * inv(Matrix(GDD)) * iM * CDP; diff --git a/examples/ex_dual2forms.jl b/examples/ex_dual2forms.jl index e94825f8..2f980581 100644 --- a/examples/ex_dual2forms.jl +++ b/examples/ex_dual2forms.jl @@ -39,11 +39,14 @@ bnd = boundary(tetrs) neu = bnd dir = Mesh(vertices(bnd), CompScienceMeshes.celltype(bnd)[]) -srt_Dir = sort.(Dir) -Edges = submesh(skeleton(Tetrs,1)) do Edge - sort(Edge) in srt_Dir && return false - return true -end +# srt_Dir = sort.(Dir) +# Edges = submesh(skeleton(Tetrs,1)) do Edge +# sort(Edge) in srt_Dir && return false +# return true +# end + +Edges = submesh(!in(Dir), skeleton(Tetrs,1)) + # Edges = skeleton(Tetrs,1) # srt_bnd_Faces = sort.(boundary(Tetrs)) # srt_neu = sort.(neu) @@ -51,10 +54,12 @@ end # !(sort(Face) in srt_bnd_Faces) # end -srt_bnd_Nodes = sort.(skeleton(boundary(Tetrs),0)) -Nodes = submesh(skeleton(Tetrs,0)) do node - !(sort(node) in srt_bnd_Nodes) -end +# srt_bnd_Nodes = sort.(skeleton(boundary(Tetrs),0)) +# Nodes = submesh(skeleton(Tetrs,0)) do node +# !(sort(node) in srt_bnd_Nodes) +# end + +Nodes = submesh(!in(skeleton(boundary(Tetrs),0)), skeleton(Tetrs,0)) @show numcells(Edges) # @show length(Faces) @@ -66,13 +71,14 @@ end E = 1 Edge = cells(Edges)[E] -pos = cartesian(CompScienceMeshes.center(chart(Edges, Edge))) +pos = cartesian(CompScienceMeshes.center(chart(Edges, E))) # v = argmin(norm.(vertices(tetrs) .- Ref(pos))) -support1 = submesh(tetr -> Edge[1] in tetr, tetrs.mesh) -support2 = submesh(tetr -> Edge[2] in tetr, tetrs.mesh) +support1 = submesh((m,tetr) -> Edge[1] in CompScienceMeshes.indices(m,tetr), tetrs.mesh) +support2 = submesh((m,tetr) -> Edge[2] in CompScienceMeshes.indices(m,tetr), tetrs.mesh) # port = submesh(face -> v in face, boundary(support1)) # port2 = submesh(face -> v in face, boundary(support2)) -port = submesh(face -> sort(face) in sort.(boundary(support2)), boundary(support1)) +# port = submesh(face -> sort(face) in sort.(boundary(support2)), boundary(support1)) +port = submesh(in(boundary(support2)), boundary(support1)) # for p in port # @assert sort(p) in sort.(port2) # end @@ -85,24 +91,38 @@ support = CompScienceMeshes.union(support1, support2) length(skeleton(support,2)) + length(port) edges = skeleton(support,1) -cells_bnd_edges = [sort(c) for c in skeleton(boundary(support),1)] -cells_prt_edges = [sort(c) for c in skeleton(port,1)] -cells_dir_edges = [sort(c) for c in skeleton(boundary(tetrs),1)] +# cells_bnd_edges = [sort(c) for c in skeleton(boundary(support),1)] +# cells_prt_edges = [sort(c) for c in skeleton(port,1)] +# cells_dir_edges = [sort(c) for c in skeleton(boundary(tetrs),1)] bnd_support = boundary(support) # cells_bnd_tetrs = sort.(boundary(tetrs)) -srt_dir = sort.(dir) -dir_cap_bnd = submesh(face -> sort(face) in sort.(dir), bnd_support) -cells_bnd_dir_cap_bnd = sort.(boundary(dir_cap_bnd)) - -int_edges = submesh(edges) do edge - sort(edge) in cells_bnd_dir_cap_bnd && return false - sort(edge) in cells_dir_edges && return true - sort(edge) in cells_bnd_edges && return false - sort(edge) in cells_prt_edges && return false +# srt_dir = sort.(dir) +# dir_cap_bnd = submesh(face -> sort(face) in sort.(dir), bnd_support) +dir_cap_bnd = submesh(in(dir), bnd_support) +# cells_bnd_dir_cap_bnd = sort.(boundary(dir_cap_bnd)) + +in_bnd_dir_cap_bnd = in(boundary(dir_cap_bnd)) +in_dir_edges = in(skeleton(boundary(tetrs),1)) +in_bnd_edges = in(skeleton(boundary(support),1)) +in_prt_edges = in(skeleton(port,1)) + +int_edges = submesh(edges) do m,edge + in_bnd_dir_cap_bnd(m,edge) && return false + in_bnd_edges(m,edge) && return true + in_dir_edges(m,edge) && return false + in_prt_edges(m,edge) && return false return true end + +# int_edges = submesh(edges) do edge +# sort(edge) in cells_bnd_dir_cap_bnd && return false +# sort(edge) in cells_dir_edges && return true +# sort(edge) in cells_bnd_edges && return false +# sort(edge) in cells_prt_edges && return false +# return true +# end @show length(int_edges) Nd_int = BEAST.nedelecc3d(support, int_edges) @@ -118,16 +138,28 @@ Q2 = freeze() Q3 = assemble(Id, curl(Nd_int), curl(Nd_int)) rank(Q3) -cells_bnd_faces = [sort(c) for c in boundary(support)] -cells_prt_faces = [sort(c) for c in port] -srt_dir_cap_bnd = sort.(dir_cap_bnd) -int_faces = submesh(skeleton(support,2)) do face - # sort(face) in cells_bnd_tetrs && return true - sort(face) in srt_dir_cap_bnd && return true - sort(face) in cells_bnd_faces && return false - sort(face) in cells_prt_faces && return false +# cells_bnd_faces = [sort(c) for c in boundary(support)] +# cells_prt_faces = [sort(c) for c in port] +# srt_dir_cap_bnd = sort.(dir_cap_bnd) + +in_dir_cap_bnd = in(dir_cap_bnd) +in_bnd_faces = in(boundary(support)) +in_prt_faces = in(port) + +int_faces = submesh(skeleton(support,2)) do m,face + in_dir_cap_bnd(m,face) && return true + in_bnd_faces(m,face) && return false + in_prt_faces(m,face) && return false return true end + +# int_faces = submesh(skeleton(support,2)) do face +# # sort(face) in cells_bnd_tetrs && return true +# sort(face) in srt_dir_cap_bnd && return true +# sort(face) in cells_bnd_faces && return false +# sort(face) in cells_prt_faces && return false +# return true +# end @show length(int_faces) @assert length(int_faces) + length(boundary(support)) + length(port) == length(skeleton(support,2)) + length(dir_cap_bnd) diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl index f54b2bb0..664b21be 100644 --- a/examples/ex_duals.jl +++ b/examples/ex_duals.jl @@ -10,7 +10,7 @@ Faces = CompScienceMeshes.skeleton_fast(Tetrs, 2) Edges = CompScienceMeshes.skeleton_fast(Tetrs, 1) Nodes = CompScienceMeshes.skeleton_fast(Tetrs, 0) -hemi = submesh(Tetrs) do Tetr +hemi = submesh(Tetrs) do m,Tetr cartesian(CompScienceMeshes.center(chart(Tetrs, Tetr)))[3] < 0 end @@ -119,7 +119,7 @@ fcr, geo = facecurrents(eg, ncurl_primal1_hemi) Plotly.plot(patch(geo, norm.(fcr))) tetrs = geometry(dual1) -bary_hemi = submesh(tetrs) do tetr +bary_hemi = submesh(tetrs) do m,tetr cartesian(CompScienceMeshes.center(chart(tetrs, tetr)))[1] < 0 end diff --git a/examples/ex_fem.jl b/examples/ex_fem.jl index b4ee5a0f..79535964 100644 --- a/examples/ex_fem.jl +++ b/examples/ex_fem.jl @@ -1,6 +1,6 @@ using CompScienceMeshes using BEAST -using Makeitso +# using Makeitso using SparseArrays function isdivconforming(space) @@ -84,12 +84,13 @@ tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.15) bndry = boundary(tetrs) edges = skeleton(tetrs, 1) -bndry_edges = [sort(c) for c in cells(skeleton(bndry, 1))] -function is_interior(edge) - !(sort(edge) in bndry_edges) -end +# bndry_edges = [sort(c) for c in cells(skeleton(bndry, 1))] +# function is_interior(edge) +# !(sort(edge) in bndry_edges) +# end -interior_edges = submesh(is_interior, edges) +# interior_edges = submesh(is_interior, edges) +interior_edges = submesh(!in(skeleton(bndry,1)), edges) @assert numcells(interior_edges) + numcells(skeleton(bndry,1)) == numcells(edges) X = BEAST.nedelecc3d(tetrs, interior_edges) @@ -119,7 +120,7 @@ plot(norm.(vals2), m=2) # Inspect the value of a single basis function p = 120 -tet = chart(tetrs, cells(tetrs)[p]) +tet = chart(tetrs, p) nbd = neighborhood(tet, [0.5, 0.5, 0.0]) edg = simplex(tet.vertices[1], tet.vertices[2]) tgt = normalize(edg.vertices[2] - edg.vertices[1]) @@ -160,8 +161,9 @@ TF, Idcs = isdivconforming(ttXplus) @show length(Idcs) # error("stop") -@target Q ()->BEAST.dual2forms(tetrs, skeleton(tetrs,1), Dir) -Q = @make Q +# @target Q ()->BEAST.dual2forms(tetrs, skeleton(tetrs,1), Dir) +# Q = @make Q +Q = BEAST.dual2forms(tetrs, skeleton(tetrs,1), Dir) QXplus = assemble(Id, Q, Xplus) @@ -182,5 +184,5 @@ fcr3, geo3 = facecurrents(v, divergence(ttXplus)); # length(geometry(ttXplus)) == length(tetrs2) # # Conn = connectivity(tetrs1, tetrs2) -fcr, geo = facecurrents(u, tXhemi) +# fcr, geo = facecurrents(u, X) Plotly.plot(patch(geo, norm.(fcr))) diff --git a/examples/ex_restrict_space.jl b/examples/ex_restrict_space.jl index 80182d70..28b3aae9 100644 --- a/examples/ex_restrict_space.jl +++ b/examples/ex_restrict_space.jl @@ -2,7 +2,7 @@ using CompScienceMeshes using BEAST sphere = CompScienceMeshes.tetmeshsphere(1.0, 0.35) -hemi = submesh(tet -> cartesian(CompScienceMeshes.center(chart(sphere,tet)))[3] < 0, sphere) +hemi = submesh((m,tet) -> cartesian(CompScienceMeshes.center(chart(m,tet)))[3] < 0, sphere) X = BEAST.nedelecd3d(sphere) Y = BEAST.restrict(X, hemi) diff --git a/examples/helmholtz3d_neumann.jl b/examples/helmholtz3d_neumann.jl index 0df708b6..6dfaa5fc 100644 --- a/examples/helmholtz3d_neumann.jl +++ b/examples/helmholtz3d_neumann.jl @@ -30,14 +30,14 @@ fcr1, geo1 = facecurrents(x1, X) fcr2, geo2 = facecurrents(x2, X) using Plots -plot(title="Comparse 1st and 2nd kind eqs.") -plot!(norm.(fcr1),c=:blue,label="1st") -scatter!(norm.(fcr2),c=:red,label="2nd") +Plots.plot(title="Comparse 1st and 2nd kind eqs.") +Plots.plot!(norm.(fcr1),c=:blue,label="1st") +Plots.scatter!(norm.(fcr2),c=:red,label="2nd") import Plotly -Plotly.plot(patch(Γ, norm.(fcr1))) -Plotly.plot(patch(Γ, norm.(fcr2))) - +plt1 = Plotly.plot(patch(Γ, norm.(fcr1))) +plt2 = Plotly.plot(patch(Γ, norm.(fcr2))) +display([plt1 plt2]) ys = range(-2,2,length=200) zs = range(-2,2,length=200) diff --git a/examples/junction_classic.jl b/examples/junction_classic.jl index 0c96fa26..f5e77789 100644 --- a/examples/junction_classic.jl +++ b/examples/junction_classic.jl @@ -19,8 +19,8 @@ u = gmres(efie) Θ, Φ = range(0.0,stop=π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] -farfield = potential(MWFarField3D(κ*im), ffpoints, u, X) +farfield = potential(MWFarField3D(wavenumber=κ), ffpoints, u, X) using Plots using LinearAlgebra -plot(Θ,norm.(farfield)) +Plots.plot(Θ,norm.(farfield)) diff --git a/examples/mfie_monopolar.jl b/examples/mfie_monopolar.jl index 3452ce4b..21cf4816 100644 --- a/examples/mfie_monopolar.jl +++ b/examples/mfie_monopolar.jl @@ -29,9 +29,11 @@ fcr2, _ = facecurrents(u2, Z) using LinearAlgebra using Plots -plot(title="Compare current density") -plot!(norm.(fcr1), label="MxMIFE") -scatter!(norm.(fcr2), label="DGMFIE", c=:red) +Plots.plot(title="Compare current density") +Plots.plot!(norm.(fcr1), label="MxMIFE") +Plots.scatter!(norm.(fcr2), label="DGMFIE", c=:red) +u = u2 +X = Z include("utils/postproc.jl") include("utils/plotresults.jl") diff --git a/examples/mthelmholtz.jl b/examples/mthelmholtz.jl index cbd30b06..270fbe25 100644 --- a/examples/mthelmholtz.jl +++ b/examples/mthelmholtz.jl @@ -70,7 +70,7 @@ w1 = eigvals(Sxx) w2 = eigvals(Q) using Plots -plot() -scatter!(w1) -scatter!(w2) +Plots.plot() +Plots.scatter!(w1) +Plots.scatter!(w2) diff --git a/examples/mtjunction.jl b/examples/mtjunction.jl index 1b207ee2..125ed771 100644 --- a/examples/mtjunction.jl +++ b/examples/mtjunction.jl @@ -132,5 +132,5 @@ pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in ffd_mt = potential(MWFarField3D(wavenumber=κ), pts, u1, X) ffd_st = potential(MWFarField3D(wavenumber=κ), pts, ust, X123) -plot(norm.(ffd_mt)) -scatter!(norm.(ffd_st)) \ No newline at end of file +Plots.plot(norm.(ffd_mt)) +Plots.scatter!(norm.(ffd_st)) \ No newline at end of file diff --git a/examples/nitsche.jl b/examples/nitsche.jl index df99562c..d35e6cf7 100644 --- a/examples/nitsche.jl +++ b/examples/nitsche.jl @@ -21,9 +21,13 @@ in_interior2 = CompScienceMeshes.interior_tpredicate(Γ2) in_interior3 = CompScienceMeshes.interior_tpredicate(Γ3) on_junction = CompScienceMeshes.overlap_gpredicate(γ) -edges1 = skeleton(e -> in_interior1(e) || on_junction(chart(Γ1,e)), Γ1,1) -edges2 = skeleton(e -> in_interior2(e) || on_junction(chart(Γ2,e)), Γ2,1) -edges3 = skeleton(e -> in_interior3(e) || on_junction(chart(Γ3,e)), Γ3,1) +edges1 = submesh((m,e)->in_interior1(m,e) || on_junction(chart(m,e)), skeleton(Γ1,1)) +edges2 = submesh((m,e)->in_interior2(m,e) || on_junction(chart(m,e)), skeleton(Γ2,1)) +edges3 = submesh((m,e)->in_interior3(m,e) || on_junction(chart(m,e)), skeleton(Γ3,1)) + +# edges1 = skeleton(e -> in_interior1(e) || on_junction(chart(Γ1,e)), Γ1,1) +# edges2 = skeleton(e -> in_interior2(e) || on_junction(chart(Γ2,e)), Γ2,1) +# edges3 = skeleton(e -> in_interior3(e) || on_junction(chart(Γ3,e)), Γ3,1) X1 = raviartthomas(Γ1, edges1) X2 = raviartthomas(Γ2, edges2) @@ -40,7 +44,7 @@ u = solve(eq) Θ, Φ = range(0.0,stop=π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] -farfield = potential(MWFarField3D(κ*im), ffpoints, u, X) +farfield = potential(MWFarField3D(wavenumber=κ), ffpoints, u, X) using Plots using LinearAlgebra diff --git a/examples/planewave.jl b/examples/planewave.jl index d2c5071a..3721685d 100644 --- a/examples/planewave.jl +++ b/examples/planewave.jl @@ -19,4 +19,4 @@ fcr, geo = facecurrents(x, Y) using Plotly using LinearAlgebra -Plotly.plot(patch(geo.mesh, norm.(fcr), (0.0, 1.0))) +Plotly.plot(patch(geo.mesh, norm.(fcr); caxis=(0.0, 1.0))) diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 0c78f7fb..24f38d61 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -69,8 +69,8 @@ ffj = potential(MWFarField3D(κ*im, η), ffpoints, u[j], X) ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) using Plots -plot(xlabel="theta") -plot!(Θ,norm.(ff),label="far field",title="PMCHWT") +Plots.plot(xlabel="theta") +Plots.plot!(Θ,norm.(ff),label="far field",title="PMCHWT") error() #import Plotly @@ -114,14 +114,14 @@ E_in, H_in = fetch(task2) E_tot = E_in + E_ex H_tot = H_in + H_ex -contour(real.(getindex.(E_tot,1))) -contour(real.(getindex.(H_tot,2))) +Plots.contour(real.(getindex.(E_tot,1))) +Plots.contour(real.(getindex.(H_tot,2))) -heatmap(Z, Y, real.(getindex.(E_tot,1))) -heatmap(Z, Y, real.(getindex.(H_tot,2))) +Plots.heatmap(Z, Y, real.(getindex.(E_tot,1))) +Plots.heatmap(Z, Y, real.(getindex.(H_tot,2))) -plot(real.(getindex.(E_tot[:,51],1))) -plot!(real.(getindex.(H_tot[:,51],2))) +Plots.plot(real.(getindex.(E_tot[:,51],1))) +Plots.plot!(real.(getindex.(H_tot[:,51],2))) # Compare the far field and the field far @@ -130,6 +130,6 @@ E_far, H_far = nearfield(u[m],u[j],X,X,κ,η, ffradius .* ffpoints) nxE_far = cross.(ffpoints, E_far) * (4π*ffradius) / exp(-im*κ*ffradius) Et_far = -cross.(ffpoints, nxE_far) -plot() -plot!(Θ, norm.(ff),label="far field") -scatter!(Θ, norm.(Et_far), label="field far") +Plots.plot() +Plots.plot!(Θ, norm.(ff),label="far field") +Plots.scatter!(Θ, norm.(Et_far), label="field far") diff --git a/examples/projectors.jl b/examples/projectors.jl index 4809cf3c..9b726e2a 100644 --- a/examples/projectors.jl +++ b/examples/projectors.jl @@ -37,8 +37,9 @@ edges = setminus(skeleton(Γ,1), ∂Γ) verts = setminus(skeleton(Γ,0), skeleton(∂Γ,0)) Σ = Matrix(connectivity(Γ, edges, sign)) -Λ = Matrix(connectivity(Γ, verts, sign)) +Λ = Matrix(connectivity(verts, edges, sign)) +I = LinearAlgebra.I PΣ = Σ * pinv(Σ'*Σ) * Σ' PΛH = I - PΣ diff --git a/examples/tdefie_neumann.jl b/examples/tdefie_neumann.jl index f8a31ae4..3746417c 100644 --- a/examples/tdefie_neumann.jl +++ b/examples/tdefie_neumann.jl @@ -43,11 +43,7 @@ using Plotly Xefie, Δω, ω0 = fouriertransform(xefie, Δt, 0.0, 2) -<<<<<<< HEAD -ω = collect(ω0 + (0:Nt-1)*Δω) -======= ω = collect(ω0 .+ (0:Nt-1)*Δω) ->>>>>>> upstream/master _, i1 = findmin(abs.(ω.-sol)) ω1 = ω[i1] diff --git a/examples/utils/edge_values.jl b/examples/utils/edge_values.jl index cd3b20e1..5e7a03e1 100644 --- a/examples/utils/edge_values.jl +++ b/examples/utils/edge_values.jl @@ -8,17 +8,14 @@ function edge_values(space, m) Vals = zeros(scalartype(space), length(supp), length(edgs)) for (i,sh) in enumerate(space.fns[m]) tet_id = sh.cellid - tet = chart(supp, cells(supp)[tet_id]) + tet = chart(supp, tet_id) for k in nzrange(Conn, tet_id) edg_id = rowvals(Conn)[k] - # loc_id = abs(nonzeros(Conn)[k]) - # edge = CompScienceMeshes.edges(tet)[loc_id] - edge = chart(edgs, cells(edgs)[edg_id]) + edge = chart(edgs, edg_id) ctr = center(edge) tgt = tangents(ctr,1) ctr1 = neighborhood(tet, carttobary(tet, cartesian(ctr))) vals = rs(ctr1) - # @show vals val = vals[sh.refid].value Vals[tet_id, edg_id] += dot(sh.coeff * val, tgt) end diff --git a/examples/utils/plotresults.jl b/examples/utils/plotresults.jl index 972301e5..69c136bf 100644 --- a/examples/utils/plotresults.jl +++ b/examples/utils/plotresults.jl @@ -4,12 +4,12 @@ if plotresults postproc || error("Cannot plot results without going through post-processing.") @eval begin - using Plots + import Plots using LinearAlgebra - p1 = scatter(Θ, real.(norm.(ffd))) - p2 = heatmap(clamp.(real.(norm.(nfd)), 0.0, 2.0)) - p3 = contour(clamp.(real.(norm.(nfd)), 0.0, 2.0)) - plot(p1,p2,p3,layout=(3,1)) + p1 = Plots.scatter(Θ, real.(norm.(ffd))) + p2 = Plots.heatmap(clamp.(real.(norm.(nfd)), 0.0, 2.0)) + p3 = Plots.contour(clamp.(real.(norm.(nfd)), 0.0, 2.0)) + Plots.plot(p1,p2,p3,layout=(3,1)) end end diff --git a/src/bases/basis.jl b/src/bases/basis.jl index 03b96b48..c512c4ab 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -64,7 +64,7 @@ defaultquadstrat(op, tfs::DirectProductSpace, bfs::Space) = defaultquadstrat(op, # defaultquadstrat(op, tfs::DirectProductSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs.factors[1], bfs.factors[1]) # defaultquadstrat(op, tfs::RefSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs, bfs.factors[1]) # defaultquadstrat(op, tfs::DirectProductSpace, bfs::RefSpace) = defaultquadstrat(op, tfs.factors[1], bfs) - +# scalartype(sp::DirectProductSpace{T}) where {T} = T export cross, × diff --git a/src/bases/bcspace.jl b/src/bases/bcspace.jl index 83d76b3a..c2fc1395 100644 --- a/src/bases/bcspace.jl +++ b/src/bases/bcspace.jl @@ -280,7 +280,7 @@ function buildhalfbc(fine, S, v, p, onjunction, ibscaled) # This charge needs to be compensated by interior divergence total_charge = (!c_on_boundary || num_junctions == 2) ? 1 : 0 charges = if ibscaled - face_areas = [volume(chart(fine, cells(fine)[s])) for s in S] + face_areas = [volume(chart(fine, s)) for s in S] face_areas / sum(face_areas) else fill(total_charge/n, n) @@ -342,7 +342,7 @@ function buildhalfbc(fine, S, v, p, onjunction, ibscaled) face = cells(fine)[f] i = something(findfirst(==(v),face), 0) @assert i != 0 - ch = chart(fine, cells(fine)[f]) + ch = chart(fine, f) area = volume(ch) qps = quadpoints(ch, 3) @assert sum(w for (p,w) in qps) ≈ area @@ -362,7 +362,7 @@ function buildhalfbc(fine, S, v, p, onjunction, ibscaled) face = cells(fine)[f] i = something(findfirst(==(v), face), 0) @assert i != 0 - ch = chart(fine, face) + ch = chart(fine, f) area = volume(ch) vct = ch.vertices[mod1(i+2,3)] - ch.vertices[mod1(i+1,3)] γ += 0.25/area * dot(vct,vct) @@ -384,7 +384,7 @@ function buildhalfbc(fine, S, v, p, onjunction, ibscaled) face = cells(fine)[f] i = something(findfirst(==(v),face), 0) @assert i != 0 - ch = chart(fine, cells(fine)[f]) + ch = chart(fine, f) area = volume(ch) qps = quadpoints(ch, 3) @assert sum(w for (p,w) in qps) ≈ area diff --git a/src/bases/dual3d.jl b/src/bases/dual3d.jl index 725c1bf0..6d635fbf 100644 --- a/src/bases/dual3d.jl +++ b/src/bases/dual3d.jl @@ -42,7 +42,7 @@ function dual2forms_body(Edges, tetrs, bnd, dir, v2t, v2n) x0 = zeros(length(port_faces)) total_vol = sum(volume(chart(port_faces, fc)) for fc in port_faces) - tgt = tangents(center(chart(Edges, Edge)),1) + tgt = tangents(center(chart(Edges, F)),1) for (i,face) in enumerate(port_faces) chrt = chart(port_faces, face) nrm = normal(chrt) @@ -58,7 +58,7 @@ function dual2forms_body(Edges, tetrs, bnd, dir, v2t, v2n) x2_int, _, RT2_int = extend_2_form(supp2, dir2_faces, x0, port_faces) bfs[F] = Vector{Shape{T}}() - pos[F] = cartesian(center(chart(Edges,Edge))) + pos[F] = cartesian(center(chart(Edges,F))) addf!(bfs[F], x1_int, RT1_int, idcs1) addf!(bfs[F], x0, RT1_prt, idcs1) @@ -179,6 +179,7 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) num_threads = Threads.nthreads() Threads.@threads for F in 1:length(Faces) Face = Cells[F] + Chart = chart(Faces, F) myid = Threads.threadid() myid == 1 && F % 20 == 0 && @@ -220,7 +221,7 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) # Step 1: set port flux and extend to dual faces x0 = zeros(length(port_edges)) total_vol = sum(volume(chart(port_edges, edge)) for edge in port_edges) - nrm = normal(chart(Faces, Face)) + nrm = normal(Chart) for (i,edge) in enumerate(port_edges) cht = chart(port_edges, edge) tgt = tangents(center(cht),1) @@ -263,7 +264,7 @@ function dual1forms_body(Faces, tetrs, bnd, dir, v2t, v2n) addf!(fn, x3_prt, Nd3_prt, idcs3) addf!(fn, x3_int, Nd3_int, idcs3) - pos[F] = cartesian(CompScienceMeshes.center(chart(Faces, Face))) + pos[F] = cartesian(CompScienceMeshes.center(Chart)) bfs[F] = fn end diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 6759d2b3..59d12c99 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -543,7 +543,7 @@ function dualforms_init(Supp, Dir) v2t, v2n = CompScienceMeshes.vertextocellmap(tetrs) bnd = boundary(tetrs) gpred = CompScienceMeshes.overlap_gpredicate(Dir) - dir = submesh(face -> gpred(chart(bnd,face)), bnd) + dir = submesh((m,face) -> gpred(chart(m,face)), bnd) return tetrs, bnd, dir, v2t, v2n end diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 6785481c..ebf7d938 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -61,9 +61,7 @@ end function (f::LagrangeRefSpace{T,0,3})(t, ::Type{Val{:withcurl}}) where T i = one(T) z = zero(cartesian(t)) - ( - (value=i, curl=z,), - ) + SVector(((value=i, curl=z,),)) end diff --git a/src/bases/ndlcdspace.jl b/src/bases/ndlcdspace.jl index 419ef8b9..11d43b81 100644 --- a/src/bases/ndlcdspace.jl +++ b/src/bases/ndlcdspace.jl @@ -20,7 +20,7 @@ function nedelecd3d(mesh, faces) fns = Vector{Vector{Shape{T}}}(undef,num_faces) pos = Vector{P}(undef,num_faces) - for (i,face) in enumerate(cells(faces)) + for (i,face) in enumerate(faces) fns[i] = Vector{Shape{T}}() pos[i] = cartesian(center(chart(faces,face))) diff --git a/src/excitation.jl b/src/excitation.jl index dd1b4c87..c033b601 100644 --- a/src/excitation.jl +++ b/src/excitation.jl @@ -11,9 +11,10 @@ quadrule(fn::Functional, refs, p, cell, qd) = qd[1,p] Assemble the vector of test coefficients corresponding to functional `fn` and test functions `tfs`. """ -function assemble(field::Functional, tfs::Space{T}; quaddata=quaddata, quadrule=quadrule) where T +function assemble(field::Functional, tfs; quaddata=quaddata, quadrule=quadrule) - b = zeros(Complex{T}, numfunctions(tfs)) + R = scalartype(tfs) + b = zeros(Complex{R}, numfunctions(tfs)) store(v,m) = (b[m] += v) assemble!(field, tfs, store, quaddata=quaddata, quadrule=quadrule) return b diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 7df6e2d0..9253b1a2 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -28,19 +28,6 @@ function kernelvals(biop::MaxwellOperator3D, p, q) KernelValsMaxwell3D(γ, r, R, green, gradgreen) end -# function kernelvals(kernel::MaxwellOperator3DReg, p, q) - -# γ = kernel.gamma -# r = p.cart - q.cart -# R = norm(r) -# γR = γ*R -# P=typeof(γ) -# Exp = exp(-γ*R) -# green = (Exp - 1 + γR - 0.5*γR^2) / (4pi*R) -# gradgreen = ( - (γR + 1)*Exp + (1 - 0.5*γR^2) ) * (r/R^3) / (4π) - -# KernelValsMaxwell3D(γ, r, R, P(green), gradgreen) -# end struct MWSingleLayer3D{T,U} <: MaxwellOperator3D gamma::T @@ -88,7 +75,9 @@ defaultquadstrat(op::MaxwellOperator3D, tfs::RefSpace, bfs::RefSpace) = DoubleNu function quaddata(op::MaxwellOperator3D, test_local_space::RefSpace, trial_local_space::RefSpace, test_charts, trial_charts, qs::DoubleNumWiltonSauterQStrat) + T = coordtype(test_charts[1]) + tqd = quadpoints(test_local_space, test_charts, (qs.outer_rule_far,qs.outer_rule_near)) bqd = quadpoints(trial_local_space, trial_charts, (qs.inner_rule_far,qs.inner_rule_near)) @@ -97,37 +86,9 @@ function quaddata(op::MaxwellOperator3D, convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_edge,0,1)), convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_face,0,1)),) - - # High accuracy rules (use them e.g. in LF MFIE scenarios) - # tqd = quadpoints(test_local_space, test_charts, (8,8)) - # bqd = quadpoints(trial_local_space, trial_charts, (8,9)) - # leg = (_legendre(8,a,b), _legendre(10,a,b), _legendre(5,a,b),) - - return (tpoints=tqd, bpoints=bqd, gausslegendre=leg) end -# use Union type so this code can be shared between the operator -# and its regular part. -# MWSL3DGen = Union{MWSingleLayer3D,MWSingleLayer3DReg} -# MWSL3DGen = Union{MWSingleLayer3DReg} -# function integrand(biop::MWSL3DGen, kerneldata, tvals, tgeo, bvals, bgeo) - -# gx = tvals[1] -# fy = bvals[1] - -# dgx = tvals[2] -# dfy = bvals[2] - -# G = kerneldata.green -# γ = kerneldata.gamma - -# α = biop.α -# β = biop.β - -# t = (α * dot(gx, fy) + β * (dgx*dfy)) * G -# end - struct MWDoubleLayer3D{T} <: MaxwellOperator3D gamma::T end @@ -143,21 +104,9 @@ end regularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DReg(op.gamma) singularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DSng(op.gamma) -# const MWDL3DGen = Union{MWDoubleLayer3D,MWDoubleLayer3DReg} -# const MWDL3DGen = Union{MWDoubleLayer3DReg} -# function integrand(biop::MWDL3DGen, kerneldata, tvals, tgeo, bvals, bgeo) -# g = tvals[1] -# f = bvals[1] -# ∇G = kerneldata.gradgreen -# (f × g) ⋅ ∇G -# end - - -# quadrule(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd) = qrss(op, g, f, i, τ, j, σ, qd) - -# function qrss(op, g, f, i, τ, j, σ, qd) function quadrule(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd, qs::DoubleNumWiltonSauterQStrat) + T = eltype(eltype(τ.vertices)) hits = 0 dtol = 1.0e3 * eps(T) @@ -208,11 +157,6 @@ function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::BDMRefSpace, i, τ, h2 = volume(σ) xtol2 = 0.2 * 0.2 k2 = abs2(op.gamma) - # max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( - # qd.tpoints[2,i], - # DoubleQuadRule( - # qd.tpoints[2,i], - # qd.bpoints[2,j],),) return DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j],) @@ -220,10 +164,8 @@ end function qrib(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd) - # defines coincidence of points - dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) - # decides on whether to use singularity extraction + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) xtol = 0.2 k = norm(op.gamma) @@ -241,21 +183,21 @@ function qrib(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, end end - hits == 3 && return BogaertSelfPatchStrategy(5) - hits == 2 && return BogaertEdgePatchStrategy(8, 4) - hits == 1 && return BogaertPointPatchStrategy(2, 3) - rmin = xmin/k - xmin < xtol && return WiltonSERule( - qd.tpoints[1,i], - DoubleQuadRule( - qd.tpoints[2,i], - qd.bpoints[2,j], - ), - ) - return DoubleQuadRule( - qd.tpoints[1,i], - qd.bpoints[1,j], - ) + hits == 3 && return BogaertSelfPatchStrategy(5) + hits == 2 && return BogaertEdgePatchStrategy(8, 4) + hits == 1 && return BogaertPointPatchStrategy(2, 3) + rmin = xmin/k + xmin < xtol && return WiltonSERule( + qd.tpoints[1,i], + DoubleQuadRule( + qd.tpoints[2,i], + qd.bpoints[2,j], + ), + ) + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j], + ) end diff --git a/src/maxwell/nxdbllayer.jl b/src/maxwell/nxdbllayer.jl index 8c691499..5c2ef273 100644 --- a/src/maxwell/nxdbllayer.jl +++ b/src/maxwell/nxdbllayer.jl @@ -34,6 +34,33 @@ function quadrule(operator::DoubleLayerRotatedMW3D, ) end +# TODO: this method needs to go and dispatch needs to be dealt with using the defaultquadstrat mechanism +function quadrule(op::DoubleLayerRotatedMW3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd, + qs::DoubleNumWiltonSauterQStrat) + + hits = 0 + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) + dmin2 = floatmax(eltype(eltype(τ.vertices))) + for t in τ.vertices + for s in σ.vertices + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < dtol) + end + end + + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) + hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) + hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + + h2 = volume(σ) + xtol2 = 0.2 * 0.2 + k2 = abs2(op.gamma) + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j],) +end + function integrand(op::DoubleLayerRotatedMW3D, kernel_vals, test_vals, test_nbd, trial_vals, trial_nbd) n = normal(test_nbd) diff --git a/src/operator.jl b/src/operator.jl index 4f88c62a..06a878f8 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -19,7 +19,7 @@ mutable struct TransposedOperator <: Operator end scalartype(op::TransposedOperator) = scalartype(op.op) -defaultquadstrat(op::TransposedOperator, tfs, bfs) = defaultquadstrat(op.op, tfs, bfs) +defaultquadstrat(op::TransposedOperator, tfs::Space, bfs::Space) = defaultquadstrat(op.op, tfs, bfs) mutable struct LinearCombinationOfOperators{T} <: AbstractOperator coeffs::Vector{T} diff --git a/src/postproc.jl b/src/postproc.jl index fbc3d94a..2b8fe605 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -129,7 +129,8 @@ end function potential(op, points, coeffs, basis; type=SVector{3,ComplexF64}) # T = SVector{3,ComplexF64} - T = SVector{3,eltype(coeffs)} + # T = SVector{3,eltype(coeffs)} + T = type ff = zeros(T, size(points)) store(v,m,n) = (ff[m] += v*coeffs[n]) potential!(store, op, points, basis, type=T) diff --git a/src/solvers/lusolver.jl b/src/solvers/lusolver.jl index ce728402..1b2a15fc 100644 --- a/src/solvers/lusolver.jl +++ b/src/solvers/lusolver.jl @@ -38,12 +38,18 @@ function solve(eq) b = assemble(rhs, test_space_dict) Z = assemble(lhs, test_space_dict, trial_space_dict) - M = convert_to_dense(Z) - println("Sysmatrix converted to dense.") + # M = convert_to_dense(Z) + # println("Sysmatrix converted to dense.") - u = Matrix(M) \ Vector(b) + print("Converting system to Matrix...") + M = Matrix(Z) + println("done.") - return PseudoBlockVector(u, blocksizes(M,2)) + print("LU solution of the linear system...") + u = M \ Vector(b) + println("done.") + + return PseudoBlockVector(u, blocksizes(Z,2)) end From ab208736e1eaa7b961403e2fe9d3167ca5841d9f Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 29 Sep 2022 17:09:52 +0200 Subject: [PATCH 183/528] Update version to v1.6.0 --- Project.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index b86f636d..f412c214 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.5.0" +version = "1.6.0" [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" @@ -30,7 +30,7 @@ WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" CollisionDetection = "0.1" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.3.3" +CompScienceMeshes = "0.4" Compat = "2, 3, 4" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4" @@ -38,7 +38,7 @@ FillArrays = "0.11, 0.12, 0.13" IterativeSolvers = "0.9" LiftedMaps = "0.4.1" LinearMaps = "3.7" -SauterSchwab3D = "0.1.0" +SauterSchwab3D = "0.1" SauterSchwabQuadrature = "2.1.3" SparseMatrixDicts = "0.2" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" From ea2f8b52b8eb8af9ec9d4c731b6367c5beba8d13 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 3 Oct 2022 10:43:25 +0200 Subject: [PATCH 184/528] increase compat req for SSQ --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index f412c214..c226ee14 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.6.0" +version = "1.6.0-dev" [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" @@ -39,7 +39,7 @@ IterativeSolvers = "0.9" LiftedMaps = "0.4.1" LinearMaps = "3.7" SauterSchwab3D = "0.1" -SauterSchwabQuadrature = "2.1.3" +SauterSchwabQuadrature = "2.2.0" SparseMatrixDicts = "0.2" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" From 3b822f891e15215c572bd9ce846a97c188474331 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 3 Oct 2022 13:30:25 +0200 Subject: [PATCH 185/528] increased compat req for CSM and CD to use uptodate Compat.jl --- Project.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index c226ee14..36ffbd5b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.6.0-dev" +version = "1.6.0" [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" @@ -28,9 +28,9 @@ WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" -CollisionDetection = "0.1" +CollisionDetection = "0.1.5" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.4" +CompScienceMeshes = "0.4.1" Compat = "2, 3, 4" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4" From 55dce174b9bf1965cca37704aa0cca98883de6e1 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Tue, 14 Dec 2021 15:27:59 +0100 Subject: [PATCH 186/528] dsvie --- examples/dsvie.jl | 73 ++++++ examples/pmchwt.jl | 9 +- src/BEAST.jl | 4 + src/operator.jl | 9 +- src/volumeintegral/sauterschwab_ints.jl | 102 +++++++- src/volumeintegral/vsie.jl | 236 +++++++++++++++++++ src/volumeintegral/vsieops.jl | 300 ++++++++++++++++++++++++ 7 files changed, 724 insertions(+), 9 deletions(-) create mode 100644 examples/dsvie.jl create mode 100644 src/volumeintegral/vsie.jl create mode 100644 src/volumeintegral/vsieops.jl diff --git a/examples/dsvie.jl b/examples/dsvie.jl new file mode 100644 index 00000000..ebb0ea10 --- /dev/null +++ b/examples/dsvie.jl @@ -0,0 +1,73 @@ +using CompScienceMeshes, BEAST +using LinearAlgebra +using Profile +using StaticArrays + + + +ntrc = X->ntrace(X,Γ) + +T = tetmeshsphere(1.0,0.5) +X = nedelecd3d(T) +Γ = boundary(T) +Y = raviartthomas(Γ) + +@show numfunctions(X) +@show numfunctions(Y) + + +κ, η = 1.0, 1.0 +κ′, η′ = √2.0κ, η/√2.0 +ϵ_r =2.0 + + +χ = x->(1.0-1.0/ϵ_r) + +#Volume-Volume +L,I,B = VIE.singlelayer(wavenumber=κ', tau=χ), Identity(), VIE.boundary(wavenumber=κ', tau=χ) +#Volume-Surface +Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ', tau=χ)), transpose(VSIE.boundary(wavenumber=κ', tau=χ)), transpose(VSIE.doublelayer(wavenumber=κ', tau=χ)) +Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ'), VSIE.boundary(wavenumber=κ'), VSIE.doublelayer(wavenumber=κ') +#Surface-Surface +T = Maxwell3D.singlelayer(wavenumber=κ) #Outside +T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside +K = Maxwell3D.doublelayer(wavenumber=κ) #Outside +K′ = Maxwell3D.doublelayer(wavenumber=κ′) #Inside + +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +H = -1/(im*κ*η)*curl(E) + +e, h = (n × E) × n, (n × H) × n + +@hilbertspace D j m +@hilbertspace k l o +β = 1.0/ϵ_r +α, α′ = 1/η, 1/η′ + +eq = @varform (β*I[k,D]-L[k,D]-B[ntrc(k),D] + Lt[k,j]+Bt[ntrc(k),j] + Kt[k,m] + + Ls[l,D]+Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + + Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) + + +dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y + +u = solve(dvsie) + + +#Post processing +Θ, Φ = range(0.0,stop=2π,length=100), 0.0 +ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] + +# Don't forget the far field comprises two contributions +ffm = potential(MWFarField3D(κ*im), ffpoints, u[m], Y) +ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], Y) +ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) + + +ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u[D], X) +ff2 = im*κ*ffd + +using Plots +plot(xlabel="theta") +plot!(Θ,norm.(ff),label="far field",title="DVSIE") +plot!(Θ,norm.(ff2),label="far field",title="DVSIE 2") diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 24f38d61..d5b6f363 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -72,7 +72,7 @@ using Plots Plots.plot(xlabel="theta") Plots.plot!(Θ,norm.(ff),label="far field",title="PMCHWT") -error() +#= #import Plotly #using LinearAlgebra #fcrj, _ = facecurrents(u[j],X) @@ -130,6 +130,7 @@ E_far, H_far = nearfield(u[m],u[j],X,X,κ,η, ffradius .* ffpoints) nxE_far = cross.(ffpoints, E_far) * (4π*ffradius) / exp(-im*κ*ffradius) Et_far = -cross.(ffpoints, nxE_far) -Plots.plot() -Plots.plot!(Θ, norm.(ff),label="far field") -Plots.scatter!(Θ, norm.(Et_far), label="field far") +plot() +plot!(Θ, norm.(ff),label="far field") +scatter!(Θ, norm.(Et_far), label="field far") +=# diff --git a/src/BEAST.jl b/src/BEAST.jl index caec9dbe..94c3fe52 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -71,6 +71,7 @@ export DoubleLayerRotatedMW3D export MWSingleLayerPotential3D export VIEOperator +export VSIEOperator export gmres export @hilbertspace, @varform, @discretise @@ -212,8 +213,11 @@ include("volumeintegral/vie.jl") include("volumeintegral/vieexc.jl") include("volumeintegral/vieops.jl") include("volumeintegral/farfield.jl") +include("volumeintegral/vsie.jl") +include("volumeintegral/vsieops.jl") include("volumeintegral/sauterschwab_ints.jl") + include("decoupled/dpops.jl") include("decoupled/potentials.jl") diff --git a/src/operator.jl b/src/operator.jl index 06a878f8..c4e4893f 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -202,7 +202,8 @@ end -function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, store; +function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, store, + threading::Type{Threading{:multi}} = Threading{:multi}; quadstrat=defaultquadstrat(op, tfs, bfs)) store1(v,m,n) = store(v,n,m) @@ -228,7 +229,7 @@ function assemble!(op::Operator, tfs::DirectProductSpace, bfs::Space, store, thr for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) store1(v,m,n) = store(v,m + I[i], n) - assemble!(op, s, bfs, store1; quadstrat) + assemble!(op, s, bfs, store1, threading; quadstrat) end end @@ -239,7 +240,7 @@ function assemble!(op::Operator, tfs::Space, bfs::DirectProductSpace, store, thr for s in bfs.factors push!(J, last(J) + numfunctions(s)) end for (j,s) in enumerate(bfs.factors) store1(v,m,n) = store(v,m,n + J[j]) - assemble!(op, tfs, s, store1; quadstrat) + assemble!(op, tfs, s, store1, threading; quadstrat) end end @@ -249,7 +250,7 @@ function assemble!(op::Operator, tfs::DirectProductSpace, bfs::DirectProductSpac for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) store1(v,m,n) = store(v,m + I[i],n) - assemble!(op, s, bfs, store1; quadstrat) + assemble!(op, s, bfs, store1, threading; quadstrat) end end diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index b62b3e7f..2746604c 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -71,7 +71,7 @@ function reorder_dof(space::LagrangeRefSpace{Float64,0,3,1},I) return SVector{1,Int64}(1),SVector{1,Int64}(1) end - +#Method of moments volume integrals operators function momintegrals!(op::VIEOperator, test_local_space::RefSpace, trial_local_space::RefSpace, test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) @@ -168,3 +168,103 @@ function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::Do return z end + + +#Method of moments volume surface integrals operators +function momintegrals!(op::VSIEOperator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) + + #Find permutation of vertices to match location of singularity to SauterSchwab + J, I= SauterSchwab3D.reorder(strat.sing) + + #Get permutation and rel. orientatio of DoFs + K,O1 = reorder_dof(test_local_space, I) + L,O2 = reorder_dof(trial_local_space, J) + #Apply permuation to elements + + if length(I) == 4 + test_tetrahedron_element = simplex( + test_tetrahedron_element.vertices[I[1]], + test_tetrahedron_element.vertices[I[2]], + test_tetrahedron_element.vertices[I[3]], + test_tetrahedron_element.vertices[I[4]]) + elseif length(I) == 3 + test_tetrahedron_element = simplex( + test_tetrahedron_element.vertices[I[1]], + test_tetrahedron_element.vertices[I[2]], + test_tetrahedron_element.vertices[I[3]]) + end + + #test_tetrahedron_element = simplex(test_tetrahedron_element.vertices[I]...) + + if length(J) == 4 + trial_tetrahedron_element = simplex( + trial_tetrahedron_element.vertices[J[1]], + trial_tetrahedron_element.vertices[J[2]], + trial_tetrahedron_element.vertices[J[3]], + trial_tetrahedron_element.vertices[J[4]]) + elseif length(J) == 3 + trial_tetrahedron_element = simplex( + trial_tetrahedron_element.vertices[J[1]], + trial_tetrahedron_element.vertices[J[2]], + trial_tetrahedron_element.vertices[J[3]]) + end + + #trial_tetrahedron_element = simplex(trial_tetrahedron_element.vertices[J]...) + + #Define integral (returns a function that only needs barycentric coordinates) + igd = VSIEIntegrand(test_tetrahedron_element, trial_tetrahedron_element, + op, test_local_space, trial_local_space) + + #Evaluate integral + Q = SauterSchwab3D.sauterschwab_parameterized(igd, strat) + + #Undo permuation on DoFs + for j in 1 : length(L) + for i in 1 : length(K) + out[i,j] += Q[K[i],L[j]]*O1[i]*O2[j] + end + end + nothing +end + +function momintegrals!(biop::VSIEOperator, tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) + + # memory allocation here is a result from the type instability on strat + # which is on purpose, i.e. the momintegrals! method is chosen based + # on dynamic polymorphism. + womps = strat.outer_quad_points + wimps = strat.inner_quad_points + + M, N = size(z) + + for womp in womps + tgeo = womp.point + tvals = womp.value + jx = womp.weight + + for wimp in wimps + bgeo = wimp.point + bvals = wimp.value + jy = wimp.weight + + j = jx * jy + kernel = kernelvals(biop, tgeo, bgeo) + + + igd = integrand(biop, kernel, tvals, tgeo, bvals, bgeo) + + for m in 1 : length(tvals) + + for n in 1 : length(bvals) + + z[m,n] += j * igd[m,n] + end + end + end + end + + return z +end + diff --git a/src/volumeintegral/vsie.jl b/src/volumeintegral/vsie.jl new file mode 100644 index 00000000..b0dcc2b4 --- /dev/null +++ b/src/volumeintegral/vsie.jl @@ -0,0 +1,236 @@ +module VSIE + using ..BEAST + Mod = BEAST + + """ + singlelayer(;gamma, alpha, beta, tau) + singlelayer(;wavenumber, alpha, beta, tau) + + Bilinear form given by: + + ```math + α ∬_{Γ×Ω} j(x)⋅τ(y)⋅k(y) G_{γ}(x,y) + β ∬_{Γ×Ω} div j(x) τ(y) div k(y) G_{γ}(x,y) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast dyadic + """ + + function singlelayer(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = wavenumber*wavenumber) + beta == nothing && (beta = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIESingleLayer(gamma, alpha, beta, tau) + end + + function singlelayerT(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = wavenumber*wavenumber) + beta == nothing && (beta = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIESingleLayerT(gamma, alpha, beta, tau) + end + + """ + boundary(;gamma, alpha, tau) + boundary(;wavenumber, alpha, tau) + + Bilinear form given by: + + ```math + α ∬_{Γ×Ω} T_n j(x) grad G_{γ}(x,y)⋅τ(y) div k(y) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast dyadic + """ + + function boundary(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIEBoundary(gamma, alpha, tau) + end + + function boundaryT(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIEBoundaryT(gamma, alpha, tau) + end + + + + """ + doublelayer(;gamma, alpha, beta, tau) + doublelayer(;wavenumber, alpha, beta, tau) + + Bilinear form given by: + + ```math + α ∬_{Ω×Γ} j(x) ⋅ grad G_{γ}(x,y) × τ(y)⋅k(y) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast dyadic + """ + + function doublelayer(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIEDoubleLayer(gamma, alpha, tau) + end + + + function doublelayerT(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIEDoubleLayerT(gamma, alpha, tau) + end + +end \ No newline at end of file diff --git a/src/volumeintegral/vsieops.jl b/src/volumeintegral/vsieops.jl new file mode 100644 index 00000000..1235cd7c --- /dev/null +++ b/src/volumeintegral/vsieops.jl @@ -0,0 +1,300 @@ +abstract type VSIEOperator <: IntegralOperator end +abstract type VolumeSurfaceOperator <: VSIEOperator end +abstract type BoundarySurfaceOperator <: VSIEOperator end + +struct KernelValsVSIE{T,U,P,Q,K} + gamma::U + vect::P + dist::T + green::U + gradgreen::Q + tau::K +end + +function kernelvals(viop::VSIEOperator, p ,q) + Y = viop.gamma; + r = cartesian(p)-cartesian(q) + R = norm(r) + yR = Y*R + + expn = exp(-yR) + green = expn / (4*pi*R) + gradgreen = - (Y +1/R)*green/R*r + + + tau = viop.tau(cartesian(q)) + + KernelValsVSIE(Y,r,R, green, gradgreen,tau) +end + +struct VSIESingleLayer{T,U,P} <: VolumeSurfaceOperator + gamma::T + α::U + β::U + tau::P +end + +struct VSIEBoundary{T,U,P} <: BoundarySurfaceOperator + gamma::T + α::U + tau::P +end + +struct VSIEDoubleLayer{T,U,P} <: VolumeSurfaceOperator + gamma::T + α::U + tau::P +end + + +struct VSIESingleLayerT{T,U,P} <: VolumeSurfaceOperator + gamma::T + α::U + β::U + tau::P +end + +struct VSIEBoundaryT{T,U,P} <: BoundarySurfaceOperator + gamma::T + α::U + tau::P +end + +struct VSIEDoubleLayerT{T,U,P} <: VolumeSurfaceOperator + gamma::T + α::U + tau::P +end + +scalartype(op::VSIEOperator) = typeof(op.gamma) + +export VSIE + +struct VSIEIntegrand{S,T,O,K,L} + test_tetrahedron_element::S + trial_triangle_element::T + op::O + test_local_space::K + trial_local_space::L +end + + +function (igd::VSIEIntegrand)(u,v) + + #mesh points + tgeo = neighborhood(igd.test_tetrahedron_element,v) + bgeo = neighborhood(igd.trial_triangle_element,u) + + #kernel values + kerneldata = kernelvals(igd.op,tgeo,bgeo) + + #values & grad/div/curl of local shape functions + tval = igd.test_local_space(tgeo) + bval = igd.trial_local_space(bgeo) + + #jacobian + j = jacobian(tgeo) * jacobian(bgeo) + + integrand(igd.op, kerneldata,tval,tgeo,bval,tgeo) * j +end + + +function integrand(viop::VSIESingleLayer, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:3] + fy = @SVector[bvals[i].value for i in 1:4] + + dgx = @SVector[tvals[i].divergence for i in 1:3] + dfy = @SVector[bvals[i].divergence for i in 1:4] + + G = kerneldata.green + + Ty = kerneldata.tau + + α = viop.α + β = viop.β + + @SMatrix[α * dot(gx[i],Ty*fy[j]) * G + β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] +end + +function integrand(viop::VSIEBoundary, kerneldata, tvals, tgeo, bvals, bgeo) + + dgx = @SVector[tvals[i].divergence for i in 1:3] + fy = @SVector[bvals[i].value for i in 1:1] + + G = kerneldata.green + + Tx = kerneldata.tau + + α = viop.α + + @SMatrix[α * Tx * dgx[i] * fy[j] * G for i in 1:3, j in 1:1] +end + +function integrand(viop::VSIESingleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:4] + fy = @SVector[bvals[i].value for i in 1:3] + + dgx = @SVector[tvals[i].divergence for i in 1:4] + dfy = @SVector[bvals[i].divergence for i in 1:3] + + G = kerneldata.green + + Ty = kerneldata.tau + + α = viop.α + β = viop.β + + @SMatrix[α * dot(gx[i],Ty*fy[j]) * G + β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] +end + + +function integrand(viop::VSIEBoundaryT, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:1] + dfy = @SVector[bvals[i].divergence for i in 1:3] + + G = kerneldata.green + + Tx = kerneldata.tau + + α = viop.α + + @SMatrix[α * Tx * gx[i] * dfy[j] * G for i in 1:3, j in 1:1] +end + + +function integrand(viop::VSIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) + gx = @SVector[tvals[i].value for i in 1:3] + fy = @SVector[bvals[i].value for i in 1:4] + + gradG = kerneldata.gradgreen + + Ty = kerneldata.tau + + α = viop.α + + @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:3, j in 1:4] +end + +function integrand(viop::VSIEDoubleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) + gx = @SVector[tvals[i].value for i in 1:4] + fy = @SVector[bvals[i].value for i in 1:3] + + gradG = kerneldata.gradgreen + + Ty = kerneldata.tau + + α = viop.α + + @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:4, j in 1:3] +end + + +defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) + + +function quaddata(op::VSIEOperator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_charts, trial_charts, qs::SauterSchwab3DQStrat) + + #The combinations of rules (6,7) and (5,7 are) BAAAADDDD + # they result in many near singularity evaluations with any + # resemblence of accuracy going down the drain! Simply don't! + # (same for (5,7) btw...). + t_qp = quadpoints(test_local_space, test_charts, (qs.outer_rule)) + b_qp = quadpoints(trial_local_space, trial_charts, (qs.inner_rule)) + + + sing_qp = (SauterSchwab3D._legendre(qs.sauter_schwab_1D,0,1), + SauterSchwab3D._shunnham2D(qs.sauter_schwab_2D), + SauterSchwab3D._shunnham3D(qs.sauter_schwab_3D), + SauterSchwab3D._shunnham4D(qs.sauter_schwab_4D),) + + + return (tpoints=t_qp, bpoints=b_qp, sing_qp=sing_qp) +end + +quadrule(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs) = qr_volume(op, g, f, i, τ, j, σ, qd, qs) + + +function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, + qs::SauterSchwab3DQStrat) + + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) + + hits = 0 + idx_t = Int64[] + idx_s = Int64[] + sizehint!(idx_t,4) + sizehint!(idx_s,4) + dmin2 = floatmax(eltype(eltype(τ.vertices))) + D = dimension(τ)+dimension(σ) + for (i,t) in enumerate(τ.vertices) + for (j,s) in enumerate(σ.vertices) + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + if d2 < dtol + push!(idx_t,i) + push!(idx_s,j) + hits +=1 + break + end + end + end + + #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) + + hits == 3 && return SauterSchwab3D.CommonFace5D_S(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) + hits == 2 && return SauterSchwab3D.CommonEdge5D_S(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) + hits == 1 && return SauterSchwab3D.CommonVertex5D_S(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),(qd.sing_qp[3],qd.sing_qp[2])) + + + return DoubleQuadRule( + qd[1][1,i], + qd[2][1,j]) + +end + +quadrule(op::BoundarySurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs) = qr_boundary(op, g, f, i, τ, j, σ, qd, qs) + +function qr_boundary(op::BoundarySurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, + qs::SauterSchwab3DQStrat) + + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) + + hits = 0 + idx_t = Int64[] + idx_s = Int64[] + sizehint!(idx_t,4) + sizehint!(idx_s,4) + dmin2 = floatmax(eltype(eltype(τ.vertices))) + D = dimension(τ)+dimension(σ) + for (i,t) in enumerate(τ.vertices) + for (j,s) in enumerate(σ.vertices) + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + if d2 < dtol + push!(idx_t,i) + push!(idx_s,j) + hits +=1 + break + end + end + end + + #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) + + + hits == 3 && return SauterSchwab3D.CommonFace4D_S(SauterSchwab3D.Singularity4DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[3])) + hits == 2 && return SauterSchwab3D.CommonEdge4D_S(SauterSchwab3D.Singularity4DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2])) + hits == 1 && return SauterSchwab3D.CommonVertex4D_S(SauterSchwab3D.Singularity4DPoint(idx_t,idx_s),(qd.sing_qp[2])) + + + return DoubleQuadRule( + qd[1][1,i], + qd[2][1,j]) + +end + From f5bcfd471acd246c9250f293c68fff69f6abbe94 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Sat, 18 Dec 2021 15:43:34 +0100 Subject: [PATCH 187/528] update vsie implementation --- examples/dsvie.jl | 38 +++++++++++++++++++++++------------ examples/dvie.jl | 7 ++++--- examples/pmchwt.jl | 7 +++---- src/volumeintegral/vieops.jl | 2 +- src/volumeintegral/vsie.jl | 4 ++-- src/volumeintegral/vsieops.jl | 2 +- 6 files changed, 36 insertions(+), 24 deletions(-) diff --git a/examples/dsvie.jl b/examples/dsvie.jl index ebb0ea10..fa12c2ef 100644 --- a/examples/dsvie.jl +++ b/examples/dsvie.jl @@ -7,7 +7,7 @@ using StaticArrays ntrc = X->ntrace(X,Γ) -T = tetmeshsphere(1.0,0.5) +T = tetmeshsphere(1.0,0.25) X = nedelecd3d(T) Γ = boundary(T) Y = raviartthomas(Γ) @@ -18,7 +18,8 @@ Y = raviartthomas(Γ) κ, η = 1.0, 1.0 κ′, η′ = √2.0κ, η/√2.0 -ϵ_r =2.0 +ϵ_r =3.0 +ϵ_b =2.0 χ = x->(1.0-1.0/ϵ_r) @@ -26,8 +27,8 @@ Y = raviartthomas(Γ) #Volume-Volume L,I,B = VIE.singlelayer(wavenumber=κ', tau=χ), Identity(), VIE.boundary(wavenumber=κ', tau=χ) #Volume-Surface -Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ', tau=χ)), transpose(VSIE.boundary(wavenumber=κ', tau=χ)), transpose(VSIE.doublelayer(wavenumber=κ', tau=χ)) -Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ'), VSIE.boundary(wavenumber=κ'), VSIE.doublelayer(wavenumber=κ') +Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ')), transpose(VSIE.boundary(wavenumber=κ')), transpose(VSIE.doublelayer(wavenumber=κ')) +Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ', tau=χ), VSIE.boundary(wavenumber=κ', tau=χ), VSIE.doublelayer(wavenumber=κ', tau=χ) #Surface-Surface T = Maxwell3D.singlelayer(wavenumber=κ) #Outside T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside @@ -41,17 +42,21 @@ e, h = (n × E) × n, (n × H) × n @hilbertspace D j m @hilbertspace k l o -β = 1.0/ϵ_r +β = 1.0/(ϵ_r*ϵ_b) +ν = 1/ϵ_b α, α′ = 1/η, 1/η′ +γ′ = im*η′/κ′ +ζ′ = im*η′*κ′ +δ′ = im*κ′/ϵ_b -eq = @varform (β*I[k,D]-L[k,D]-B[ntrc(k),D] + Lt[k,j]+Bt[ntrc(k),j] + Kt[k,m] + - Ls[l,D]+Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + - Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) +eq = @varform (β*I[k,D]-ν*L[k,D]-ν*B[ntrc(k),D] + η′*Lt[k,j]-γ′*Bt[ntrc(k),j] + Kt[k,m] + + -δ′*Ls[l,D]-ν*Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + + ζ′*Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y -u = solve(dvsie) +u_n = solve(dvsie) #Post processing @@ -59,15 +64,22 @@ u = solve(dvsie) ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] # Don't forget the far field comprises two contributions -ffm = potential(MWFarField3D(κ*im), ffpoints, u[m], Y) -ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], Y) +ffm = potential(MWFarField3D(κ*im), ffpoints, u_n[m], Y) +ffj = potential(MWFarField3D(κ*im), ffpoints, u_n[j], Y) ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) -ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u[D], X) +ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u_n[D], X) ff2 = im*κ*ffd using Plots plot(xlabel="theta") plot!(Θ,norm.(ff),label="far field",title="DVSIE") -plot!(Θ,norm.(ff2),label="far field",title="DVSIE 2") +plot!(Θ,√6.0*norm.(ff),label="far field",title="DVSIE 2") + +import Plotly +using LinearAlgebra +fcrj, _ = facecurrents(u_n[j],Y) +fcrm, _ = facecurrents(u_n[m],Y) +Plotly.plot(patch(Γ, norm.(fcrj))) +Plotly.plot(patch(Γ, norm.(fcrm))) \ No newline at end of file diff --git a/examples/dvie.jl b/examples/dvie.jl index cd5f1868..a14ac29c 100644 --- a/examples/dvie.jl +++ b/examples/dvie.jl @@ -4,7 +4,7 @@ using Profile using StaticArrays function tau(x::SVector{U,T}) where {U,T} - 1.0-1.0/4.0 + 1.0-1.0/9.0 end ntrc = X->ntrace(X,y) @@ -15,7 +15,7 @@ y = boundary(T) @show numfunctions(X) ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) -ϵ_r =4.0 +ϵ_r =25.0 χ = tau K, I, B = VIE.singlelayer(wavenumber=κ, tau=χ), Identity(), VIE.boundary(wavenumber=κ, tau=χ) E = VIE.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) @@ -41,6 +41,7 @@ using Plots plot(xlabel="theta") plot!(Θ, norm.(ff), label="far field", title="D-VIE") +#= #NearField Z = range(-1,1,length=100) Y = range(-1,1,length=100) @@ -52,6 +53,6 @@ Enear = reshape(Enear,100,100) contour(real.(getindex.(Enear,1))) heatmap(Z, Y, real.(getindex.(Enear,1)), clim=(0.0,1.0)) - +=# diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index d5b6f363..a3c9c23b 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -8,8 +8,8 @@ X = BEAST.nedelecc3d(T) X = raviartthomas(Γ) @show numfunctions(X) -κ, η = π, 1.0 -κ′, η′ = 2.0κ, η/2.0 +κ, η = 1.0, 1.0 +κ′, η′ = √6.0κ, η/√6.0 T = Maxwell3D.singlelayer(wavenumber=κ) T′ = Maxwell3D.singlelayer(wavenumber=κ′) @@ -72,7 +72,7 @@ using Plots Plots.plot(xlabel="theta") Plots.plot!(Θ,norm.(ff),label="far field",title="PMCHWT") -#= + #import Plotly #using LinearAlgebra #fcrj, _ = facecurrents(u[j],X) @@ -133,4 +133,3 @@ Et_far = -cross.(ffpoints, nxE_far) plot() plot!(Θ, norm.(ff),label="far field") scatter!(Θ, norm.(Et_far), label="field far") -=# diff --git a/src/volumeintegral/vieops.jl b/src/volumeintegral/vieops.jl index 57a0ed5b..0879f9dd 100644 --- a/src/volumeintegral/vieops.jl +++ b/src/volumeintegral/vieops.jl @@ -178,7 +178,7 @@ function integrand(viop::VIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) end -defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) +defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) function quaddata(op::VIEOperator, diff --git a/src/volumeintegral/vsie.jl b/src/volumeintegral/vsie.jl index b0dcc2b4..57d2cb35 100644 --- a/src/volumeintegral/vsie.jl +++ b/src/volumeintegral/vsie.jl @@ -41,8 +41,8 @@ module VSIE @assert gamma != nothing - alpha == nothing && (alpha = wavenumber*wavenumber) - beta == nothing && (beta = 1.0) + alpha == nothing && (alpha = gamma) + beta == nothing && (beta = im*im/gamma) tau == nothing && (tau = x->1.0) Mod.VSIESingleLayer(gamma, alpha, beta, tau) diff --git a/src/volumeintegral/vsieops.jl b/src/volumeintegral/vsieops.jl index 1235cd7c..85222b23 100644 --- a/src/volumeintegral/vsieops.jl +++ b/src/volumeintegral/vsieops.jl @@ -192,7 +192,7 @@ function integrand(viop::VSIEDoubleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) end -defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) +defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) function quaddata(op::VSIEOperator, From 574d7e6be9b38274c00a472f2f721ff16fdd2b7a Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Wed, 16 Mar 2022 15:24:29 +0100 Subject: [PATCH 188/528] working volume surface formulation --- examples/dsvie.jl | 85 ---------- examples/dvie.jl | 46 ++++-- examples/dvsie.jl | 198 ++++++++++++++++++++++++ examples/evie.jl | 43 ++--- examples/pmchwt.jl | 57 ++++++- src/BEAST.jl | 4 + src/volumeintegral/sauterschwab_ints.jl | 14 +- src/volumeintegral/vieexc.jl | 9 ++ src/volumeintegral/vieops.jl | 25 +-- src/volumeintegral/vsie.jl | 12 +- src/volumeintegral/vsieops.jl | 86 +++++++--- 11 files changed, 411 insertions(+), 168 deletions(-) delete mode 100644 examples/dsvie.jl create mode 100644 examples/dvsie.jl diff --git a/examples/dsvie.jl b/examples/dsvie.jl deleted file mode 100644 index fa12c2ef..00000000 --- a/examples/dsvie.jl +++ /dev/null @@ -1,85 +0,0 @@ -using CompScienceMeshes, BEAST -using LinearAlgebra -using Profile -using StaticArrays - - - -ntrc = X->ntrace(X,Γ) - -T = tetmeshsphere(1.0,0.25) -X = nedelecd3d(T) -Γ = boundary(T) -Y = raviartthomas(Γ) - -@show numfunctions(X) -@show numfunctions(Y) - - -κ, η = 1.0, 1.0 -κ′, η′ = √2.0κ, η/√2.0 -ϵ_r =3.0 -ϵ_b =2.0 - - -χ = x->(1.0-1.0/ϵ_r) - -#Volume-Volume -L,I,B = VIE.singlelayer(wavenumber=κ', tau=χ), Identity(), VIE.boundary(wavenumber=κ', tau=χ) -#Volume-Surface -Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ')), transpose(VSIE.boundary(wavenumber=κ')), transpose(VSIE.doublelayer(wavenumber=κ')) -Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ', tau=χ), VSIE.boundary(wavenumber=κ', tau=χ), VSIE.doublelayer(wavenumber=κ', tau=χ) -#Surface-Surface -T = Maxwell3D.singlelayer(wavenumber=κ) #Outside -T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside -K = Maxwell3D.doublelayer(wavenumber=κ) #Outside -K′ = Maxwell3D.doublelayer(wavenumber=κ′) #Inside - -E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) -H = -1/(im*κ*η)*curl(E) - -e, h = (n × E) × n, (n × H) × n - -@hilbertspace D j m -@hilbertspace k l o -β = 1.0/(ϵ_r*ϵ_b) -ν = 1/ϵ_b -α, α′ = 1/η, 1/η′ -γ′ = im*η′/κ′ -ζ′ = im*η′*κ′ -δ′ = im*κ′/ϵ_b - -eq = @varform (β*I[k,D]-ν*L[k,D]-ν*B[ntrc(k),D] + η′*Lt[k,j]-γ′*Bt[ntrc(k),j] + Kt[k,m] + - -δ′*Ls[l,D]-ν*Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + - ζ′*Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) - - -dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y - -u_n = solve(dvsie) - - -#Post processing -Θ, Φ = range(0.0,stop=2π,length=100), 0.0 -ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] - -# Don't forget the far field comprises two contributions -ffm = potential(MWFarField3D(κ*im), ffpoints, u_n[m], Y) -ffj = potential(MWFarField3D(κ*im), ffpoints, u_n[j], Y) -ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) - - -ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u_n[D], X) -ff2 = im*κ*ffd - -using Plots -plot(xlabel="theta") -plot!(Θ,norm.(ff),label="far field",title="DVSIE") -plot!(Θ,√6.0*norm.(ff),label="far field",title="DVSIE 2") - -import Plotly -using LinearAlgebra -fcrj, _ = facecurrents(u_n[j],Y) -fcrm, _ = facecurrents(u_n[m],Y) -Plotly.plot(patch(Γ, norm.(fcrj))) -Plotly.plot(patch(Γ, norm.(fcrm))) \ No newline at end of file diff --git a/examples/dvie.jl b/examples/dvie.jl index a14ac29c..26eb2774 100644 --- a/examples/dvie.jl +++ b/examples/dvie.jl @@ -1,21 +1,22 @@ using CompScienceMeshes, BEAST using LinearAlgebra using Profile +using TimerOutputs using StaticArrays function tau(x::SVector{U,T}) where {U,T} - 1.0-1.0/9.0 + 1.0-1.0/5.0 end ntrc = X->ntrace(X,y) -T = CompScienceMeshes.tetmeshsphere(1.0,0.25) +T = tetmeshsphere(1.0,0.2) X = nedelecd3d(T) y = boundary(T) @show numfunctions(X) ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) -ϵ_r =25.0 +ϵ_r =5.0 χ = tau K, I, B = VIE.singlelayer(wavenumber=κ, tau=χ), Identity(), VIE.boundary(wavenumber=κ, tau=χ) E = VIE.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) @@ -27,8 +28,8 @@ H = -1/(im*κ*η)*curl(E) eq = @varform α*I[j,k]-K[j,k]-B[ntrc(j),k] == E[j] dbvie = @discretise eq j∈X k∈X -u = solve(dbvie) - +u_m = solve(dbvie) +#= #postprocessing Φ, Θ = [0.0], range(0,stop=2π,length=100) pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] @@ -40,19 +41,42 @@ using Plots #Farfield plot(xlabel="theta") plot!(Θ, norm.(ff), label="far field", title="D-VIE") - -#= +=# +using Plots #NearField Z = range(-1,1,length=100) Y = range(-1,1,length=100) -nfpoints = [point(0,y,z) for y in Y, z in Z] +nfpoints = [point(0,y,z) for z in Z, y in Y] -Enear = BEAST.grideval(nfpoints,α.*u,X) +Enear = BEAST.grideval(nfpoints,α.*u_m,X) Enear = reshape(Enear,100,100) contour(real.(getindex.(Enear,1))) -heatmap(Z, Y, real.(getindex.(Enear,1)), clim=(0.0,1.0)) +heatmap(Z, Y, real.(getindex.(Enear,1))) -=# +plot!(Y[2:99],real.(getindex.(Enear[2:99,50],1)),label="D-VIE (simplex)", linestyle=:dash, linecolor=:darkorange4) +#plot!(Y[2:99],real.(getindex.(Enear_simplex[2:99,50],1)),label="D-VIE (simplex)", linestyle=:solid, linecolor=:darkorange3) + +#= +import Cairo +using DataFrames, Gadfly, RDatasets +D = dataset("datasets","HairEyeColor") +palette = ["skyblue","skyblue3"] + + +D = DataFrame( + Type=["D-VIE \n (1,190 Tets.)","D-VIE \n (1,190 Tets.)","D-VIE \n (1,190 Tets.)","D-VIE \n (1,190 Tets.)","E-VIE \n (1,190 Tets.)","E-VIE \n (1,190 Tets.)","E-VIE \n (1,190 Tets.)","E-VIE \n (1,190 Tets.)","D-VIE \n (2,706 Tets.)","D-VIE \n (2,706 Tets.)","D-VIE \n (2,706 Tets.)","D-VIE \n (2,706 Tets.)","E-VIE \n (2,706 Tets.)","E-VIE \n (2,706 Tets.)","E-VIE \n (2,706 Tets.)","E-VIE \n (2,706 Tets.)"], + Quad=["Simplex","Simplex","Classic","Classic","Simplex","Simplex","Classic","Classic","Simplex","Simplex","Classic","Classic","Simplex","Simplex","Classic","Classic"], + Sing=["Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular"], + Time=[24,4.77,23.1,27.9,41.9,8.49,41.5,45.7,125,13.1,125,68.6,222,20.5,222,115], +) + +p = Gadfly.plot(D, x=:Quad, y=:Time, color=:Sing, xgroup=:Type, + Geom.subplot_grid(Geom.bar(position=:stack)), + Scale.color_discrete_manual(palette...), + Guide.xlabel(""),Guide.ylabel("Assembly time [s]"), Guide.colorkey("Interaction"),Theme(major_label_font="Times",minor_label_font="Times",key_title_font="Times", key_label_font="Times",background_color=color("white"))) +# draw(PNG("haireyecolor.png", 6.6inch, 4inch), p) +draw(PNG(6.6inch, 4inch), p) +=# \ No newline at end of file diff --git a/examples/dvsie.jl b/examples/dvsie.jl new file mode 100644 index 00000000..dfa822c5 --- /dev/null +++ b/examples/dvsie.jl @@ -0,0 +1,198 @@ +using CompScienceMeshes, BEAST +using LinearAlgebra +using Profile +using LiftedMaps +using BlockArrays +using StaticArrays + + + +ntrc = X->ntrace(X,Γ) + +T = tetmeshsphere(1.0,0.3) +X = nedelecd3d(T) +Γ = boundary(T) +Y = raviartthomas(Γ) + +@show numfunctions(X) +@show numfunctions(Y) + +Z = BEAST.buffachristiansen2(Γ) + + +κ, η = 1.0, 1.0 +κ′, η′ = √2κ, η/√2 +ϵ_r =3 +ϵ_b =2.0 +κ_in, η_in = √6κ, η/√6 + + +#χ = x->(1.0-1.0/ϵ_r) + +function tau(x::SVector{U,T}) where {U,T} + 1.0-1.0/3.0 +end + +χ = tau + + + +N = NCross() +#Volume-Volume +L,I,B = VIE.singlelayer(wavenumber=κ′, tau=χ), Identity(), VIE.boundary(wavenumber=κ′, tau=χ) +#Volume-Surface +Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ′)), transpose(VSIE.boundary(wavenumber=κ′)), transpose(VSIE.doublelayer(wavenumber=κ′)) +#Kt = VSIE.doublelayerT(wavenumber=κ′) + +Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ′, tau=χ), VSIE.boundary(wavenumber=κ′, tau=χ), VSIE.doublelayer(wavenumber=κ′, tau=χ) +#Surface-Surface +T = Maxwell3D.singlelayer(wavenumber=κ) #Outside +T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside +K = Maxwell3D.doublelayer(wavenumber=κ) #Outside +K′ = Maxwell3D.doublelayer(wavenumber=κ′) #Inside + +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +H = -1/(im*κ*η)*curl(E) + +e, h = (n × E) × n, (n × H) × n + +@hilbertspace D j m +@hilbertspace k l o +β = 1/(ϵ_r*ϵ_b) +ν = 1/ϵ_b +α, α′ = 1/η, 1/η′ +γ′ = im*η′/κ′ +ζ′ = im*κ′/(ϵ_b*η′) +δ′ = im*κ′/ϵ_b + +#= +eq = @varform (β*I[k,D] + η′*Lt[k,j]-γ′*Bt[ntrc(k),j] - Kt[k,m] + + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) + +=# + +eq = @varform (β*I[k,D]-ν*L[k,D]-ν*B[ntrc(k),D] + η′*Lt[k,j]-γ′*Bt[ntrc(k),j] - Kt[k,m] + + -δ′*Ls[l,D]-ν*Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + + -ζ′*Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) + + +dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y + +u_n = solve(dvsie) + + +#= +#preconditioner +Mxx = assemble(I,X,X) +iMxx = inv(Matrix(Mxx)) + +Tzz = assemble(T,Z,Z); println("dual discretisation assembled.") +Nyz = Matrix(assemble(N,Y,Z)); println("duality form assembled.") + +iNyz = inv(Nyz); println("duality form inverted.") +NTN = iNyz' * Tzz * iNyz + +M = zeros(Int, 3) +N = zeros(Int, 3) + +M[1] = numfunctions(X) +N[1] = numfunctions(X) +M[2] = M[3] = numfunctions(Y) +N[2] = N[3] = numfunctions(Y) + +U = BlockArrays.blockedrange(M) +V = BlockArrays.blockedrange(N) + +precond = BEAST.ZeroMap{Float32}(U, V) + +z1 = LiftedMap(iMxx,Block(1),Block(1),U,V) +z2 = LiftedMap(NTN,Block(2),Block(2),U,V) +z3 = LiftedMap(NTN,Block(3),Block(3),U,V) +precond = precond +ϵ_r*z1 + z2 + z3 + +A_dvsie_precond = precond*A_dvsie + +#GMREs +import IterativeSolvers +cT = promote_type(eltype(A_dvsie), eltype(rhs)) +x = PseudoBlockVector{cT}(undef, M) +fill!(x, 0) +x, ch = IterativeSolvers.gmres!(x, A_dvsie, Rhs, log=true, reltol=1e-4) +fill!(x, 0) +x, ch = IterativeSolvers.gmres!(x, precond*A_dvsie, precond*Rhs, log=true, reltol=1e-8) +=# + +#Post processing +Θ, Φ = range(0.0,stop=2π,length=100), 0.0 +ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] + +# Don't forget the far field comprises two contributions +ffm = potential(MWFarField3D(κ*im), ffpoints, u_n[m], Y) +ffj = potential(MWFarField3D(κ*im), ffpoints, u_n[j], Y) +ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) + + +ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u_n[D], X) +ff2 = im*κ*ffd + +using Plots +plot(xlabel="theta") +plot!(Θ,norm.(ff),label="far field",title="DVSIE") +plot!(Θ,√6.0*norm.(ff),label="far field",title="DVSIE 2") + +import Plotly +using LinearAlgebra +fcrj, _ = facecurrents(u_n[j],Y) +fcrm, _ = facecurrents(u_n[m],Y) +vsie_j = Plotly.plot([patch(Γ, norm.(fcrj))], Plotly.Layout(title="j D-VSIE")) +vsie_m = Plotly.plot(patch(Γ, norm.(fcrm)), Plotly.Layout(title="m D-VSIE")) + +#NearField +function nearfield(um,uj,Xm,Xj,κ,η,points, + Einc=(x->point(0,0,0)), + Hinc=(x->point(0,0,0))) + + K = BEAST.MWDoubleLayerField3D(wavenumber=κ) + T = BEAST.MWSingleLayerField3D(wavenumber=κ) + + Em = potential(K, points, um, Xm) + Ej = potential(T, points, uj, Xj) + E = -Em + η*Ej + Einc.(points) + + Hm = potential(T, points, um, Xm) + Hj = potential(K, points, uj, Xj) + H = 1/η*Hm + Hj + Hinc.(points) + + return E, H +end + +Zz = range(-1,1,length=100) +Yy = range(-1,1,length=100) +nfpoints = [point(0.0,y,z) for z in Zz, y in Yy] + + +import Base.Threads: @spawn +task1 = @spawn nearfield(u_n[m],u_n[j],Y,Y,κ,η,nfpoints,E,H) +task2 = @spawn nearfield(-u_n[m],-u_n[j],Y,Y,κ_in,η_in,nfpoints) + +E_ex, H_ex = fetch(task1) +E_in, H_in = fetch(task2) + + + +Enear = BEAST.grideval(nfpoints,β.* u_n[D],X) +Enear = reshape(Enear,100,100) + +contour(real.(getindex.(Enear,1))) +heatmap(Zz, Yy, real.(getindex.(Enear,1))) + +heatmap(Zz, Yy, real.(getindex.(E_in,1))) +heatmap(Zz, Yy, real.(getindex.(E_ex,1))) + +contour(real.(getindex.(E_ex,1))) + + +Dd = range(1,100,step=1) +plot!(Yy,real.(getindex.(E_in[Dd,50],1))) +plot(collect(Yy)[2:99],real.(getindex.(Enear[2:99,50],1))) \ No newline at end of file diff --git a/examples/evie.jl b/examples/evie.jl index 2ef54e01..358512bb 100644 --- a/examples/evie.jl +++ b/examples/evie.jl @@ -3,19 +3,22 @@ using LinearAlgebra using Profile using StaticArrays -function tau(x::SVector{U,T}) where {U,T} - 4.0-1.0 -end + ttrc = X->ttrace(X,y) -T = CompScienceMeshes.tetmeshsphere(1.0,0.25) +T= tetmeshsphere(1.0,0.2) X = nedelecc3d(T) y = boundary(T) @show numfunctions(X) ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) -ϵ_r = 4.0 +ϵ_r = 5.0 + +function tau(x::SVector{U,T}) where {U,T} + 5.0 -1.0 +end + χ = tau K, I, B = VIE.singlelayer2(wavenumber=κ, tau=χ), Identity(), VIE.boundary2(wavenumber=κ, tau=χ) E = VIE.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) @@ -27,29 +30,31 @@ H = -1/(im*κ*η)*curl(E) eq = @varform α*I[j,k]-K[j,k]+B[ttrc(j),k] == E[j] evie = @discretise eq j∈X k∈X -u = solve(evie) +u_n = solve(evie) #postprocessing -Φ, Θ = [0.0], range(0,stop=2π,length=100) -pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ ] -ffe = potential(VIE.farfield(wavenumber=κ, tau=χ), pts, u, X) -ff = ffe - -using Plots +#Φ, Θ = [0.0], range(0,stop=2π,length=100) +#pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ ] +#ffe = potential(VIE.farfield(wavenumber=κ, tau=χ), pts, u_n, X) +#ff = ffe #Farfield -plot(xlabel="theta") -plot!(Θ, norm.(ff), label="far field", title="E-VIE") +#plot(xlabel="theta") +#plot!(Θ, norm.(ff), label="far field", title="E-VIE") + +using Plots #Nearfield Z = range(-1,1,length=100) Y = range(-1,1,length=100) -nfpoints = [point(0,y,z) for y in Y, z in Z] +nfpoints = [point(0,y,z) for z in Z, y in Y] + +Enear2 = BEAST.grideval(nfpoints,u_n,X) +Enear2 = reshape(Enear2,100,100) -Enear = BEAST.grideval(nfpoints,u,X) -Enear = reshape(Enear,100,100) +contour(real.(getindex.(Enear2,1))) +heatmap(Z, Y, real.(getindex.(Enear2,1))) -contour(real.(getindex.(Enear,1))) -heatmap(Z, Y, real.(getindex.(Enear,1)), clim=(0.0,1.0)) +plot!(Y[2:99],real.(getindex.(Enear2[2:99,50],1)),label="E-VIE (simplex)", linestyle=:dash, linecolor=:darkolivegreen4) diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index a3c9c23b..94d301e5 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -1,5 +1,7 @@ using CompScienceMeshes, BEAST using LinearAlgebra +using LiftedMaps +using BlockArrays T = CompScienceMeshes.tetmeshsphere(1.0,0.12) X = BEAST.nedelecc3d(T) @@ -7,9 +9,13 @@ X = BEAST.nedelecc3d(T) X = raviartthomas(Γ) @show numfunctions(X) +Y = BEAST.buffachristiansen2(Γ) + κ, η = 1.0, 1.0 -κ′, η′ = √6.0κ, η/√6.0 +κ′, η′ = √5.0κ, η/√5.0 + +N = NCross() T = Maxwell3D.singlelayer(wavenumber=κ) T′ = Maxwell3D.singlelayer(wavenumber=κ′) @@ -60,6 +66,40 @@ u = gmres(pmchwt) # @time for i in 1:300; BEAST.LinearMaps.mul!(y, Z, u); end +#preconditioner +#= +Tyy = assemble(T,Y,Y); println("dual discretisation assembled.") +Nxy = Matrix(assemble(N,X,Y)); println("duality form assembled.") + +iNxy = inv(Nxy); println("duality form inverted.") +NTN = iNxy' * Tyy * iNxy + +M = zeros(Int, 2) +N = zeros(Int, 2) + +M[1] = M[2] = numfunctions(X) +N[1] = N[2] = numfunctions(X) + +U = BlockArrays.blockedrange(M) +V = BlockArrays.blockedrange(N) + +precond = BEAST.ZeroMap{Float32}(U, V) + +z1 = LiftedMap(NTN,Block(1),Block(1),U,V) +z2 = LiftedMap(NTN,Block(2),Block(2),U,V) +precond = precond + z1 + z2 + +A_pmchwt_precond = precond*A_pmchwt + +#GMREs +import IterativeSolvers +cT = promote_type(eltype(A_pmchwt), eltype(rhs)) +x = PseudoBlockVector{cT}(undef, M) +fill!(x, 0) +x, ch = IterativeSolvers.gmres!(x, A_pmchwt, rhs, log=true, reltol=1e-6) +fill!(x, 0) +x, ch = IterativeSolvers.gmres!(x, precond*A_pmchwt, precond*rhs, log=true, reltol=1e-6) +=# Θ, Φ = range(0.0,stop=2π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] @@ -73,12 +113,12 @@ Plots.plot(xlabel="theta") Plots.plot!(Θ,norm.(ff),label="far field",title="PMCHWT") -#import Plotly -#using LinearAlgebra -#fcrj, _ = facecurrents(u[j],X) -#fcrm, _ = facecurrents(u[m],X) -#Plotly.plot(patch(Γ, norm.(fcrj))) -#Plotly.plot(patch(Γ, norm.(fcrm))) +import Plotly +using LinearAlgebra +fcrj, _ = facecurrents(u[j],X) +fcrm, _ = facecurrents(u[m],X) +Plotly.plot(patch(Γ, norm.(fcrj)),Plotly.Layout(title="j PMCHWT")) +Plotly.plot(patch(Γ, norm.(fcrm)),Plotly.Layout(title="m PMCHWT")) function nearfield(um,uj,Xm,Xj,κ,η,points, @@ -123,7 +163,7 @@ Plots.heatmap(Z, Y, real.(getindex.(H_tot,2))) Plots.plot(real.(getindex.(E_tot[:,51],1))) Plots.plot!(real.(getindex.(H_tot[:,51],2))) - +#= # Compare the far field and the field far ffradius = 100.0 E_far, H_far = nearfield(u[m],u[j],X,X,κ,η, ffradius .* ffpoints) @@ -133,3 +173,4 @@ Et_far = -cross.(ffpoints, nxE_far) plot() plot!(Θ, norm.(ff),label="far field") scatter!(Θ, norm.(Et_far), label="field far") +=# \ No newline at end of file diff --git a/src/BEAST.jl b/src/BEAST.jl index 94c3fe52..16e1cd93 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -14,6 +14,8 @@ using FastGaussQuadrature using LinearMaps using LiftedMaps +using TimerOutputs + import LinearAlgebra: cross, dot import LinearAlgebra: ×, ⋅ @@ -247,4 +249,6 @@ export x̂, ŷ, ẑ const n = NormalVector() export n +const to = TimerOutput() + end # module diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index 2746604c..3c140015 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -75,7 +75,7 @@ end function momintegrals!(op::VIEOperator, test_local_space::RefSpace, trial_local_space::RefSpace, test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) - + @timeit to "singular" begin #Find permutation of vertices to match location of singularity to SauterSchwab J, I= SauterSchwab3D.reorder(strat.sing) @@ -127,11 +127,13 @@ function momintegrals!(op::VIEOperator, out[i,j] += Q[K[i],L[j]]*O1[i]*O2[j] end end + + end nothing end function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) - + @timeit to "regular" begin # memory allocation here is a result from the type instability on strat # which is on purpose, i.e. the momintegrals! method is chosen based # on dynamic polymorphism. @@ -165,7 +167,8 @@ function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::Do end end end - + + end return z end @@ -238,6 +241,7 @@ function momintegrals!(biop::VSIEOperator, tshs, bshs, tcell, bcell, z, strat::D wimps = strat.inner_quad_points M, N = size(z) + for womp in womps tgeo = womp.point @@ -254,7 +258,7 @@ function momintegrals!(biop::VSIEOperator, tshs, bshs, tcell, bcell, z, strat::D igd = integrand(biop, kernel, tvals, tgeo, bvals, bgeo) - + for m in 1 : length(tvals) for n in 1 : length(bvals) @@ -265,6 +269,6 @@ function momintegrals!(biop::VSIEOperator, tshs, bshs, tcell, bcell, z, strat::D end end - return z + #return z end diff --git a/src/volumeintegral/vieexc.jl b/src/volumeintegral/vieexc.jl index d1f5e92b..2dc1cf32 100644 --- a/src/volumeintegral/vieexc.jl +++ b/src/volumeintegral/vieexc.jl @@ -19,6 +19,15 @@ planewavevie(; ) = PlaneWaveVIE(direction, polarization, wavenumber, amplitude) function (e::PlaneWaveVIE)(p) + k = e.wavenumber + d = e.direction + u = e.polarisation + a = e.amplitude + x = p + a * exp(-im * k * dot(d, x)) * u + end + + function (e::PlaneWaveVIE)(p::CompScienceMeshes.MeshPointNM) k = e.wavenumber d = e.direction u = e.polarisation diff --git a/src/volumeintegral/vieops.jl b/src/volumeintegral/vieops.jl index 0879f9dd..611b2fe4 100644 --- a/src/volumeintegral/vieops.jl +++ b/src/volumeintegral/vieops.jl @@ -178,7 +178,7 @@ function integrand(viop::VIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) end -defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) +defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) function quaddata(op::VIEOperator, @@ -230,15 +230,18 @@ function qr_volume(op::VolumeOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, q end end - #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) - - + #Simplex-product quadrature rules hits == 4 && return SauterSchwab3D.CommonVolume6D_S(SauterSchwab3D.Singularity6DVolume(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[4])) hits == 3 && return SauterSchwab3D.CommonFace6D_S(SauterSchwab3D.Singularity6DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 2 && return SauterSchwab3D.CommonEdge6D_S(SauterSchwab3D.Singularity6DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3],qd.sing_qp[4])) hits == 1 && return SauterSchwab3D.CommonVertex6D_S(SauterSchwab3D.Singularity6DPoint(idx_t,idx_s),qd.sing_qp[3]) - - + #= + #Classic tensor-product quadrature rules + hits == 4 && return SauterSchwab3D.CommonVolume6D(SauterSchwab3D.Singularity6DVolume(idx_t,idx_s),qd.sing_qp[1]) + hits == 3 && return SauterSchwab3D.CommonFace6D(SauterSchwab3D.Singularity6DFace(idx_t,idx_s),qd.sing_qp[1]) + hits == 2 && return SauterSchwab3D.CommonEdge6D(SauterSchwab3D.Singularity6DEdge(idx_t,idx_s),qd.sing_qp[1]) + hits == 1 && return SauterSchwab3D.CommonVertex6D(SauterSchwab3D.Singularity6DPoint(idx_t,idx_s),qd.sing_qp[1]) + =# return DoubleQuadRule( qd[1][1,i], @@ -273,13 +276,15 @@ function qr_boundary(op::BoundaryOperator, g::RefSpace, f::RefSpace, i, τ, j, end end - #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) - - + hits == 3 && return SauterSchwab3D.CommonFace5D_S(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 2 && return SauterSchwab3D.CommonEdge5D_S(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 1 && return SauterSchwab3D.CommonVertex5D_S(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),(qd.sing_qp[3],qd.sing_qp[2])) - + #= + hits == 3 && return SauterSchwab3D.CommonFace5D(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),qd.sing_qp[1]) + hits == 2 && return SauterSchwab3D.CommonEdge5D(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),qd.sing_qp[1]) + hits == 1 && return SauterSchwab3D.CommonVertex5D(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),qd.sing_qp[1]) + =# return DoubleQuadRule( qd[1][1,i], diff --git a/src/volumeintegral/vsie.jl b/src/volumeintegral/vsie.jl index 57d2cb35..9947f567 100644 --- a/src/volumeintegral/vsie.jl +++ b/src/volumeintegral/vsie.jl @@ -41,14 +41,14 @@ module VSIE @assert gamma != nothing - alpha == nothing && (alpha = gamma) - beta == nothing && (beta = im*im/gamma) + alpha == nothing && (alpha = -gamma) + beta == nothing && (beta = -1/gamma) tau == nothing && (tau = x->1.0) Mod.VSIESingleLayer(gamma, alpha, beta, tau) end - function singlelayerT(; + function singlelayer2(; gamma=nothing, wavenumber=nothing, alpha=nothing, @@ -73,11 +73,11 @@ module VSIE @assert gamma != nothing - alpha == nothing && (alpha = wavenumber*wavenumber) - beta == nothing && (beta = 1.0) + alpha == nothing && (alpha = -gamma) + beta == nothing && (beta = -1/gamma) tau == nothing && (tau = x->1.0) - Mod.VSIESingleLayerT(gamma, alpha, beta, tau) + Mod.VSIESingleLayer2(gamma, alpha, beta, tau) end """ diff --git a/src/volumeintegral/vsieops.jl b/src/volumeintegral/vsieops.jl index 85222b23..7338e7d3 100644 --- a/src/volumeintegral/vsieops.jl +++ b/src/volumeintegral/vsieops.jl @@ -1,6 +1,9 @@ abstract type VSIEOperator <: IntegralOperator end abstract type VolumeSurfaceOperator <: VSIEOperator end abstract type BoundarySurfaceOperator <: VSIEOperator end +abstract type VSIEOperatorT <: VSIEOperator end +abstract type VolumeSurfaceOperatorT <: VolumeSurfaceOperator end +abstract type BoundarySurfaceOperatorT <: BoundarySurfaceOperator end struct KernelValsVSIE{T,U,P,Q,K} gamma::U @@ -20,7 +23,6 @@ function kernelvals(viop::VSIEOperator, p ,q) expn = exp(-yR) green = expn / (4*pi*R) gradgreen = - (Y +1/R)*green/R*r - tau = viop.tau(cartesian(q)) @@ -46,26 +48,26 @@ struct VSIEDoubleLayer{T,U,P} <: VolumeSurfaceOperator tau::P end - -struct VSIESingleLayerT{T,U,P} <: VolumeSurfaceOperator +#= +struct VSIESingleLayer2{T,U,P} <: VolumeSurfaceOperator gamma::T α::U β::U tau::P end -struct VSIEBoundaryT{T,U,P} <: BoundarySurfaceOperator +struct VSIEBoundaryT{T,U,P} <: BoundarySurfaceOperatorT gamma::T α::U tau::P end -struct VSIEDoubleLayerT{T,U,P} <: VolumeSurfaceOperator +struct VSIEDoubleLayerT{T,U,P} <: VolumeSurfaceOperatorT gamma::T α::U tau::P end - +=# scalartype(op::VSIEOperator) = typeof(op.gamma) export VSIE @@ -78,6 +80,15 @@ struct VSIEIntegrand{S,T,O,K,L} trial_local_space::L end +#= +struct VSIEIntegrandT{S,T,O,K,L} + test_tetrahedron_element::S + trial_triangle_element::T + op::O + test_local_space::K + trial_local_space::L +end +=# function (igd::VSIEIntegrand)(u,v) @@ -99,6 +110,28 @@ function (igd::VSIEIntegrand)(u,v) end +#= +function (igd::VSIEIntegrandT)(u,v) + + #mesh points + tgeo = neighborhood(igd.test_tetrahedron_element,u) + bgeo = neighborhood(igd.trial_triangle_element,v) + + #kernel values + kerneldata = kernelvals(igd.op,tgeo,bgeo) + + #values & grad/div/curl of local shape functions + tval = igd.test_local_space(tgeo) + bval = igd.trial_local_space(bgeo) + + #jacobian + j = jacobian(tgeo) * jacobian(bgeo) + + integrand(igd.op, kerneldata,tval,tgeo,bval,tgeo) * j +end +=# + + function integrand(viop::VSIESingleLayer, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:3] @@ -124,20 +157,21 @@ function integrand(viop::VSIEBoundary, kerneldata, tvals, tgeo, bvals, bgeo) G = kerneldata.green - Tx = kerneldata.tau + Ty = kerneldata.tau α = viop.α - @SMatrix[α * Tx * dgx[i] * fy[j] * G for i in 1:3, j in 1:1] + @SMatrix[α * Ty * dgx[i] * fy[j] * G for i in 1:3, j in 1:1] end -function integrand(viop::VSIESingleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) +#= +function integrand(viop::VSIESingleLayer2, kerneldata, tvals, tgeo, bvals, bgeo) - gx = @SVector[tvals[i].value for i in 1:4] - fy = @SVector[bvals[i].value for i in 1:3] + gx = @SVector[tvals[i].value for i in 1:3] + fy = @SVector[bvals[i].value for i in 1:4] - dgx = @SVector[tvals[i].divergence for i in 1:4] - dfy = @SVector[bvals[i].divergence for i in 1:3] + dgx = @SVector[tvals[i].divergence for i in 1:3] + dfy = @SVector[bvals[i].divergence for i in 1:4] G = kerneldata.green @@ -146,7 +180,7 @@ function integrand(viop::VSIESingleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) α = viop.α β = viop.β - @SMatrix[α * dot(gx[i],Ty*fy[j]) * G + β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] + @SMatrix[α * dot(gx[i],Ty*fy[j]) * G - β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] end @@ -163,7 +197,7 @@ function integrand(viop::VSIEBoundaryT, kerneldata, tvals, tgeo, bvals, bgeo) @SMatrix[α * Tx * gx[i] * dfy[j] * G for i in 1:3, j in 1:1] end - +=# function integrand(viop::VSIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:3] @@ -178,21 +212,23 @@ function integrand(viop::VSIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:3, j in 1:4] end +#= function integrand(viop::VSIEDoubleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:4] fy = @SVector[bvals[i].value for i in 1:3] + gradG = kerneldata.gradgreen - + Ty = kerneldata.tau α = viop.α - @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:4, j in 1:3] + @SMatrix[α * transpose(cross(fy[j],gx[i])) *gradG for i in 1:4, j in 1:3] end +=# - -defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) +defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) function quaddata(op::VSIEOperator, @@ -221,7 +257,9 @@ quadrule(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs::SauterSchwab3DQStrat) - + + @assert (length(τ.vertices)==3 && length(σ.vertices)==4) "Expected simplex wrong" + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) hits = 0 @@ -236,8 +274,8 @@ function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j d2 = LinearAlgebra.norm_sqr(t-s) dmin2 = min(dmin2, d2) if d2 < dtol - push!(idx_t,i) - push!(idx_s,j) + push!(idx_t,j) + push!(idx_s,i) hits +=1 break end @@ -249,8 +287,8 @@ function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j hits == 3 && return SauterSchwab3D.CommonFace5D_S(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 2 && return SauterSchwab3D.CommonEdge5D_S(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 1 && return SauterSchwab3D.CommonVertex5D_S(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),(qd.sing_qp[3],qd.sing_qp[2])) - - + #hits == 0 && return SauterSchwab3D.PositiveDistance5D_S(SauterSchwab3D.Singularity5DPositiveDistance(),(qd.sing_qp[3],qd.sing_qp[2])) + return DoubleQuadRule( qd[1][1,i], qd[2][1,j]) From 385950ce14e1b004e2552c0266a9c466476e021c Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Wed, 16 Mar 2022 15:33:53 +0100 Subject: [PATCH 189/528] mixed mueller formulation --- examples/mueller.jl | 88 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 examples/mueller.jl diff --git a/examples/mueller.jl b/examples/mueller.jl new file mode 100644 index 00000000..5c95731a --- /dev/null +++ b/examples/mueller.jl @@ -0,0 +1,88 @@ +using CompScienceMeshes, BEAST + + +T = tetmeshsphere(1.0,0.3) +X = nedelecc3d(T) +Γ = boundary(T) + +X, Y = raviartthomas(Γ), buffachristiansen(Γ) + +ω = 1.0 +ϵ, ϵ′ = 1.0, 5.0 +μ, μ′ = 1.0, 1.0 +κ, η = ω*√(ϵ*μ), √(μ/ϵ) +κ′, η′ = ω*√(ϵ′*μ′), √(μ′/ϵ′) + + + +N = NCross() + +T = Maxwell3D.singlelayer(wavenumber=κ) +T′ = Maxwell3D.singlelayer(wavenumber=κ′) +K = Maxwell3D.doublelayer(wavenumber=κ) +K′ = Maxwell3D.doublelayer(wavenumber=κ′) + +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +H = -1/(im*κ*η)*curl(E) +e, h = (n × E) × n, (n × H) × n + +@hilbertspace j m +@hilbertspace k l + +α, α′ = μ/η, μ′/η′ +mueller = @discretise( + (ϵ*η*T-ϵ′*η′*T′)[k,j] - ((ϵ+ϵ′)/2*N+ϵ*K-ϵ′*K′)[k,m] + +((μ+μ′)/2*N+μ*K-μ′*K′)[l,j] + (α*T-α′*T′)[l,m] == -ϵ*e[k] - μ*h[l], +j∈X, m∈X, k∈Y, l∈Y) +u,A_mueller,rhs_mueller = solve(mueller) + +#postprocessing +using Plots +import Plotly +using LinearAlgebra +fcrj, _ = facecurrents(u[j],X) +fcrm, _ = facecurrents(u[m],X) +Plotly.plot(patch(Γ, norm.(fcrj)),Plotly.Layout(title="j Mueller")) +Plotly.plot(patch(Γ, norm.(fcrm)),Plotly.Layout(title="m Mueller")) + + +function nearfield(um,uj,Xm,Xj,κ,η,points, + Einc=(x->point(0,0,0)), + Hinc=(x->point(0,0,0))) + + K = BEAST.MWDoubleLayerField3D(wavenumber=κ) + T = BEAST.MWSingleLayerField3D(wavenumber=κ) + + Em = potential(K, points, um, Xm) + Ej = potential(T, points, uj, Xj) + E = -Em + η * Ej + Einc.(points) + + Hm = potential(T, points, um, Xm) + Hj = potential(K, points, uj, Xj) + H = 1/η*Hm + Hj + Hinc.(points) + + return E, H +end + + +Z = range(-1,1,length=100) +Y = range(-1,1,length=100) +nfpoints = [point(0,y,z) for z in Z, y in Y] + +import Base.Threads: @spawn +task1 = @spawn nearfield(u[m],u[j],X,X,κ,η,nfpoints,E,H) +task2 = @spawn nearfield(-u[m],-u[j],X,X,κ′,η′,nfpoints) + +E_ex, H_ex = fetch(task1) +E_in, H_in = fetch(task2) + +E_tot = E_in + E_ex +H_tot = H_in + H_ex + +contour(real.(getindex.(E_tot,1))) +contour(real.(getindex.(H_tot,2))) + +heatmap(Z, Y, real.(getindex.(E_tot,1)),title="E_tot Mueller") +#heatmap(Z, Y, real.(getindex.(H_tot,2))) +#heatmap(Z, Y, real.(getindex.(E_in,1))) +#heatmap(Z, Y, real.(getindex.(E_ex,1))) \ No newline at end of file From d6bc1c718a7d2bd9e45733113948fa9e03371412 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Wed, 6 Apr 2022 10:19:29 +0200 Subject: [PATCH 190/528] updated farfield --- examples/pmchwt.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 94d301e5..58a0fd57 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -104,8 +104,8 @@ x, ch = IterativeSolvers.gmres!(x, precond*A_pmchwt, precond*rhs, log=true, rel ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] # Don't forget the far field comprises two contributions -ffm = potential(MWFarField3D(κ*im, η), ffpoints, u[m], X) -ffj = potential(MWFarField3D(κ*im, η), ffpoints, u[j], X) +ffm = potential(MWFarField3D(gamma=κ*im), ffpoints, u[m], X) +ffj = potential(MWFarField3D(gamma=κ*im), ffpoints, u[j], X) ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) using Plots From b5cc846613cef4e54334db175ef0a77942d35b5a Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Tue, 28 Jun 2022 10:50:44 +0200 Subject: [PATCH 191/528] quadratic lagrange basisfunctions --- src/BEAST.jl | 7 +- src/bases/bdmdiv.jl | 3 + src/bases/lagrange.jl | 134 ++++++++++++++++++++++++- src/bases/local/bdmlocal.jl | 4 + src/bases/local/laglocal.jl | 89 ++++++++++++++++ src/bases/local/ndlocal.jl | 5 + src/maxwell/mwops.jl | 32 ++++++ src/maxwell/sauterschwabints_bdm_rt.jl | 95 ++++++++++++++++++ src/postproc/segcurrents.jl | 2 +- 9 files changed, 366 insertions(+), 5 deletions(-) create mode 100644 src/maxwell/sauterschwabints_bdm_rt.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 16e1cd93..a60b4b0d 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -25,7 +25,7 @@ export dot export planewave export RefSpace, numfunctions, coordtype, scalartype, assemblydata, geometry, refspace, valuetype -export lagrangecxd0, lagrangec0d1, duallagrangec0d1 +export lagrangecxd0, lagrangec0d1, duallagrangec0d1, lagrangec0d2 export duallagrangecxd0 export lagdimension export restrict @@ -189,10 +189,11 @@ include("timedomain/zdomain.jl") # Support for Maxwell equations include("maxwell/mwexc.jl") include("maxwell/mwops.jl") -include("maxwell/nxdbllayer.jl") include("maxwell/wiltonints.jl") -include("maxwell/sauterschwabints_bdm.jl") include("maxwell/sauterschwabints_rt.jl") +include("maxwell/sauterschwabints_bdm.jl") +include("maxwell/sauterschwabints_bdm_rt.jl") +include("maxwell/nxdbllayer.jl") include("maxwell/nitsche.jl") include("maxwell/farfield.jl") include("maxwell/nearfield.jl") diff --git a/src/bases/bdmdiv.jl b/src/bases/bdmdiv.jl index 76851f5d..055a204d 100644 --- a/src/bases/bdmdiv.jl +++ b/src/bases/bdmdiv.jl @@ -4,6 +4,9 @@ struct BDMBasis{T,M,P} <: Space{T} pos::Vector{P} end +BDMBasis(geo, fns) = BDMBasis(geo, fns, Vector{vertextype(geo)}(undef,length(fns))) + + refspace(s::BDMBasis{T}) where {T} = BDMRefSpace{T}() subset(s::BDMBasis,I) = BDMBasis(s.geo, s.fns[I], s.pos[I]) diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 59d12c99..869b8512 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -478,6 +478,137 @@ function duallagrangec0d1(mesh, mesh2, pred, ::Type{Val{2}}) LagrangeBasis{1,0,NF}(geometry, fns, pos) end +""" + lagrangec0d2(mesh) -> basis + +Build lagrangec0d2 elements, including boundary vertices (meaning assuming a closed mesh). +""" + +function lagrangec0d2(mesh) + + #mark non connectet vertices + verts = skeleton(mesh, 0) + detached = trues(numvertices(mesh)) + for v in cells(verts) + detached[v] = false + end + + #identify + bnd = boundary(mesh) + bndverts = skeleton(bnd, 0) + notonbnd = trues(numvertices(mesh)) + for v in cells(bndverts) + notonbnd[v] = false + end + + vertexlist = findall(.!detached) + + edges = skeleton(mesh, 1; sort) + cps = cellpairs(mesh, edges, dropjunctionpair=true) + #only interior edges + ids = findall(x -> x>0, cps[2,:]) + + lagrangec0d2(mesh,vertexlist,cps,Val{dimension(mesh)+1}) +end + + +function lagrangec0d2(mesh, vertexlist::Vector, cellpairs::Array{Int,2}, ::Type{Val{3}}) + + T = coordtype(mesh) + U = universedimension(mesh) + + numvertices = length(vertexlist) + numedges = size(cellpairs,2) + + cellids, ncells = vertextocellmap(mesh) + + Cells = cells(mesh) + Verts = vertices(mesh) + + # create the local shapes + functions = Vector{Vector{Shape{Float64}}}(undef,numvertices+numedges) + positions = Vector{vertextype(mesh)}(undef,numvertices+numedges) + + #temp containers for vertex dof + fns = Vector{Vector{Shape{Float64}}}() + pos = Vector{vertextype(mesh)}() + + sizehint!(fns, length(vertexlist)) + sizehint!(pos, length(vertexlist)) + + #shape function associated with vetex dof + for v in vertexlist + + numshapes = ncells[v] + numshapes == 0 && continue + + shapes = Vector{Shape{Float64}}(undef,numshapes) + for s in 1: numshapes + c = cellids[v,s] + # cell = mesh.faces[c] + cell = Cells[c] + + localid = something(findfirst(isequal(v), cell),0) + @assert localid != 0 + + shapes[s] = Shape(c, localid, 1.0) + end + #functions[v] = shapes + #positions[v] = Verts[v] + + push!(fns, shapes) + push!(pos, mesh.vertices[v]) + end + + @assert length(fns) == numvertices + + for i in 1:numvertices + functions[i] = fns[i] + positions[i] = pos[i] + end + + #shape function associated with edge dof + for i in 1:numedges + #internal edge + if cellpairs[2,i] > 0 + c1 = cellpairs[1,i]; cell1 = Cells[c1] #mesh.faces[c1] + c2 = cellpairs[2,i]; cell2 = Cells[c2] #mesh.faces[c2] + e1, e2 = getcommonedge(cell1, cell2) + functions[numvertices+i] = [ + Shape(c1, abs(e1)+3, 1.0), + Shape(c2, abs(e2)+3, 1.0)] + isct = intersect(cell1, cell2) + @assert length(isct) == 2 + @assert !(cell1[abs(e1)] in isct) + @assert !(cell2[abs(e2)] in isct) + + v1 = cell1[mod1(abs(e1)+1,3)] + v2 = cell1[mod1(abs(e1)+2,3)] + + edge = simplex(mesh.vertices[[v1,v2]]...) + + positions[numvertices+i] = cartesian(center(edge)) + #boundary edge + else + c1 = cellpairs[1,i]; cell1 = Cells[c1] + e1 = cellpairs[2,i] + functions[numvertices+i] = [ + Shape(c1, abs(e1)+3, +1.0)] + + v1 = cell1[mod1(abs(e1)+1,3)] + v2 = cell1[mod1(abs(e1)+2,3)] + edge = simplex(mesh.vertices[[v1,v2]]...) + + positions[numvertices+i] = cartesian(center(edge)) + end + end + + + NF = 6 + LagrangeBasis{2,0,NF}(mesh, functions, positions) +end + + gradient(space::LagrangeBasis{1,0}, geo, fns) = NDLCCBasis(geo, fns) # gradient(space::LagrangeBasis{1,0}, geo::CompScienceMeshes.AbstractMesh{U,3} where {U}, fns) = NDBasis(geo, fns) @@ -486,10 +617,11 @@ curl(space::LagrangeBasis{1,0}, geo, fns) = RTBasis(geo, fns) gradient(space::LagrangeBasis{1,0,<:CompScienceMeshes.AbstractMesh{<:Any,2}}, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, space.pos) - gradient(space::LagrangeBasis{1,0,<:CompScienceMeshes.AbstractMesh{<:Any,3}}, geo, fns) = NDBasis(geo, fns, space.pos) +curl(space::LagrangeBasis{2,0},geo, fns) = BDMBasis(geo, fns) + # # Sclar trace for Laggrange element based spaces # diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index cc6ff83a..e9d6c5cf 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -1,5 +1,9 @@ struct BDMRefSpace{T} <: RefSpace{T,6} end +function valuetype(ref::BDMRefSpace{T}, charttype::Type) where {T} + SVector{universedimension(charttype),T} +end + function (f::BDMRefSpace)(p) u,v = parametric(p) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index ebf7d938..953edbb0 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -7,9 +7,13 @@ numfunctions(s::LagrangeRefSpace{T,D,2}) where {T,D} = D+1 numfunctions(s::LagrangeRefSpace{T,0,3}) where {T} = 1 numfunctions(s::LagrangeRefSpace{T,1,3}) where {T} = 3 +numfunctions(s::LagrangeRefSpace{T,2,3}) where {T} = 6 + + valuetype(ref::LagrangeRefSpace{T}, charttype) where {T} = SVector{numfunctions(ref), Tuple{T,T}} + # Evaluate constant lagrange elements on anything (ϕ::LagrangeRefSpace{T,0})(tp) where {T} = SVector(((value=one(T), derivative=zero(T)),)) @@ -38,6 +42,25 @@ function (f::LagrangeRefSpace{T,1,3})(t) where T (value=w, curl=(p[2]-p[1])/j)) end +valuetype(ref::LagrangeRefSpace{T,1,3}, charttype) where {T} = + SVector{1,T} + +# Evaluate quadratic lagrange elements on a triangle +function (f::LagrangeRefSpace{T,2,3})(t) where T + u,v,w, = barycentric(t) + SVector( + (value=((2*u-1)*u),), + (value=((2*v-1)*v),), + (value=((2*w-1)*w),), + (value=(4*v*w),), + (value=(4*w*u),), + (value=(4*u*v),), + ) +end + +valuetype(ref::LagrangeRefSpace{T,2,3}, charttype) where {T} = + SVector{1, T} + """ f(tangent_space, Val{:withcurl}) @@ -71,6 +94,26 @@ function curl(ref::LagrangeRefSpace, sh, el) return [sh1, sh2] end +function curl(ref::LagrangeRefSpace{T,2,3}, sh, el) where T + + j = 1.0 #volume(el) * factorial(dimension(el)) + + if sh.refid < 4 + sh1 = Shape(sh.cellid, mod1(2*sh.refid+1,6), -sh.coeff*j) + sh2 = Shape(sh.cellid, mod1(2*sh.refid+2,6), 3*sh.coeff*j) + sh3 = Shape(sh.cellid, mod1(2*sh.refid+3,6), -3*sh.coeff*j) + sh4 = Shape(sh.cellid, mod1(2*sh.refid+4,6), sh.coeff*j) + + return [sh1, sh2, sh3, sh4] + else + sh1 = Shape(sh.cellid, 2*mod1(sh.refid,3)-1, 4*sh.coeff*j) + sh2 = Shape(sh.cellid, 2*mod1(sh.refid,3), -4*sh.coeff*j) + + return [sh1, sh2] + end + +end + function gradient(ref::LagrangeRefSpace{T,1,4}, sh, tet) where {T} this_vert = tet.vertices[sh.refid] # other_verts = deleteat(tet.vertices, sh.refid) @@ -226,3 +269,49 @@ function restrict(f::LagrangeRefSpace{T,1}, dom1, dom2) where T return Q end + + +function restrict(f::LagrangeRefSpace{T,2}, dom1, dom2) where T + + D = numfunctions(f) + Q = zeros(T, D, D) + + # for each point of the new domain + for i in 1:3 + + #vertices + v = dom2.vertices[i] + + # find the barycentric coordinates in dom1 + uvn = carttobary(dom1, v) + + # evaluate the shape functions in this point + x = neighborhood(dom1, uvn) + fx = f(x) + + for j in 1:D + Q[j,i] = fx[j][1] + end + + + #edges + # find the center of edge i of dom2 + a = dom2.vertices[mod1(i+1,3)] + b = dom2.vertices[mod1(i+2,3)] + v = (a + b) / 2 + + # find the barycentric coordinates in dom1 + uvn = carttobary(dom1, v) + + # evaluate the shape functions in this point + x = neighborhood(dom1, uvn) + fx = f(x) + + for j in 4:D + Q[j,i+3] = fx[j][1] + end + end + + return Q +end + diff --git a/src/bases/local/ndlocal.jl b/src/bases/local/ndlocal.jl index 0c400837..04083764 100644 --- a/src/bases/local/ndlocal.jl +++ b/src/bases/local/ndlocal.jl @@ -8,6 +8,11 @@ there is no well defined concept of adjacent-ness. """ mutable struct NDRefSpace{T} <: RefSpace{T,3} end +function valuetype(ref::NDRefSpace{T}, charttype::Type) where {T} + SVector{universedimension(charttype),T} +end + + function (ϕ::NDRefSpace)(nbd) u, v = parametric(nbd) diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 9253b1a2..49cb77ce 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -163,6 +163,38 @@ function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::BDMRefSpace, i, τ, end +function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::RTRefSpace, i, τ, j, σ, qd, + qs::DoubleNumWiltonSauterQStrat) + + hits = 0 + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) + dmin2 = floatmax(eltype(eltype(τ.vertices))) + for t in τ.vertices + for s in σ.vertices + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < dtol) + end + end + + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) + hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) + hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + + h2 = volume(σ) + xtol2 = 0.2 * 0.2 + k2 = abs2(op.gamma) + # max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( + # qd.tpoints[2,i], + # DoubleQuadRule( + # qd.tpoints[2,i], + # qd.bpoints[2,j],),) + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j],) +end + + function qrib(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd) dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) diff --git a/src/maxwell/sauterschwabints_bdm_rt.jl b/src/maxwell/sauterschwabints_bdm_rt.jl new file mode 100644 index 00000000..b03d6ee8 --- /dev/null +++ b/src/maxwell/sauterschwabints_bdm_rt.jl @@ -0,0 +1,95 @@ + + +struct MWDL3DIntegrand3{C,O,L,M} + test_triangular_element::C + trial_triangular_element::C + op::O + test_local_space::L + trial_local_space::M +end + +function (igd::MWDL3DIntegrand3)(u,v) + + γ = igd.op.gamma + + x = neighborhood(igd.test_triangular_element,u) + y = neighborhood(igd.trial_triangular_element,v) + + r = cartesian(x) - cartesian(y) + R = norm(r) + G = exp(-γ*R)/(4π*R) + + GG = -(γ + 1/R) * G / R * r + + f = igd.test_local_space(x) #BDM + g = igd.trial_local_space(y) #RT + + j = jacobian(x) * jacobian(y) + + G = @SVector [cross(GG,(j*g[1].value)), cross(GG,(j*g[2].value)), cross(GG,(j*g[3].value))] + + + f1 = f[1].value + f2 = f[2].value + f3 = f[3].value + f4 = f[4].value + f5 = f[5].value + f6 = f[6].value + + SMatrix{6,3}( + dot(f1,G[1]), + dot(f2,G[1]), + dot(f3,G[1]), + dot(f4,G[1]), + dot(f5,G[1]), + dot(f6,G[1]), + dot(f1,G[2]), + dot(f2,G[2]), + dot(f3,G[2]), + dot(f4,G[2]), + dot(f5,G[2]), + dot(f6,G[2]), + dot(f1,G[3]), + dot(f2,G[3]), + dot(f3,G[3]), + dot(f4,G[3]), + dot(f5,G[3]), + dot(f6,G[3])) +end + + +function momintegrals!(op::MWDoubleLayer3D, + test_local_space::BDMRefSpace, trial_local_space::RTRefSpace, + test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) + + I, J, K, L = SauterSchwabQuadrature.reorder( + test_triangular_element.vertices, + trial_triangular_element.vertices, strat) + + test_triangular_element = simplex( + test_triangular_element.vertices[I[1]], + test_triangular_element.vertices[I[2]], + test_triangular_element.vertices[I[3]]) + + trial_triangular_element = simplex( + trial_triangular_element.vertices[J[1]], + trial_triangular_element.vertices[J[2]], + trial_triangular_element.vertices[J[3]]) + + igd = MWDL3DIntegrand3(test_triangular_element, trial_triangular_element, + op, test_local_space, trial_local_space) + Q = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) + + A = levicivita(K) == 1 ? @SVector[1,2] : @SVector[2,1] + + for j ∈ 1:3 + for i ∈ 1:3 + p = K[i] + for m in 1:2 + a = A[m] + out[2*(i-1)+m,j] += Q[2*(p-1)+a,L[j]] + end + end + end + nothing +end diff --git a/src/postproc/segcurrents.jl b/src/postproc/segcurrents.jl index 0d45d3ff..b2e5355d 100644 --- a/src/postproc/segcurrents.jl +++ b/src/postproc/segcurrents.jl @@ -45,7 +45,7 @@ function grideval(points, coeffs, basis; type=nothing) vals = refs(neighborhood(chart,u)) for r in 1 : numfunctions(refs) for (m,w) in ad[i, r] - values[j] += w * coeffs[m] * vals[r][1] + values[j] += w * coeffs[m] * P(vals[r][1]) end end end From 4a8d1e989b58d9d23fb6db0cfcd5de7c551c6423 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Fri, 7 Oct 2022 11:38:41 +0200 Subject: [PATCH 192/528] Remove TimerOutputs --- examples/dvie.jl | 8 +++++--- examples/dvsie.jl | 8 ++++---- src/BEAST.jl | 8 ++++---- src/volumeintegral/sauterschwab_ints.jl | 8 ++++---- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/examples/dvie.jl b/examples/dvie.jl index 26eb2774..1b86906e 100644 --- a/examples/dvie.jl +++ b/examples/dvie.jl @@ -1,8 +1,10 @@ using CompScienceMeshes, BEAST + using LinearAlgebra -using Profile -using TimerOutputs +#using Profile +#using TimerOutputs using StaticArrays +using Plots function tau(x::SVector{U,T}) where {U,T} 1.0-1.0/5.0 @@ -42,7 +44,7 @@ using Plots plot(xlabel="theta") plot!(Θ, norm.(ff), label="far field", title="D-VIE") =# -using Plots + #NearField Z = range(-1,1,length=100) Y = range(-1,1,length=100) diff --git a/examples/dvsie.jl b/examples/dvsie.jl index dfa822c5..2249dc17 100644 --- a/examples/dvsie.jl +++ b/examples/dvsie.jl @@ -1,8 +1,8 @@ using CompScienceMeshes, BEAST using LinearAlgebra using Profile -using LiftedMaps -using BlockArrays +#using LiftedMaps +#using BlockArrays using StaticArrays @@ -128,8 +128,8 @@ x, ch = IterativeSolvers.gmres!(x, precond*A_dvsie, precond*Rhs, log=true, relt ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] # Don't forget the far field comprises two contributions -ffm = potential(MWFarField3D(κ*im), ffpoints, u_n[m], Y) -ffj = potential(MWFarField3D(κ*im), ffpoints, u_n[j], Y) +ffm = potential(MWFarField3D(gamma=κ*im), ffpoints, u_n[m], Y) +ffj = potential(MWFarField3D(gamma=κ*im), ffpoints, u_n[j], Y) ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) diff --git a/src/BEAST.jl b/src/BEAST.jl index a60b4b0d..85fefa64 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -14,7 +14,7 @@ using FastGaussQuadrature using LinearMaps using LiftedMaps -using TimerOutputs +#using TimerOutputs import LinearAlgebra: cross, dot import LinearAlgebra: ×, ⋅ @@ -190,10 +190,10 @@ include("timedomain/zdomain.jl") include("maxwell/mwexc.jl") include("maxwell/mwops.jl") include("maxwell/wiltonints.jl") -include("maxwell/sauterschwabints_rt.jl") +include("maxwell/nxdbllayer.jl") include("maxwell/sauterschwabints_bdm.jl") include("maxwell/sauterschwabints_bdm_rt.jl") -include("maxwell/nxdbllayer.jl") +include("maxwell/sauterschwabints_rt.jl") include("maxwell/nitsche.jl") include("maxwell/farfield.jl") include("maxwell/nearfield.jl") @@ -250,6 +250,6 @@ export x̂, ŷ, ẑ const n = NormalVector() export n -const to = TimerOutput() +#const to = TimerOutput() end # module diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index 3c140015..36eb9337 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -75,7 +75,7 @@ end function momintegrals!(op::VIEOperator, test_local_space::RefSpace, trial_local_space::RefSpace, test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) - @timeit to "singular" begin + #@timeit to "singular" begin #Find permutation of vertices to match location of singularity to SauterSchwab J, I= SauterSchwab3D.reorder(strat.sing) @@ -128,12 +128,12 @@ function momintegrals!(op::VIEOperator, end end - end + #end #end of timeit nothing end function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) - @timeit to "regular" begin + #@timeit to "regular" begin # memory allocation here is a result from the type instability on strat # which is on purpose, i.e. the momintegrals! method is chosen based # on dynamic polymorphism. @@ -168,7 +168,7 @@ function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::Do end end - end + #end #end of @timeit return z end From 2ca5a816c31ac2159b55787704b235e8e94d6861 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 12 Oct 2022 12:44:18 +0200 Subject: [PATCH 193/528] set version to v1.7.0 --- Project.toml | 4 +- src/integralop.jl | 118 ++++++++++++++++------------- src/maxwell/sauterschwabints_rt.jl | 103 ++++++++++++------------- test/test_assemble_refinements.jl | 21 +++-- test/test_ss_nested_meshes.jl | 33 ++++++-- 5 files changed, 153 insertions(+), 126 deletions(-) diff --git a/Project.toml b/Project.toml index 36ffbd5b..a5880a3b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.6.0" +version = "1.7.0" [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" @@ -30,7 +30,7 @@ WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" CollisionDetection = "0.1.5" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.4.1" +CompScienceMeshes = "0.5" Compat = "2, 3, 4" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4" diff --git a/src/integralop.jl b/src/integralop.jl index 33d075e4..70d5b448 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -68,8 +68,8 @@ Computes the matrix of operator biop wrt the finite element spaces tfs and bfs function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; quadstrat=defaultquadstrat(biop, tfs, bfs)) - test_elements, tad = assemblydata(tfs) - bsis_elements, bad = assemblydata(bfs) + test_elements, tad, tcells = assemblydata(tfs) + bsis_elements, bad, bcells = assemblydata(bfs) tshapes = refspace(tfs); num_tshapes = numfunctions(tshapes) bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes) @@ -80,23 +80,15 @@ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; qd = quaddata(biop, tshapes, bshapes, test_elements, bsis_elements, quadstrat) zlocal = zeros(scalartype(biop, tfs, bfs), 2num_tshapes, 2num_bshapes) - # if !CompScienceMeshes.refines(tfs.geo, bfs.geo) - # assemblechunk_body!(biop, - # tshapes, test_elements, tad, - # bshapes, bsis_elements, bad, - # qd, zlocal, store; quadstrat) - # else if CompScienceMeshes.refines(tgeo, bgeo) - # @info "assemblechunk: test refines trial mesh" - assemblechunk_body_nested_meshes!(biop, - tshapes, test_elements, tad, - bshapes, bsis_elements, bad, - qd, zlocal, store; quadstrat) + assemblechunk_body_test_refines_trial!(biop, + tfs, test_elements, tad, tcells, + bfs, bsis_elements, bad, bcells, + qd, zlocal, store; quadstrat) elseif CompScienceMeshes.refines(bgeo, tgeo) - # @info "assemblechunk: trial refines test mesh" assemblechunk_body_trial_refines_test!(biop, - tshapes, test_elements, tad, - bshapes, bsis_elements, bad, + tfs, test_elements, tad, tcells, + bfs, bsis_elements, bad, bcells, qd, zlocal, store; quadstrat) else assemblechunk_body!(biop, @@ -141,20 +133,27 @@ function assemblechunk_body!(biop, end -function assemblechunk_body_nested_meshes!(biop, - test_shapes, test_elements, test_assembly_data, - trial_shapes, trial_elements, trial_assembly_data, - qd, zlocal, store; quadstrat) +function assemblechunk_body_test_refines_trial!(biop, + test_functions, test_charts, test_assembly_data, test_cells, + trial_functions, trial_charts, trial_assembly_data, trial_cells, + qd, zlocal, store; quadstrat) + + test_shapes = refspace(test_functions) + trial_shapes = refspace(trial_functions) myid = Threads.threadid() myid == 1 && print("dots out of 10: ") - todo, done, pctg = length(test_elements), 0, 0 - for (p,tcell) in enumerate(test_elements) - for (q,bcell) in enumerate(trial_elements) + todo, done, pctg = length(test_charts), 0, 0 + for (p,(tcell,tchart)) in enumerate(zip(test_cells, test_charts)) + for (q,(bcell,bchart)) in enumerate(zip(trial_cells, trial_charts)) fill!(zlocal, 0) - qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, qd, quadstrat) - momintegrals_nested!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule, quadstrat) + qrule = quadrule(biop, test_shapes, trial_shapes, p, tchart, q, bchart, qd, quadstrat) + momintegrals_test_refines_trial!(zlocal, biop, + test_functions, tcell, tchart, + trial_functions, bcell, bchart, + qrule, quadstrat) + I = length(test_assembly_data[p]) J = length(trial_assembly_data[q]) for j in 1 : J, i in 1 : I @@ -162,7 +161,7 @@ function assemblechunk_body_nested_meshes!(biop, for (n,b) in trial_assembly_data[q][j] zb = zij*b for (m,a) in test_assembly_data[p][i] - store(a*zb, m, n) + store(a*zb, m, n) end end end end done += 1 @@ -176,36 +175,43 @@ end function assemblechunk_body_trial_refines_test!(biop, - test_shapes, test_elements, test_assembly_data, - trial_shapes, trial_elements, trial_assembly_data, + test_functions, test_charts, test_assembly_data, test_cells, + trial_functions, trial_charts, trial_assembly_data, trial_cells, qd, zlocal, store; quadstrat) -myid = Threads.threadid() -myid == 1 && print("dots out of 10: ") -todo, done, pctg = length(test_elements), 0, 0 -for (p,tcell) in enumerate(test_elements) - for (q,bcell) in enumerate(trial_elements) + test_shapes = refspace(test_functions) + trial_shapes = refspace(trial_functions) - fill!(zlocal, 0) - qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, qd, quadstrat) - momintegrals_trial_refines_test!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule, quadstrat) - I = length(test_assembly_data[p]) - J = length(trial_assembly_data[q]) - for j in 1 : J, i in 1 : I - zij = zlocal[i,j] - for (n,b) in trial_assembly_data[q][j] - zb = zij*b - for (m,a) in test_assembly_data[p][i] - store(a*zb, m, n) - end end end end - - done += 1 - new_pctg = round(Int, done / todo * 100) - if new_pctg > pctg + 9 - myid == 1 && print(".") - pctg = new_pctg - end end -myid == 1 && println("") + myid = Threads.threadid() + myid == 1 && print("dots out of 10: ") + todo, done, pctg = length(test_charts), 0, 0 + for (p,(tcell,tchart)) in enumerate(zip(test_cells, test_charts)) + for (q,(bcell,bchart)) in enumerate(zip(trial_cells, trial_charts)) + + fill!(zlocal, 0) + qrule = quadrule(biop, test_shapes, trial_shapes, p, tchart, q, bchart, qd, quadstrat) + momintegrals_trial_refines_test!(zlocal, biop, + test_functions, tcell, tchart, + trial_functions, bcell, bchart, + qrule, quadstrat) + + I = length(test_assembly_data[p]) + J = length(trial_assembly_data[q]) + for j in 1 : J, i in 1 : I + zij = zlocal[i,j] + for (n,b) in trial_assembly_data[q][j] + zb = zij*b + for (m,a) in test_assembly_data[p][i] + store(a*zb, m, n) + end end end end + + done += 1 + new_pctg = round(Int, done / todo * 100) + if new_pctg > pctg + 9 + myid == 1 && print(".") + pctg = new_pctg + end end + myid == 1 && println("") end @@ -362,7 +368,11 @@ function assembleblock_body_nested!(biop::IntegralOperator, fill!(zlocals[Threads.threadid()], 0) qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) - momintegrals_nested!(biop, test_shapes, trial_shapes, tcell, bcell, zlocals[Threads.threadid()], qrule, quadstrat) + momintegrals_test_refines_trial!(zlocals[Threads.threadid()], biop, + tfs, p, tcell, + bfs, q, bcell, + qrule, quadstrat) + # momintegrals_test_refines_trial!(biop, test_shapes, trial_shapes, tcell, bcell, zlocals[Threads.threadid()], qrule, quadstrat) for j in 1 : size(zlocals[Threads.threadid()],2) for i in 1 : size(zlocals[Threads.threadid()],1) diff --git a/src/maxwell/sauterschwabints_rt.jl b/src/maxwell/sauterschwabints_rt.jl index b842554c..32745e47 100644 --- a/src/maxwell/sauterschwabints_rt.jl +++ b/src/maxwell/sauterschwabints_rt.jl @@ -109,33 +109,37 @@ function (igd::Integrand{<:MWDoubleLayer3DReg})(x,y,f,g) return _krondot(fvalue, G) end -const MWOperator3D = Union{MWSingleLayer3D, MWDoubleLayer3D} -function momintegrals_nested!(op::MWOperator3D, - test_local_space::RTRefSpace, trial_local_space::RTRefSpace, - test_chart, trial_chart, out, strat::SauterSchwabStrategy, quadstrat) +function momintegrals_test_refines_trial!(out, op, + test_functions, test_cell, test_chart, + trial_functions, trial_cell, trial_chart, + quadrule, quadstrat) - # 1. Refine the trial_chart - p1, p2, p3 = trial_chart.vertices + test_local_space = refspace(test_functions) + trial_local_space = refspace(trial_functions) - # TODO: generalise this to include more general refinements - e1 = cartesian(neighborhood(trial_chart, (0,1/2))) - e2 = cartesian(neighborhood(trial_chart, (1/2,0))) - e3 = cartesian(neighborhood(trial_chart, (1/2,1/2))) + momintegrals!(op, test_local_space, trial_local_space, + test_chart, trial_chart, out, quadrule) +end + +# const MWOperator3D = Union{MWSingleLayer3D, MWDoubleLayer3D} +function momintegrals_test_refines_trial!(out, op, + test_functions, test_cell, test_chart, + trial_functions, trial_cell, trial_chart, + qr::SauterSchwabStrategy, quadstrat) - ct = cartesian(center(trial_chart)) + test_local_space = refspace(test_functions) + trial_local_space = refspace(trial_functions) - refined_trial_chart = SA[ - simplex(ct, p1, e3), - simplex(ct, e3, p2), - simplex(ct, p2, e1), - simplex(ct, e1, p3), - simplex(ct, p3, e2), - simplex(ct, e2, p1)] + test_mesh = geometry(test_functions) + trial_mesh = geometry(trial_functions) + + parent_mesh = CompScienceMeshes.parent(test_mesh) + trial_charts = [chart(test_mesh, p) for p in CompScienceMeshes.children(parent_mesh, trial_cell)] qd = quaddata(op, test_local_space, trial_local_space, - [test_chart], refined_trial_chart, quadstrat) + [test_chart], trial_charts, quadstrat) - for (q,chart) in enumerate(refined_trial_chart) + for (q,chart) in enumerate(trial_charts) qr = quadrule(op, test_local_space, trial_local_space, 1, test_chart, q ,chart, qd, quadstrat) @@ -147,54 +151,42 @@ function momintegrals_nested!(op::MWOperator3D, for j in 1:3 for i in 1:3 for k in 1:3 - out[i,j] += zlocal[i,k] * Q[j,k] - end end end - end -end + out[i,j] += zlocal[i,k] * Q[j,k] +end end end end end -function momintegrals_nested!(op::IntegralOperator, - test_local_space::RefSpace, trial_local_space::RefSpace, - test_chart, trial_chart, out, strat, quadstrat) - momintegrals!(op, test_local_space, trial_local_space, - test_chart, trial_chart, out, strat) -end -function momintegrals_trial_refines_test!(op::IntegralOperator, - test_local_space::RefSpace, trial_local_space::RefSpace, - test_chart, trial_chart, out, strat, quadstrat) +function momintegrals_trial_refines_test!(out, op, + test_functions, test_cell, test_chart, + trial_functions, trial_cell, trial_chart, + quadrule, quadstrat) + + test_local_space = refspace(test_functions) + trial_local_space = refspace(trial_functions) momintegrals!(op, test_local_space, trial_local_space, - test_chart, trial_chart, out, strat) + test_chart, trial_chart, out, quadrule) end -function momintegrals_trial_refines_test!(op::MWOperator3D, - test_local_space::RTRefSpace, trial_local_space::RTRefSpace, - test_chart, trial_chart, out, strat::SauterSchwabStrategy, quadstrat) - - # 1. Refine the test_chart - p1, p2, p3 = test_chart.vertices +function momintegrals_trial_refines_test!(out, op, + test_functions, test_cell, test_chart, + trial_functions, trial_cell, trial_chart, + qr::SauterSchwabStrategy, quadstrat) - # TODO: generalise this to include more general refinements - e1 = cartesian(neighborhood(test_chart, (0,1/2))) - e2 = cartesian(neighborhood(test_chart, (1/2,0))) - e3 = cartesian(neighborhood(test_chart, (1/2,1/2))) + test_local_space = refspace(test_functions) + trial_local_space = refspace(trial_functions) - ct = cartesian(center(test_chart)) + test_mesh = geometry(test_functions) + trial_mesh = geometry(trial_functions) - refined_test_chart = SA[ - simplex(ct, p1, e3), - simplex(ct, e3, p2), - simplex(ct, p2, e1), - simplex(ct, e1, p3), - simplex(ct, p3, e2), - simplex(ct, e2, p1)] + parent_mesh = CompScienceMeshes.parent(trial_mesh) + test_charts = [chart(trial_mesh, p) for p in CompScienceMeshes.children(parent_mesh, test_cell)] qd = quaddata(op, test_local_space, trial_local_space, - refined_test_chart, [trial_chart], quadstrat) + test_charts, [trial_chart], quadstrat) - for (p,chart) in enumerate(refined_test_chart) + for (p,chart) in enumerate(test_charts) qr = quadrule(op, test_local_space, trial_local_space, p, chart, 1, trial_chart, qd, quadstrat) @@ -206,8 +198,7 @@ function momintegrals_trial_refines_test!(op::MWOperator3D, for j in 1:3 for i in 1:3 for k in 1:3 - # out[i,j] += zlocal[i,k] * Q[j,k] - out[i,j] += Q[i,k] * zlocal[k,j] + out[i,j] += Q[i,k] * zlocal[k,j] end end end end end \ No newline at end of file diff --git a/test/test_assemble_refinements.jl b/test/test_assemble_refinements.jl index b737c962..04d7ef60 100644 --- a/test/test_assemble_refinements.jl +++ b/test/test_assemble_refinements.jl @@ -26,28 +26,33 @@ Y = buffachristiansen(Γ) Kop = Maxwell3D.doublelayer(wavenumber=0.0) -K1 = assemble(Kop, X, Y) -K2 = assemble(Kop, Y, X) +qs = BEAST.defaultquadstrat(Kop, X, Y) +qs = BEAST.DoubleNumWiltonSauterQStrat(1, 1, 12, 13, 12, 12, 12, 12) +K1 = assemble(Kop, X, Y; quadstrat=qs) +K2 = assemble(Kop, Y, X; quadstrat=qs) -@test K1[1,1] ≈ K2[1,1] rtol=1e-3 +K1[1,1] - K2[1,1] +@test K1[1,1] ≈ K2[1,1] rtol=1e-7 -# Verify that quadrature strategy is passed true -qds(i) = BEAST.DoubleNumWiltonSauterQStrat(1,1,6,7,i,i,i,i) +# Verify that quadrature strategy is passed through +qds(i) = BEAST.DoubleNumWiltonSauterQStrat(3,3,8,8,i,i,i,i) Kxy_1 = (assemble(Kop, X, Y, quadstrat=qds(1)))[1,1] Kxy_2 = (assemble(Kop, X, Y, quadstrat=qds(5)))[1,1] Kxy_3 = (assemble(Kop, X, Y, quadstrat=qds(10)))[1,1] +# Kxy_4 = (assemble(Kop, X, Y, quadstrat=qds(15)))[1,1] -@test 0.1 < abs(Kxy_1 - Kxy_3)/abs(Kxy_3) < 0.2 -@test 0.0008 < abs(Kxy_2 - Kxy_3)/abs(Kxy_3) < 0.0009 +@test 0.1 < abs(Kxy_1 - Kxy_3)/abs(Kxy_3) < 0.4 +@test 0.0008 < abs(Kxy_2 - Kxy_3)/abs(Kxy_3) < 0.002 Kyx_1 = (assemble(Kop, Y, X, quadstrat=qds(1)))[1,1] Kyx_2 = (assemble(Kop, Y, X, quadstrat=qds(5)))[1,1] Kyx_3 = (assemble(Kop, Y, X, quadstrat=qds(10)))[1,1] +# Kyx_4 = (assemble(Kop, Y, X, quadstrat=qds(15)))[1,1] @test 0.2 < abs(Kyx_1 - Kyx_3)/abs(Kyx_3) < 0.3 @test 0.0006 < abs(Kyx_2 - Kyx_3)/abs(Kyx_3) < 0.0007 -Γtr = translate(Γ, SVector(2.0, 0.0, 0.0)) +Γtr = CompScienceMeshes.translate(Γ, SVector(2.0, 0.0, 0.0)) Γ2 = weld(Γ, Γtr) diff --git a/test/test_ss_nested_meshes.jl b/test/test_ss_nested_meshes.jl index 2fea234b..8eff8cb2 100644 --- a/test/test_ss_nested_meshes.jl +++ b/test/test_ss_nested_meshes.jl @@ -17,11 +17,25 @@ d = point(1/3,-1/3,0) # Relative weights chosen to make the two terms equally important 𝒜 = Maxwell3D.singlelayer(wavenumber=0.0, alpha=22.0, beta=1.0) -𝒳 = BEAST.RTRefSpace{Float64}() + +coarse = Mesh([o,x̂,ŷ], [CompScienceMeshes.index(1,2,3)]) +fine = barycentric_refinement(coarse) + +X = raviartthomas(coarse, skeleton(coarse,1)) +Y = raviartthomas(fine, skeleton(fine,1)) + +𝒳 = refspace(X) T = scalartype(𝒜, 𝒳, 𝒳) +@test 𝒳 == refspace(Y) +@test CompScienceMeshes.parent(geometry(Y)) == geometry(X) -test_chart = simplex(e,x̂,c) -trial_chart = simplex(o,x̂,ŷ) +p = 6 +trial_chart = chart(coarse,1) +test_chart = chart(fine,p) +# @show test_chart.vertices + +# test_chart_old = simplex(e,x̂,c) +# trial_chart_old = simplex(o,x̂,ŷ) test_quadpoints = BEAST.quadpoints(𝒳, [test_chart], (12,))[1,1] trial_quadpoints = BEAST.quadpoints(𝒳, [trial_chart], (13,))[1,1] @@ -39,13 +53,20 @@ end qs_strat = BEAST.DoubleNumWiltonSauterQStrat(1,1,6,7,10,10,10,10) -sauterschwab = BEAST.SauterSchwabQuadrature.CommonFace(nothing) +sauterschwab = BEAST.SauterSchwabQuadrature.CommonFace(BEAST._legendre(10,0.0,1.0)) out_ss = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) -BEAST.momintegrals_nested!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss,sauterschwab,qs_strat) +BEAST.momintegrals_test_refines_trial!(out_ss, 𝒜, + Y, p, test_chart, + X, 1, trial_chart, + sauterschwab, qs_strat) + wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) out_dw = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) -BEAST.momintegrals_nested!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw,wiltonsingext,qs_strat) +BEAST.momintegrals_test_refines_trial!(out_dw, 𝒜, + Y, p, test_chart, + X, 1, trial_chart, + wiltonsingext, qs_strat) @show norm(out_ss-out_dw) / norm(out_dw) From 4add0099421a2378355214d3609c0b2ea8738d04 Mon Sep 17 00:00:00 2001 From: Paula Respondek Date: Wed, 5 Oct 2022 11:11:36 +0200 Subject: [PATCH 194/528] Add Helmholtz3D Near Operators The singlelayer, transposed doublelayer and hypersingular near field operator are added to enable potential and field calculation away from the surfaces for the Helmholtz case. Unit tests and an example studying the convergence with h-refinement were added. --- examples/hh3dexample.jl | 225 ++++++++++++++++++++++++++++++++++++ src/BEAST.jl | 6 + src/helmholtz3d/hh3dnear.jl | 70 ++++++++++- test/runtests.jl | 1 + test/test_hh3d_nearfield.jl | 180 +++++++++++++++++++++++++++++ 5 files changed, 479 insertions(+), 3 deletions(-) create mode 100644 examples/hh3dexample.jl create mode 100644 test/test_hh3d_nearfield.jl diff --git a/examples/hh3dexample.jl b/examples/hh3dexample.jl new file mode 100644 index 00000000..f4560ce8 --- /dev/null +++ b/examples/hh3dexample.jl @@ -0,0 +1,225 @@ +using BEAST +using CompScienceMeshes +using StaticArrays +using LinearAlgebra +using Plotly + +## Looking at convergence +hs = [0.3, 0.2, 0.1, 0.09]#,0.08,0.07,0.06,0.04] +ir = 0.8 +err_IDPSL_pot = zeros(Float64, length(hs)) +err_IDPDL_pot = zeros(Float64, length(hs)) +err_INPSL_pot = zeros(Float64, length(hs)) +err_INPDL_pot = zeros(Float64, length(hs)) +err_IDPSL_field = zeros(Float64, length(hs)) +err_IDPDL_field = zeros(Float64, length(hs)) +err_INPSL_field = zeros(Float64, length(hs)) +err_INPDL_field = zeros(Float64, length(hs)) + +err_EDPSL_pot = zeros(Float64, length(hs)) +err_EDPDL_pot = zeros(Float64, length(hs)) +err_ENPSL_pot = zeros(Float64, length(hs)) +err_ENPDL_pot = zeros(Float64, length(hs)) + +err_EDPSL_field = zeros(Float64, length(hs)) +err_EDPDL_field = zeros(Float64, length(hs)) +err_ENPSL_field = zeros(Float64, length(hs)) +err_ENPDL_field = zeros(Float64, length(hs)) + +for (i, h) in enumerate(hs) + r = 50.0 + sphere = meshsphere(r, h * r) + X0 = lagrangecxd0(sphere) + X1 = lagrangec0d1(sphere) + + S = Helmholtz3D.singlelayer(; gamma=0.0) + D = Helmholtz3D.doublelayer(; gamma=0.0) + Dt = Helmholtz3D.doublelayer_transposed(; gamma=0.0) + N = -Helmholtz3D.hypersingular(; gamma=0.0) + + q = 100.0 + ϵ = 1.0 + + pos1 = SVector(r * 1.5, 0.0, 0.0) + pos2 = SVector(-r * 1.5, 0.0, 0.0) + Φ_inc(x) = q / (4 * π * ϵ) * (1 / (norm(x - pos1)) - 1 / (norm(x - pos2))) + + function ∂nΦ_inc(x) + return -q / (r * 4 * π * ϵ) * ( + (norm(x)^2 - dot(pos1, x)) / (norm(x - pos1)^3) - + (norm(x)^2 - dot(pos2, x)) / (norm(x - pos2)^3) + ) + end + + function Efield(x) + return q / (4 * π * ϵ) * + ((x - pos1) / (norm(x - pos1)^3) - (x - pos2) / (norm(x - pos2)^3)) + end + + gD0 = assemble(ScalarTrace(Φ_inc), X0) + gD1 = assemble(ScalarTrace(Φ_inc), X1) + gN = assemble(ScalarTrace(∂nΦ_inc), X1) + + G = assemble(Identity(), X1, X1) + o = ones(numfunctions(X1)) + + M_IDPSL = assemble(S, X0, X0) + M_IDPDL = (-1 / 2 * assemble(Identity(), X1, X1) + assemble(D, X1, X1)) + + M_INPSL = (1 / 2 * assemble(Identity(), X1, X1) + assemble(Dt, X1, X1)) + G * o * o' * G + M_INPDL = -assemble(N, X1, X1) + G * o * o' * G + + ρ_IDPSL = M_IDPSL \ (-gD0) + ρ_IDPDL = M_IDPDL \ (gD1) + ρ_INPSL = M_INPSL \ (-gN) + ρ_INPDL = M_INPDL \ (-gN) + + pts = meshsphere(r * ir, r * ir * 0.6).vertices + + pot_IDPSL = potential(HH3DSingleLayerNear(0.0), pts, ρ_IDPSL, X0; type=ComplexF64) + pot_IDPDL = potential(HH3DDoubleLayerNear(0.0), pts, ρ_IDPDL, X1; type=ComplexF64) + pot_INPSL = potential(HH3DSingleLayerNear(0.0), pts, ρ_INPSL, X1; type=ComplexF64) + pot_INPDL = potential(HH3DDoubleLayerNear(0.0), pts, ρ_INPDL, X1; type=ComplexF64) + + err_IDPSL_pot[i] = norm(pot_IDPSL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) + err_IDPDL_pot[i] = norm(pot_IDPDL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) + err_INPSL_pot[i] = norm(pot_INPSL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) + err_INPDL_pot[i] = norm(pot_INPDL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) + + field_IDPSL = potential(HH3DDoubleLayerTransposedNear(0.0), pts, ρ_IDPSL, X0) + field_IDPDL = potential(HH3DHyperSingularNear(0.0), pts, ρ_IDPDL, X1) + field_INPSL = potential(HH3DDoubleLayerTransposedNear(0.0), pts, ρ_INPSL, X1) + field_INPDL = potential(HH3DHyperSingularNear(0.0), pts, ρ_INPDL, X1) + + err_IDPSL_field[i] = norm(field_IDPSL - Efield.(pts)) / norm(Efield.(pts)) + err_IDPDL_field[i] = norm(field_IDPDL + Efield.(pts)) / norm(Efield.(pts)) + err_INPSL_field[i] = norm(field_INPSL - Efield.(pts)) / norm(Efield.(pts)) + err_INPDL_field[i] = norm(field_INPDL + Efield.(pts)) / norm(Efield.(pts)) + + pos1 = SVector(r * 0.5, 0.0, 0.0) + pos2 = SVector(-r * 0.5, 0.0, 0.0) + + # potential of point charges + Φ_inc(x) = q / (4 * π * ϵ) * (1 / (norm(x - pos1)) - 1 / (norm(x - pos2))) + function ∂nΦ_inc(x) + return -q / (r * 4 * π * ϵ) * ( + (norm(x)^2 - dot(pos1, x)) / (norm(x - pos1)^3) - + (norm(x)^2 - dot(pos2, x)) / (norm(x - pos2)^3) + ) + end + + # Efield(x) = -grad Φ_inc(x) + function Efield(x) + return q / (4 * π * ϵ) * + ((x - pos1) / (norm(x - pos1)^3) - (x - pos2) / (norm(x - pos2)^3)) + end + + gD0 = assemble(ScalarTrace(Φ_inc), X0) + gD1 = assemble(ScalarTrace(Φ_inc), X1) + gN = assemble(ScalarTrace(∂nΦ_inc), X1) + + G = assemble(Identity(), X1, X1) + o = ones(numfunctions(X1)) + + M_EDPSL = assemble(S, X0, X0) + M_EDPDL = (1 / 2 * assemble(Identity(), X1, X1) + assemble(D, X1, X1)) + + M_ENPSL = + (-1 / 2 * assemble(Identity(), X1, X1) + assemble(Dt, X1, X1)) + G * o * o' * G + M_ENPDL = -assemble(N, X1, X1) + G * o * o' * G + + ρ_EDPSL = M_EDPSL \ (-gD0) + ρ_EDPDL = M_EDPDL \ (gD1) + + ρ_ENPSL = M_ENPSL \ (-gN) + ρ_ENPDL = M_ENPDL \ (-gN) + + testsphere = meshsphere(r / ir, r / ir * 0.6) + pts = testsphere.vertices[norm.(testsphere.vertices) .> r] + + pot_EDPSL = potential(HH3DSingleLayerNear(0.0), pts, ρ_EDPSL, X0; type=ComplexF64) + pot_EDPDL = potential(HH3DDoubleLayerNear(0.0), pts, ρ_EDPDL, X1; type=ComplexF64) + + pot_ENPSL = potential(HH3DSingleLayerNear(0.0), pts, ρ_ENPSL, X1; type=ComplexF64) + pot_ENPDL = potential(HH3DDoubleLayerNear(0.0), pts, ρ_ENPDL, X1; type=ComplexF64) + + err_EDPSL_pot[i] = norm(pot_EDPSL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) + err_EDPDL_pot[i] = norm(pot_EDPDL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) + err_ENPSL_pot[i] = norm(pot_ENPSL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) + err_ENPDL_pot[i] = norm(pot_ENPDL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) + + field_EDPSL = -potential(HH3DDoubleLayerTransposedNear(0.0), pts, ρ_EDPSL, X0) + field_EDPDL = potential(HH3DHyperSingularNear(0.0), pts, ρ_EDPDL, X1) + field_ENPSL = -potential(HH3DDoubleLayerTransposedNear(0.0), pts, ρ_ENPSL, X1) + field_ENPDL = potential(HH3DHyperSingularNear(0.0), pts, ρ_ENPDL, X1) + + err_EDPSL_field[i] = norm(field_EDPSL + Efield.(pts)) / norm(Efield.(pts)) + err_EDPDL_field[i] = norm(field_EDPDL + Efield.(pts)) / norm(Efield.(pts)) + err_ENPSL_field[i] = norm(field_ENPSL + Efield.(pts)) / norm(Efield.(pts)) + err_ENPDL_field[i] = norm(field_ENPDL + Efield.(pts)) / norm(Efield.(pts)) +end + +## +Plotly.plot( + [ + Plotly.scatter(; x=hs, y=err_IDPSL_pot[:, end], name="IDPSL"), + Plotly.scatter(; x=hs, y=err_IDPDL_pot[:, end], name="IDPDL"), + Plotly.scatter(; x=hs, y=err_INPSL_pot[:, end], name="INPSL"), + Plotly.scatter(; x=hs, y=err_INPDL_pot[:, end], name="INPDL"), + ], + Layout(; + xaxis_type="log", + yaxis_type="log", + xaxis_title="h / r", + yaxis_title="rel. error", + title="Errors - potential - interior problem", + ), +) + +Plotly.plot( + [ + Plotly.scatter(; x=hs, y=err_EDPSL_pot[:, end], name="EDPSL"), + Plotly.scatter(; x=hs, y=err_EDPDL_pot[:, end], name="EDPDL"), + Plotly.scatter(; x=hs, y=err_ENPSL_pot[:, end], name="ENPSL"), + Plotly.scatter(; x=hs, y=err_ENPDL_pot[:, end], name="ENPDL"), + ], + Layout(; + xaxis_type="log", + yaxis_type="log", + xaxis_title="h / r", + yaxis_title="rel. error", + title="Errors - potential - exterior problem", + ), +) + +Plotly.plot( + [ + Plotly.scatter(; x=hs, y=err_IDPSL_field[:, end], name="IDPSL"), + Plotly.scatter(; x=hs, y=err_IDPDL_field[:, end], name="IDPDL"), + Plotly.scatter(; x=hs, y=err_INPSL_field[:, end], name="INPSL"), + Plotly.scatter(; x=hs, y=err_INPDL_field[:, end], name="INPDL"), + ], + Layout(; + xaxis_type="log", + yaxis_type="log", + xaxis_title="h / r", + yaxis_title="rel. error", + title="Errors - field - interior problem", + ), +) + +Plotly.plot( + [ + Plotly.scatter(; x=hs, y=err_EDPSL_field[:, end], name="EDPSL"), + Plotly.scatter(; x=hs, y=err_EDPDL_field[:, end], name="EDPDL"), + Plotly.scatter(; x=hs, y=err_ENPSL_field[:, end], name="ENPSL"), + Plotly.scatter(; x=hs, y=err_ENPDL_field[:, end], name="ENPDL"), + ], + Layout(; + xaxis_type="log", + yaxis_type="log", + xaxis_title="h / r", + yaxis_title="rel. error", + title="Errors - field - exterior problem", + ), +) \ No newline at end of file diff --git a/src/BEAST.jl b/src/BEAST.jl index caec9dbe..b772914d 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -53,6 +53,12 @@ export HH3DSingleLayerTDBIO export HH3DDoubleLayerTDBIO export ∂n export HH3DHyperSingularFDBIO + +export HH3DSingleLayerNear +export HH3DDoubleLayerNear +export HH3DDoubleLayerTransposedNear +export HH3DHyperSingularNear + export NitscheHH3 export MWSingleLayerTDIO export MWDoubleLayerTDIO diff --git a/src/helmholtz3d/hh3dnear.jl b/src/helmholtz3d/hh3dnear.jl index c6a949b3..4b8157f6 100644 --- a/src/helmholtz3d/hh3dnear.jl +++ b/src/helmholtz3d/hh3dnear.jl @@ -3,6 +3,18 @@ struct HH3DDoubleLayerNear{K} gamma::K end +struct HH3DSingleLayerNear{K} + gamma::K +end + +struct HH3DHyperSingularNear{K} + gamma::K +end + +struct HH3DDoubleLayerTransposedNear{K} + gamma::K +end + function HH3DDoubleLayerNear(;wavenumber=error("wavenumber is a required argument")) if iszero(real(wavenumber)) HH3DDoubleLayerNear(-imag(wavenumber)) @@ -11,10 +23,36 @@ function HH3DDoubleLayerNear(;wavenumber=error("wavenumber is a required argumen end end -quaddata(op::HH3DDoubleLayerNear,rs,els) = quadpoints(rs,els,(3,)) -quadrule(op::HH3DDoubleLayerNear,refspace,p,y,q,el,qdata) = qdata[1,q] +function HH3DSingleLayerNear(;wavenumber=error("wavenumber is a required argument")) + if iszero(real(wavenumber)) + HH3DSingleLayerNear(-imag(wavenumber)) + else + HH3DSingleLayerNear(wavenumber*im) + end +end -function kernelvals(op::HH3DDoubleLayerNear,y,p) +function HH3DHyperSingularNear(;wavenumber=error("wavenumber is a required argument")) + if iszero(real(wavenumber)) + HH3DHyperSingularNear(-imag(wavenumber)) + else + HH3DHyperSingularNear(wavenumber*im) + end +end + +function HH3DDoubleLayerTransposedNear(;wavenumber=error("wavenumber is a required argument")) + if iszero(real(wavenumber)) + HH3DDoubleLayerTransposedNear(-imag(wavenumber)) + else + HH3DDoubleLayerTranposedNear(wavenumber*im) + end +end + +HH3DNear = Union{HH3DSingleLayerNear, HH3DDoubleLayerNear, HH3DDoubleLayerTransposedNear, HH3DHyperSingularNear} + +quaddata(op::HH3DNear,rs,els) = quadpoints(rs,els,(3,)) +quadrule(op::HH3DNear,refspace,p,y,q,el,qdata) = qdata[1,q] + +function kernelvals(op::HH3DNear,y,p) γ = op.gamma x = cartesian(p) @@ -44,3 +82,29 @@ function integrand(op::HH3DDoubleLayerNear,krn,y,f,p) return ∂G∂n * fx end + +function integrand(op::HH3DSingleLayerNear, krn, y, f, p) + G = krn.green + fx = f.value + + return G * fx +end + +function integrand(op::HH3DDoubleLayerTransposedNear, krn, y, f, p) + ∇G = krn.gradgreen + fx = f.value + + return ∇G * fx +end + +function integrand(op::HH3DHyperSingularNear, krn, y, f, p) + G = krn.green + nx = krn.nx + γ = krn.γ + invR = 1/krn.R + fx = f.value + r = krn.r + + # returns ∇ᵣn̂'∇ᵣ'G - strong singularity, probably makes problems for small distances R + return (nx * G * invR * (γ + invR) - r * dot(nx,r) * G * invR^2 * (γ^2 + 3 * γ * invR + 3 * invR^2)) * fx +end diff --git a/test/runtests.jl b/test/runtests.jl index 7477ff6d..daa6c09d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -57,6 +57,7 @@ include("test_nitschehh3d.jl") include("test_curlcurlgreen.jl") include("test_hh3dtd_exc.jl") include("test_hh3dexc.jl") +include("test_hh3d_nearfield.jl") include("test_tdassembly.jl") include("test_tdhhdbl.jl") include("test_tdmwdbl.jl") diff --git a/test/test_hh3d_nearfield.jl b/test/test_hh3d_nearfield.jl new file mode 100644 index 00000000..ba239020 --- /dev/null +++ b/test/test_hh3d_nearfield.jl @@ -0,0 +1,180 @@ +using BEAST +using CompScienceMeshes +using StaticArrays +using LinearAlgebra +using Test + +@testset "Helmholtz potential operators" begin + r = 10.0 + λ = 20 * r + k = 2 * π / λ + + sphere = meshsphere(r, 0.2 * r) + X0 = lagrangecxd0(sphere) + X1 = lagrangec0d1(sphere) + + S = Helmholtz3D.singlelayer(; gamma=im * k) + D = Helmholtz3D.doublelayer(; gamma=im * k) + Dt = Helmholtz3D.doublelayer_transposed(; gamma=im * k) + N = Helmholtz3D.hypersingular(; gamma=im * k) + + q = 100.0 + ϵ = 1.0 + + # Interior problem + # Formulations from Sauter and Schwab, Boundary Element Methods(2011), Chapter 3.4.1.1 + pos1 = SVector(r * 1.5, 0.0, 0.0) # positioning of point charges + pos2 = SVector(-r * 1.5, 0.0, 0.0) + + # Potential of point charges + function Φ_inc(x) + return q / (4 * π * ϵ) * ( + exp(-im * k * norm(x - pos1)) / (norm(x - pos1)) - + exp(-im * k * norm(x - pos2)) / (norm(x - pos2)) + ) + end + + function ∂nΦ_inc(x) + return -q / (4 * π * ϵ * r) * ( + (dot( + x, + (x - pos1) * exp(-im * k * norm(x - pos1)) / (norm(x - pos1)^2) * + (im * k + 1 / (norm(x - pos1))), + )) - (dot( + x, + (x - pos2) * exp(-im * k * norm(x - pos2)) / (norm(x - pos2)^2) * + (im * k + 1 / (norm(x - pos2))), + )) + ) + end + + gD0 = assemble(ScalarTrace(Φ_inc), X0) + gD1 = assemble(ScalarTrace(Φ_inc), X1) + gN = assemble(ScalarTrace(∂nΦ_inc), X1) + + G = assemble(Identity(), X1, X1) + o = ones(numfunctions(X1)) + + # Interior Dirichlet problem + M_IDPSL = assemble(S, X0, X0) # Single layer (SL) + M_IDPDL = (-1 / 2 * assemble(Identity(), X1, X1) + assemble(D, X1, X1)) # Double layer (DL) + + # Interior Neumann problem + # Neumann derivative from DL potential with deflected nullspace + M_INPDL = -assemble(N, X1, X1) + G * o * o' * G + # Neumann derivative from SL potential with deflected nullspace + M_INPSL = (1 / 2 * assemble(Identity(), X1, X1) + assemble(Dt, X1, X1)) + G * o * o' * G + + ρ_IDPSL = M_IDPSL \ (-gD0) + ρ_IDPDL = M_IDPDL \ (gD1) + + ρ_INPDL = M_INPDL \ (gN) + ρ_INPSL = M_INPSL \ (-gN) + + pts = meshsphere(0.8 * r, 0.8 * 0.6 * r).vertices # sphere inside on which the potential and field are evaluated + + pot_IDPSL = potential(HH3DSingleLayerNear(im * k), pts, ρ_IDPSL, X0; type=ComplexF64) + pot_IDPDL = potential(HH3DDoubleLayerNear(im * k), pts, ρ_IDPDL, X1; type=ComplexF64) + + pot_INPSL = potential(HH3DSingleLayerNear(im * k), pts, ρ_INPSL, X1; type=ComplexF64) + pot_INPDL = potential(HH3DDoubleLayerNear(im * k), pts, ρ_INPDL, X1; type=ComplexF64) + + # Total field inside should be zero + err_IDPSL_pot = norm(pot_IDPSL + Φ_inc.(pts)) / norm(Φ_inc.(pts)) + err_IDPDL_pot = norm(pot_IDPDL + Φ_inc.(pts)) / norm(Φ_inc.(pts)) + err_INPSL_pot = norm(pot_INPSL + Φ_inc.(pts)) / norm(Φ_inc.(pts)) + err_INPDL_pot = norm(pot_INPDL + Φ_inc.(pts)) / norm(Φ_inc.(pts)) + + # Efield(x) = - grad Φ_inc(x) + function Efield(x) + return q / (4 * π * ϵ) * ( + ( + (x - pos1) * exp(-im * k * norm(x - pos1)) / (norm(x - pos1)^2) * + (im * k + 1 / (norm(x - pos1))) + ) - ( + (x - pos2) * exp(-im * k * norm(x - pos2)) / (norm(x - pos2)^2) * + (im * k + 1 / (norm(x - pos2))) + ) + ) + end + + field_IDPSL = -potential(HH3DDoubleLayerTransposedNear(im * k), pts, ρ_IDPSL, X0) + field_IDPDL = potential(HH3DHyperSingularNear(im * k), pts, ρ_IDPDL, X1) + field_INPSL = -potential(HH3DDoubleLayerTransposedNear(im * k), pts, ρ_INPSL, X1) + field_INPDL = potential(HH3DHyperSingularNear(im * k), pts, ρ_INPDL, X1) + + err_IDPSL_field = norm(field_IDPSL + Efield.(pts)) / norm(Efield.(pts)) + err_IDPDL_field = norm(field_IDPDL + Efield.(pts)) / norm(Efield.(pts)) + err_INPSL_field = norm(field_INPSL + Efield.(pts)) / norm(Efield.(pts)) + err_INPDL_field = norm(field_INPDL + Efield.(pts)) / norm(Efield.(pts)) + + # Exterior problem + # formulations from Sauter and Schwab, Boundary Element Methods(2011), Chapter 3.4.1.2 + + pos1 = SVector(r * 0.5, 0.0, 0.0) + pos2 = SVector(-r * 0.5, 0.0, 0.0) + + gD0 = assemble(ScalarTrace(Φ_inc), X0) + gD1 = assemble(ScalarTrace(Φ_inc), X1) + gN = assemble(ScalarTrace(∂nΦ_inc), X1) + + G = assemble(Identity(), X1, X1) + o = ones(numfunctions(X1)) + + M_EDPSL = assemble(S, X0, X0) + M_EDPDL = (1 / 2 * assemble(Identity(), X1, X1) + assemble(D, X1, X1)) + + M_ENPDL = -assemble(N, X1, X1) + G * o * o' * G + M_ENPSL = -1 / 2 * assemble(Identity(), X1, X1) + assemble(Dt, X1, X1) + G * o * o' * G + + ρ_EDPSL = M_EDPSL \ (-gD0) + ρ_EDPDL = M_EDPDL \ (gD1) + + ρ_ENPDL = M_ENPDL \ gN + ρ_ENPSL = M_ENPSL \ (-gN) + + testsphere = meshsphere(1.2 * r, 1.2 * 0.6 * r) + pts = testsphere.vertices[norm.(testsphere.vertices) .> r] + + pot_EDPSL = potential(HH3DSingleLayerNear(im * k), pts, ρ_EDPSL, X0; type=ComplexF64) + pot_EDPDL = potential(HH3DDoubleLayerNear(im * k), pts, ρ_EDPDL, X1; type=ComplexF64) + pot_ENPDL = potential(HH3DDoubleLayerNear(im * k), pts, ρ_ENPDL, X1; type=ComplexF64) + pot_ENPSL = potential(HH3DSingleLayerNear(im * k), pts, ρ_ENPSL, X1; type=ComplexF64) + + err_EDPSL_pot = norm(pot_EDPSL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) + err_EDPDL_pot = norm(pot_EDPDL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) + err_ENPSL_pot = norm(pot_ENPSL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) + err_ENPDL_pot = norm(pot_ENPDL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) + + field_EDPSL = -potential(HH3DDoubleLayerTransposedNear(im * k), pts, ρ_EDPSL, X0) + field_EDPDL = potential(HH3DHyperSingularNear(im * k), pts, ρ_EDPDL, X1) + field_ENPSL = -potential(HH3DDoubleLayerTransposedNear(im * k), pts, ρ_ENPSL, X1) + field_ENPDL = potential(HH3DHyperSingularNear(im * k), pts, ρ_ENPDL, X1) + + err_EDPSL_field = norm(field_EDPSL + Efield.(pts)) / norm(Efield.(pts)) + err_EDPDL_field = norm(field_EDPDL + Efield.(pts)) / norm(Efield.(pts)) + err_ENPSL_field = norm(field_ENPSL + Efield.(pts)) / norm(Efield.(pts)) + err_ENPDL_field = norm(field_ENPDL + Efield.(pts)) / norm(Efield.(pts)) + + # errors of interior problems + @test err_IDPSL_pot < 0.01 + @test err_IDPDL_pot < 0.01 + @test err_INPSL_pot < 0.02 + @test err_INPDL_pot < 0.01 + + @test err_IDPSL_field < 0.01 + @test err_IDPDL_field < 0.02 + @test err_INPSL_field < 0.02 + @test err_INPDL_field < 0.01 + + # errors of exterior problems + @test err_EDPSL_pot < 0.01 + @test err_EDPDL_pot < 0.02 + @test err_ENPSL_pot < 0.01 + @test err_ENPDL_pot < 0.01 + + @test err_EDPSL_field < 0.01 + @test err_EDPDL_field < 0.03 + @test err_ENPSL_field < 0.01 + @test err_ENPDL_field < 0.02 +end \ No newline at end of file From adc34074094fae7ea3a1cc41d3f71c443edfcd99 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 28 Oct 2022 13:07:36 +0200 Subject: [PATCH 195/528] factored out all convop structs in ConvolutionOperators.jl --- Project.toml | 2 + examples/poltor.jl | 79 ++++++++++ examples/tdefie.jl | 7 +- examples/tdmfie.jl | 2 +- examples/tdmfie_sym.jl | 2 +- examples/tdpmchwt.jl | 50 +++++- examples/tdpmchwt_int.jl | 107 +++++++++++++ src/BEAST.jl | 7 +- src/maxwell/sauterschwabints_bdm.jl | 6 +- src/solvers/lusolver.jl | 13 +- src/solvers/solver.jl | 231 ++++++++++++++++++---------- src/timedomain/motlu.jl | 184 +++++++++++----------- src/timedomain/tdintegralop.jl | 7 +- src/utils/sparsend.jl | 12 +- test/runtests.jl | 2 +- test/test_compressed_storage.jl | 76 +++++++-- test/test_raviartthomas.jl | 6 +- test/test_rtx.jl | 4 +- test/test_tdhhdbl.jl | 6 +- test/test_tdmwdbl.jl | 13 +- test/test_tdop_scaling.jl | 9 +- test/test_ttrace.jl | 4 +- 22 files changed, 613 insertions(+), 216 deletions(-) create mode 100644 examples/poltor.jl create mode 100644 examples/tdpmchwt_int.jl diff --git a/Project.toml b/Project.toml index a5880a3b..4f93c09b 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ CollisionDetection = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" +ConvolutionOperators = "15927181-a1bb-497c-b745-8dbf505c019d" Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" @@ -32,6 +33,7 @@ CollisionDetection = "0.1.5" Combinatorics = "0.7, 1" CompScienceMeshes = "0.5" Compat = "2, 3, 4" +ConvolutionOperators = "0.2" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4" FillArrays = "0.11, 0.12, 0.13" diff --git a/examples/poltor.jl b/examples/poltor.jl new file mode 100644 index 00000000..501d1001 --- /dev/null +++ b/examples/poltor.jl @@ -0,0 +1,79 @@ +using CompScienceMeshes +using LinearAlgebra + +function cone(p,q; sizemode="absolute", sizeref=2, kwargs...) + x = getindex.(p,1) + y = getindex.(p,2) + z = getindex.(p,3) + u = getindex.(q,1) + v = getindex.(q,2) + w = getindex.(q,3) + Plotly.cone(;x,y,z,u,v,w, sizemode, sizeref, kwargs...) +end + + +fn = joinpath(dirname(pathof(CompScienceMeshes)),"geos/torus.geo") +Γ = CompScienceMeshes.meshgeo(fn; dim=2, h=0.5) + +Γ0 = skeleton(Γ,0) +Γ1 = skeleton(Γ,1) + +Σ = connectivity(Γ, Γ1, sign) +Λ = connectivity(Γ0, Γ1, sign) + +# using Plotly +# Plotly.plot([patch(Γ, opacity=0.5), wireframe(Γ)]) + +using BEAST +X = raviartthomas(Γ) +Y = buffachristiansen(Γ) + +K = Maxwell3D.doublelayer(gamma=0.0) +Id = BEAST.Identity() +Nx = BEAST.NCross() + +M = K + 0.5Nx +qs = BEAST.defaultquadstrat(M,Y,X) +qs[2] = BEAST.DoubleNumWiltonSauterQStrat(6, 7, 6, 7, 9, 9, 9, 9) + +Myx = assemble(M, Y, X, quadstrat=qs) + +using LinearAlgebra +(;U,S,V) = svd(Myx) +v0 = V[:,end] + +fcr, geo = facecurrents(v0, X) +pts = [cartesian(center(chart(Γ,p))) for p in Γ] + +import Plotly +pt1 = patch(Γ,norm.(fcr), opacity=0.5) +pt2 = cone(pts, fcr, sizeref=0.2) +Plotly.plot([pt1, pt2]) + +G = assemble(Id,X,X) + + +norm(v0) +norm(Σ'*v0) +norm(Λ'*G*v0) + +# Study the kernel of the PMCHWT +SL = Maxwell3D.singlelayer(wavenumber=0.01) +DL = Maxwell3D.doublelayer(wavenumber=0.01) + +@hilbertspace m j +@hilbertspace k l +a = + DL[k,m] - SL[k,j] + + SL[l,m] + DL[l,j] + +A = assemble(@discretise(a, m∈X, j∈X, k∈X, l∈X)) +M = Matrix(A) +(;U,S,V) = svd(M) + +using Plots +plotly() +plot() + +v0 = V[:,end] +v1 = V[:,end-1] diff --git a/examples/tdefie.jl b/examples/tdefie.jl index b58e4dcd..13374a94 100644 --- a/examples/tdefie.jl +++ b/examples/tdefie.jl @@ -1,6 +1,6 @@ using CompScienceMeshes, BEAST, LinearAlgebra -# Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) -Γ = meshsphere(radius=1.0, h=0.1) +Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +# Γ = meshsphere(radius=1.0, h=0.1) X = raviartthomas(Γ) @@ -44,3 +44,6 @@ _, i1 = findmin(abs.(ω.-1.0)) ω1 = ω[i1] ue = Xefie[:,i1] / fouriertransform(gaussian)(ω1) + +fcr, geo = facecurrents(ue, X) +Plotly.plot(patch(geo, norm.(fcr))) \ No newline at end of file diff --git a/examples/tdmfie.jl b/examples/tdmfie.jl index 94d4221a..b538ea3a 100644 --- a/examples/tdmfie.jl +++ b/examples/tdmfie.jl @@ -5,7 +5,7 @@ using CompScienceMeshes, BEAST X = raviartthomas(Γ) Y = buffachristiansen(Γ) -Δt, Nt = 0.3, 200 +Δt, Nt = 0.1, 200 T = timebasisshiftedlagrange(Δt, Nt, 2) δ = timebasisdelta(Δt, Nt) diff --git a/examples/tdmfie_sym.jl b/examples/tdmfie_sym.jl index e9e87bdc..4bb90a6a 100644 --- a/examples/tdmfie_sym.jl +++ b/examples/tdmfie_sym.jl @@ -5,7 +5,7 @@ using CompScienceMeshes, BEAST X = raviartthomas(Γ) Y = BEAST.nedelec(Γ) -Δt, Nt = 0.3, 200 +Δt, Nt = 0.1, 200 T = timebasisshiftedlagrange(Δt, Nt, 2) δ = timebasisdelta(Δt, Nt) diff --git a/examples/tdpmchwt.jl b/examples/tdpmchwt.jl index 69a3e671..a7971f0b 100644 --- a/examples/tdpmchwt.jl +++ b/examples/tdpmchwt.jl @@ -6,8 +6,11 @@ using CompScienceMeshes, BEAST using LinearAlgebra # Γ = meshcuboid(1.0, 1.0, 1.0, 0.25) -h = 0.3 -Γ = meshsphere(1.0, h) +# h = 0.3 +fn = joinpath(dirname(pathof(CompScienceMeshes)),"geos/torus.geo") +Γ = CompScienceMeshes.meshgeo(fn; dim=2, h=0.4) + +# Γ = meshsphere(1.0, h) X = raviartthomas(Γ) Y = buffachristiansen(Γ, ibscaled=true) @@ -15,14 +18,14 @@ sol = 1.0 T = TDMaxwell3D.singlelayer(speedoflight=sol, numdiffs=1) K = TDMaxwell3D.doublelayer(speedoflight=sol, numdiffs=1) -Δt, Nt = 0.6, 200 +Δt, Nt = 0.4, 300 δ = timebasisdelta(Δt, Nt) T0 = timebasiscxd0(Δt, Nt) T1 = timebasisshiftedlagrange(Δt,Nt,1) T2 = timebasisshiftedlagrange(Δt, Nt, 2) T3 = timebasisshiftedlagrange(Δt, Nt, 3) -duration = 20 * Δt +duration = 40 * Δt delay = 1.5 * duration amplitude = 1.0 gaussian = creategaussian(duration, delay, amplitude) @@ -39,6 +42,14 @@ pmchwt = @discretise( 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], j∈X⊗T2, m∈Y⊗T2, k∈X⊗δ, l∈Y⊗δ) +pmchwt = @discretise( + 2.0T[k,j] + 2.0K[k,m] - + 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], + j∈X⊗T2, m∈X⊗T2, k∈X⊗δ, l∈X⊗δ) + +Z = BEAST.td_assemble(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict); +# error() + u = solve(pmchwt) # nX = numfunctions(X) @@ -62,3 +73,34 @@ u = solve(pmchwt) # using LinearAlgebra # fcrj, _ = facecurrents(uj,X) # PlotlyJS.plot(patch(Γ, norm.(fcrj))) + +# Study the pmchwt static nullspace +pmchwt = @discretise( + 2.0T[k,j] + 2.0K[k,m] - + 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], + j∈X⊗T2, m∈X⊗T2, k∈X⊗δ, l∈X⊗δ) + + +# Z = BEAST.td_assemble(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict) +# function cast(Z) +# # kmax = maximum(length.(Z)) +# kmax = size(Z,3) +# Q = zeros(eltype(eltype(Z)), size(Z)..., kmax) +# for m in axes(Q,1) +# for n in axes(Q,2) +# for k in eachindex(Z[m,n]) +# Q[m,n,k] = Z[m,n][k] +# end +# end +# end +# return Q +# end +# Q = cast(Z) + +# C = companion(Q) +# w = eigvals(C) + +# using Plots +# plotly() +# plot(exp.(im*range(0,2pi,length=200))) +# scatter!(w) \ No newline at end of file diff --git a/examples/tdpmchwt_int.jl b/examples/tdpmchwt_int.jl new file mode 100644 index 00000000..8f76b2a5 --- /dev/null +++ b/examples/tdpmchwt_int.jl @@ -0,0 +1,107 @@ +# using Pkg +# Pkg.activate(@__DIR__) +# Pkg.instantiate() + +using CompScienceMeshes, BEAST +using LinearAlgebra + +# Γ = meshcuboid(1.0, 1.0, 1.0, 0.25) +# h = 0.3 +fn = joinpath(dirname(pathof(CompScienceMeshes)),"geos/torus.geo") +Γ = CompScienceMeshes.meshgeo(fn; dim=2, h=0.4) + +# Γ = meshsphere(1.0, h) +X = raviartthomas(Γ) +Y = buffachristiansen(Γ, ibscaled=true) + +sol = 1.0 +T = TDMaxwell3D.singlelayer(speedoflight=sol, numdiffs=0) +K = TDMaxwell3D.doublelayer(speedoflight=sol, numdiffs=0) + +Δt, Nt = 0.4, 300 +δ = timebasisdelta(Δt, Nt) +T0 = timebasiscxd0(Δt, Nt) +T1 = timebasisshiftedlagrange(Δt,Nt,1) +T2 = timebasisshiftedlagrange(Δt, Nt, 2) +T3 = timebasisshiftedlagrange(Δt, Nt, 3) + +duration = 40 * Δt +delay = 1.5 * duration +amplitude = 1.0 +gaussian = creategaussian(duration, delay, amplitude) + +direction, polarisation = ẑ, x̂ +E = BEAST.planewave(polarisation, direction, gaussian, 1.0) +H = direction × E + +@hilbertspace j m +@hilbertspace k l + +# pmchwt = @discretise( +# 2.0T[k,j] + 2.0K[k,m] - +# 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], +# j∈X⊗T2, m∈Y⊗T2, k∈X⊗δ, l∈Y⊗δ) + +pmchwt = @discretise( + 2.0T[k,j] + 2.0K[k,m] - + 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], + j∈X⊗T1, m∈X⊗T1, k∈X⊗δ, l∈X⊗δ) + +# Z = BEAST.td_assemble(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict); +# error() + +u = solve(pmchwt) +error() + +# nX = numfunctions(X) +# uj = u[1:nX] +# um = u[nX+1:end] + +# Θ, Φ = range(0.0,stop=2π,length=100), 0.0 +# ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] +# +# # Don't forgt the far field comprises two contributions +# κ, η = 1.0, 1.0 +# ffm = potential(MWFarField3D(κ*im), ffpoints, um, X) +# ffj = potential(MWFarField3D(κ*im), ffpoints, uj, X) +# ff = η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) +# +# using Plots +# plot(xlabel="theta") +# plot!(Θ,norm.(ffm),label="far field") +# +# import PlotlyJS +# using LinearAlgebra +# fcrj, _ = facecurrents(uj,X) +# PlotlyJS.plot(patch(Γ, norm.(fcrj))) + +# Study the pmchwt static nullspace +pmchwt = @discretise( + 2.0T[k,j] + 2.0K[k,m] - + 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], + j∈X⊗T2, m∈X⊗T2, k∈X⊗δ, l∈X⊗δ) + + +# Z = BEAST.td_assemble(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict) +# function cast(Z) +# # kmax = maximum(length.(Z)) +# kmax = size(Z,3) +# Q = zeros(eltype(eltype(Z)), size(Z)..., kmax) +# for m in axes(Q,1) +# for n in axes(Q,2) +# for k in eachindex(Z[m,n]) +# Q[m,n,k] = Z[m,n][k] +# end +# end +# end +# return Q +# end +# Q = cast(Z) + +# C = companion(Q) +# w = eigvals(C) + +# using Plots +# plotly() +# plot(exp.(im*range(0,2pi,length=200))) +# scatter!(w) \ No newline at end of file diff --git a/src/BEAST.jl b/src/BEAST.jl index b772914d..f3072666 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -8,6 +8,7 @@ using FillArrays using BlockArrays using SparseMatrixDicts +using ConvolutionOperators using SauterSchwabQuadrature using SauterSchwab3D using FastGaussQuadrature @@ -125,12 +126,12 @@ function convolve end include("utils/polynomial.jl") include("utils/mixedblockarrays.jl") -include("timedomain/convop.jl") -include("utils/sparsend.jl") +# include("timedomain/convop.jl") +# include("utils/sparsend.jl") include("utils/specialfns.jl") include("utils/combinatorics.jl") include("utils/linearspace.jl") -include("utils/matrixconv.jl") +# include("utils/matrixconv.jl") include("utils/polyeig.jl") include("utils/zeromap.jl") diff --git a/src/maxwell/sauterschwabints_bdm.jl b/src/maxwell/sauterschwabints_bdm.jl index 0294b835..2956fd36 100644 --- a/src/maxwell/sauterschwabints_bdm.jl +++ b/src/maxwell/sauterschwabints_bdm.jl @@ -48,11 +48,11 @@ end function momintegrals!(op::Operator, test_local_space::RefSpace, trial_local_space::RefSpace, - test_chart, trial_chart, out, strat::SauterSchwabStrategy) + test_chart, trial_chart, out, rule::SauterSchwabStrategy) I, J, _, _ = SauterSchwabQuadrature.reorder( test_chart.vertices, - trial_chart.vertices, strat) + trial_chart.vertices, rule) test_chart = simplex( test_chart.vertices[I[1]], @@ -65,7 +65,7 @@ function momintegrals!(op::Operator, trial_chart.vertices[J[3]]) igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) - G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) + G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, rule) K = dof_permutation(test_local_space, I) L = dof_permutation(trial_local_space, J) diff --git a/src/solvers/lusolver.jl b/src/solvers/lusolver.jl index 1b2a15fc..588d81ed 100644 --- a/src/solvers/lusolver.jl +++ b/src/solvers/lusolver.jl @@ -60,8 +60,13 @@ function td_solve(eq) # bilform = eq.equation.lhs A = td_assemble(eq.equation.lhs, eq.test_space_dict, eq.trial_space_dict) - S = timeslice(A,1) - iS = inv(Array(S)) + T = eltype(A) + S = zeros(T, size(A)[1:2]) + ConvolutionOperators.timeslice!(S, A, 1) + + # S = timeslice(A,1) + # iS = inv(Array(S)) + iS = inv(S) b = td_assemble(eq.equation.rhs, eq.test_space_dict) @@ -83,7 +88,9 @@ function timeslice(A::BlockArray, k) isassigned(A.blocks, i, j) || continue A[Block(i,j)] isa Zeros && continue A[Block(i,j)] isa Fill && continue - S[Block(i,j)] = A[Block(i,j)].convop[:,:,k] + Aij = A[Block(i,j)] + Ak = AbstractArray(A[Block(i,j)].convop)[:,:,k] + S[Block(i,j)] = Ak end end diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 2788be48..f860b28f 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -171,6 +171,15 @@ function assemble(lform::LinForm, test_space_dict) return B end + +struct SpaceTimeData{T} <: AbstractArray{Vector{T},1} + data::Array{T,2} +end + +Base.eltype(x::SpaceTimeData{T}) where {T} = Vector{T} +Base.size(x::SpaceTimeData) = (size(x.data)[1],) +Base.getindex(x::SpaceTimeData, i::Int) = x.data[i,:] + function td_assemble(lform::LinForm, test_space_dict) terms = lform.terms @@ -185,8 +194,17 @@ function td_assemble(lform::LinForm, test_space_dict) I = [numfunctions(spatialbasis(test_space_dict[i])) for i in 1:length(lform.test_space)] - BT = SparseND.SpaceTimeData{T} - B = BlockArray(undef_blocks, BT, I) + M = zeros(Int, length(test_space_dict)) + for (p,x) in test_space_dict M[p]=numfunctions(spatialbasis(x)) end + + N = [numfunctions(temporalbasis(test_space_dict[1]))] + B = BlockArray{T}(undef, M, N) + + # row_axis = BlockArrays.blockedrange(M) + # col_axis = BlockArrays.blockedrange(N) + + # BT = SpaceTimeData{T} + # B = BlockArray(undef_blocks, BT, I) for t in terms @@ -195,6 +213,7 @@ function td_assemble(lform::LinForm, test_space_dict) m = t.test_id X = test_space_dict[m] o = t.test_ops + # act with the various ops on X for op in reverse(o) @@ -203,57 +222,57 @@ function td_assemble(lform::LinForm, test_space_dict) end b = assemble(a, X) - B[Block(m)] = SparseND.SpaceTimeData{T}(α*b) + B[Block(m),Block(1)] = α*b end return B end -function assemble_hide(bilform::BilForm, test_space_dict::Space{U}, trial_space_dict) where U - - lhterms = bilform.terms - T = Complex{U} # TDOD: Fix this - - blocksizes1 = Int[] - for p in 1:length(bilform.test_space) - X = test_space_dict[p] - push!(blocksizes1, numfunctions(X)) - end - - blocksizes2 = Int[] - for q in 1:length(bilform.trial_space) - Y = trial_space_dict[q] - push!(blocksizes2, numfunctions(Y)) - end - - # allocate the memory for the matrices - A = zeros(T, sum(blocksizes1), sum(blocksizes2)) - Z = PseudoBlockArray{T}(A, blocksizes1, blocksizes2) - # For each block, compute the interaction matrix - for t in lhterms - - α = t.coeff - a = t.kernel - - m = t.test_id - x = test_space_dict[m] - for op in reverse(t.test_ops) - x = op[end](op[1:end-1]..., x) - end - - n = t.trial_id - y = trial_space_dict[n] - for op in reverse(t.trial_ops) - y = op[end](op[1:end-1]..., y) - end - - z = assemble(a, x, y) - # @show m n norm(z) - Z[Block(m,n)] += α * z - end - - return Z -end +# function assemble_hide(bilform::BilForm, test_space_dict::Space{U}, trial_space_dict) where U + +# lhterms = bilform.terms +# T = Complex{U} # TDOD: Fix this + +# blocksizes1 = Int[] +# for p in 1:length(bilform.test_space) +# X = test_space_dict[p] +# push!(blocksizes1, numfunctions(X)) +# end + +# blocksizes2 = Int[] +# for q in 1:length(bilform.trial_space) +# Y = trial_space_dict[q] +# push!(blocksizes2, numfunctions(Y)) +# end + +# # allocate the memory for the matrices +# A = zeros(T, sum(blocksizes1), sum(blocksizes2)) +# Z = PseudoBlockArray{T}(A, blocksizes1, blocksizes2) +# # For each block, compute the interaction matrix +# for t in lhterms + +# α = t.coeff +# a = t.kernel + +# m = t.test_id +# x = test_space_dict[m] +# for op in reverse(t.test_ops) +# x = op[end](op[1:end-1]..., x) +# end + +# n = t.trial_id +# y = trial_space_dict[n] +# for op in reverse(t.trial_ops) +# y = op[end](op[1:end-1]..., y) +# end + +# z = assemble(a, x, y) +# # @show m n norm(z) +# Z[Block(m,n)] += α * z +# end + +# return Z +# end function assemble(bilform::BilForm, test_space_dict, trial_space_dict; @@ -298,48 +317,100 @@ end +# function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict) + +# lhterms = bilform.terms + +# T = Float32 +# for term in bilform.terms +# T = scalartype(T,term.coeff) +# T = scalartype(T,term.kernel) +# end +# for kv in test_space_dict; T = scalartype(T,kv[2]) end +# for kv in trial_space_dict; T = scalartype(T,kv[2]) end + +# I = [numfunctions(spatialbasis(test_space_dict[i])) for i in 1:length(bilform.test_space)] +# J = [numfunctions(spatialbasis(trial_space_dict[i])) for i in 1:length(bilform.trial_space)] + +# Z = BlockArray{Vector{T}}(zero_blocks, I, J) + +# # For each block, compute the interaction matrix +# for t in lhterms + +# @show (t.coeff,t.kernel) +# a = t.coeff * t.kernel + +# m = t.test_id +# x = test_space_dict[m] +# for op in reverse(t.test_ops) +# x = op[end](op[1:end-1]..., x) +# end + +# n = t.trial_id +# y = trial_space_dict[n] +# for op in reverse(t.trial_ops) +# y = op[end](op[1:end-1]..., y) +# end + +# z = assemble(a, x, y) +# @warn "variation formulations where combinations of test and trial space recur multiple times are not supported!" +# Z[Block(m,n)] = ConvolutionOperators.ConvOpAsMatrix(z) + +# end +# return Z +# end + + function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict) - lhterms = bilform.terms + lhterms = bilform.terms - T = Float32 - for term in bilform.terms - T = scalartype(T,term.coeff) - T = scalartype(T,term.kernel) - end - for kv in test_space_dict; T = scalartype(T,kv[2]) end - for kv in trial_space_dict; T = scalartype(T,kv[2]) end + # T = Float32 + # for term in bilform.terms + # T = scalartype(T,term.coeff) + # T = scalartype(T,term.kernel) + # end + # for kv in test_space_dict; T = scalartype(T,kv[2]) end + # for kv in trial_space_dict; T = scalartype(T,kv[2]) end - I = [numfunctions(spatialbasis(test_space_dict[i])) for i in 1:length(bilform.test_space)] - J = [numfunctions(spatialbasis(trial_space_dict[i])) for i in 1:length(bilform.trial_space)] + # I = [numfunctions(spatialbasis(test_space_dict[i])) for i in 1:length(bilform.test_space)] + # J = [numfunctions(spatialbasis(trial_space_dict[i])) for i in 1:length(bilform.trial_space)] -# BT = SparseND.MatrixOfConvolutions{T} -# Z = BlockArray(undef_blocks, BT, I, J) + # rowaxis = BlockArrays.blockedrange([numfunctions(spatialbasis(test_space_dict[i])) for i in 1:length(test_space_dict)]) + # colaxis = BlockArrays.blockedrange([numfunctions(spatialbasis(trial_space_dict[i])) for i in 1:length(trial_space_dict)]) - Z = BlockArray{Vector{T}}(zero_blocks, I, J) + M = zeros(Int, length(test_space_dict)) + N = zeros(Int, length(trial_space_dict)) - # For each block, compute the interaction matrix - for t in lhterms + for (p,x) in test_space_dict M[p]=numfunctions(spatialbasis(x)) end + for (p,x) in trial_space_dict N[p]=numfunctions(spatialbasis(x)) end - @show (t.coeff,t.kernel) - a = t.coeff * t.kernel + row_axis = BlockArrays.blockedrange(M) + col_axis = BlockArrays.blockedrange(N) - m = t.test_id - x = test_space_dict[m] - for op in reverse(t.test_ops) - x = op[end](op[1:end-1]..., x) - end + # Z = BlockArray{Vector{T}}(zero_blocks, row_axis, col_axis) + Z = ConvolutionOperators.ZeroConvOp(row_axis, col_axis) - n = t.trial_id - y = trial_space_dict[n] - for op in reverse(t.trial_ops) - y = op[end](op[1:end-1]..., y) - end + for t in lhterms - z = assemble(a, x, y) - @warn "variation formulations where combinations of test and trial space recur multiple times are not supported!" - Z[Block(m,n)] = SparseND.MatrixOfConvolutions(z) + a = t.coeff * t.kernel + + m = t.test_id + x = test_space_dict[m] + for op in reverse(t.test_ops) + x = op[end](op[1:end-1]..., x) + end + + n = t.trial_id + y = trial_space_dict[n] + for op in reverse(t.trial_ops) + y = op[end](op[1:end-1]..., y) + end + + z = assemble(a, x, y) + Z += ConvolutionOperators.LiftedConvOp(z, row_axis, col_axis, Block(m), Block(n)) + # Z[Block(m,n)] = ConvolutionOperators.ConvOpAsMatrix(z) - end - return Z + end + return Z end diff --git a/src/timedomain/motlu.jl b/src/timedomain/motlu.jl index 4c65653d..61be5840 100644 --- a/src/timedomain/motlu.jl +++ b/src/timedomain/motlu.jl @@ -1,8 +1,97 @@ - - - - - +# function timeslice(Z::ConvolutionOperators.ConvOp, k) +# T = eltype(Z.data) +# Zk = zeros(T, size(Z)[1:2]) +# for n in axes(Z,2) +# for m in axes(Z,1) +# Zk[m,n] = Z[m,n,k] +# end end +# return Zk +# end + + + + + + +# function marchonintime(W0,Z,B,I) +# T = eltype(W0) +# M,N = size(Z) +# @assert M == size(B,1) +# x = zeros(T,N,I) +# for i in 1:I +# b = B[:,i] - convolve(Z,x,i,2) +# x[:,i] += W0 * b +# (i % 10 == 0) && print(i, "[", I, "] - ") +# end +# return x +# end + +# function marchonintime(iZ0, Z::ConvolutionOperators.AbstractConvOp, B, Nt) + +# T = eltype(iZ0) +# Ns = size(Z,1) +# x = zeros(T,Ns,Nt) +# csx = zeros(T,Ns,Nt) +# y = zeros(T,Ns) + +# todo, done, pct = Nt, 0, 0 +# for i in 1:Nt +# fill!(y,0) +# ConvolutionOperators.convolve!(y, Z, x, csx, i, 2, Nt) +# y .*= -1 +# y .+= B[:,i] +# # @show norm(B[:,i]) + +# x[:,i] .+= iZ0 * y +# if i > 1 +# csx[:,i] .= csx[:,i-1] .+ x[:,i] +# else +# csx[:,i] .= x[:,i] +# end + +# done += 1 +# new_pct = round(Int, done / todo * 100) +# new_pct > pct+9 && (println("[$new_pct]"); pct=new_pct) +# end +# x +# end + +# function convolve(Z::BlockArray, x, i, j_start) +# # ax1 = axes(Z,1) +# ax2 = axes(Z,2) +# T = eltype(eltype(Z)) +# y = PseudoBlockVector{T}(undef,blocklengths(axes(Z,1))) +# fill!(y,0) +# for I in blockaxes(Z,1) +# for J in blockaxes(Z,2) +# xJ = view(x, ax2[J], :) +# try +# ZIJ = Z[I,J].banded +# y[I] .+= convolve(ZIJ, xJ, i, j_start) +# catch +# @info "Skipping unassigned block." +# continue +# end +# end +# end +# return y +# end + +# function convolve!(y,Z::BlockArray, x, csx, i, j_start, j_stop) +# ax1 = axes(Z,1) +# ax2 = axes(Z,2) +# T = eltype(eltype(Z)) +# fill!(y,0) +# for I in blockaxes(Z,1) +# for J in blockaxes(Z,2) +# xJ = view(x, ax2[J], :) +# csxJ = view(csx, ax2[J], :) +# yI = view(y, ax1[I]) +# ConvolutionOperators.convolve!(yI, Z[I,J], xJ, csxJ, i, j_start, j_stop) +# end +# end +# return y +# end """ marchonintime(W0,Z,B,I) @@ -13,86 +102,6 @@ of a time translation invariant retarded potential operator. `W0` is the inverse the slice `Z[:,:,1]`. """ function marchonintime(W0,Z,B,I) - T = eltype(W0) - M,N = size(Z) - @assert M == size(B,1) - x = zeros(T,N,I) - for i in 1:I - b = B[:,i] - convolve(Z,x,i,2) - x[:,i] += W0 * b - (i % 10 == 0) && print(i, "[", I, "] - ") - end - return x -end - -function marchonintime(iZ0, Z::ConvOp, B, Nt) - - T = eltype(iZ0) - Ns = size(Z,1) - x = zeros(T,Ns,Nt) - csx = zeros(T,Ns,Nt) - y = zeros(T,Ns) - - todo, done, pct = Nt, 0, 0 - for i in 1:Nt - fill!(y,0) - convolve!(y, Z, x, csx, i, 2, Nt) - y .*= -1 - y .+= B[:,i] - # @show norm(B[:,i]) - - x[:,i] .+= iZ0 * y - if i > 1 - csx[:,i] .= csx[:,i-1] .+ x[:,i] - else - csx[:,i] .= x[:,i] - end - - done += 1 - new_pct = round(Int, done / todo * 100) - new_pct > pct+9 && (println("[$new_pct]"); pct=new_pct) - end - x -end - -function convolve(Z::BlockArray, x, i, j_start) - # ax1 = axes(Z,1) - ax2 = axes(Z,2) - T = eltype(eltype(Z)) - y = PseudoBlockVector{T}(undef,blocklengths(axes(Z,1))) - fill!(y,0) - for I in blockaxes(Z,1) - for J in blockaxes(Z,2) - xJ = view(x, ax2[J], :) - try - ZIJ = Z[I,J].banded - y[I] .+= convolve(ZIJ, xJ, i, j_start) - catch - @info "Skipping unassigned block." - continue - end - end - end - return y -end - -function convolve!(y,Z::BlockArray, x, csx, i, j_start, j_stop) - ax1 = axes(Z,1) - ax2 = axes(Z,2) - T = eltype(eltype(Z)) - fill!(y,0) - for I in blockaxes(Z,1) - for J in blockaxes(Z,2) - xJ = view(x, ax2[J], :) - csxJ = view(csx, ax2[J], :) - yI = view(y, ax1[I]) - convolve!(yI, Z[I,J], xJ, csxJ, i, j_start, j_stop) - end - end - return y -end - -function marchonintime(W0,Z::BlockArray,B,I) T = eltype(W0) M,N = size(W0) @@ -103,13 +112,14 @@ function marchonintime(W0,Z::BlockArray,B,I) csx = zeros(T,N,I) for i in 1:I - R = [ B[j][i] for j in 1:N ] + # R = [ B[j][i] for j in 1:N ] + R = B[:,i] # @show norm(R) k_start = 2 k_stop = I fill!(y,0) - convolve!(y,Z,x,csx,i,k_start,k_stop) + ConvolutionOperators.convolve!(y,Z,x,csx,i,k_start,k_stop) b = R - y x[:,i] .+= W0 * b if i > 1 diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 543b3044..76be01f7 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -39,7 +39,8 @@ function allocatestorage(op::RetardedPotential, testST, basisST, kmax = maximum(K1); Z = zeros(eltype(op), M, N, kmax) store1(v,m,n,k) = (Z[m,n,k] += v) - return ()->MatrixConvolution(Z), store1 + # return ()->MatrixConvolution(Z), store1 + return ()->ConvolutionOperators.DenseConvOp(Z), store1 end @@ -116,7 +117,7 @@ function allocatestorage(op::RetardedPotential, testST, basisST, tail = zeros(T, M, N) # kmax = maximum(K1) len = has_tail ? Nt : maximum(K1) - Z = ConvOp(data, K0, K1, tail, len) + Z = ConvolutionOperators.ConvOp(data, K0, K1, tail, len) function store1(v,m,n,k) if Z.k0[m,n] ≤ k ≤ Z.k1[m,n] @@ -197,7 +198,7 @@ function assemble_chunk!(op::RetardedPotential, testST, trialST, store; wdim = numfunctions(W) z = zeros(eltype(op), udim, vdim, wdim) - @show length(testels) length(trialels) + # @show length(testels) length(trialels) myid == 1 && print("dots out of 10: ") todo, done, pctg = length(testels), 0, 0 diff --git a/src/utils/sparsend.jl b/src/utils/sparsend.jl index ae2ee957..4572c0bc 100644 --- a/src/utils/sparsend.jl +++ b/src/utils/sparsend.jl @@ -21,12 +21,12 @@ BEAST.convolve!(y, Z::FillArrays.Zeros, x, csx, i, k_start, k_stop) = nothing BEAST.convolve!(y, Z::FillArrays.Fill, x, csx, i, k_start, k_stop) = nothing -struct SpaceTimeData{T} <: AbstractArray{Vector{T},1} - data::Array{T,2} -end +# struct SpaceTimeData{T} <: AbstractArray{Vector{T},1} +# data::Array{T,2} +# end -Base.eltype(x::SpaceTimeData{T}) where {T} = Vector{T} -Base.size(x::SpaceTimeData) = (size(x.data)[1],) -Base.getindex(x::SpaceTimeData, i::Int) = x.data[i,:] +# Base.eltype(x::SpaceTimeData{T}) where {T} = Vector{T} +# Base.size(x::SpaceTimeData) = (size(x.data)[1],) +# Base.getindex(x::SpaceTimeData, i::Int) = x.data[i,:] end # module diff --git a/test/runtests.jl b/test/runtests.jl index daa6c09d..84b3e55c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -62,7 +62,7 @@ include("test_tdassembly.jl") include("test_tdhhdbl.jl") include("test_tdmwdbl.jl") include("test_compressed_storage.jl") -include("test_matrixconv.jl") +# include("test_matrixconv.jl") include("test_tdop_scaling.jl") include("test_tdrhs_scaling.jl") diff --git a/test/test_compressed_storage.jl b/test/test_compressed_storage.jl index e20d393c..d101812b 100644 --- a/test/test_compressed_storage.jl +++ b/test/test_compressed_storage.jl @@ -46,7 +46,20 @@ fr2, store2 = BEAST.allocatestorage(SL0, X⊗δ, X⊗T1, BEAST.assemble!(SL0, X⊗δ, X⊗T1, store1); Z1 = fr1() BEAST.assemble!(SL0, X⊗δ, X⊗T1, store2); Z2 = fr2() -@test norm(Z1-Z2,Inf) < 1e-12 +for k in axes(Z1,3) + local T = scalartype(SL0, X⊗δ, X⊗T1) + Z1k = zeros(T, size(Z1)[1:2]) + Z2k = zeros(T, size(Z2)[1:2]) + BEAST.ConvolutionOperators.timeslice!(Z1k, Z1, k) + BEAST.ConvolutionOperators.timeslice!(Z2k, Z2, k) + @test norm(Z1k .- Z2k, Inf) < 1e-12 +end +# for m in axes(Z1,1) +# for n in axes(Z1,2) +# for k in axes(Z1,3) +# @test norm(Z1[m,n,k] - Z2[m,n,k]) < 1e-12 +# end end end +# @test norm(Z1-Z2,Inf) < 1e-12 fr3, store7 = BEAST.allocatestorage(SL1, X⊗δ, X⊗T2, BEAST.Val{:bandedstorage}, @@ -74,16 +87,33 @@ BEAST.assemble!(SL1, X⊗δ, X⊗T2, store8); Z4 = fr4() b = assemble(E, X⊗δ) b1 = assemble(E1, X⊗δ) -W1 = inv(Z1[:,:,1]) -W2 = inv(Z2[:,:,1]) +function timeslice(T,Z,k) + Zk = zeros(T, size(Z)[1:2]) + BEAST.ConvolutionOperators.timeslice!(Zk, Z, k) +end + +T = scalartype(SL0, X⊗δ, X⊗T1) +W1 = inv(timeslice(T,Z1,1)) +W2 = inv(timeslice(T,Z2,1)) + +# W1 = inv(AbstractArray(Z1)[:,:,1]) +# W2 = inv(AbstractArray(Z2)[:,:,1]) +# W1 = inv(BEAST.timeslice(Z1,1)) +# W2 = inv(BEAST.timeslice(Z2,1)) @test norm(W1-W2,Inf) < 1e-12 -W3 = inv(Z3[:,:,1]) -W4 = inv(Z4[:,:,1]) +T = scalartype(SL1, X⊗δ, X⊗T2) +W3 = inv(timeslice(T,Z3,1)) +W4 = inv(timeslice(T,Z4,1)) + +# W3 = inv(AbstractArray(Z3)[:,:,1]) +# W4 = inv(AbstractArray(Z4)[:,:,1]) +# W3 = inv(BEAST.timeslice(Z3,1)) +# W4 = inv(BEAST.timeslice(Z4,1)) @test norm(W3-W4) < 1e-12 x1 = marchonintime(W1, Z1, b, Nt) -x2 = marchonintime(W1, Z2, b, Nt) +x2 = marchonintime(W2, Z2, b, Nt) @test norm(x1-x2,Inf) < 1e-12 x3 = marchonintime(W3, Z3, b1, Nt) @@ -110,7 +140,21 @@ fr10, store10 = BEAST.allocatestorage(DLh..., Z9 = fr9() Z10 = fr10() -@test norm(Z9-Z10,Inf) < 1e-12 +for k in axes(Z9,3) + local T = scalartype(DLh...) + Z9k = zeros(T, size(Z9)[1:2]) + Z10k = zeros(T, size(Z10)[1:2]) + BEAST.ConvolutionOperators.timeslice!(Z9k, Z9, k) + BEAST.ConvolutionOperators.timeslice!(Z10k, Z10, k) + @test norm(Z9k .- Z10k, Inf) < 1e-12 +end + +# # @test norm(Z9-Z10,Inf) < 1e-12 +# for m in axes(Z9,1) +# for n in axes(Z9,2) +# for k in axes(Z9,3) +# @test norm(Z9[m,n,k] - Z10[m,n,k]) < 1e-12 +# end end end iDLh = (integrate(DL0), Y2⊗δ, X2⊗T1) fr11, store11 = BEAST.allocatestorage(iDLh..., BEAST.Val{:densestorage}, BEAST.LongDelays{:ignore}) @@ -122,5 +166,19 @@ BEAST.assemble!(iDLh..., store11); Z11 = fr11() BEAST.assemble!(iDLh..., store12); Z12 = fr12() # BEAST.assemble!(iDLh..., store13); Z13 = fr13() -@test norm(Z11-Z12,Inf) < 1e-8 -# @test norm(Z11-Z13,Inf) < 1e-8 \ No newline at end of file +# @test norm(Z11-Z12,Inf) < 1e-8 +# for m in axes(Z11,1) +# for n in axes(Z11,2) +# for k in axes(Z11,3) +# @test norm(Z11[m,n,k] - Z12[m,n,k]) < 1e-12 +# end end end +# @test norm(Z11-Z13,Inf) < 1e-8 + +for k in axes(Z11,3) + local T = scalartype(iDLh...) + Z11k = zeros(T, size(Z11)[1:2]) + Z12k = zeros(T, size(Z12)[1:2]) + BEAST.ConvolutionOperators.timeslice!(Z11k, Z11, k) + BEAST.ConvolutionOperators.timeslice!(Z12k, Z12, k) + @test norm(Z11k .- Z12k, Inf) < 1e-12 +end \ No newline at end of file diff --git a/test/test_raviartthomas.jl b/test/test_raviartthomas.jl index d2a38fa6..5a646a6a 100644 --- a/test/test_raviartthomas.jl +++ b/test/test_raviartthomas.jl @@ -37,13 +37,13 @@ for T in [Float32,Float64] faces = skeleton(mesh, 2) idcs = faces.faces[1] verts = vertices(mesh, idcs) - p = simplex(verts, Val{2}) + local p = simplex(verts, Val{2}) @test volume(p) == T(1/8) - edges = skeleton(mesh,1) + local edges = skeleton(mesh,1) @test numcells(edges) == 16 - cps = cellpairs(mesh, edges) + local cps = cellpairs(mesh, edges) @test size(cps) == (2,16) # select only inner edges diff --git a/test/test_rtx.jl b/test/test_rtx.jl index cc99d0e5..29507d28 100644 --- a/test/test_rtx.jl +++ b/test/test_rtx.jl @@ -13,8 +13,8 @@ for T in [Float32, Float64] i = 12 c = X.fns[i][1].cellid - ch = chart(m, c) - ctr = cartesian(center(ch)) + local ch = chart(m, c) + local ctr = cartesian(center(ch)) @test ctr ≈ p[i] for (i,f) in enumerate(X.fns) diff --git a/test/test_tdhhdbl.jl b/test/test_tdhhdbl.jl index 2103d4e1..f56de5e0 100644 --- a/test/test_tdhhdbl.jl +++ b/test/test_tdhhdbl.jl @@ -33,7 +33,11 @@ W = Y ⊗ T fr2, store2 = BEAST.allocatestorage(K, V, V, Val{:densestorage}, BEAST.LongDelays{:ignore}) BEAST.assemble!(K, W, V, store2) Z = fr2() -@test all(==(0), Z[:,:,1]) + +T = scalartype(K,V,V) +Z1 = zeros(T, size(Z)[1:2]) +BEAST.ConvolutionOperators.timeslice!(Z1, Z, 1) +@test all(==(0), Z1) # import WiltonInts84 # trial_element = chart(G, first(cells(G))) diff --git a/test/test_tdmwdbl.jl b/test/test_tdmwdbl.jl index 1dc7df79..81fb700f 100644 --- a/test/test_tdmwdbl.jl +++ b/test/test_tdmwdbl.jl @@ -29,7 +29,11 @@ fr1, store1 = BEAST.allocatestorage(K, W, V, BEAST.assemble!(K, W, V, store1) Z = fr1() -@test all(==(0), Z[:,:,1]) + +T = scalartype(K,W,V) +Z1 = zeros(T, size(Z)[1:2]) +BEAST.ConvolutionOperators.timeslice!(Z1, Z, 1) +@test all(==(0), Z1) W = X⊗δ V = Y⊗T2 @@ -38,7 +42,12 @@ K = TDMaxwell3D.doublelayer(speedoflight=1.0, numdiffs=1) fr2, store2 = BEAST.allocatestorage(K, W, V, Val{:densestorage}, BEAST.LongDelays{:ignore}) BEAST.assemble!(K, W, V, store2) Z2 = fr2() -@test all(==(0), Z2[:,:,1]) + +T = scalartype(K,W,V) +Z21 = zeros(T, size(Z)[1:2]) +BEAST.ConvolutionOperators.timeslice!(Z21, Z2, 1) +@test all(==(0), Z21) +# @test all(==(0), Z2[:,:,1]) γ = geometry(Y) ch1 = chart(G, first(G)) diff --git a/test/test_tdop_scaling.jl b/test/test_tdop_scaling.jl index becab824..6ced9036 100644 --- a/test/test_tdop_scaling.jl +++ b/test/test_tdop_scaling.jl @@ -39,7 +39,10 @@ t = MWSingleLayerTDIO(sol,-1/sol,-sol,2,0) Z3 = assemble(t, W, V) m = n = 1 -@test findall(Z1[m,n,:] .!= 0) == findall(Z3[m,n,:] .!= 0) +M1 = AbstractArray(Z1) +M3 = AbstractArray(Z3) -I = findall(Z1[m,n,:] .!= 0) -@test all(sol*Z1[m,n,I] .≈ Z3[m,n,I]) +@test findall(M1[m,n,:] .!= 0) == findall(M3[m,n,:] .!= 0) + +I = findall(M1[m,n,:] .!= 0) +@test all(sol*M1[m,n,I] .≈ M3[m,n,I]) diff --git a/test/test_ttrace.jl b/test/test_ttrace.jl index 6e66e70d..067f27ea 100644 --- a/test/test_ttrace.jl +++ b/test/test_ttrace.jl @@ -19,13 +19,13 @@ for T in [Float32, Float64] i = 3 local j = 3 - edg = simplex(p1, p2) + local edg = simplex(p1, p2) x = BEAST.NDLCDRefSpace{T}() y = BEAST.NDRefSpace{T}() - p = neighborhood(tet, T.([0.5, 0.5, 0.0])) + local p = neighborhood(tet, T.([0.5, 0.5, 0.0])) r = neighborhood(tri, T.([0.5, 0.5])) @test carttobary(edg, cartesian(p)) ≈ T.([0.5]) From 3d97758e68e1b6ef26f4c8fe8961f581106aabec Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 1 Nov 2022 16:50:15 +0100 Subject: [PATCH 196/528] td examples compute polyeigs --- Project.toml | 4 ++-- examples/tdefie_nodot.jl | 7 +++++-- examples/tdpmchwt.jl | 6 +++--- examples/tdpmchwt_int.jl | 22 ++++++++++++++++++---- src/BEAST.jl | 4 ++-- src/utils/polynomial.jl | 18 +++++++++--------- 6 files changed, 39 insertions(+), 22 deletions(-) diff --git a/Project.toml b/Project.toml index 4f93c09b..77923120 100644 --- a/Project.toml +++ b/Project.toml @@ -31,9 +31,9 @@ WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" CollisionDetection = "0.1.5" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.5" +CompScienceMeshes = "0.5.1" Compat = "2, 3, 4" -ConvolutionOperators = "0.2" +ConvolutionOperators = "0.3" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4" FillArrays = "0.11, 0.12, 0.13" diff --git a/examples/tdefie_nodot.jl b/examples/tdefie_nodot.jl index e23cd5c9..7d0e7c45 100644 --- a/examples/tdefie_nodot.jl +++ b/examples/tdefie_nodot.jl @@ -1,10 +1,11 @@ using CompScienceMeshes, BEAST # Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) -Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +Γ = meshsphere(radius=1.0, h=0.4) X = raviartthomas(Γ) -Δt, Nt = 0.6, 200 +Δt, Nt = 1.2, 200 T = timebasisshiftedlagrange(Δt, Nt, 1) U = timebasisdelta(Δt, Nt) @@ -24,6 +25,8 @@ SL = TDMaxwell3D.singlelayer(speedoflight=1.0) @hilbertspace j @hilbertspace j′ efie_nodot = @discretise SL[j′,j] == E[j′] j∈V j′∈W +error() + xefie_nodot = solve(efie_nodot) Xefie, Δω, ω0 = fouriertransform(xefie_nodot, Δt, 0.0, 2) diff --git a/examples/tdpmchwt.jl b/examples/tdpmchwt.jl index a7971f0b..3512296f 100644 --- a/examples/tdpmchwt.jl +++ b/examples/tdpmchwt.jl @@ -8,7 +8,7 @@ using LinearAlgebra # Γ = meshcuboid(1.0, 1.0, 1.0, 0.25) # h = 0.3 fn = joinpath(dirname(pathof(CompScienceMeshes)),"geos/torus.geo") -Γ = CompScienceMeshes.meshgeo(fn; dim=2, h=0.4) +Γ = CompScienceMeshes.meshgeo(fn; dim=2, h=1.0) # Γ = meshsphere(1.0, h) X = raviartthomas(Γ) @@ -18,7 +18,7 @@ sol = 1.0 T = TDMaxwell3D.singlelayer(speedoflight=sol, numdiffs=1) K = TDMaxwell3D.doublelayer(speedoflight=sol, numdiffs=1) -Δt, Nt = 0.4, 300 +Δt, Nt = 1.6, 300 δ = timebasisdelta(Δt, Nt) T0 = timebasiscxd0(Δt, Nt) T1 = timebasisshiftedlagrange(Δt,Nt,1) @@ -48,7 +48,7 @@ pmchwt = @discretise( j∈X⊗T2, m∈X⊗T2, k∈X⊗δ, l∈X⊗δ) Z = BEAST.td_assemble(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict); -# error() +error() u = solve(pmchwt) diff --git a/examples/tdpmchwt_int.jl b/examples/tdpmchwt_int.jl index 8f76b2a5..1a1a0f6c 100644 --- a/examples/tdpmchwt_int.jl +++ b/examples/tdpmchwt_int.jl @@ -8,7 +8,7 @@ using LinearAlgebra # Γ = meshcuboid(1.0, 1.0, 1.0, 0.25) # h = 0.3 fn = joinpath(dirname(pathof(CompScienceMeshes)),"geos/torus.geo") -Γ = CompScienceMeshes.meshgeo(fn; dim=2, h=0.4) +Γ = CompScienceMeshes.meshgeo(fn; dim=2, h=1.0) # Γ = meshsphere(1.0, h) X = raviartthomas(Γ) @@ -18,7 +18,7 @@ sol = 1.0 T = TDMaxwell3D.singlelayer(speedoflight=sol, numdiffs=0) K = TDMaxwell3D.doublelayer(speedoflight=sol, numdiffs=0) -Δt, Nt = 0.4, 300 +Δt, Nt = 1.6, 300 δ = timebasisdelta(Δt, Nt) T0 = timebasiscxd0(Δt, Nt) T1 = timebasisshiftedlagrange(Δt,Nt,1) @@ -47,8 +47,22 @@ pmchwt = @discretise( 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], j∈X⊗T1, m∈X⊗T1, k∈X⊗δ, l∈X⊗δ) -# Z = BEAST.td_assemble(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict); -# error() +Z = BEAST.td_assemble(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict); +error() + + +function cones(mesh, arrows; sizeref=2) + centers = [cartesian(CompScienceMeshes.center(chart(mesh,cell))) for cell in mesh] + x = getindex.(centers,1) + y = getindex.(centers,2) + z = getindex.(centers,3) + u = getindex.(arrows,1) + v = getindex.(arrows,2) + w = getindex.(arrows,3) + Plotly.cone(x=x,y=y,z=z,u=u,v=v,w=w,sizemode="absolute", sizeref=sizeref) +end + + u = solve(pmchwt) error() diff --git a/src/BEAST.jl b/src/BEAST.jl index f3072666..3792a2f0 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -125,14 +125,14 @@ using SparseArrays function convolve end include("utils/polynomial.jl") -include("utils/mixedblockarrays.jl") +# include("utils/mixedblockarrays.jl") # include("timedomain/convop.jl") # include("utils/sparsend.jl") include("utils/specialfns.jl") include("utils/combinatorics.jl") include("utils/linearspace.jl") # include("utils/matrixconv.jl") -include("utils/polyeig.jl") +# include("utils/polyeig.jl") include("utils/zeromap.jl") include("bases/basis.jl") diff --git a/src/utils/polynomial.jl b/src/utils/polynomial.jl index 88e6d345..5a13cf00 100644 --- a/src/utils/polynomial.jl +++ b/src/utils/polynomial.jl @@ -111,13 +111,13 @@ function substitute(p::Polynomial, q::Polynomial) return r end -mutable struct PieceWisePolynomial -end +# mutable struct PieceWisePolynomial +# end -function Bernstein(n,u) - basis = zeros(n+1,1) - for i = 0:n - basis[i+1] = binomial(n,i) * u^i * (1.0-u)^(n-i) - end - basis -end +# function Bernstein(n,u) +# basis = zeros(n+1,1) +# for i = 0:n +# basis[i+1] = binomial(n,i) * u^i * (1.0-u)^(n-i) +# end +# basis +# end From dc2114167190c5afb23d820d660c1472df605458 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 1 Nov 2022 16:57:52 +0100 Subject: [PATCH 197/528] pruned unused code --- Project.toml | 2 +- src/BEAST.jl | 5 -- src/timedomain/convop.jl | 96 ------------------------- src/utils/matrixconv.jl | 129 ---------------------------------- src/utils/mixedblockarrays.jl | 39 ---------- src/utils/polyeig.jl | 27 ------- src/utils/sparsend.jl | 32 --------- 7 files changed, 1 insertion(+), 329 deletions(-) delete mode 100644 src/timedomain/convop.jl delete mode 100644 src/utils/matrixconv.jl delete mode 100644 src/utils/mixedblockarrays.jl delete mode 100644 src/utils/polyeig.jl delete mode 100644 src/utils/sparsend.jl diff --git a/Project.toml b/Project.toml index 77923120..6f209e8d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.7.0" +version = "1.7.1" [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" diff --git a/src/BEAST.jl b/src/BEAST.jl index 3792a2f0..b52e030a 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -125,14 +125,9 @@ using SparseArrays function convolve end include("utils/polynomial.jl") -# include("utils/mixedblockarrays.jl") -# include("timedomain/convop.jl") -# include("utils/sparsend.jl") include("utils/specialfns.jl") include("utils/combinatorics.jl") include("utils/linearspace.jl") -# include("utils/matrixconv.jl") -# include("utils/polyeig.jl") include("utils/zeromap.jl") include("bases/basis.jl") diff --git a/src/timedomain/convop.jl b/src/timedomain/convop.jl deleted file mode 100644 index 57a613e0..00000000 --- a/src/timedomain/convop.jl +++ /dev/null @@ -1,96 +0,0 @@ -struct ConvOp{T} <: AbstractArray{T,3} - data::Array{T,3} - k0::Array{Int,2} - k1::Array{Int,2} - tail::Array{T,2} - length::Int -end - -function Base.size(obj::ConvOp) - return (size(obj.data)[2:3]...,obj.length) -end - -function Base.getindex(obj::ConvOp, m::Int, n::Int, k::Int) - if k < obj.k0[m,n] - return zero(eltype(obj.data)) - end - - if k > obj.k1[m,n] - return obj.tail[m,n] - end - - return obj.data[k-obj.k0[m,n]+1,m,n] -end - - -function convolve!(y, Z::ConvOp, x, X, j, k_start, k_stop=size(Z,3)) - for n in axes(x,1) - for m in axes(y,1) - k0 = Z.k0[m,n] - k1 = Z.k1[m,n] - for k in max(k0,k_start):min(k1,k_stop) - p = k - k0 + 1 - j-k < 0 && continue - y[m] += Z.data[p,m,n] * x[n,j-k+1] - end - - j-k1 > 0 || continue - y[m] += Z.tail[m,n] * X[n,j-k1] - end - end -end - - -function polyeig(Z::ConvOp) - kmax = maximum(Z.k1) - M = size(Z,1) - Q = zeros(eltype(Z), 2M, 2M, kmax+1) - for k in 1:kmax - Q[1:M,1:M,k] .= Z[:,:,k] - end - Id = Matrix{eltype(Z)}(LinearAlgebra.I,M,M) - Q[M+1:2M,1:M,1] .= -Id - Q[M+1:2M,M+1:2M,1] .= Id - Q[M+1:2M,M+1:2M,2] .= -Id - Q[1:M,M+1:2M,kmax+1] .= Z[:,:,kmax+1] - return eigvals(companion(Q)), Q - # return Q -end - - -function polyeig(Z) - return eigvals(companion(Z)) -end - - -function Base.:+(a::ConvOp, b::ConvOp) - - @assert size(a.data) == size(b.data) - M,N = size(a.data) - T = promote_type(eltype(a.data), eltype(b.data)) - - k0 = min.(a.k0, b.k0) - k1 = max.(a.k1, b.k1) - - bandwidth = maximum(k1 - k0) + 1 - data = zeros(T, bandwidth, M, N) - - k1max = maximum(k1) - tail = zeros(T,M,N) - for m in 1:M, n in 1:N - tail[m,n] = a[m,n,k1max+1] + b[m,n,k1max+1] - end - - for m in 1:M - for n in 1:N - for k in k0[m,n]:k1[m,n] - data[k,m,n] = a[m,n,k] + b[m,n,k] - end - end - end - - lgt = max(a.length, b.length) - return ConvOp(data, tail, k0, k1, lgt) -end - - diff --git a/src/utils/matrixconv.jl b/src/utils/matrixconv.jl deleted file mode 100644 index 60648943..00000000 --- a/src/utils/matrixconv.jl +++ /dev/null @@ -1,129 +0,0 @@ -struct MatrixConvolution{T} <: AbstractArray{T,3} - arr::Array{T,3} -end - -function Base.:+(x::MatrixConvolution, y::MatrixConvolution) - - A = x.arr - B = y.arr - - T = promote_type(eltype(A), eltype(B)) - @assert axes(A)[1:2] == axes(B)[1:2] - - kmax = max(size(A,3), size(B,3)) - C = zeros(T,size(A)[1:2]...,kmax) - - for k in axes(A,3) - C[:,:,k] += A[:,:,k] - end - - for k in axes(B,3) - C[:,:,k] += B[:,:,k] - end - - return MatrixConvolution(C) -end - - -Base.size(x::MatrixConvolution) = size(x.arr) -Base.getindex(x::MatrixConvolution, i::Int) = x.arr[i] -Base.IndexStyle(x::MatrixConvolution) = IndexLinear() - - -function Base.:*(x::MatrixConvolution, y::Matrix) - - A = x.arr - - T = promote_type(eltype(A), eltype(y)) - @assert axes(A,2) == axes(y,1) - - C = zeros(T, size(A)...) - for k in axes(C,3) - C[:,:,k] = A[:,:,k]*y - end - - return MatrixConvolution(C) -end - -function Base.:*(y::Matrix, x::MatrixConvolution) - - A = x.arr - - T = promote_type(eltype(A), eltype(y)) - @assert axes(A,2) == axes(y,1) - - C = zeros(T, size(A)...) - for k in axes(C,3) - C[:,:,k] = y*A[:,:,k] - end - - return MatrixConvolution(C) -end - - -Base.:*(a::Number, x::MatrixConvolution) = MatrixConvolution(a * x.arr) -Base.:*(x::MatrixConvolution, a::Number) = MatrixConvolution(x.arr * a) -Base.:/(x::MatrixConvolution, a::Number) = MatrixConvolution(x.arr / a) -Base.:-(x::MatrixConvolution) = (-1) * x - -function convolve(Z::Array,x,j,k0) - M,N,K = size(Z) - y = similar(Z,M) - fill!(y,0) - for k ∈ k0 : min(j,K) - i = j - k + 1 - y += Z[:,:,k] * x[:,i] - end - return y -end - -convolve(x::MatrixConvolution, y::Matrix, i, j) = convolve(x.arr, y, i, j) - - -function Base.hvcat((M,N)::Tuple{Int,Int}, as::MatrixConvolution...) - kmax = maximum(size(a,3) for a in as) - - @assert length(as) == M*N - - li = LinearIndices((1:N,1:M)) - for m in 1:M - a = as[li[1,m]] - M1 = size(a,1) - for n in 2:N - a = as[li[n,m]] - @assert size(a,1) == M1 - end - end - - for n in 1:N - a = as[li[n,1]] - N1 = size(a,2) - for m in 2:M - a = as[li[n,m]] - @assert size(a,2) == N1 - end - end - - Ms = [size(as[li[1,i]],1) for i in 1:M] - Ns = [size(as[li[j,1]],2) for j in 1:N] - - cMs = pushfirst!(cumsum(Ms),0) - cNs = pushfirst!(cumsum(Ns),0) - T = promote_type(eltype.(as)...) - data = zeros(T, last(cMs), last(cNs), kmax) - - @show size(data) - @show eltype(data) - - for m in 1:M - I = cMs[m]+1 : cMs[m+1] - for n in 1:N - J = cNs[n]+1 : cNs[n+1] - a = as[li[n,m]] - K = 1:size(a,3) - data[I,J,K] .= a - end - end - - return MatrixConvolution(data) -end \ No newline at end of file diff --git a/src/utils/mixedblockarrays.jl b/src/utils/mixedblockarrays.jl deleted file mode 100644 index cccdcea8..00000000 --- a/src/utils/mixedblockarrays.jl +++ /dev/null @@ -1,39 +0,0 @@ -struct ZeroBlockInitializer end -const zero_blocks = ZeroBlockInitializer() - -import Base.Cartesian: @nloops, @ntuple -import BlockArrays: BlockArray, undef_blocks, blockaxes -import FillArrays: Zeros, Fill - -""" -Initialise a BlockArray where each block can have a different type. - - BlockArray{T}(zero_blocks, blocksize...) -""" -@generated function BlockArray{T}(::ZeroBlockInitializer, blocksizes::Vararg{AbstractVector{Int},N}) where {T,N} - return quote - B = BlockArray{T,N,Array{AbstractArray{T,N},N}}(undef_blocks, blocksizes...) - axs = axes(B) - @nloops $N block dim->blockaxes(B,dim) begin - block_indices = @ntuple $N block - indices = getindex.(axs, block_indices) - block_size = length.(indices) - B[block_indices...] = Zeros{T}(block_size...) - end - return B - end -end - -@generated function BlockArray{T}(::ZeroBlockInitializer, blocksizes::Vararg{AbstractVector{Int},N}) where {T <: Array,N} - return quote - B = BlockArray{T,N,Array{AbstractArray{T,N},N}}(undef_blocks, blocksizes...) - axs = axes(B) - @nloops $N block dim->blockaxes(B,dim) begin - block_indices = @ntuple $N block - indices = getindex.(axs, block_indices) - block_size = length.(indices) - B[block_indices...] = Fill{T}(T(), block_size...) - end - return B - end -end diff --git a/src/utils/polyeig.jl b/src/utils/polyeig.jl deleted file mode 100644 index 5de11de4..00000000 --- a/src/utils/polyeig.jl +++ /dev/null @@ -1,27 +0,0 @@ -export companion - -function companion(Z) - - T = eltype(Z) - K = size(Z,3) - @assert K > 1 - - M, N = size(Z)[1:2] - C = similar(Z, M*(K-1), N*(K-1)) - fill!(C,0) - - @assert M == N - Id = Matrix{T}(I, M, N) - for m in 2:K-1 - n = m-1 - C[(m-1)*M+1:m*M, (n-1)*N+1:n*N] = Id - end - - W = -inv(Z[:,:,1]) - for n in 1:K-1 - m = 1 - C[(m-1)*M+1:m*M, (n-1)*N+1:n*N] = W*Z[:,:,n+1] - end - - return C -end diff --git a/src/utils/sparsend.jl b/src/utils/sparsend.jl deleted file mode 100644 index 4572c0bc..00000000 --- a/src/utils/sparsend.jl +++ /dev/null @@ -1,32 +0,0 @@ -module SparseND - -import ...BEAST -import FillArrays - -struct MatrixOfConvolutions{T} <: AbstractArray{Vector{T},2} - # banded::AbstractArray{T,3} - convop::BEAST.ConvOp{T} -end - -function Base.eltype(x::MatrixOfConvolutions{T}) where {T} - Vector{T} -end -Base.size(x::MatrixOfConvolutions) = size(x.convop)[1:2] -function Base.getindex(x::MatrixOfConvolutions, m, n) - return x.convop[m,n,:] -end - -BEAST.convolve!(y, Z::MatrixOfConvolutions, x, csx, i, k_start, k_stop) = BEAST.convolve!(y, Z.convop, x, csx, i, k_start, k_stop) -BEAST.convolve!(y, Z::FillArrays.Zeros, x, csx, i, k_start, k_stop) = nothing -BEAST.convolve!(y, Z::FillArrays.Fill, x, csx, i, k_start, k_stop) = nothing - - -# struct SpaceTimeData{T} <: AbstractArray{Vector{T},1} -# data::Array{T,2} -# end - -# Base.eltype(x::SpaceTimeData{T}) where {T} = Vector{T} -# Base.size(x::SpaceTimeData) = (size(x.data)[1],) -# Base.getindex(x::SpaceTimeData, i::Int) = x.data[i,:] - -end # module From 86f30a4971e3cebeed3640ac8f4c071bba4615d7 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 1 Nov 2022 23:17:01 +0100 Subject: [PATCH 198/528] removed unused source files --- src/utils/debug_tools.jl | 15 --------------- src/utils/liftmap.jl | 17 ----------------- src/utils/spacetimedyad.jl | 29 ----------------------------- 3 files changed, 61 deletions(-) delete mode 100644 src/utils/debug_tools.jl delete mode 100644 src/utils/liftmap.jl delete mode 100644 src/utils/spacetimedyad.jl diff --git a/src/utils/debug_tools.jl b/src/utils/debug_tools.jl deleted file mode 100644 index 87851767..00000000 --- a/src/utils/debug_tools.jl +++ /dev/null @@ -1,15 +0,0 @@ -function show_which_quadrule(op,X,Y) - - test_charts, test_ad = assemblydata(X) - trial_charts, trial_ad = assemblydata(Y) - - x = refspace(X) - y = refspace(Y) - - @show @which quaddata(op, x, y, test_charts, trial_charts) - qd = quaddata(op, x, y, test_charts, trial_charts) - @show @which quadrule(op, x, y, 1, first(test_charts), 1, first(trial_charts), qd) - - - nothing -end diff --git a/src/utils/liftmap.jl b/src/utils/liftmap.jl deleted file mode 100644 index cf196918..00000000 --- a/src/utils/liftmap.jl +++ /dev/null @@ -1,17 +0,0 @@ -# Utils to lift LinearMaps to LinearMaps acting on larger vector spaces -import LinearMaps -import LinearAlgebra - -struct LiftedMap{T,TI,TJ} <: LinearMap{T} - A::LinearMap{T} - I::TI - J::TJ -end - -function LinearAlgebra.mul!(y::AbstractVector, L::LiftedMap, x::AbstractVector, α::Number, β::Number) - - yI = view(y, L.I) - xJ = view(x, L.J) - AIJ = L.A - LinearAlgebra.mul!(yI, AIJ, xJ, α, β) -end \ No newline at end of file diff --git a/src/utils/spacetimedyad.jl b/src/utils/spacetimedyad.jl deleted file mode 100644 index a692627e..00000000 --- a/src/utils/spacetimedyad.jl +++ /dev/null @@ -1,29 +0,0 @@ -""" -Array type of rank 3 (taking three scalar indices) that is used for the storage -of discrete convolution operators that happen to be the tensor product of a spatial -part and a temporal part. - -Using a dedicated storage type for these operators saves both on memory and computation -time for convolutions with space-time data. -""" -mutable struct SpaceTimeDyad{A,B,T} <: AbstractArray{3,T} - spatial_factor::A - temporal_factor::B -end - -# Implementation of a minimalistic array interface -# Note that setindex! is not supported. There is no way to enforce maintaining -# the dyadic structure during element modification. -import Base: size, getindex, linearindexing -size(X::SpaceTimeDyad) = (size(X.spatial_factor)..., length(X.temporal_factor)) -linearindexing(::Type{T}) where {T<:SpaceTimeDyad} = Base.LinearSlow() -function getindex(X::SpaceTimeDyad, m::Int, n::Int, k::Int) - T = promote_type(eltype(X.spatial_factor), eltype(X.temporal_factor)) - k > length(X.spacetial_factor) && return zero(T) - return X.spatial_factor[m,n] * X.temporal_factor[k] -end - - -function convolve(X::SpaceTimeDyad, x::Array{T,2}) - -end From b342c4a18eff49d640e35b47353a46cd8250bc91 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Wed, 2 Nov 2022 00:59:17 +0000 Subject: [PATCH 199/528] CompatHelper: bump compat for FastGaussQuadrature to 0.5, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 6f209e8d..eefc86da 100644 --- a/Project.toml +++ b/Project.toml @@ -35,7 +35,7 @@ CompScienceMeshes = "0.5.1" Compat = "2, 3, 4" ConvolutionOperators = "0.3" FFTW = "0.2.3, 1" -FastGaussQuadrature = "0.3, 0.4" +FastGaussQuadrature = "0.3, 0.4, 0.5" FillArrays = "0.11, 0.12, 0.13" IterativeSolvers = "0.9" LiftedMaps = "0.4.1" From 57949004ad6d09eb5fc34a0f1cb4b779e352d6ed Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 4 Nov 2022 11:31:30 +0100 Subject: [PATCH 200/528] set version to v1.8.0 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index eefc86da..c6ac471c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.7.1" +version = "1.8.0" [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" From 17b7b27803db394ec424358903848d1170ff0b77 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sun, 6 Nov 2022 18:49:48 +0100 Subject: [PATCH 201/528] square torus template in examples --- examples/assets/rectangular_torus.geo | 128 ++++++++++++++++++++++++++ examples/poltor.jl | 50 ++++------ examples/tdpmchwt.jl | 32 ++++--- 3 files changed, 164 insertions(+), 46 deletions(-) create mode 100644 examples/assets/rectangular_torus.geo diff --git a/examples/assets/rectangular_torus.geo b/examples/assets/rectangular_torus.geo new file mode 100644 index 00000000..a24b9571 --- /dev/null +++ b/examples/assets/rectangular_torus.geo @@ -0,0 +1,128 @@ +h = 1.0; +H = 1.0; +// Gmsh project created on Sun Nov 06 14:23:16 2022 +SetFactory("Built-in"); +//+ +Point(1) = {2, -2, 0, h}; +//+ +Point(2) = {2, 2, 0, h}; +//+ +Point(3) = {-2, 2, 0, h}; +//+ +Point(4) = {-2, -2, 0, h}; +//+ +Point(5) = {1, -1, 0, h}; +//+ +Point(6) = {1, 1, 0, h}; +//+ +Point(7) = {-1, 1, 0, h}; +//+ +Point(8) = {-1, -1, 0, h}; +//+ +Line(1) = {2, 3}; +//+ +Line(2) = {3, 4}; +//+ +Line(3) = {4, 1}; +//+ +Line(4) = {1, 2}; +//+ +Line(5) = {6, 7}; +//+ +Line(6) = {7, 8}; +//+ +Line(7) = {8, 5}; +//+ +Line(8) = {5, 6}; +//+ +Point(9) = {2, -2, H, h}; +//+ +Point(10) = {2, 2, H, h}; +//+ +Point(11) = {-2, 2, H, h}; +//+ +Point(12) = {-2, -2, H, h}; +//+ +Point(13) = {1, -1, H, h}; +//+ +Point(14) = {1, 1, H, h}; +//+ +Point(15) = {-1, 1, H, h}; +//+ +Point(16) = {-1, -1, H, h}; +//+ +Line(9) = {10, 11}; +//+ +Line(10) = {11, 12}; +//+ +Line(11) = {12, 9}; +//+ +Line(12) = {9, 10}; +//+ +Line(13) = {14, 15}; +//+ +Line(14) = {15, 16}; +//+ +Line(15) = {16, 13}; +//+ +Line(16) = {13, 14}; +//+ +Line(17) = {2, 10}; +//+ +Line(18) = {3, 11}; +//+ +Line(19) = {4, 12}; +//+ +Line(20) = {1, 9}; +//+ +Line(21) = {6, 14}; +//+ +Line(22) = {7, 15}; +//+ +Line(23) = {8, 16}; +//+ +Line(24) = {5, 13}; +//+ +Curve Loop(1) = {-12, 17, 4, -20}; +//+ +Plane Surface(1) = {1}; +//+ +Curve Loop(2) = {1, 18, -9, -17}; +//+ +Plane Surface(2) = {2}; +//+ +Curve Loop(3) = {-18, -10, 19, 2}; +//+ +Plane Surface(3) = {3}; +//+ +Curve Loop(4) = {-19, -11, 20, 3}; +//+ +Plane Surface(4) = {4}; +//+ +Curve Loop(5) = {24, 16, -21, -8}; +//+ +Plane Surface(5) = {5}; +//+ +Curve Loop(6) = {-5, -22, 13, 21}; +//+ +Plane Surface(6) = {6}; +//+ +Curve Loop(7) = {-6, -23, 14, 22}; +//+ +Plane Surface(7) = {7}; +//+ +Curve Loop(8) = {23, 15, -24, -7}; +//+ +Plane Surface(8) = {8}; +//+ +Curve Loop(9) = {10, 11, 12, 9}; +//+ +Curve Loop(10) = {14, 15, 16, 13}; +//+ +Plane Surface(9) = {9, 10}; +//+ +Curve Loop(11) = {-3, -4, -1, -2}; +//+ +Curve Loop(12) = {-8, -5, -6, -7}; +//+ +Plane Surface(10) = {11, 12}; diff --git a/examples/poltor.jl b/examples/poltor.jl index 501d1001..8bdb86e6 100644 --- a/examples/poltor.jl +++ b/examples/poltor.jl @@ -1,19 +1,21 @@ using CompScienceMeshes using LinearAlgebra -function cone(p,q; sizemode="absolute", sizeref=2, kwargs...) - x = getindex.(p,1) - y = getindex.(p,2) - z = getindex.(p,3) - u = getindex.(q,1) - v = getindex.(q,2) - w = getindex.(q,3) - Plotly.cone(;x,y,z,u,v,w, sizemode, sizeref, kwargs...) -end +# function cone(p,q; sizemode="absolute", sizeref=2, kwargs...) +# x = getindex.(p,1) +# y = getindex.(p,2) +# z = getindex.(p,3) +# u = getindex.(q,1) +# v = getindex.(q,2) +# w = getindex.(q,3) +# Plotly.cone(;x,y,z,u,v,w, sizemode, sizeref, kwargs...) +# end fn = joinpath(dirname(pathof(CompScienceMeshes)),"geos/torus.geo") -Γ = CompScienceMeshes.meshgeo(fn; dim=2, h=0.5) +fn = joinpath(@__DIR__, "assets/rectangular_torus.geo") +h = 0.08 +Γ = CompScienceMeshes.meshgeo(fn; dim=2, h) Γ0 = skeleton(Γ,0) Γ1 = skeleton(Γ,1) @@ -43,11 +45,11 @@ using LinearAlgebra v0 = V[:,end] fcr, geo = facecurrents(v0, X) -pts = [cartesian(center(chart(Γ,p))) for p in Γ] +# pts = [cartesian(center(chart(Γ,p))) for p in Γ] import Plotly pt1 = patch(Γ,norm.(fcr), opacity=0.5) -pt2 = cone(pts, fcr, sizeref=0.2) +pt2 = CompScienceMeshes.cones(Γ, fcr, sizeref=0.4) Plotly.plot([pt1, pt2]) G = assemble(Id,X,X) @@ -57,23 +59,7 @@ norm(v0) norm(Σ'*v0) norm(Λ'*G*v0) -# Study the kernel of the PMCHWT -SL = Maxwell3D.singlelayer(wavenumber=0.01) -DL = Maxwell3D.doublelayer(wavenumber=0.01) - -@hilbertspace m j -@hilbertspace k l -a = - DL[k,m] - SL[k,j] + - SL[l,m] + DL[l,j] - -A = assemble(@discretise(a, m∈X, j∈X, k∈X, l∈X)) -M = Matrix(A) -(;U,S,V) = svd(M) - -using Plots -plotly() -plot() - -v0 = V[:,end] -v1 = V[:,end-1] +# Dict{Any, Any} with 3 entries: +# 0.5 => 0.370836 +# 0.25 => 0.315732 +# 0.125 => 0.265276 \ No newline at end of file diff --git a/examples/tdpmchwt.jl b/examples/tdpmchwt.jl index 3512296f..6aaaa4cc 100644 --- a/examples/tdpmchwt.jl +++ b/examples/tdpmchwt.jl @@ -7,18 +7,18 @@ using LinearAlgebra # Γ = meshcuboid(1.0, 1.0, 1.0, 0.25) # h = 0.3 -fn = joinpath(dirname(pathof(CompScienceMeshes)),"geos/torus.geo") -Γ = CompScienceMeshes.meshgeo(fn; dim=2, h=1.0) +# fn = joinpath(dirname(pathof(CompScienceMeshes)),"geos/torus.geo") +# Γ = CompScienceMeshes.meshgeo(fn; dim=2, h=1.0) -# Γ = meshsphere(1.0, h) +Γ = meshsphere(radius=1.0, h=0.35) X = raviartthomas(Γ) -Y = buffachristiansen(Γ, ibscaled=true) +Y = buffachristiansen(Γ) sol = 1.0 T = TDMaxwell3D.singlelayer(speedoflight=sol, numdiffs=1) K = TDMaxwell3D.doublelayer(speedoflight=sol, numdiffs=1) -Δt, Nt = 1.6, 300 +Δt, Nt = 0.1, 300 δ = timebasisdelta(Δt, Nt) T0 = timebasiscxd0(Δt, Nt) T1 = timebasisshiftedlagrange(Δt,Nt,1) @@ -37,10 +37,10 @@ H = direction × E @hilbertspace j m @hilbertspace k l -pmchwt = @discretise( - 2.0T[k,j] + 2.0K[k,m] - - 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], - j∈X⊗T2, m∈Y⊗T2, k∈X⊗δ, l∈Y⊗δ) +# pmchwt = @discretise( +# 2.0T[k,j] + 2.0K[k,m] - +# 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], +# j∈X⊗T2, m∈Y⊗T2, k∈X⊗δ, l∈Y⊗δ) pmchwt = @discretise( 2.0T[k,j] + 2.0K[k,m] - @@ -48,10 +48,14 @@ pmchwt = @discretise( j∈X⊗T2, m∈X⊗T2, k∈X⊗δ, l∈X⊗δ) Z = BEAST.td_assemble(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict); -error() +w = BEAST.ConvolutionOperators.polyvals(Z) +# error() u = solve(pmchwt) +using Plots +plot(u[1,:]) + # nX = numfunctions(X) # uj = u[1:nX] # um = u[nX+1:end] @@ -75,10 +79,10 @@ u = solve(pmchwt) # PlotlyJS.plot(patch(Γ, norm.(fcrj))) # Study the pmchwt static nullspace -pmchwt = @discretise( - 2.0T[k,j] + 2.0K[k,m] - - 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], - j∈X⊗T2, m∈X⊗T2, k∈X⊗δ, l∈X⊗δ) +# pmchwt = @discretise( +# 2.0T[k,j] + 2.0K[k,m] - +# 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], +# j∈X⊗T2, m∈X⊗T2, k∈X⊗δ, l∈X⊗δ) # Z = BEAST.td_assemble(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict) From 69086bcad5dced23f4fdae37eb7c4ece1b451584 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 7 Nov 2022 13:19:21 +0100 Subject: [PATCH 202/528] reorganise quadrature code over files and dirs --- src/BEAST.jl | 6 +- src/maxwell/mwops.jl | 82 ++++++++++ src/maxwell/nxdbllayer.jl | 37 ++++- src/maxwell/sauterschwabints_bdm.jl | 104 ------------- .../sauterschwabints.jl} | 143 +++++++++--------- 5 files changed, 190 insertions(+), 182 deletions(-) delete mode 100644 src/maxwell/sauterschwabints_bdm.jl rename src/{maxwell/sauterschwabints_rt.jl => quadrature/sauterschwabints.jl} (61%) diff --git a/src/BEAST.jl b/src/BEAST.jl index b52e030a..ebd5b2b7 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -162,12 +162,14 @@ include("bases/stagedtimestep.jl") include("bases/timebasis.jl") include("bases/tensorbasis.jl") +include("operator.jl") + include("quadrature/quadstrats.jl") include("quadrature/double_quadrature.jl") include("quadrature/singularity_extraction.jl") +include("quadrature/sauterschwabints.jl") include("excitation.jl") -include("operator.jl") include("localop.jl") include("multiplicativeop.jl") include("identityop.jl") @@ -190,8 +192,6 @@ include("maxwell/mwexc.jl") include("maxwell/mwops.jl") include("maxwell/nxdbllayer.jl") include("maxwell/wiltonints.jl") -include("maxwell/sauterschwabints_bdm.jl") -include("maxwell/sauterschwabints_rt.jl") include("maxwell/nitsche.jl") include("maxwell/farfield.jl") include("maxwell/nearfield.jl") diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 9253b1a2..0cb87dec 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -237,3 +237,85 @@ function qrdf(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, ) end + +################################################################################ +# +# Kernel definitions +# +################################################################################ + +const i4pi = 1 / (4pi) +function (igd::Integrand{<:MWSingleLayer3D})(x,y,f,g) + α = igd.operator.α + β = igd.operator.β + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1 / R + green = exp(-γ*R)*(i4pi*iR) + + αG = α * green + βG = β * green + + _integrands(f,g) do fi,gj + αG * dot(fi.value, gj.value) + βG * dot(fi.divergence, gj.divergence) + end +end + +function (igd::Integrand{<:MWSingleLayer3DReg})(x,y,f,g) + α = igd.operator.α + β = igd.operator.β + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + γR = γ*R + # iR = 1 / R + green = (expm1(-γR) + γR - 0.5*γR^2) / (4pi*R) + + αG = α * green + βG = β * green + + _integrands(f,g) do fi,gj + αG * dot(fi.value, gj.value) + βG * dot(fi.divergence, gj.divergence) + end +end + + +function (igd::Integrand{<:MWDoubleLayer3D})(x,y,f,g) + + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1/R + green = exp(-γ*R)*(iR*i4pi) + gradgreen = -(γ + iR) * green * (iR * r) + + fvalue = getvalue(f) + gvalue = getvalue(g) + G = cross.(Ref(gradgreen), gvalue) + return _krondot(fvalue, G) +end + + +function (igd::Integrand{<:MWDoubleLayer3DReg})(x,y,f,g) + + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + γR = γ*R + iR = 1/R + expo = exp(-γR) + green = (expo - 1 + γR - 0.5*γR^2) * (i4pi*iR) + gradgreen = ( -(γR + 1)*expo + (1 - 0.5*γR^2) ) * (i4pi*iR^3) * r + + fvalue = getvalue(f) + gvalue = getvalue(g) + G = cross.(Ref(gradgreen), gvalue) + return _krondot(fvalue, G) +end + + diff --git a/src/maxwell/nxdbllayer.jl b/src/maxwell/nxdbllayer.jl index 5c2ef273..b51e7e2e 100644 --- a/src/maxwell/nxdbllayer.jl +++ b/src/maxwell/nxdbllayer.jl @@ -61,12 +61,37 @@ function quadrule(op::DoubleLayerRotatedMW3D, g::RTRefSpace, f::RTRefSpace, i, qd.bpoints[1,j],) end -function integrand(op::DoubleLayerRotatedMW3D, kernel_vals, test_vals, test_nbd, trial_vals, trial_nbd) +# function integrand(op::DoubleLayerRotatedMW3D, kernel_vals, test_vals, test_nbd, trial_vals, trial_nbd) - n = normal(test_nbd) - g = test_vals[1] - f = trial_vals[1] - ∇G = kernel_vals.gradgreen +# n = normal(test_nbd) +# g = test_vals[1] +# f = trial_vals[1] +# ∇G = kernel_vals.gradgreen - return g ⋅ (n × (∇G × f)) +# return g ⋅ (n × (∇G × f)) +# end + +function (igd::Integrand{<:DoubleLayerRotatedMW3D})(u,v) + + x = neighborhood(igd.test_chart,u) + y = neighborhood(igd.trial_chart,v) + j = jacobian(x) * jacobian(y) + nx = normal(x) + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1/R + γ = igd.operator.gamma + G = exp(-γ*R)/(4π*R) + K = -(γ + iR) * G * (iR * r) + + f = igd.local_test_space(x) + g = igd.local_trial_space(y) + + fvalue = getvalue(f) + gvalue = getvalue(g) + + jKg = cross.(Ref(K), j*gvalue) + jnxKg = cross.(Ref(nx), jKg) + return _krondot(fvalue, jnxKg) end diff --git a/src/maxwell/sauterschwabints_bdm.jl b/src/maxwell/sauterschwabints_bdm.jl deleted file mode 100644 index 2956fd36..00000000 --- a/src/maxwell/sauterschwabints_bdm.jl +++ /dev/null @@ -1,104 +0,0 @@ -struct Integrand{Op,LSt,LSb,Elt,Elb} - operator::Op - local_test_space::LSt - local_trial_space::LSb - test_chart::Elt - trial_chart::Elb -end - -getvalue(a::SVector{N}) where {N} = SVector{N}(getvalue(a.data)) -getvalue(a::NTuple{1}) = (a[1].value,) -getvalue(a::NTuple{N}) where {N} = tuple(a[1].value, getvalue(Base.tail(a))...) - -getdivergence(a::SVector{N}) where {N} = SVector{N}(getdivergence(a.data)) -getdivergence(a::NTuple{1}) = (a[1].divergence,) -getdivergence(a::NTuple{N}) where {N} = tuple(a[1].divergence, getdivergence(Base.tail(a))...) - -function _krondot_gen(a::Type{U}, b::Type{V}) where {U<:SVector{N}, V<:SVector{M}} where {M,N} - ex = :(SMatrix{N,M}(())) - for m in 1:M - for n in 1:N - push!(ex.args[2].args, :(dot(a[$n], b[$m]))) - end - end - return ex -end - -@generated function _krondot(a::SVector{N}, b::SVector{M}) where {M,N} - ex = _krondot_gen(a,b) - return ex -end - -function _integrands_gen(::Type{U}, ::Type{V}) where {U<:SVector{N}, V<:SVector{M}} where {M,N} - ex = :(SMatrix{N,M}(())) - for m in 1:M - for n in 1:N - # push!(ex.args[2].args, :(dot(a[$n], b[$m]))) - push!(ex.args[2].args, :(f(a[$n], b[$m]))) - end - end - return ex -end - -@generated function _integrands(f, a::SVector{N}, b::SVector{M}) where {M,N} - ex = _integrands_gen(a,b) - # println(ex) - return ex -end - -function momintegrals!(op::Operator, - test_local_space::RefSpace, trial_local_space::RefSpace, - test_chart, trial_chart, out, rule::SauterSchwabStrategy) - - I, J, _, _ = SauterSchwabQuadrature.reorder( - test_chart.vertices, - trial_chart.vertices, rule) - - test_chart = simplex( - test_chart.vertices[I[1]], - test_chart.vertices[I[2]], - test_chart.vertices[I[3]]) - - trial_chart = simplex( - trial_chart.vertices[J[1]], - trial_chart.vertices[J[2]], - trial_chart.vertices[J[3]]) - - igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) - G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, rule) - - K = dof_permutation(test_local_space, I) - L = dof_permutation(trial_local_space, J) - for i in 1:numfunctions(test_local_space) - for j in 1:numfunctions(trial_local_space) - out[i,j] = G[K[i],L[j]] - end end - - nothing -end - - -function (igd::Integrand{<:DoubleLayerRotatedMW3D})(u,v) - - x = neighborhood(igd.test_chart,u) - y = neighborhood(igd.trial_chart,v) - j = jacobian(x) * jacobian(y) - nx = normal(x) - - r = cartesian(x) - cartesian(y) - R = norm(r) - iR = 1/R - γ = igd.operator.gamma - G = exp(-γ*R)/(4π*R) - K = -(γ + iR) * G * (iR * r) - - f = igd.local_test_space(x) - g = igd.local_trial_space(y) - - fvalue = getvalue(f) - gvalue = getvalue(g) - - jKg = cross.(Ref(K), j*gvalue) - jnxKg = cross.(Ref(nx), jKg) - return _krondot(fvalue, jnxKg) -end diff --git a/src/maxwell/sauterschwabints_rt.jl b/src/quadrature/sauterschwabints.jl similarity index 61% rename from src/maxwell/sauterschwabints_rt.jl rename to src/quadrature/sauterschwabints.jl index 32745e47..8d68152f 100644 --- a/src/maxwell/sauterschwabints_rt.jl +++ b/src/quadrature/sauterschwabints.jl @@ -1,7 +1,12 @@ +struct Integrand{Op,LSt,LSb,Elt,Elb} + operator::Op + local_test_space::LSt + local_trial_space::LSb + test_chart::Elt + trial_chart::Elb +end - -const i4pi = 1 / (4pi) function (igd::Integrand)(u,v) x = neighborhood(igd.test_chart,u) @@ -13,6 +18,46 @@ function (igd::Integrand)(u,v) return jacobian(x) * jacobian(y) * igd(x,y,f,g) end +getvalue(a::SVector{N}) where {N} = SVector{N}(getvalue(a.data)) +getvalue(a::NTuple{1}) = (a[1].value,) +getvalue(a::NTuple{N}) where {N} = tuple(a[1].value, getvalue(Base.tail(a))...) + +getdivergence(a::SVector{N}) where {N} = SVector{N}(getdivergence(a.data)) +getdivergence(a::NTuple{1}) = (a[1].divergence,) +getdivergence(a::NTuple{N}) where {N} = tuple(a[1].divergence, getdivergence(Base.tail(a))...) + +function _krondot_gen(a::Type{U}, b::Type{V}) where {U<:SVector{N}, V<:SVector{M}} where {M,N} + ex = :(SMatrix{N,M}(())) + for m in 1:M + for n in 1:N + push!(ex.args[2].args, :(dot(a[$n], b[$m]))) + end + end + return ex +end +@generated function _krondot(a::SVector{N}, b::SVector{M}) where {M,N} + ex = _krondot_gen(a,b) + return ex +end + +function _integrands_gen(::Type{U}, ::Type{V}) where {U<:SVector{N}, V<:SVector{M}} where {M,N} + ex = :(SMatrix{N,M}(())) + for m in 1:M + for n in 1:N + # push!(ex.args[2].args, :(dot(a[$n], b[$m]))) + push!(ex.args[2].args, :(f(a[$n], b[$m]))) + end + end + return ex +end +@generated function _integrands(f, a::SVector{N}, b::SVector{M}) where {M,N} + ex = _integrands_gen(a,b) + # println(ex) + return ex +end + + + function _integrands_leg_gen(f::Type{U}, g::Type{V}) where {U<:SVector{N}, V<:SVector{M}} where {M,N} ex = :(SMatrix{N,M}(())) @@ -23,8 +68,10 @@ function _integrands_leg_gen(f::Type{U}, g::Type{V}) where {U<:SVector{N}, V<:SV end return ex end +@generated function _integrands_leg(op, kervals, f::SVector{N}, x, g::SVector{M}, y) where {M,N} + _integrands_leg_gen(f, g) +end -@generated _integrands_leg(op, kervals, f::SVector{N}, x, g::SVector{M}, y) where {M,N} = _integrands_leg_gen(f, g) # Support for legacy kernels function (igd::Integrand)(x,y,f,g) @@ -35,80 +82,38 @@ function (igd::Integrand)(x,y,f,g) end +function momintegrals!(op::Operator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_chart, trial_chart, out, rule::SauterSchwabStrategy) -function (igd::Integrand{<:MWSingleLayer3D})(x,y,f,g) - α = igd.operator.α - β = igd.operator.β - γ = igd.operator.gamma - - r = cartesian(x) - cartesian(y) - R = norm(r) - iR = 1 / R - green = exp(-γ*R)*(i4pi*iR) - - αG = α * green - βG = β * green - - _integrands(f,g) do fi,gj - αG * dot(fi.value, gj.value) + βG * dot(fi.divergence, gj.divergence) - end -end - -function (igd::Integrand{<:MWSingleLayer3DReg})(x,y,f,g) - α = igd.operator.α - β = igd.operator.β - γ = igd.operator.gamma + I, J, _, _ = SauterSchwabQuadrature.reorder( + test_chart.vertices, + trial_chart.vertices, rule) - r = cartesian(x) - cartesian(y) - R = norm(r) - γR = γ*R - # iR = 1 / R - green = (expm1(-γR) + γR - 0.5*γR^2) / (4pi*R) + test_chart = simplex( + test_chart.vertices[I[1]], + test_chart.vertices[I[2]], + test_chart.vertices[I[3]]) - αG = α * green - βG = β * green + trial_chart = simplex( + trial_chart.vertices[J[1]], + trial_chart.vertices[J[2]], + trial_chart.vertices[J[3]]) - _integrands(f,g) do fi,gj - αG * dot(fi.value, gj.value) + βG * dot(fi.divergence, gj.divergence) - end -end + igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) + G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, rule) + K = dof_permutation(test_local_space, I) + L = dof_permutation(trial_local_space, J) + for i in 1:numfunctions(test_local_space) + for j in 1:numfunctions(trial_local_space) + out[i,j] = G[K[i],L[j]] + end end -function (igd::Integrand{<:MWDoubleLayer3D})(x,y,f,g) - - γ = igd.operator.gamma - - r = cartesian(x) - cartesian(y) - R = norm(r) - iR = 1/R - green = exp(-γ*R)*(iR*i4pi) - gradgreen = -(γ + iR) * green * (iR * r) - - fvalue = getvalue(f) - gvalue = getvalue(g) - G = cross.(Ref(gradgreen), gvalue) - return _krondot(fvalue, G) + nothing end -function (igd::Integrand{<:MWDoubleLayer3DReg})(x,y,f,g) - - γ = igd.operator.gamma - - r = cartesian(x) - cartesian(y) - R = norm(r) - γR = γ*R - iR = 1/R - expo = exp(-γR) - green = (expo - 1 + γR - 0.5*γR^2) * (i4pi*iR) - gradgreen = ( -(γR + 1)*expo + (1 - 0.5*γR^2) ) * (i4pi*iR^3) * r - - fvalue = getvalue(f) - gvalue = getvalue(g) - G = cross.(Ref(gradgreen), gvalue) - return _krondot(fvalue, G) -end - function momintegrals_test_refines_trial!(out, op, test_functions, test_cell, test_chart, trial_functions, trial_cell, trial_chart, @@ -201,4 +206,4 @@ function momintegrals_trial_refines_test!(out, op, out[i,j] += Q[i,k] * zlocal[k,j] end end end end -end \ No newline at end of file +end From 7b52040bbe884e0fe9f54cebcd62fb3b0c906e09 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 21 Nov 2022 11:20:01 +0100 Subject: [PATCH 203/528] duallagrangecxd0 compatible with new CSM --- src/bases/lagrange.jl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 59d12c99..67fda06c 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -138,7 +138,8 @@ end function duallagrangecxd0(mesh, vertices::CompScienceMeshes.AbstractMesh{U,1}) where {U} - vertexlist = Int[v[1] for v in vertices] + # vertexlist = Int[v[1] for v in vertices] + vertexlist =Int[CompScienceMeshes.indices(vertices, v)[1] for v in vertices] return duallagrangecxd0(mesh, vertexlist) end @@ -560,10 +561,12 @@ function dual0forms_body(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}, refd, bn bfs = Vector{Vector{S}}(undef, length(mesh)) pos = Vector{V}(undef, length(mesh)) - Cells = cells(mesh) + # Cells = cells(mesh) + Cells = [c for c in mesh] num_threads = Threads.nthreads() for F in 1:length(mesh) - Cell = Cells[F] + # Cell = Cells[F] + Cell = CompScienceMeshes.indices(mesh, Cells[F]) myid = Threads.threadid() # myid == 1 && F % 20 == 0 && @@ -641,7 +644,7 @@ function dual0forms_body(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}, refd, bn addf!(fn, x3_prt, Lg3_prt, idcs3) addf!(fn, x3_int, Lg3_int, idcs3) - pos[F] = cartesian(CompScienceMeshes.center(chart(mesh, Cell))) + pos[F] = cartesian(CompScienceMeshes.center(chart(mesh, Cells[F]))) bfs[F] = fn end From 8c172dd4e6d13c6e42c73fd9f0ec01984b0142c2 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 21 Nov 2022 16:11:11 +0100 Subject: [PATCH 204/528] Setting quadstrat for td operators enabled --- examples/tdefie.jl | 4 +- examples/tdefie_nodot.jl | 8 ++-- examples/tdmfie.jl | 2 +- examples/tdpmchwt.jl | 11 ++++-- src/maxwell/timedomain/mwtdops.jl | 64 +++++++------------------------ src/quadrature/quadstrats.jl | 4 ++ 6 files changed, 32 insertions(+), 61 deletions(-) diff --git a/examples/tdefie.jl b/examples/tdefie.jl index 13374a94..db22d470 100644 --- a/examples/tdefie.jl +++ b/examples/tdefie.jl @@ -12,7 +12,7 @@ U = timebasisdelta(Δt, Nt) V = X ⊗ T W = X ⊗ U -duration = 20 * Δt * 2 +duration = 20 * Δt delay = 1.5 * duration amplitude = 1.0 gaussian = creategaussian(duration, delay, amplitude) @@ -22,6 +22,8 @@ E = planewave(polarisation, direction, derive(gaussian), 1.0) @hilbertspace j @hilbertspace j′ +BEAST.@defaultquadstrat (SL, W, V) BEAST.OuterNumInnerAnalyticQStrat(7) + SL = TDMaxwell3D.singlelayer(speedoflight=1.0, numdiffs=1) tdefie = @discretise SL[j′,j] == -1.0E[j′] j∈V j′∈W xefie = solve(tdefie) diff --git a/examples/tdefie_nodot.jl b/examples/tdefie_nodot.jl index 7d0e7c45..d948c6cd 100644 --- a/examples/tdefie_nodot.jl +++ b/examples/tdefie_nodot.jl @@ -1,11 +1,11 @@ using CompScienceMeshes, BEAST # Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) -# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -Γ = meshsphere(radius=1.0, h=0.4) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +# Γ = meshsphere(radius=1.0, h=0.4) X = raviartthomas(Γ) -Δt, Nt = 1.2, 200 +Δt, Nt = 0.1, 200 T = timebasisshiftedlagrange(Δt, Nt, 1) U = timebasisdelta(Δt, Nt) @@ -25,7 +25,7 @@ SL = TDMaxwell3D.singlelayer(speedoflight=1.0) @hilbertspace j @hilbertspace j′ efie_nodot = @discretise SL[j′,j] == E[j′] j∈V j′∈W -error() +# error() xefie_nodot = solve(efie_nodot) diff --git a/examples/tdmfie.jl b/examples/tdmfie.jl index b538ea3a..89620f75 100644 --- a/examples/tdmfie.jl +++ b/examples/tdmfie.jl @@ -12,7 +12,7 @@ T = timebasisshiftedlagrange(Δt, Nt, 2) V = X ⊗ T W = Y ⊗ δ -duration = 20 * Δt * 2 +duration = 20 * Δt delay = 1.5 * duration amplitude = 1.0 gaussian = creategaussian(duration, delay, amplitude) diff --git a/examples/tdpmchwt.jl b/examples/tdpmchwt.jl index 6aaaa4cc..fd9d9d7d 100644 --- a/examples/tdpmchwt.jl +++ b/examples/tdpmchwt.jl @@ -42,20 +42,23 @@ H = direction × E # 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], # j∈X⊗T2, m∈Y⊗T2, k∈X⊗δ, l∈Y⊗δ) +BEAST.@defaultquadstrat (T, X⊗δ, X⊗T2) BEAST.OuterNumInnerAnalyticQStrat(3) +BEAST.@defaultquadstrat (K, X⊗δ, X⊗T2) BEAST.OuterNumInnerAnalyticQStrat(3) + pmchwt = @discretise( 2.0T[k,j] + 2.0K[k,m] - 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], j∈X⊗T2, m∈X⊗T2, k∈X⊗δ, l∈X⊗δ) -Z = BEAST.td_assemble(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict); -w = BEAST.ConvolutionOperators.polyvals(Z) +# Z = BEAST.td_assemble(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict); +# w = BEAST.ConvolutionOperators.polyvals(Z) # error() u = solve(pmchwt) using Plots -plot(u[1,:]) - +scatter!(u[1,:]) +nothing # nX = numfunctions(X) # uj = u[1:nX] # um = u[nX+1:end] diff --git a/src/maxwell/timedomain/mwtdops.jl b/src/maxwell/timedomain/mwtdops.jl index 5a0becea..0c91094e 100644 --- a/src/maxwell/timedomain/mwtdops.jl +++ b/src/maxwell/timedomain/mwtdops.jl @@ -74,10 +74,10 @@ end # module TDMaxwell3D export TDMaxwell3D -defaultquadstrat(::MWSingleLayerTDIO, tfs, bfs) = nothing +defaultquadstrat(::MWSingleLayerTDIO, tfs, bfs) = OuterNumInnerAnalyticQStrat(3) function quaddata(op::MWSingleLayerTDIO, testrefs, trialrefs, timerefs, - testels, trialels, timeels, ::Nothing) + testels, trialels, timeels, quadstrat::OuterNumInnerAnalyticQStrat) dmax = numfunctions(timerefs)-1 bn = binomial.((0:dmax),(0:dmax)') @@ -85,12 +85,12 @@ function quaddata(op::MWSingleLayerTDIO, testrefs, trialrefs, timerefs, V = eltype(testels[1].vertices) ws = WiltonInts84.workspace(V) # quadpoints(testrefs, testels, (3,)), bn, ws - quadpoints(testrefs, testels, (3,)), bn, ws + quadpoints(testrefs, testels, (quadstrat.outer_rule,)), bn, ws end quadrule(op::MWSingleLayerTDIO, testrefs, trialrefs, timerefs, - p, testel, q, trialel, r, timeel, qd, ::Nothing) = WiltonInts84Strat(qd[1][1,p],qd[2],qd[3]) + p, testel, q, trialel, r, timeel, qd, ::OuterNumInnerAnalyticQStrat) = WiltonInts84Strat(qd[1][1,p],qd[2],qd[3]) struct TransposedStorage{F} @@ -100,44 +100,6 @@ end @inline (f::TransposedStorage)(v,m,n,k) = f.store(v,n,m,k) -# function allocatestorage(op::MWDoubleLayerTDIO, testST, basisST, -# ::Type{Val{:bandedstorage}}, -# ::Type{LongDelays{:ignore}}) - -# # tfs = spatialbasis(testST) -# # bfs = spatialbasis(basisST) -# X, T = spatialbasis(testST), temporalbasis(testST) -# Y, U = spatialbasis(basisST), temporalbasis(basisST) - -# if CompScienceMeshes.refines(geometry(Y), geometry(X)) -# testST = Y⊗T -# basisST = X⊗U -# end - -# M = numfunctions(X) -# N = numfunctions(Y) - -# K0 = fill(typemax(Int), M, N) -# K1 = zeros(Int, M, N) - -# function store(v,m,n,k) -# K0[m,n] = min(K0[m,n],k) -# K1[m,n] = max(K1[m,n],k) -# end - -# aux = EmptyRP(op.speed_of_light) -# print("Allocating memory for convolution operator: ") -# assemble!(aux, testST, basisST, store) -# println("\nAllocated memory for convolution operator.") - -# maxk1 = maximum(K1) -# bandwidth = maximum(K1 .- K0 .+ 1) -# data = zeros(eltype(op), bandwidth, M, N) -# Z = SparseND.Banded3D(K0, data, maxk1) -# store1(v,m,n,k) = (Z[m,n,k] += v) -# return ()->Z, store1 -# end - function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, store, threading=Threading{:multi}; quadstrat=defaultquadstrat(dl,W,V)) @@ -168,41 +130,41 @@ function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, # return assemble_chunk!(dl, W, V, store1) end -defaultquadstrat(::MWDoubleLayerTDIO, tfs, bfs) = nothing +defaultquadstrat(::MWDoubleLayerTDIO, tfs, bfs) = OuterNumInnerAnalyticQStrat(3) function quaddata(op::MWDoubleLayerTDIO, testrefs, trialrefs, timerefs, - testels, trialels, timeels, quadstrat::Nothing) + testels, trialels, timeels, quadstrat::OuterNumInnerAnalyticQStrat) dmax = numfunctions(timerefs)-1 bn = binomial.((0:dmax),(0:dmax)') V = eltype(testels[1].vertices) ws = WiltonInts84.workspace(V) - # quadpoints(testrefs, testels, (3,)), bn, ws - quadpoints(testrefs, testels, (3,)), bn, ws + + quadpoints(testrefs, testels, (quadstrat.outer_rule,)), bn, ws end quadrule(op::MWDoubleLayerTDIO, testrefs, trialrefs, timerefs, - p, testel, q, trialel, r, timeel, qd, quadstrat::Nothing) = + p, testel, q, trialel, r, timeel, qd, quadstrat::OuterNumInnerAnalyticQStrat) = WiltonInts84Strat(qd[1][1,p],qd[2],qd[3]) -defaultquadstrat(::MWDoubleLayerTransposedTDIO, tfs, bfs) = nothing +defaultquadstrat(::MWDoubleLayerTransposedTDIO, tfs, bfs) = OuterNumInnerAnalyticQStrat(3) function quaddata(op::MWDoubleLayerTransposedTDIO, testrefs, trialrefs, timerefs, - testels, trialels, timeels, quadstrat::Nothing) + testels, trialels, timeels, quadstrat::OuterNumInnerAnalyticQStrat) dmax = numfunctions(timerefs)-1 bn = binomial.((0:dmax),(0:dmax)') V = eltype(testels[1].vertices) ws = WiltonInts84.workspace(V) - quadpoints(testrefs, testels, (3,)), bn, ws + quadpoints(testrefs, testels, (quadstrat.outer_rule,)), bn, ws end quadrule(op::MWDoubleLayerTransposedTDIO, testrefs, trialrefs, timerefs, - p, testel, q, trialel, r, timeel, qd, quadstrat::Nothing) = + p, testel, q, trialel, r, timeel, qd, quadstrat::OuterNumInnerAnalyticQStrat) = WiltonInts84Strat(qd[1][1,p],qd[2],qd[3]) function momintegrals!(z, op::MWDoubleLayerTransposedTDIO, diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index e12990eb..43c10700 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -25,6 +25,10 @@ struct SauterSchwab3DQStrat{R,S} sauter_schwab_4D::S end +struct OuterNumInnerAnalyticQStrat{R} + outer_rule::R +end + defaultquadstrat(op, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bfs)) macro defaultquadstrat(dop, body) From bdd93641f087b1aa881791463ef1b04a753b7c54 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 23 Nov 2022 11:26:02 +0100 Subject: [PATCH 205/528] ZeroMap uses _unsafe_mul! --- src/utils/zeromap.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/zeromap.jl b/src/utils/zeromap.jl index f33390fc..b19d2cdc 100644 --- a/src/utils/zeromap.jl +++ b/src/utils/zeromap.jl @@ -11,7 +11,7 @@ LinearMaps.MulStyle(A::ZeroMap) = LinearMaps.FiveArg() Base.size(A::ZeroMap) = (length(A.range), length(A.domain),) Base.axes(A::ZeroMap) = (A.range, A.domain) -function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector, α::Number, β::Number) +function LinearMaps._unsafe_mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector, α::Number, β::Number) y .*= β end @@ -20,12 +20,12 @@ end # return Y # end -function LinearAlgebra.mul!(Y::AbstractMatrix, X::ZeroMap, c::Number, a::Number, b::Number) +function LinearMaps._unsafe_mul!(Y::AbstractMatrix, X::ZeroMap, c::Number, a::Number, b::Number) rmul!(Y, b) return Y end -function LinearAlgebra.mul!(Y::AbstractMatrix, X::ZeroMap, c::Number) +function LinearMaps._unsafe_mul!(Y::AbstractMatrix, X::ZeroMap, c::Number) fill!(Y, false) return Y end @@ -38,7 +38,7 @@ end # end -function LinearAlgebra.mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector) +function LinearMaps._unsafe_mul!(y::AbstractVector, L::ZeroMap, x::AbstractVector) y .= 0 end From b704edc407621efb60a27b7ca6c306eb7ed320e0 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 7 Dec 2022 16:06:19 +0100 Subject: [PATCH 206/528] better support to assemble tensor ops --- examples/efie.jl | 3 ++- examples/tdefie.jl | 8 ++++---- examples/tdmfie.jl | 4 ++-- src/postproc.jl | 2 +- src/timedomain/tdtimeops.jl | 8 ++++---- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/examples/efie.jl b/examples/efie.jl index cac0c599..fd2ad405 100644 --- a/examples/efie.jl +++ b/examples/efie.jl @@ -3,6 +3,7 @@ using BEAST Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) # Γ = meshsphere(radius=1.0, h=0.1) +# Γ = CompScienceMeshes.meshmobius(h=0.035) X = raviartthomas(Γ) κ, η = 1.0, 1.0 @@ -14,7 +15,7 @@ e = (n × E) × n @hilbertspace j @hilbertspace k efie = @discretise t[k,j]==e[k] j∈X k∈X -u = gmres(efie) +u = gmres(efie; restart=1500) include("utils/postproc.jl") include("utils/plotresults.jl") diff --git a/examples/tdefie.jl b/examples/tdefie.jl index db22d470..3a410f3e 100644 --- a/examples/tdefie.jl +++ b/examples/tdefie.jl @@ -1,6 +1,6 @@ using CompScienceMeshes, BEAST, LinearAlgebra Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) -# Γ = meshsphere(radius=1.0, h=0.1) +Γ = meshsphere(radius=1.0, h=0.1) X = raviartthomas(Γ) @@ -12,7 +12,7 @@ U = timebasisdelta(Δt, Nt) V = X ⊗ T W = X ⊗ U -duration = 20 * Δt +duration = 2 * 20 * Δt delay = 1.5 * duration amplitude = 1.0 gaussian = creategaussian(duration, delay, amplitude) @@ -22,9 +22,9 @@ E = planewave(polarisation, direction, derive(gaussian), 1.0) @hilbertspace j @hilbertspace j′ -BEAST.@defaultquadstrat (SL, W, V) BEAST.OuterNumInnerAnalyticQStrat(7) - SL = TDMaxwell3D.singlelayer(speedoflight=1.0, numdiffs=1) +# BEAST.@defaultquadstrat (SL, W, V) BEAST.OuterNumInnerAnalyticQStrat(7) + tdefie = @discretise SL[j′,j] == -1.0E[j′] j∈V j′∈W xefie = solve(tdefie) diff --git a/examples/tdmfie.jl b/examples/tdmfie.jl index 89620f75..49d5d21f 100644 --- a/examples/tdmfie.jl +++ b/examples/tdmfie.jl @@ -1,6 +1,6 @@ using CompScienceMeshes, BEAST Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -# Γ = meshsphere(1.0, 0.4) +Γ = meshsphere(radius=1.0, h=0.1) X = raviartthomas(Γ) Y = buffachristiansen(Γ) @@ -12,7 +12,7 @@ T = timebasisshiftedlagrange(Δt, Nt, 2) V = X ⊗ T W = Y ⊗ δ -duration = 20 * Δt +duration = 2 * 20 * Δt delay = 1.5 * duration amplitude = 1.0 gaussian = creategaussian(duration, delay, amplitude) diff --git a/src/postproc.jl b/src/postproc.jl index 2b8fe605..082ad4da 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -150,7 +150,7 @@ function potential(op, points, coeffs, space::DirectProductSpace; type=SVector{3 # T = SVector{3,eltype(coeffs)} T = type ff = zeros(T, size(points)) - @show size(ff) + # @show size(ff) @assert length(coeffs) == numfunctions(space) diff --git a/src/timedomain/tdtimeops.jl b/src/timedomain/tdtimeops.jl index 8f73d332..f889fc4f 100644 --- a/src/timedomain/tdtimeops.jl +++ b/src/timedomain/tdtimeops.jl @@ -86,12 +86,12 @@ function allocatestorage(op::TensorOperator, test_functions, trial_functions, K0 = ones(Int, M, N) bandwidth = numintervals(time_basis_function) - 1 K1 = ones(Int,M,N) .+ (bandwidth - 1) - T = scalartype(op) + T = scalartype(op, test_functions, trial_functions) data = zeros(T, bandwidth, M, N) tail = zeros(T, M, N) Nt = numfunctions(temporalbasis(trial_functions)) - Z = ConvOp(data, K0, K1, tail, Nt) + Z = ConvolutionOperators.ConvOp(data, K0, K1, tail, Nt) function store1(v,m,n,k) if Z.k0[m,n] ≤ k ≤ Z.k1[m,n] Z.data[k - Z.k0[m,n] + 1,m,n] += v @@ -99,7 +99,7 @@ function allocatestorage(op::TensorOperator, test_functions, trial_functions, Z.tail[m,n] += v end end - return Z, store1 + return ()->Z, store1 end # function allocatestorage(op::TensorOperator, test_functions, trial_functions) @@ -132,7 +132,7 @@ end # end function assemble!(operator::TensorOperator, testfns, trialfns, store, - threading = Threading{:multi}) + threading = Threading{:multi}; quadstrat=defaultquadstrat(operator, testfns, trialfns)) space_operator = operator.spatial_factor time_operator = operator.temporal_factor From 1943bdb7b9de36aace7ade5da9f0e12fd4092536 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 9 Dec 2022 11:11:10 +0100 Subject: [PATCH 207/528] set version to v1.8.1 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c6ac471c..8579e162 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.8.0" +version = "1.8.1" [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" From 72ef98197d08d7ba9327987d037bded4051e6a60 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 9 Dec 2022 13:01:28 +0100 Subject: [PATCH 208/528] increase compat reqs for ConvolutionOperators --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 8579e162..50977b3e 100644 --- a/Project.toml +++ b/Project.toml @@ -33,7 +33,7 @@ CollisionDetection = "0.1.5" Combinatorics = "0.7, 1" CompScienceMeshes = "0.5.1" Compat = "2, 3, 4" -ConvolutionOperators = "0.3" +ConvolutionOperators = "0.4" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4, 0.5" FillArrays = "0.11, 0.12, 0.13" From b602bfe70b40a6140fc15ac722c7e79de4867de1 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Wed, 18 Jan 2023 23:17:11 +0100 Subject: [PATCH 209/528] Helmholtz3D: DirichletTrace and new excitations Helmholtz3D is now a bit closer to the Maxwell case by offering a Dirichlet Trace. New excitations: - Monopole source (e.g., useful for electrostatic charges) - Linear potential (e.g., for uniform static fields) --- src/BEAST.jl | 5 +- src/helmholtz3d/hh3dexc.jl | 130 +++++++++++++++++++++++++++++++++++- test/test_hh3d_nearfield.jl | 58 +++++----------- test/test_hh3dexc.jl | 6 ++ 4 files changed, 156 insertions(+), 43 deletions(-) diff --git a/src/BEAST.jl b/src/BEAST.jl index ebd5b2b7..d59803d3 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -52,8 +52,11 @@ export DoubleLayerTransposed export HyperSingular export HH3DSingleLayerTDBIO export HH3DDoubleLayerTDBIO -export ∂n +export ∂n, grad export HH3DHyperSingularFDBIO +export DirichletTrace +export HH3DMonopole, gradHH3DMonopole +export HH3DLinearPotential export HH3DSingleLayerNear export HH3DDoubleLayerNear diff --git a/src/helmholtz3d/hh3dexc.jl b/src/helmholtz3d/hh3dexc.jl index c2d2d267..e52745b2 100644 --- a/src/helmholtz3d/hh3dexc.jl +++ b/src/helmholtz3d/hh3dexc.jl @@ -1,6 +1,6 @@ -struct HH3DPlaneWave{T,P} <: Functional +struct HH3DPlaneWave{T,P} direction::P wavenumber::T amplitude::T @@ -18,6 +18,118 @@ function (f::HH3DPlaneWave)(r) a * exp(-im*k*dot(d,r)) end +""" + HH3DLinearPotential + +A potential that linearly increases in `direction` with scaling coefficient `amplitude`. +Its negative gradient will be a uniform vector field pointing in the opposite direction. +""" +struct HH3DLinearPotential{T,P} + direction::P + amplitude::T +end + +function HH3DLinearPotential(; direction=SVector(1,0,0), amplitude=1.0) + HH3DLinearPotential(direction ./ norm(direction), amplitude) +end + +function (f::HH3DLinearPotential)(r) + d = f.direction + a = f.amplitude + return a * dot(d, r) +end + +struct gradHH3DLinearPotential{T,P} + direction::P + amplitude::T +end + +function gradHH3DLinearPotential(;direction=SVector(0.0,0.0,0.0), amplitude=1.0) + gradHH3DLinearPotential(direction, amplitude) +end + +function (f::gradHH3DLinearPotential)(r) + d = f.direction + a = f.amplitude + + return a * d +end + +function grad(m::HH3DLinearPotential) + return gradHH3DLinearPotential(m.direction, m.amplitude) +end + +*(a::Number, m::HH3DLinearPotential) = HH3DLinearPotential(m.direction, a * m.amplitude) +*(a::Number, m::gradHH3DLinearPotential) = gradHH3DLinearPotential(m.direction, a * m.amplitude) + +dot(::NormalVector, m::gradHH3DLinearPotential) = NormalDerivative(HH3DLinearPotential(m.direction, m.amplitude)) + +""" + HH3DMonopole + +Potential of a monopole-type point source (e.g., of an electric charge) +""" +struct HH3DMonopole{T,P} + position::P + wavenumber::T + amplitude::T +end + +function HH3DMonopole(;position=SVector(0.0,0.0,0.0), wavenumber=0.0, amplitude=1.0) + w, a = promote(wavenumber, amplitude) + HH3DMonopole(position, w, a) +end + +function (f::HH3DMonopole)(r) + k = f.wavenumber + p = f.position + a = f.amplitude + + return a*exp(-im * k * norm(r - p)) / (norm(r - p)) +end + +struct gradHH3DMonopole{T,P} + position::P + wavenumber::T + amplitude::T +end + +function gradHH3DMonopole(;position=SVector(0.0,0.0,0.0), wavenumber=0.0, amplitude=1.0) + w, a = promote(wavenumber, amplitude) + gradHH3DMonopole(position, w, a) +end + +function (f::gradHH3DMonopole)(r) + a = f.amplitude + k = f.wavenumber + p = f.position + vecR = r - p + R = norm(vecR) + + return -a * vecR * exp(-im * k * R) / R^2 * (im * k + 1 / R) +end + +function grad(m::HH3DMonopole) + return gradHH3DMonopole(m.position, m.wavenumber, m.amplitude) +end + +*(a::Number, m::HH3DMonopole) = HH3DMonopole(m.position, m.wavenumber, a * m.amplitude) +*(a::Number, m::gradHH3DMonopole) = gradHH3DMonopole(m.position, m.wavenumber, a * m.amplitude) + +dot(::NormalVector, m::gradHH3DMonopole) = NormalDerivative(HH3DMonopole(m.position, m.wavenumber, m.amplitude)) + +mutable struct DirichletTrace{F} <: Functional + field::F +end + +function (ϕ::DirichletTrace)(p) + F = ϕ.field + x = cartesian(p) + return F(x) +end + +integrand(::DirichletTrace, test_vals, field_vals) = dot(test_vals[1], field_vals) + struct NormalDerivative{F} <: Functional field::F end @@ -34,5 +146,19 @@ function (f::NormalDerivative{T})(manipoint) where T<:HH3DPlaneWave -im*k*a * dot(d,n) * exp(-im*k*dot(d,r)) end +function (f::NormalDerivative{T})(manipoint) where T<:HH3DLinearPotential + gradient = f.field.amplitude * f.field.direction + n = normal(manipoint) + return dot(n, gradient) +end + +function (f::NormalDerivative{T})(manipoint) where T<:HH3DMonopole + m = f.field + grad_m = grad(m) + n = normal(manipoint) + r = cartesian(manipoint) + + return dot(n, grad_m(r)) +end -integrand(::NormalDerivative, test_vals, field_vals) = dot(test_vals[1], field_vals) +integrand(::NormalDerivative, test_vals, field_vals) = dot(test_vals[1], field_vals) \ No newline at end of file diff --git a/test/test_hh3d_nearfield.jl b/test/test_hh3d_nearfield.jl index ba239020..c4af6fb3 100644 --- a/test/test_hh3d_nearfield.jl +++ b/test/test_hh3d_nearfield.jl @@ -4,7 +4,7 @@ using StaticArrays using LinearAlgebra using Test -@testset "Helmholtz potential operators" begin +#@testset "Helmholtz potential operators" begin r = 10.0 λ = 20 * r k = 2 * π / λ @@ -26,31 +26,16 @@ using Test pos1 = SVector(r * 1.5, 0.0, 0.0) # positioning of point charges pos2 = SVector(-r * 1.5, 0.0, 0.0) + charge1 = HH3DMonopole(position=pos1, amplitude=q/(4*π*ϵ), wavenumber=k) + charge2 = HH3DMonopole(position=pos2, amplitude=-q/(4*π*ϵ), wavenumber=k) + # Potential of point charges - function Φ_inc(x) - return q / (4 * π * ϵ) * ( - exp(-im * k * norm(x - pos1)) / (norm(x - pos1)) - - exp(-im * k * norm(x - pos2)) / (norm(x - pos2)) - ) - end - - function ∂nΦ_inc(x) - return -q / (4 * π * ϵ * r) * ( - (dot( - x, - (x - pos1) * exp(-im * k * norm(x - pos1)) / (norm(x - pos1)^2) * - (im * k + 1 / (norm(x - pos1))), - )) - (dot( - x, - (x - pos2) * exp(-im * k * norm(x - pos2)) / (norm(x - pos2)^2) * - (im * k + 1 / (norm(x - pos2))), - )) - ) - end - - gD0 = assemble(ScalarTrace(Φ_inc), X0) - gD1 = assemble(ScalarTrace(Φ_inc), X1) - gN = assemble(ScalarTrace(∂nΦ_inc), X1) + + Φ_inc(x) = charge1(x) + charge2(x) + + gD0 = assemble(DirichletTrace(charge1), X0) + assemble(DirichletTrace(charge2), X0) + gD1 = assemble(DirichletTrace(charge1), X1) + assemble(DirichletTrace(charge2), X1) + gN = assemble(∂n(charge1), X1) + assemble(BEAST.n ⋅ grad(charge2), X1) G = assemble(Identity(), X1, X1) o = ones(numfunctions(X1)) @@ -86,17 +71,7 @@ using Test err_INPDL_pot = norm(pot_INPDL + Φ_inc.(pts)) / norm(Φ_inc.(pts)) # Efield(x) = - grad Φ_inc(x) - function Efield(x) - return q / (4 * π * ϵ) * ( - ( - (x - pos1) * exp(-im * k * norm(x - pos1)) / (norm(x - pos1)^2) * - (im * k + 1 / (norm(x - pos1))) - ) - ( - (x - pos2) * exp(-im * k * norm(x - pos2)) / (norm(x - pos2)^2) * - (im * k + 1 / (norm(x - pos2))) - ) - ) - end + Efield(x) = -grad(charge1)(x) + -grad(charge2)(x) field_IDPSL = -potential(HH3DDoubleLayerTransposedNear(im * k), pts, ρ_IDPSL, X0) field_IDPDL = potential(HH3DHyperSingularNear(im * k), pts, ρ_IDPDL, X1) @@ -114,9 +89,12 @@ using Test pos1 = SVector(r * 0.5, 0.0, 0.0) pos2 = SVector(-r * 0.5, 0.0, 0.0) - gD0 = assemble(ScalarTrace(Φ_inc), X0) - gD1 = assemble(ScalarTrace(Φ_inc), X1) - gN = assemble(ScalarTrace(∂nΦ_inc), X1) + charge1 = HH3DMonopole(position=pos1, amplitude=q/(4*π*ϵ), wavenumber=k) + charge2 = HH3DMonopole(position=pos2, amplitude=-q/(4*π*ϵ), wavenumber=k) + + gD0 = assemble(DirichletTrace(charge1), X0) + assemble(DirichletTrace(charge2), X0) + gD1 = assemble(DirichletTrace(charge1), X1) + assemble(DirichletTrace(charge2), X1) + gN = assemble(∂n(charge1), X1) + assemble(∂n(charge2), X1) G = assemble(Identity(), X1, X1) o = ones(numfunctions(X1)) @@ -177,4 +155,4 @@ using Test @test err_EDPDL_field < 0.03 @test err_ENPSL_field < 0.01 @test err_ENPDL_field < 0.02 -end \ No newline at end of file +#end \ No newline at end of file diff --git a/test/test_hh3dexc.jl b/test/test_hh3dexc.jl index fd5434f1..a42fdf39 100644 --- a/test/test_hh3dexc.jl +++ b/test/test_hh3dexc.jl @@ -16,6 +16,12 @@ for T in [Float32, Float64] @test v1 ≈ +1 @test v2 ≈ -1 + lp = HH3DLinearPotential(direction=point(T,0,1,0), amplitude=2.0) + @test lp(point(T,1,1,0)) == T(2.0) + + gradlp = grad(lp) + @test gradlp(point(T,1,1,0)) == point(T, 0, 2, 0) + import BEAST.∂n p = ∂n(f) From dbd920fed0ccfe47daa78617779c5cbe59773f79 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Thu, 19 Jan 2023 13:09:59 +0100 Subject: [PATCH 210/528] fixup: reenable testset --- test/test_hh3d_nearfield.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_hh3d_nearfield.jl b/test/test_hh3d_nearfield.jl index c4af6fb3..dc4fc28b 100644 --- a/test/test_hh3d_nearfield.jl +++ b/test/test_hh3d_nearfield.jl @@ -4,7 +4,7 @@ using StaticArrays using LinearAlgebra using Test -#@testset "Helmholtz potential operators" begin +@testset "Helmholtz potential operators" begin r = 10.0 λ = 20 * r k = 2 * π / λ @@ -155,4 +155,4 @@ using Test @test err_EDPDL_field < 0.03 @test err_ENPSL_field < 0.01 @test err_ENPDL_field < 0.02 -#end \ No newline at end of file +end \ No newline at end of file From d4d552e5b501cd2d590e1ecb83ba8ba8b1824b29 Mon Sep 17 00:00:00 2001 From: JoshuaTetzner Date: Fri, 27 Jan 2023 16:05:07 +0100 Subject: [PATCH 211/528] Propagate novel mixed discretization handling to blockassembler Recently, the handling of mixed discretization involving both dual and primal functions has been updated in the assembler function. This commit modifies the blockassembler function to follow this novel handling of the assembler function. --- src/integralop.jl | 126 ++++++++++++++++++++++++++++++++++-- test/test_blockassembler.jl | 28 ++++++++ 2 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 test/test_blockassembler.jl diff --git a/src/integralop.jl b/src/integralop.jl index 70d5b448..577daf99 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -82,9 +82,9 @@ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; if CompScienceMeshes.refines(tgeo, bgeo) assemblechunk_body_test_refines_trial!(biop, - tfs, test_elements, tad, tcells, - bfs, bsis_elements, bad, bcells, - qd, zlocal, store; quadstrat) + tfs, test_elements, tad, tcells, + bfs, bsis_elements, bad, bcells, + qd, zlocal, store; quadstrat) elseif CompScienceMeshes.refines(bgeo, tgeo) assemblechunk_body_trial_refines_test!(biop, tfs, test_elements, tad, tcells, @@ -223,16 +223,26 @@ function blockassembler(biop::IntegralOperator, tfs::Space, bfs::Space; trial_elements, trial_assembly_data, quadrature_data, zlocals = assembleblock_primer(biop, tfs, bfs; quadstrat) - if !CompScienceMeshes.refines(tfs.geo, bfs.geo) + tgeo = geometry(tfs) + bgeo = geometry(bfs) + + if CompScienceMeshes.refines(tgeo, bgeo) return (test_ids, trial_ids, store) -> begin - assembleblock_body!(biop, + assembleblock_body_test_refines_trial!(biop, + tfs, test_ids, test_elements, test_assembly_data, + bfs, trial_ids, trial_elements, trial_assembly_data, + quadrature_data, zlocals, store; quadstrat) + end + elseif CompScienceMeshes.refines(bgeo, tgeo) + return (test_ids, trial_ids, store) -> begin + assembleblock_body_trial_refines_test!(biop, tfs, test_ids, test_elements, test_assembly_data, bfs, trial_ids, trial_elements, trial_assembly_data, quadrature_data, zlocals, store; quadstrat) end else return (test_ids, trial_ids, store) -> begin - assembleblock_body_nested!(biop, + assembleblock_body!(biop, tfs, test_ids, test_elements, test_assembly_data, bfs, trial_ids, trial_elements, trial_assembly_data, quadrature_data, zlocals, store; quadstrat) @@ -336,6 +346,110 @@ function assembleblock_body!(biop::IntegralOperator, store(a*zlocals[Threads.threadid()][i,j]*b, m′, n′) end end end end end end end +function assembleblock_body_trial_refines_test!(biop::IntegralOperator, + tfs, test_ids, test_elements, test_assembly_data, + bfs, trial_ids, bsis_elements, trial_assembly_data, + quadrature_data, zlocals, store; quadstrat) + + test_shapes = refspace(tfs) + trial_shapes = refspace(bfs) + + # Enumerate all the active test elements + active_test_el_ids = Vector{Int}() + active_trial_el_ids = Vector{Int}() + + test_id_in_blk = Dict{Int,Int}() + trial_id_in_blk = Dict{Int,Int}() + + for (i,m) in enumerate(test_ids); test_id_in_blk[m] = i; end + for (i,m) in enumerate(trial_ids); trial_id_in_blk[m] = i; end + + for m in test_ids, sh in tfs.fns[m]; push!(active_test_el_ids, sh.cellid); end + for m in trial_ids, sh in bfs.fns[m]; push!(active_trial_el_ids, sh.cellid); end + + active_test_el_ids = unique!(sort!(active_test_el_ids)) + active_trial_el_ids = unique!(sort!(active_trial_el_ids)) + + @assert length(active_test_el_ids) <= length(test_elements) + @assert length(active_trial_el_ids) <= length(bsis_elements) + + @assert maximum(active_test_el_ids) <= length(test_elements) "$(maximum(active_test_el_ids)), $(length(test_elements))" + @assert maximum(active_trial_el_ids) <= length(bsis_elements) "$(maximum(active_trial_el_ids)), $(length(bsis_elements))" + + for p in active_test_el_ids + tcell = test_elements[p] + for q in active_trial_el_ids + bcell = bsis_elements[q] + + fill!(zlocals[Threads.threadid()], 0) + qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) + momintegrals_trial_refines_test!(zlocals[Threads.threadid()], biop, + tfs, p, tcell, + bfs, q, bcell, + qrule, quadstrat) + for j in 1 : size(zlocals[Threads.threadid()],2) + for i in 1 : size(zlocals[Threads.threadid()],1) + for (n,b) in trial_assembly_data[q,j] + n′ = get(trial_id_in_blk, n, 0) + n′ == 0 && continue + for (m,a) in test_assembly_data[p,i] + m′ = get(test_id_in_blk, m, 0) + m′ == 0 && continue + store(a*zlocals[Threads.threadid()][i,j]*b, m′, n′) +end end end end end end end + +function assembleblock_body_test_refines_trial!(biop::IntegralOperator, + tfs, test_ids, test_elements, test_assembly_data, + bfs, trial_ids, bsis_elements, trial_assembly_data, + quadrature_data, zlocals, store; quadstrat) + + test_shapes = refspace(tfs) + trial_shapes = refspace(bfs) + + # Enumerate all the active test elements + active_test_el_ids = Vector{Int}() + active_trial_el_ids = Vector{Int}() + + test_id_in_blk = Dict{Int,Int}() + trial_id_in_blk = Dict{Int,Int}() + + for (i,m) in enumerate(test_ids); test_id_in_blk[m] = i; end + for (i,m) in enumerate(trial_ids); trial_id_in_blk[m] = i; end + + for m in test_ids, sh in tfs.fns[m]; push!(active_test_el_ids, sh.cellid); end + for m in trial_ids, sh in bfs.fns[m]; push!(active_trial_el_ids, sh.cellid); end + + active_test_el_ids = unique!(sort!(active_test_el_ids)) + active_trial_el_ids = unique!(sort!(active_trial_el_ids)) + + @assert length(active_test_el_ids) <= length(test_elements) + @assert length(active_trial_el_ids) <= length(bsis_elements) + + @assert maximum(active_test_el_ids) <= length(test_elements) "$(maximum(active_test_el_ids)), $(length(test_elements))" + @assert maximum(active_trial_el_ids) <= length(bsis_elements) "$(maximum(active_trial_el_ids)), $(length(bsis_elements))" + + for p in active_test_el_ids + tcell = test_elements[p] + for q in active_trial_el_ids + bcell = bsis_elements[q] + + fill!(zlocals[Threads.threadid()], 0) + qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) + momintegrals_test_refines_trial!(zlocals[Threads.threadid()], biop, + tfs, p, tcell, + bfs, q, bcell, + qrule, quadstrat) + for j in 1 : size(zlocals[Threads.threadid()],2) + for i in 1 : size(zlocals[Threads.threadid()],1) + for (n,b) in trial_assembly_data[q,j] + n′ = get(trial_id_in_blk, n, 0) + n′ == 0 && continue + for (m,a) in test_assembly_data[p,i] + m′ = get(test_id_in_blk, m, 0) + m′ == 0 && continue + store(a*zlocals[Threads.threadid()][i,j]*b, m′, n′) +end end end end end end end + function assembleblock_body_nested!(biop::IntegralOperator, tfs, test_ids, test_elements, test_assembly_data, diff --git a/test/test_blockassembler.jl b/test/test_blockassembler.jl new file mode 100644 index 00000000..91fdb4fc --- /dev/null +++ b/test/test_blockassembler.jl @@ -0,0 +1,28 @@ +using BEAST +using CompScienceMeshes +using LinearAlgebra +using Test + +r = 10.0 +λ = 20 * r +k = 2 * π / λ + +sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in"), T=Float64) + +D = Maxwell3D.doublelayer(wavenumber=k) +X = raviartthomas(sphere) +Y = buffachristiansen(sphere) + +A = assemble(D, X, X) + +@views blkasm = BEAST.blockassembler(D, X, X) + +@views function assembler(Z, tdata, sdata) + @views store(v,m,n) = (Z[m,n] += v) + blkasm(tdata,sdata,store) +end + +A_blk = zeros(ComplexF64, length(X.fns), length(Y.fns)) +assembler(A_blk, [1:length(X.fns);], [1:length(Y.fns);]) + +@test norm(A - A_blk) ≈ 0 atol=eps(Float64) From 4dacc06a9dd8b57135dfa29e825263466910baca Mon Sep 17 00:00:00 2001 From: Paula Respondek Date: Fri, 27 Jan 2023 16:45:28 +0100 Subject: [PATCH 212/528] Fix sign of Helmholtz3D DoubleLayerNear Operator The sign of the operator was changed to match Literature, and occurances in code were adapted. --- examples/hh3dexample.jl | 92 ++++++++++++++++--------------------- src/helmholtz3d/hh3dnear.jl | 2 +- test/test_hh3d_nearfield.jl | 18 ++++---- 3 files changed, 50 insertions(+), 62 deletions(-) diff --git a/examples/hh3dexample.jl b/examples/hh3dexample.jl index f4560ce8..a68e2c18 100644 --- a/examples/hh3dexample.jl +++ b/examples/hh3dexample.jl @@ -35,44 +35,40 @@ for (i, h) in enumerate(hs) S = Helmholtz3D.singlelayer(; gamma=0.0) D = Helmholtz3D.doublelayer(; gamma=0.0) Dt = Helmholtz3D.doublelayer_transposed(; gamma=0.0) - N = -Helmholtz3D.hypersingular(; gamma=0.0) + N = Helmholtz3D.hypersingular(; gamma=0.0) q = 100.0 ϵ = 1.0 +# Interior problem +# Formulations from Sauter and Schwab, Boundary Element Methods(2011), Chapter 3.4.1.1 pos1 = SVector(r * 1.5, 0.0, 0.0) pos2 = SVector(-r * 1.5, 0.0, 0.0) - Φ_inc(x) = q / (4 * π * ϵ) * (1 / (norm(x - pos1)) - 1 / (norm(x - pos2))) - function ∂nΦ_inc(x) - return -q / (r * 4 * π * ϵ) * ( - (norm(x)^2 - dot(pos1, x)) / (norm(x - pos1)^3) - - (norm(x)^2 - dot(pos2, x)) / (norm(x - pos2)^3) - ) - end + charge1 = HH3DMonopole(position=pos1, amplitude=q/(4*π*ϵ), wavenumber=0.0) + charge2 = HH3DMonopole(position=pos2, amplitude=-q/(4*π*ϵ), wavenumber=0.0) - function Efield(x) - return q / (4 * π * ϵ) * - ((x - pos1) / (norm(x - pos1)^3) - (x - pos2) / (norm(x - pos2)^3)) - end + #Potential of point charges + Φ_inc(x) = charge1(x) + charge2(x) - gD0 = assemble(ScalarTrace(Φ_inc), X0) - gD1 = assemble(ScalarTrace(Φ_inc), X1) - gN = assemble(ScalarTrace(∂nΦ_inc), X1) + gD0 = assemble(DirichletTrace(charge1), X0) + assemble(DirichletTrace(charge2), X0) + gD1 = assemble(DirichletTrace(charge1), X1) + assemble(DirichletTrace(charge2), X1) + gN = assemble(∂n(charge1), X1) + assemble(BEAST.n ⋅ grad(charge2), X1) G = assemble(Identity(), X1, X1) o = ones(numfunctions(X1)) - + #Interior Dirichlet problem - compare Sauter & Schwab M_IDPSL = assemble(S, X0, X0) M_IDPDL = (-1 / 2 * assemble(Identity(), X1, X1) + assemble(D, X1, X1)) + #Interior Neumann problem M_INPSL = (1 / 2 * assemble(Identity(), X1, X1) + assemble(Dt, X1, X1)) + G * o * o' * G - M_INPDL = -assemble(N, X1, X1) + G * o * o' * G + M_INPDL = assemble(N, X1, X1) + G * o * o' * G ρ_IDPSL = M_IDPSL \ (-gD0) - ρ_IDPDL = M_IDPDL \ (gD1) + ρ_IDPDL = M_IDPDL \ (-gD1) ρ_INPSL = M_INPSL \ (-gN) - ρ_INPDL = M_INPDL \ (-gN) + ρ_INPDL = M_INPDL \ (gN) pts = meshsphere(r * ir, r * ir * 0.6).vertices @@ -81,42 +77,37 @@ for (i, h) in enumerate(hs) pot_INPSL = potential(HH3DSingleLayerNear(0.0), pts, ρ_INPSL, X1; type=ComplexF64) pot_INPDL = potential(HH3DDoubleLayerNear(0.0), pts, ρ_INPDL, X1; type=ComplexF64) + # Total potential inside should be zero err_IDPSL_pot[i] = norm(pot_IDPSL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) err_IDPDL_pot[i] = norm(pot_IDPDL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) err_INPSL_pot[i] = norm(pot_INPSL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) err_INPDL_pot[i] = norm(pot_INPDL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) - field_IDPSL = potential(HH3DDoubleLayerTransposedNear(0.0), pts, ρ_IDPSL, X0) - field_IDPDL = potential(HH3DHyperSingularNear(0.0), pts, ρ_IDPDL, X1) - field_INPSL = potential(HH3DDoubleLayerTransposedNear(0.0), pts, ρ_INPSL, X1) - field_INPDL = potential(HH3DHyperSingularNear(0.0), pts, ρ_INPDL, X1) + Efield(x) = -grad(charge1)(x) + -grad(charge2)(x) + + field_IDPSL = -potential(HH3DDoubleLayerTransposedNear(0.0), pts, ρ_IDPSL, X0) + field_IDPDL = -potential(HH3DHyperSingularNear(0.0), pts, ρ_IDPDL, X1) + field_INPSL = -potential(HH3DDoubleLayerTransposedNear(0.0), pts, ρ_INPSL, X1) + field_INPDL = -potential(HH3DHyperSingularNear(0.0), pts, ρ_INPDL, X1) - err_IDPSL_field[i] = norm(field_IDPSL - Efield.(pts)) / norm(Efield.(pts)) + # Total field inside should be zero + err_IDPSL_field[i] = norm(field_IDPSL + Efield.(pts)) / norm(Efield.(pts)) err_IDPDL_field[i] = norm(field_IDPDL + Efield.(pts)) / norm(Efield.(pts)) - err_INPSL_field[i] = norm(field_INPSL - Efield.(pts)) / norm(Efield.(pts)) + err_INPSL_field[i] = norm(field_INPSL + Efield.(pts)) / norm(Efield.(pts)) err_INPDL_field[i] = norm(field_INPDL + Efield.(pts)) / norm(Efield.(pts)) + # Exterior problem + # formulations from Sauter and Schwab, Boundary Element Methods(2011), Chapter 3.4.1.2 + pos1 = SVector(r * 0.5, 0.0, 0.0) pos2 = SVector(-r * 0.5, 0.0, 0.0) - # potential of point charges - Φ_inc(x) = q / (4 * π * ϵ) * (1 / (norm(x - pos1)) - 1 / (norm(x - pos2))) - function ∂nΦ_inc(x) - return -q / (r * 4 * π * ϵ) * ( - (norm(x)^2 - dot(pos1, x)) / (norm(x - pos1)^3) - - (norm(x)^2 - dot(pos2, x)) / (norm(x - pos2)^3) - ) - end - - # Efield(x) = -grad Φ_inc(x) - function Efield(x) - return q / (4 * π * ϵ) * - ((x - pos1) / (norm(x - pos1)^3) - (x - pos2) / (norm(x - pos2)^3)) - end - - gD0 = assemble(ScalarTrace(Φ_inc), X0) - gD1 = assemble(ScalarTrace(Φ_inc), X1) - gN = assemble(ScalarTrace(∂nΦ_inc), X1) + charge1 = HH3DMonopole(position=pos1, amplitude=q/(4*π*ϵ), wavenumber=0.0) + charge2 = HH3DMonopole(position=pos2, amplitude=-q/(4*π*ϵ), wavenumber=0.0) + + gD0 = assemble(DirichletTrace(charge1), X0) + assemble(DirichletTrace(charge2), X0) + gD1 = assemble(DirichletTrace(charge1), X1) + assemble(DirichletTrace(charge2), X1) + gN = assemble(∂n(charge1), X1) + assemble(∂n(charge2), X1) G = assemble(Identity(), X1, X1) o = ones(numfunctions(X1)) @@ -124,22 +115,19 @@ for (i, h) in enumerate(hs) M_EDPSL = assemble(S, X0, X0) M_EDPDL = (1 / 2 * assemble(Identity(), X1, X1) + assemble(D, X1, X1)) - M_ENPSL = - (-1 / 2 * assemble(Identity(), X1, X1) + assemble(Dt, X1, X1)) + G * o * o' * G - M_ENPDL = -assemble(N, X1, X1) + G * o * o' * G + M_ENPSL = (-1 / 2 * assemble(Identity(), X1, X1) + assemble(Dt, X1, X1)) + G * o * o' * G + M_ENPDL = assemble(N, X1, X1) + G * o * o' * G ρ_EDPSL = M_EDPSL \ (-gD0) - ρ_EDPDL = M_EDPDL \ (gD1) - + ρ_EDPDL = M_EDPDL \ (-gD1) ρ_ENPSL = M_ENPSL \ (-gN) - ρ_ENPDL = M_ENPDL \ (-gN) + ρ_ENPDL = M_ENPDL \ (gN) testsphere = meshsphere(r / ir, r / ir * 0.6) pts = testsphere.vertices[norm.(testsphere.vertices) .> r] pot_EDPSL = potential(HH3DSingleLayerNear(0.0), pts, ρ_EDPSL, X0; type=ComplexF64) pot_EDPDL = potential(HH3DDoubleLayerNear(0.0), pts, ρ_EDPDL, X1; type=ComplexF64) - pot_ENPSL = potential(HH3DSingleLayerNear(0.0), pts, ρ_ENPSL, X1; type=ComplexF64) pot_ENPDL = potential(HH3DDoubleLayerNear(0.0), pts, ρ_ENPDL, X1; type=ComplexF64) @@ -149,9 +137,9 @@ for (i, h) in enumerate(hs) err_ENPDL_pot[i] = norm(pot_ENPDL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) field_EDPSL = -potential(HH3DDoubleLayerTransposedNear(0.0), pts, ρ_EDPSL, X0) - field_EDPDL = potential(HH3DHyperSingularNear(0.0), pts, ρ_EDPDL, X1) + field_EDPDL = -potential(HH3DHyperSingularNear(0.0), pts, ρ_EDPDL, X1) field_ENPSL = -potential(HH3DDoubleLayerTransposedNear(0.0), pts, ρ_ENPSL, X1) - field_ENPDL = potential(HH3DHyperSingularNear(0.0), pts, ρ_ENPDL, X1) + field_ENPDL = -potential(HH3DHyperSingularNear(0.0), pts, ρ_ENPDL, X1) err_EDPSL_field[i] = norm(field_EDPSL + Efield.(pts)) / norm(Efield.(pts)) err_EDPDL_field[i] = norm(field_EDPDL + Efield.(pts)) / norm(Efield.(pts)) diff --git a/src/helmholtz3d/hh3dnear.jl b/src/helmholtz3d/hh3dnear.jl index 4b8157f6..3ec763e5 100644 --- a/src/helmholtz3d/hh3dnear.jl +++ b/src/helmholtz3d/hh3dnear.jl @@ -73,7 +73,7 @@ end function integrand(op::HH3DDoubleLayerNear,krn,y,f,p) - ∇G = krn.gradgreen + ∇G = -krn.gradgreen nx = krn.nx fx = f.value diff --git a/test/test_hh3d_nearfield.jl b/test/test_hh3d_nearfield.jl index dc4fc28b..b39e8b11 100644 --- a/test/test_hh3d_nearfield.jl +++ b/test/test_hh3d_nearfield.jl @@ -40,18 +40,18 @@ using Test G = assemble(Identity(), X1, X1) o = ones(numfunctions(X1)) - # Interior Dirichlet problem + # Interior Dirichlet problem - compare Sauter & Schwab eqs. 3.81 M_IDPSL = assemble(S, X0, X0) # Single layer (SL) M_IDPDL = (-1 / 2 * assemble(Identity(), X1, X1) + assemble(D, X1, X1)) # Double layer (DL) # Interior Neumann problem # Neumann derivative from DL potential with deflected nullspace - M_INPDL = -assemble(N, X1, X1) + G * o * o' * G + M_INPDL = assemble(N, X1, X1) + G * o * o' * G # Neumann derivative from SL potential with deflected nullspace M_INPSL = (1 / 2 * assemble(Identity(), X1, X1) + assemble(Dt, X1, X1)) + G * o * o' * G ρ_IDPSL = M_IDPSL \ (-gD0) - ρ_IDPDL = M_IDPDL \ (gD1) + ρ_IDPDL = M_IDPDL \ (-gD1) ρ_INPDL = M_INPDL \ (gN) ρ_INPSL = M_INPSL \ (-gN) @@ -74,9 +74,9 @@ using Test Efield(x) = -grad(charge1)(x) + -grad(charge2)(x) field_IDPSL = -potential(HH3DDoubleLayerTransposedNear(im * k), pts, ρ_IDPSL, X0) - field_IDPDL = potential(HH3DHyperSingularNear(im * k), pts, ρ_IDPDL, X1) + field_IDPDL = -potential(HH3DHyperSingularNear(im * k), pts, ρ_IDPDL, X1) field_INPSL = -potential(HH3DDoubleLayerTransposedNear(im * k), pts, ρ_INPSL, X1) - field_INPDL = potential(HH3DHyperSingularNear(im * k), pts, ρ_INPDL, X1) + field_INPDL = -potential(HH3DHyperSingularNear(im * k), pts, ρ_INPDL, X1) err_IDPSL_field = norm(field_IDPSL + Efield.(pts)) / norm(Efield.(pts)) err_IDPDL_field = norm(field_IDPDL + Efield.(pts)) / norm(Efield.(pts)) @@ -102,11 +102,11 @@ using Test M_EDPSL = assemble(S, X0, X0) M_EDPDL = (1 / 2 * assemble(Identity(), X1, X1) + assemble(D, X1, X1)) - M_ENPDL = -assemble(N, X1, X1) + G * o * o' * G + M_ENPDL = assemble(N, X1, X1) + G * o * o' * G M_ENPSL = -1 / 2 * assemble(Identity(), X1, X1) + assemble(Dt, X1, X1) + G * o * o' * G ρ_EDPSL = M_EDPSL \ (-gD0) - ρ_EDPDL = M_EDPDL \ (gD1) + ρ_EDPDL = M_EDPDL \ (-gD1) ρ_ENPDL = M_ENPDL \ gN ρ_ENPSL = M_ENPSL \ (-gN) @@ -125,9 +125,9 @@ using Test err_ENPDL_pot = norm(pot_ENPDL + Φ_inc.(pts)) ./ norm(Φ_inc.(pts)) field_EDPSL = -potential(HH3DDoubleLayerTransposedNear(im * k), pts, ρ_EDPSL, X0) - field_EDPDL = potential(HH3DHyperSingularNear(im * k), pts, ρ_EDPDL, X1) + field_EDPDL = -potential(HH3DHyperSingularNear(im * k), pts, ρ_EDPDL, X1) field_ENPSL = -potential(HH3DDoubleLayerTransposedNear(im * k), pts, ρ_ENPSL, X1) - field_ENPDL = potential(HH3DHyperSingularNear(im * k), pts, ρ_ENPDL, X1) + field_ENPDL = -potential(HH3DHyperSingularNear(im * k), pts, ρ_ENPDL, X1) err_EDPSL_field = norm(field_EDPSL + Efield.(pts)) / norm(Efield.(pts)) err_EDPDL_field = norm(field_EDPDL + Efield.(pts)) / norm(Efield.(pts)) From d5c9877ec10040e2fc207a7f736206d5a69e199b Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 7 Feb 2023 17:06:55 +0100 Subject: [PATCH 213/528] allow creation of vectors of hilbert generators of length 1 --- src/utils/variational.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/variational.jl b/src/utils/variational.jl index 365a1586..157e05f2 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -205,7 +205,7 @@ macro hilbertspace(syms...) len = stop-start+1 - if len == 1 + if syms[s] isa Symbol sym = syms[s] push!(ex.args, :($(esc(sym)) = HilbertVector($k,$space,[]))) k += 1 From 5f23b3b3c6c2ec923b9f8102d5dc148acd099030 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 13 Feb 2023 17:09:32 +0100 Subject: [PATCH 214/528] blockoperators example --- examples/blockoperators.jl | 23 +++++++++++++++ examples/junction_calderon.jl | 55 +++++++++++++++++++++++++++++++++++ src/operator.jl | 34 ++++++++++++++++++++++ src/solvers/solver.jl | 9 ++++++ src/utils/variational.jl | 14 +++++++++ 5 files changed, 135 insertions(+) create mode 100644 examples/blockoperators.jl create mode 100644 examples/junction_calderon.jl diff --git a/examples/blockoperators.jl b/examples/blockoperators.jl new file mode 100644 index 00000000..6cb5580f --- /dev/null +++ b/examples/blockoperators.jl @@ -0,0 +1,23 @@ +using CompScienceMeshes +using BEAST + +m = meshsphere(radius=1.0, h=0.35) +X = raviartthomas(m) + +@hilbertspace m j +@hilbertspace k l + +@hilbertspace u +@hilbertspace v + +κ = 3.0 +η = 1.0 +T = Maxwell3D.singlelayer(wavenumber=κ) +K = Maxwell3D.doublelayer(wavenumber=κ) +H = 0.5 * BEAST.NCross() + +A = K[k,m] - T[k,j] + T[l,m] + K[l,j] +B = A[u,v] + +Bh = @discretise B u∈X×X v∈X×X +Z = assemble(Bh) \ No newline at end of file diff --git a/examples/junction_calderon.jl b/examples/junction_calderon.jl new file mode 100644 index 00000000..c05cab54 --- /dev/null +++ b/examples/junction_calderon.jl @@ -0,0 +1,55 @@ +using CompScienceMeshes +using BEAST +using LinearAlgebra + +width, height, h = 1.0, 0.5, 0.1 +ℑ₁ = meshrectangle(width, height, h) +ℑ₂ = CompScienceMeshes.rotate(ℑ₁, 0.5π * x̂) +ℑ₃ = CompScienceMeshes.rotate(ℑ₁, 1.0π * x̂) +Γ₁ = weld(ℑ₁,-ℑ₂) +Γ₂ = weld(ℑ₂,-ℑ₃) + +X₁ = raviartthomas(Γ₁) +X₂ = raviartthomas(Γ₂) + +Y₁ = buffachristiansen(Γ₁) +Y₂ = buffachristiansen(Γ₂) + +X = X₁ × X₂ +Y = Y₁ × Y₂ + +@hilbertspace p +@hilbertspace q + +κ = 3.0 +SL = Maxwell3D.singlelayer(wavenumber=κ) +N = NCross() +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +e = (n × E) × n; + +import BEAST: diag, blocks +Sxx = assemble(@discretise blocks(SL)[p,q] p∈X q∈X) +ex = assemble(e, X) + +# Build the preconditioner +Nxy = assemble(@discretise(BEAST.diag(N)[p,q], p∈X, q∈Y)) +Dyx = BEAST.GMRESSolver(Nxy, tol=2e-12, restart=250, verbose=false) +Dxy = BEAST.GMRESSolver(transpose(Nxy), tol=2e-12, restart=250, verbose=false) +Syy = BEAST.assemble(@discretise BEAST.diag(SL)[p,q] p∈Y q∈Y) +P = Dxy * Syy * Dyx + +# Solve system without and with preconditioner +u1, ch1 = solve(BEAST.GMRESSolver(Sxx,tol=2e-5, restart=250), ex) +u2, ch2 = solve(BEAST.GMRESSolver(P*Sxx, tol=2e-5, restart=250), P*ex) + +# Compute and visualise the far field +Φ, Θ = [0.0], range(0,stop=π,length=100) +pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] +near1 = potential(MWFarField3D(wavenumber=κ), pts, u1, X) +near2 = potential(MWFarField3D(wavenumber=κ), pts, u2, X) + +using Plots +@show norm(u1-u2) +plot(title="far field") +plot!(Θ, real.(getindex.(near1,1)), label="no preconditioner") +scatter!(Θ, real.(getindex.(near2,1)), label="Calderon preconditioner") \ No newline at end of file diff --git a/src/operator.jl b/src/operator.jl index 06a878f8..769487f6 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -280,3 +280,37 @@ function assemble!(op::BlockDiagonalOperator, U::DirectProductSpace, V::DirectPr k += 1 end end + + +struct BlockFullOperators <: AbstractOperator + op::AbstractOperator +end + +blocks(op::AbstractOperator) = BlockFullOperators(op) + +scalartype(op::BlockFullOperators) = scalartype(op.op) +defaultquadstrat(op::BlockFullOperators, U::DirectProductSpace, V::DirectProductSpace) = defaultquadstrat(op.op, U, V) + + +function assemble!(op::BlockFullOperators, U::DirectProductSpace, V::DirectProductSpace, + store, threading=Threading{:multi}; + quadstrat = defaultquadstrat(op, U, V)) + + # @assert length(U.factors) == length(V.factors) + I = Int[0]; for u in U.factors push!(I, last(I) + numfunctions(u)) end + J = Int[0]; for v in V.factors push!(J, last(J) + numfunctions(v)) end + + # k = 1 + # for (u,v) in zip(U.factors, V.factors) + # store1(v,m,n) = store(v, I[k] + m, J[k] + n) + # assemble!(op.op, u, v, store1; quadstrat) + # k += 1 + # end + + for (k,u) in enumerate(U.factors) + for (l,v) in enumerate(V.factors) + store1(x,m,n) = store(x, I[k]+m, J[l]+n) + assemble!(op.op, u, v, store1; quadstrat) + end + end +end \ No newline at end of file diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index f860b28f..b5c9a7c2 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -316,6 +316,15 @@ function assemble(bilform::BilForm, test_space_dict, trial_space_dict; end +function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace) + + test_space_dict = Dict(enumerate(X.factors)) + trial_space_dict = Dict(enumerate(Y.factors)) + + z = assemble(bf, test_space_dict, trial_space_dict) +end + + # function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict) diff --git a/src/utils/variational.jl b/src/utils/variational.jl index 157e05f2..c088e1a1 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -292,6 +292,20 @@ function getindex(A::BEAST.BlockDiagonalOperator, V::Vector{HilbertVector}, U::V return BilForm(first(V).space, first(U).space, terms) end +function getindex(A::BEAST.BlockFullOperators, V::Vector{HilbertVector}, U::Vector{HilbertVector}) + op = A.op + terms = Vector{BilTerm}() + # @assert length(V) == length(U) + for v in V + for u in U + term = BilTerm(v.idx, u.idx, v.opstack, u.opstack, 1, op) + push!(terms, term) + end + end + return BilForm(first(V).space, first(U).space, terms) +end + + function getindex(op::Any, V::Vector{HilbertVector}, U::Vector{HilbertVector}) terms = Vector{BilTerm}() for v in V From 197a4da938c7256092a04a1b68fb3ae011496ea9 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 14 Feb 2023 13:55:34 +0100 Subject: [PATCH 215/528] lagc0d2 with bugs --- src/bases/lagrange.jl | 45 +++++++++++++++++++ src/bases/local/laglocal.jl | 88 +++++++++++-------------------------- 2 files changed, 70 insertions(+), 63 deletions(-) diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 67fda06c..c6397785 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -298,6 +298,51 @@ function lagrangec0d1(mesh, nodes::CompScienceMeshes.AbstractMesh{U,1} where {U} end +function lagrangec0d2(mesh::CompScienceMeshes.AbstractMesh{U,3}, + nodes::CompScienceMeshes.AbstractMesh{U,1}, + edges::CompScienceMeshes.AbstractMesh{U,2}) where {U} + + Conn = connectivity(nodes, mesh, abs) + rows = rowvals(Conn) + vals = nonzeros(Conn) + + T = coordtype(mesh) + P = vertextype(mesh) + S = Shape{T} + + fns = Vector{Vector{S}}() + pos = Vector{P}() + for (i,node) in enumerate(nodes) + fn = Vector{S}() + for k in nzrange(Conn,i) + cellid = rows[k] + refid = vals[k] + push!(fn, Shape(cellid, refid, T(1.0))) + end + push!(fns,fn) + push!(pos,cartesian(center(chart(nodes,node)))) + end + + Conn = connectivity(edges, mesh, abs) + rows = rowvals(Conn) + vals = nonzeros(Conn) + + for (i,edge) in enumerate(edges) + fn = Vector{S}() + for k in nzrange(Conn,i) + cellid = rows[k] + refid = vals[k] + push!(fn, Shape(cellid, 3+refid, T(1.0))) + end + push!(fns,fn) + push!(pos,cartesian(center(chart(edges,edge)))) + end + + NF = 6 + LagrangeBasis{2,0,NF}(mesh, fns, pos) +end + + duallagrangec0d1(mesh) = duallagrangec0d1(mesh, barycentric_refinement(mesh), x->false, Val{dimension(mesh)+1}) function duallagrangec0d1(mesh, jct) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index ebf7d938..7047a78a 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -100,69 +100,6 @@ function gradient(ref::LagrangeRefSpace{T,1,3} where {T}, sh, el) return [sh1, sh2] end -# function gradient(phi::LagrangeRefSpace{T,1,3}, shape, chart) -# -# r = shape.refid -# vert = chart.vertices[r] -# face = faces(chart)[r] -# n = normal(face) -# h = dot(vert - cartesian(center(face)),n) -# gradphi = h*n -# -# output = Shape{T}[] -# for s in faces(chart) -# -# end - - -# function gradient(ref::LagrangeRefSpace{T,1,3}, sh, tri) where {T} - -# this_vert = tri.vertices[sh.refid] -# opp_edge = faces(tri)[sh.refid] -# ctr_opp_face = center(opp_edge) -# # n = normal(ctr_opp_face) -# n = -normalize(cross(opp_edge.tangents[1], normal(tri))) -# h = -dot(this_vert - cartesian(ctr_opp_face), n) -# @assert h > 0 "h = $h" -# gradval = -(1/h)*n -# output = Vector{Shape{T}}() -# for (i,edge) in enumerate(CompScienceMeshes.edges(tri)) -# ctr_edge = center(edge) -# tgt = tangents(ctr_edge,1) -# tgt = normalize(tgt) -# lgt = volume(edge) -# cff = -lgt * dot(tgt, gradval) -# isapprox(cff, 0, atol=sqrt(eps(T))) && continue -# push!(output, Shape(sh.cellid, i, sh.coeff * cff)) -# end -# return output -# end - - -# function gradient(ref::LagrangeRefSpace{T,1,3}, sh, tri) where {T} - -# this_vert = tri.vertices[sh.refid] -# opp_edge = faces(tri)[sh.refid] -# ctr_opp_face = center(opp_edge) -# # n = normal(ctr_opp_face) -# n = -normalize(cross(opp_edge.tangents[1], normal(tri))) -# h = -dot(this_vert - cartesian(ctr_opp_face), n) -# @assert h > 0 "h = $h" -# gradval = -(1/h)*n -# output = Vector{Shape{T}}() -# for (i,edge) in enumerate(CompScienceMeshes.edges(tri)) -# ctr_edge = center(edge) -# tgt = tangents(ctr_edge,1) -# tgt = normalize(tgt) -# lgt = volume(edge) -# cff = -lgt * dot(tgt, gradval) -# isapprox(cff, 0, atol=sqrt(eps(T))) && continue -# push!(output, Shape(sh.cellid, i, sh.coeff * cff)) -# end -# return output -# end - - function gradient(ref::LagrangeRefSpace{T,1,2}, sh, seg) where {T} sh.refid == 1 && return [Shape(sh.cellid, 1, +sh.coeff/volume(seg))] @@ -226,3 +163,28 @@ function restrict(f::LagrangeRefSpace{T,1}, dom1, dom2) where T return Q end + + + +## Quadratic Lagrange element on a triangle +function (f::LagrangeRefSpace{T,2,3})(t) where T + u,v,w, = barycentric(t) + + j = jacobian(t) + p = t.patch + + # SVector( + # (value=u, curl=(p[3]-p[2])/j), + # (value=v, curl=(p[1]-p[3])/j), + # (value=w, curl=(p[2]-p[1])/j) + # ) + + SVector( + (value=u*(2*u-1),), + (value=v*(2*v-1),), + (value=w*(2*w-1),), + (value=4*v*w,), + (value=4*w*u,), + (value=4*u*v,), + ) +end \ No newline at end of file From b5a095d472bba69cedc57cca1aa5562212a81c70 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 14 Feb 2023 14:29:12 +0100 Subject: [PATCH 216/528] working lag0 to lag2 projector example --- examples/lagc0d2.jl | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 examples/lagc0d2.jl diff --git a/examples/lagc0d2.jl b/examples/lagc0d2.jl new file mode 100644 index 00000000..bc39d067 --- /dev/null +++ b/examples/lagc0d2.jl @@ -0,0 +1,28 @@ +using CompScienceMeshes +using BEAST +using LinearAlgebra + +import Plotly + +m = meshsphere(radius=1.0, h=0.25) +nodes = skeleton(m,0) +edges = skeleton(m,1) + +X = BEAST.lagrangec0d2(m, nodes, edges) +Y = BEAST.lagrangecxd0(m) + +uⁱ = Helmholtz3D.planewave(wavenumber=1.0, direction=point(0,0,1)) +f = strace(uⁱ,m) +fy = assemble(f,Y) + +Id = BEAST.Identity() +qs = BEAST.SingleNumQStrat(8) +Gxy = assemble(Id, X, Y, quadstrat=qs) +Gxx = assemble(Id, X, X, quadstrat=qs) +Gyy = assemble(Id, Y, Y, quadstrat=qs) + +Pxy = inv(Matrix(Gxx)) * Gxy * inv(Matrix(Gyy)) + +fX = Pxy * fy +fcr, geo = facecurrents(fX, X) +Plotly.plot(patch(m, real.(fcr))) \ No newline at end of file From ce5f326c97cbe40e7a6e1e33e73bbce9f3e6301c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 2 Mar 2023 13:23:32 +0100 Subject: [PATCH 217/528] towards nested assembly --- src/solvers/solver.jl | 206 ++++++++++++++---------------------------- 1 file changed, 67 insertions(+), 139 deletions(-) diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index b5c9a7c2..03bff829 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -66,7 +66,6 @@ function discretise(lf::LinForm, space_mappings::Pair...) for sm in space_mappings found = false - # sm.first.space == bf.trial_space && (dict = trial_space_dict; found = true) sm.first.space == lf.test_space && (dict = test_space_dict; found = true) @assert found "Vector $(sm.first) not found in test space" @@ -75,7 +74,6 @@ function discretise(lf::LinForm, space_mappings::Pair...) end # check that all symbols where mapped - # for p in eachindex(bf.trial_space) @assert haskey(trial_space_dict,p) end for p in eachindex(lf.test_space) @assert haskey(test_space_dict, p) end DiscreteLinform(lf, test_space_dict) @@ -136,13 +134,9 @@ assemble(dlf::DiscreteLinform) = assemble(dlf.linform, dlf.test_space_dict) function assemble(lform::LinForm, test_space_dict) T=ComplexF64 terms = lform.terms - #T = Complex{typeof(terms[1].functional.field.wavenumber)} - #T = Complex{eltype(vertextype(test_space_dict[1].geo))} - # I = Int[1] blocksizes1 = Int[] for p in 1:length(lform.test_space) X = test_space_dict[p] - # push!(I, last(I) + numfunctions(X)) push!(blocksizes1, numfunctions(X)) end @@ -190,7 +184,6 @@ function td_assemble(lform::LinForm, test_space_dict) T = scalartype(T,term.functional) end for kv in test_space_dict; T = scalartype(T,kv[2]) end - # @show T I = [numfunctions(spatialbasis(test_space_dict[i])) for i in 1:length(lform.test_space)] @@ -200,12 +193,6 @@ function td_assemble(lform::LinForm, test_space_dict) N = [numfunctions(temporalbasis(test_space_dict[1]))] B = BlockArray{T}(undef, M, N) - # row_axis = BlockArrays.blockedrange(M) - # col_axis = BlockArrays.blockedrange(N) - - # BT = SpaceTimeData{T} - # B = BlockArray(undef_blocks, BT, I) - for t in terms α = t.coeff @@ -228,166 +215,110 @@ function td_assemble(lform::LinForm, test_space_dict) return B end -# function assemble_hide(bilform::BilForm, test_space_dict::Space{U}, trial_space_dict) where U - -# lhterms = bilform.terms -# T = Complex{U} # TDOD: Fix this - -# blocksizes1 = Int[] -# for p in 1:length(bilform.test_space) -# X = test_space_dict[p] -# push!(blocksizes1, numfunctions(X)) -# end - -# blocksizes2 = Int[] -# for q in 1:length(bilform.trial_space) -# Y = trial_space_dict[q] -# push!(blocksizes2, numfunctions(Y)) -# end - -# # allocate the memory for the matrices -# A = zeros(T, sum(blocksizes1), sum(blocksizes2)) -# Z = PseudoBlockArray{T}(A, blocksizes1, blocksizes2) -# # For each block, compute the interaction matrix -# for t in lhterms - -# α = t.coeff -# a = t.kernel - -# m = t.test_id -# x = test_space_dict[m] -# for op in reverse(t.test_ops) -# x = op[end](op[1:end-1]..., x) -# end - -# n = t.trial_id -# y = trial_space_dict[n] -# for op in reverse(t.trial_ops) -# y = op[end](op[1:end-1]..., y) -# end - -# z = assemble(a, x, y) -# # @show m n norm(z) -# Z[Block(m,n)] += α * z -# end - -# return Z -# end - -function assemble(bilform::BilForm, test_space_dict, trial_space_dict; - materialize=BEAST.assemble) +# function assemble(bilform::BilForm, test_space_dict, trial_space_dict; +# materialize=BEAST.assemble) - @assert !isempty(bilform.terms) +# @assert !isempty(bilform.terms) - M = zeros(Int, length(test_space_dict)) - N = zeros(Int, length(trial_space_dict)) +# M = zeros(Int, length(test_space_dict)) +# N = zeros(Int, length(trial_space_dict)) - for (p,x) in test_space_dict M[p]=numfunctions(x) end - for (p,x) in trial_space_dict N[p]=numfunctions(x) end +# for (p,x) in test_space_dict M[p]=numfunctions(x) end +# for (p,x) in trial_space_dict N[p]=numfunctions(x) end - U = BlockArrays.blockedrange(M) - V = BlockArrays.blockedrange(N) +# U = BlockArrays.blockedrange(M) +# V = BlockArrays.blockedrange(N) - Z = ZeroMap{Float32}(U, V) +# Z = ZeroMap{Float32}(U, V) - for term in bilform.terms +# for term in bilform.terms - x = test_space_dict[term.test_id] - for op in reverse(term.test_ops) - x = op[end](op[1:end-1]..., x) - end +# x = test_space_dict[term.test_id] +# for op in reverse(term.test_ops) +# x = op[end](op[1:end-1]..., x) +# end - y = trial_space_dict[term.trial_id] - for op in reverse(term.trial_ops) - y = op[end](op[1:end-1]..., y) - end +# y = trial_space_dict[term.trial_id] +# for op in reverse(term.trial_ops) +# y = op[end](op[1:end-1]..., y) +# end - z = materialize(term.kernel, x, y) +# z = materialize(term.kernel, x, y) - I = Block(term.test_id) - J = Block(term.trial_id) - zlifted = LiftedMap(z,I,J,U,V) +# I = Block(term.test_id) +# J = Block(term.trial_id) +# zlifted = LiftedMap(z,I,J,U,V) - Z = Z + term.coeff * zlifted - end +# Z = Z + term.coeff * zlifted +# end - return Z -end +# return Z +# end +function assemble(bilform::BilForm, test_space_dict, trial_space_dict; + materialize=BEAST.assemble) -function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace) + xfactors = Vector{AbstractSpace}(undef, length(test_space_dict)) + yfactors = Vector{AbstractSpace}(undef, length(trial_space_dict)) - test_space_dict = Dict(enumerate(X.factors)) - trial_space_dict = Dict(enumerate(Y.factors)) + for (p,x) in test_space_dict xfactors[p] = x end + for (q,y) in trial_space_dict yfactors[q] = y end - z = assemble(bf, test_space_dict, trial_space_dict) + X = DirectProductSpace(xfactors) + Y = DirectProductSpace(yfactors) + + return assemble(bilform, X, Y; materialize) end +function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; + materialize=BEAST.assemble) + @assert !isempty(bf.terms) -# function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict) + M = length.(X.factors) + N = length.(Y.factors) -# lhterms = bilform.terms + U = BlockArrays.blockedrange(M) + V = BlockArrays.blockedrange(N) -# T = Float32 -# for term in bilform.terms -# T = scalartype(T,term.coeff) -# T = scalartype(T,term.kernel) -# end -# for kv in test_space_dict; T = scalartype(T,kv[2]) end -# for kv in trial_space_dict; T = scalartype(T,kv[2]) end + Z = ZeroMap{Float32}(U,V) + for term in bf.terms + x = X.factors[term.test_id] + for op in reverse(term.test_ops) + x = op[end](op[1:end-1]..., x) + end -# I = [numfunctions(spatialbasis(test_space_dict[i])) for i in 1:length(bilform.test_space)] -# J = [numfunctions(spatialbasis(trial_space_dict[i])) for i in 1:length(bilform.trial_space)] + y = Y.factors[term.trial_id] + for op in reverse(term.trial_ops) + y = op[end](op[1:end-1]..., y) + end -# Z = BlockArray{Vector{T}}(zero_blocks, I, J) + z = materialize(term.kernel, x, y) -# # For each block, compute the interaction matrix -# for t in lhterms + I = Block(term.test_id) + J = Block(term.trial_id) + Z = Z + term.coeff * LiftedMap(z, I, J, U, V) + end -# @show (t.coeff,t.kernel) -# a = t.coeff * t.kernel + return Z +end -# m = t.test_id -# x = test_space_dict[m] -# for op in reverse(t.test_ops) -# x = op[end](op[1:end-1]..., x) -# end -# n = t.trial_id -# y = trial_space_dict[n] -# for op in reverse(t.trial_ops) -# y = op[end](op[1:end-1]..., y) -# end +# function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace) -# z = assemble(a, x, y) -# @warn "variation formulations where combinations of test and trial space recur multiple times are not supported!" -# Z[Block(m,n)] = ConvolutionOperators.ConvOpAsMatrix(z) - -# end -# return Z +# test_space_dict = Dict(enumerate(X.factors)) +# trial_space_dict = Dict(enumerate(Y.factors)) + +# z = assemble(bf, test_space_dict, trial_space_dict) # end + function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict) lhterms = bilform.terms - # T = Float32 - # for term in bilform.terms - # T = scalartype(T,term.coeff) - # T = scalartype(T,term.kernel) - # end - # for kv in test_space_dict; T = scalartype(T,kv[2]) end - # for kv in trial_space_dict; T = scalartype(T,kv[2]) end - - # I = [numfunctions(spatialbasis(test_space_dict[i])) for i in 1:length(bilform.test_space)] - # J = [numfunctions(spatialbasis(trial_space_dict[i])) for i in 1:length(bilform.trial_space)] - - # rowaxis = BlockArrays.blockedrange([numfunctions(spatialbasis(test_space_dict[i])) for i in 1:length(test_space_dict)]) - # colaxis = BlockArrays.blockedrange([numfunctions(spatialbasis(trial_space_dict[i])) for i in 1:length(trial_space_dict)]) - M = zeros(Int, length(test_space_dict)) N = zeros(Int, length(trial_space_dict)) @@ -397,7 +328,6 @@ function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict) row_axis = BlockArrays.blockedrange(M) col_axis = BlockArrays.blockedrange(N) - # Z = BlockArray{Vector{T}}(zero_blocks, row_axis, col_axis) Z = ConvolutionOperators.ZeroConvOp(row_axis, col_axis) for t in lhterms @@ -418,8 +348,6 @@ function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict) z = assemble(a, x, y) Z += ConvolutionOperators.LiftedConvOp(z, row_axis, col_axis, Block(m), Block(n)) - # Z[Block(m,n)] = ConvolutionOperators.ConvOpAsMatrix(z) - end return Z end From 1656f6fe2c343344c10ea06aa4cf445ba2cf6edb Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 2 Mar 2023 13:24:18 +0100 Subject: [PATCH 218/528] length and in for AbstractSpace --- src/bases/basis.jl | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/bases/basis.jl b/src/bases/basis.jl index c512c4ab..e95da45a 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -2,6 +2,9 @@ abstract type RefSpace{T,D} end abstract type AbstractSpace end abstract type Space{T} <: AbstractSpace end +Base.length(s::AbstractSpace) = numfunctions(s) +Base.in(x, s::AbstractSpace) = (x => s) + """ scalartype(s) @@ -53,8 +56,14 @@ geometry(s::Space) = s.geo basisfunction(s::Space, i) = s.fns[i] numfunctions(space::Space) = length(space.fns) -mutable struct DirectProductSpace{T} <: AbstractSpace - factors::Vector{Space{T}} +mutable struct DirectProductSpace{T,S<:AbstractSpace} <: AbstractSpace + factors::Vector{S} +end + +function DirectProductSpace(factors::Vector{S}) where {S<:AbstractSpace} + @assert !isempty(factors) + T = scalartype(factors...) + return DirectProductSpace{T,S}(factors) end defaultquadstrat(op, tfs::DirectProductSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs.factors[1], bfs.factors[1]) @@ -66,14 +75,26 @@ defaultquadstrat(op, tfs::DirectProductSpace, bfs::Space) = defaultquadstrat(op, # defaultquadstrat(op, tfs::DirectProductSpace, bfs::RefSpace) = defaultquadstrat(op, tfs.factors[1], bfs) # scalartype(sp::DirectProductSpace{T}) where {T} = T -export cross, × +# export cross, × +export × -cross(a::Space{T}, b::Space{T}) where {T} = DirectProductSpace(Space{T}[a,b]) -cross(a::DirectProductSpace{T}, b::Space{T}) where {T} = DirectProductSpace(Space{T}[a.factors; b]) +function Base.:+(x::AbstractSpace...) + T = scalartype(x...) + return DirectProductSpace{T}([x...]) +end + +cross(a::Space{T}, b::Space{T}) where {T} = DirectProductSpace{T,Space{T}}(Space{T}[a,b]) +cross(a::DirectProductSpace{T}, b::Space{T}) where {T} = DirectProductSpace{T,Space{T}}([a.factors; b]) numfunctions(S::DirectProductSpace) = sum([numfunctions(s) for s in S.factors]) scalartype(s::DirectProductSpace{T}) where {T} = T geometry(x::DirectProductSpace) = weld(x.geo...) +children(x::AbstractSpace) = () +children(x::DirectProductSpace) = x.factors + +Base.iterate(x::DirectProductSpace) = iterate(x.factors) +Base.iterate(x::DirectProductSpace, state) = iterate(x.factors, state) + struct Shape{T} cellid::Int refid::Int From e940d31444aede49236143425d6abaa2e99261ce Mon Sep 17 00:00:00 2001 From: jay prakash Date: Wed, 8 Mar 2023 12:27:52 +0100 Subject: [PATCH 219/528] bug fix computation of restriction of Nedelec fns --- src/bases/local/ndlocal.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bases/local/ndlocal.jl b/src/bases/local/ndlocal.jl index 0c400837..9f0c196f 100644 --- a/src/bases/local/ndlocal.jl +++ b/src/bases/local/ndlocal.jl @@ -61,7 +61,7 @@ function restrict(ϕ::NDRefSpace{T}, dom1, dom2) where T for j in 1:K # Q[j,i] = dot(y[j][1], m) * l - Q[i,j] = dot(y[j][1], t) + Q[j,i] = dot(y[j][1], t) end end From def2cb267e0f165cad26041412a3dd4129a65874 Mon Sep 17 00:00:00 2001 From: azuccott Date: Thu, 9 Mar 2023 15:24:24 +0100 Subject: [PATCH 220/528] prova commit --- src/bases/local/laglocal.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 7047a78a..74b3b446 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -187,4 +187,5 @@ function (f::LagrangeRefSpace{T,2,3})(t) where T (value=4*w*u,), (value=4*u*v,), ) -end \ No newline at end of file +end +#commento test \ No newline at end of file From f1b96e9a9aff0f4c68336999586be08cf671dee5 Mon Sep 17 00:00:00 2001 From: azuccott Date: Thu, 9 Mar 2023 15:29:45 +0100 Subject: [PATCH 221/528] annulla commit test --- src/bases/local/laglocal.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 74b3b446..63bd1091 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -188,4 +188,3 @@ function (f::LagrangeRefSpace{T,2,3})(t) where T (value=4*u*v,), ) end -#commento test \ No newline at end of file From e32b5273cdad834b31b61bc3f56da46fd294841b Mon Sep 17 00:00:00 2001 From: jay prakash Date: Mon, 13 Mar 2023 13:40:10 +0100 Subject: [PATCH 222/528] tests for restrict Nedelec & assembly tensor idop --- test/test_restrict.jl | 14 ++++++++++++++ test/test_tensor_identityop.jl | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 test/test_tensor_identityop.jl diff --git a/test/test_restrict.jl b/test/test_restrict.jl index 5a7d267d..8877dce0 100644 --- a/test/test_restrict.jl +++ b/test/test_restrict.jl @@ -67,4 +67,18 @@ for T in [Float32, Float64] 0 1 0 0 -1 2] // 4 end + + # Test restriction of Nedelec elements + ψ = BEAST.NDRefSpace{T}() + Q = restrict(ψ, p, p) + if T==Float64 + @test Q == Matrix(LinearAlgebra.I, 3, 3) + + q = simplex([T.(e0+e1), T.(2*e1), T.(e1+e2)], Val{2}) + Q = restrict(ψ, p, q) + @test Q == [ + 2 -1 0 + 0 1 0 + 0 -1 2] // 4 + end end \ No newline at end of file diff --git a/test/test_tensor_identityop.jl b/test/test_tensor_identityop.jl new file mode 100644 index 00000000..4ccc64f2 --- /dev/null +++ b/test/test_tensor_identityop.jl @@ -0,0 +1,16 @@ +using Test +using BEAST +using CompScienceMeshes + +Γ = meshrectangle(1.0, 1.0, 0.5, 3) +X = raviartthomas(Γ) +fns = numfunctions(X) +id = Identity()⊗Identity() + +Δt, Nt = 1.01, 10 +δ = timebasisdelta(Δt,Nt) +h = timebasisc0d1(Δt,Nt) + +idop = (id, X⊗δ, X⊗h) +G = assemble(idop...) +@test size(G) == (fns, fns, Nt) \ No newline at end of file From 491c1fb5d0bfc3da20940a645e5d22391874ca64 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 15 Mar 2023 11:41:59 +0100 Subject: [PATCH 223/528] update DofInterpolate to new CSM mesh interface --- src/bases/basis.jl | 2 +- src/interpolation.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bases/basis.jl b/src/bases/basis.jl index e95da45a..4252eb19 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -80,7 +80,7 @@ export × function Base.:+(x::AbstractSpace...) T = scalartype(x...) - return DirectProductSpace{T}([x...]) + return DirectProductSpace{T, AbstractSpace}([x...]) end cross(a::Space{T}, b::Space{T}) where {T} = DirectProductSpace{T,Space{T}}(Space{T}[a,b]) diff --git a/src/interpolation.jl b/src/interpolation.jl index df8c5e92..212f442e 100644 --- a/src/interpolation.jl +++ b/src/interpolation.jl @@ -15,7 +15,7 @@ function DofInterpolate(basis::LagrangeBasis, field) cell = cells(basis.geo)[cellid] - tria = chart(basis.geo, cell) + tria = chart(basis.geo, cellid) vid = cell[refid] @@ -58,7 +58,7 @@ function DofInterpolate(basis::RTBasis, field) edge = simplex(basis.geo.vertices[[v1,v2]]...) t = tangents(center(edge),1) - tria = chart(basis.geo, cell) + tria = chart(basis.geo, cellid) n = normal(center(tria)) From 9969015c449977f53ade137f7dcb36aab402060a Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 28 Mar 2023 18:13:52 +0200 Subject: [PATCH 224/528] overhaul of system assembly logic --- Project.toml | 8 +- examples/blockoperators.jl | 23 ----- examples/builddual.jl | 6 +- examples/capacitor.jl | 2 +- examples/capacitor_new.jl | 8 +- examples/{ => disabled}/scomplex_efie.jl | 0 examples/{ => disabled}/timeonly.jl | 0 examples/dot_tdmfie.jl | 3 +- examples/dpie.jl | 16 +++- examples/ex_dual1forms.jl | 6 +- examples/ex_dual1forms_bis.jl | 4 +- examples/ex_duals.jl | 2 +- examples/ex_fem.jl | 2 +- examples/ex_globalmultitrace.jl | 104 ++++++++++++++++++++++ examples/ex_interpolation.jl | 46 ++++++---- examples/junction_calderon.jl | 15 +++- examples/nitsche.jl | 7 ++ examples/pmchwt.jl | 77 +++++++---------- examples/projectors.jl | 80 ++++++++--------- examples/utils/edge_values.jl | 2 +- src/BEAST.jl | 3 + src/bases/basis.jl | 7 +- src/bases/bdm3dspace.jl | 2 +- src/bases/lagrange.jl | 3 +- src/helmholtz2d/helmholtzop.jl | 10 ++- src/helmholtz3d/hh3dexc.jl | 18 +++- src/identityop.jl | 2 +- src/integralop.jl | 10 ++- src/interpolation.jl | 2 +- src/maxwell/mwexc.jl | 12 ++- src/maxwell/nxdbllayer.jl | 36 ++++++-- src/operator.jl | 49 ++++++----- src/postproc.jl | 4 +- src/solvers/itsolver.jl | 61 ++----------- src/solvers/lusolver.jl | 84 ++++++++++-------- src/solvers/solver.jl | 105 +++++++++++++++++------ src/timedomain/tdtimeops.jl | 3 +- src/utils/variational.jl | 51 +++++++++++ src/volumeintegral/vieexc.jl | 2 + 39 files changed, 561 insertions(+), 314 deletions(-) delete mode 100644 examples/blockoperators.jl rename examples/{ => disabled}/scomplex_efie.jl (100%) rename examples/{ => disabled}/timeonly.jl (100%) create mode 100644 examples/ex_globalmultitrace.jl diff --git a/Project.toml b/Project.toml index 50977b3e..0597b390 100644 --- a/Project.toml +++ b/Project.toml @@ -3,6 +3,7 @@ uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" version = "1.8.1" [deps] +AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" CollisionDetection = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" @@ -18,6 +19,7 @@ IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LiftedMaps = "d22a30c1-52ac-4762-a8c9-5838452405e0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" +NestedUnitRanges = "032820ab-dc03-4b49-91f4-7d58d4da98b3" SauterSchwab3D = "0a13313b-1c00-422e-8263-562364ed9544" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" @@ -28,6 +30,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] +AbstractTrees = "0.4.4" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" CollisionDetection = "0.1.5" Combinatorics = "0.7, 1" @@ -38,8 +41,9 @@ FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4, 0.5" FillArrays = "0.11, 0.12, 0.13" IterativeSolvers = "0.9" -LiftedMaps = "0.4.1" -LinearMaps = "3.7" +LiftedMaps = "0.5" +LinearMaps = "3.7 - 3.9" +NestedUnitRanges = "0.1" SauterSchwab3D = "0.1" SauterSchwabQuadrature = "2.2.0" SparseMatrixDicts = "0.2" diff --git a/examples/blockoperators.jl b/examples/blockoperators.jl deleted file mode 100644 index 6cb5580f..00000000 --- a/examples/blockoperators.jl +++ /dev/null @@ -1,23 +0,0 @@ -using CompScienceMeshes -using BEAST - -m = meshsphere(radius=1.0, h=0.35) -X = raviartthomas(m) - -@hilbertspace m j -@hilbertspace k l - -@hilbertspace u -@hilbertspace v - -κ = 3.0 -η = 1.0 -T = Maxwell3D.singlelayer(wavenumber=κ) -K = Maxwell3D.doublelayer(wavenumber=κ) -H = 0.5 * BEAST.NCross() - -A = K[k,m] - T[k,j] + T[l,m] + K[l,j] -B = A[u,v] - -Bh = @discretise B u∈X×X v∈X×X -Z = assemble(Bh) \ No newline at end of file diff --git a/examples/builddual.jl b/examples/builddual.jl index c7f44294..84308adc 100644 --- a/examples/builddual.jl +++ b/examples/builddual.jl @@ -71,11 +71,11 @@ G3 = assemble(BEAST.NCross(), bcs3, rts) Q3 = assemble(BEAST.Identity(), divergence(bcs3), divergence(bcs3)) using LinearAlgebra -@show cond(G3) +@show cond(Matrix(G3)) using LinearAlgebra -@assert (numcells(Faces) - 1) == (size(Q,1) - rank(Q)) -@assert cond(G) < 3.5 +# @assert (numcells(Faces) - 1) == (size(Q,1) - rank(Q)) +@assert cond(Matrix(G1)) < 3.5 function compress!(space) T = scalartype(space) diff --git a/examples/capacitor.jl b/examples/capacitor.jl index 9cf29290..ff7e36ba 100644 --- a/examples/capacitor.jl +++ b/examples/capacitor.jl @@ -34,7 +34,7 @@ CompScienceMeshes.translate!(γ₁, point(0.0,0.0,d)) # define the excitation V₀ = 1.0 -f = ScalarTrace(p -> V₀) +f = ScalarTrace{typeof(V₀)}(p -> V₀) #define basis function RT = rt_ports(Γ,[γ₁,γ₀]) diff --git a/examples/capacitor_new.jl b/examples/capacitor_new.jl index ca6ac5fd..83437846 100644 --- a/examples/capacitor_new.jl +++ b/examples/capacitor_new.jl @@ -26,7 +26,7 @@ h = 1/60 #size of meshes κ = 2pi / 100 V₀ = 1.0 -f = ScalarTrace(p -> V₀) +f = ScalarTrace{typeof(V₀)}(p -> V₀) edges_all = skeleton(Γ, 1) edges_int = submesh(!in(boundary(Γ)), edges_all) @@ -77,6 +77,10 @@ efie = @discretise( @varform( x∈X, y0∈Y0, y1∈Y1, z∈Z) u = solve(efie) +# assemble(efie.equation.rhs, efie.test_space_dict) +# @enter assemble(efie.equation.rhs, X + Y0 + Y1 + Z) +# assemble(efie.equation.lhs, efie.test_space_dict, efie.trial_space_dict) + # You can access the current coefficients pertaining to subspaces # using the Hilbert space 'placeholder' @show length(u[x]) @@ -84,7 +88,7 @@ u = solve(efie) @show length(u[y1]) @show length(u[z]) -S = X + Y0 + Y1 + Z +S = (((X + Y0) + Y1) + Z) fcr, geo = facecurrents(u, S) Plotly.plot(patch(geo, norm.(fcr))) |> display diff --git a/examples/scomplex_efie.jl b/examples/disabled/scomplex_efie.jl similarity index 100% rename from examples/scomplex_efie.jl rename to examples/disabled/scomplex_efie.jl diff --git a/examples/timeonly.jl b/examples/disabled/timeonly.jl similarity index 100% rename from examples/timeonly.jl rename to examples/disabled/timeonly.jl diff --git a/examples/dot_tdmfie.jl b/examples/dot_tdmfie.jl index 4a8555a5..cc77adb9 100644 --- a/examples/dot_tdmfie.jl +++ b/examples/dot_tdmfie.jl @@ -36,7 +36,8 @@ N = BEAST.TemporalDifferentiation(NCross()⊗Identity()) M = 0.5*N + 1.0*K Z_mfie = assemble(M, W, V, storage_policy = Val{:bandedstorage}) b_mfie = assemble(H, W) -dot_xmfie = marchonintime(inv(Z_mfie[:,:,1]), Z_mfie, b_mfie, Nt) +# dot_xmfie = marchonintime(inv(Z_mfie[:,:,1]), Z_mfie, b_mfie, Nt) +dot_xmfie = marchonintime(inv(BEAST.ConvolutionOperators.timeslice(Z_mfie,1)), Z_mfie, b_mfie, Nt) # Xmfie, Δω, ω0 = fouriertransform(xmfie, Δt, 0.0, 2) # ω = collect(ω0 .+ (0:Nt-1)*Δω) diff --git a/examples/dpie.jl b/examples/dpie.jl index 8db88497..e80825f1 100644 --- a/examples/dpie.jl +++ b/examples/dpie.jl @@ -33,8 +33,8 @@ dvg = divergence @hilbertspace k q κ = 1.0 -curlA = strace(((x,y,z),)->-exp(-im*κ*z)*ŷ, Γ) -ndotA = dot(n, ((x,y,z),)->-x*exp(-im*κ*z)*ẑ) +curlA = BEAST.ScalarTrace{ComplexF64}(((x,y,z),)->-exp(-im*κ*z)*ŷ) +ndotA = BEAST.NDotTrace{ComplexF64}(((x,y,z),)->-x*exp(-im*κ*z)*ẑ) a = @varform s[k,j] + s[dvg(k),p] + s[q,dvg(j)] - κ^2*s[q,p] == curlA[k]+ndotA[q] @@ -42,8 +42,16 @@ Eq = @discretise a k∈X q∈Y j∈X p∈Y u = solve(Eq) -uj = u[1:numfunctions(X)] -up = u[numfunctions(X)+1:end] +# lform = Eq.equation.rhs +# @which scalartype(lform.terms[2].functional.field) +# @enter assemble(Eq.equation.rhs, Eq.test_space_dict) +# assemble(Eq.equation.lhs, Eq.test_space_dict, Eq.trial_space_dict) + +# uj = u[1:numfunctions(X)] +# up = u[numfunctions(X)+1:end] + +uj = u[j] +up = u[p] xrange = range(-2.0, stop=2.0, length=40) # xrange = [0.0] diff --git a/examples/ex_dual1forms.jl b/examples/ex_dual1forms.jl index c94614db..9b12e48d 100644 --- a/examples/ex_dual1forms.jl +++ b/examples/ex_dual1forms.jl @@ -75,7 +75,7 @@ Faces = submesh(!in(bnd_Tetrs), skeleton(Tetrs,2)) F = 396 Face = cells(Faces)[F] -pos = cartesian(center(chart(Faces, F))) +pos = cartesian(CompScienceMeshes.center(chart(Faces, F))) supp1 = submesh((m,tet) -> Face[1] in CompScienceMeshes.indices(m,tet), tetrs.mesh) supp2 = submesh((m,tet) -> Face[2] in CompScienceMeshes.indices(m,tet), tetrs.mesh) @@ -133,7 +133,7 @@ Nd_int = BEAST.nedelecc3d(supp, int_edges) x0 = ones(length(port)) / length(port) for (i,edge) in enumerate(port) - tgt = tangents(center(chart(port,edge)),1) + tgt = tangents(CompScienceMeshes.center(chart(port,edge)),1) if dot(normal(chart(Faces,F)), tgt) < 0 x0[i] *= -1 end @@ -197,7 +197,7 @@ include(joinpath(@__DIR__, "utils/edge_values.jl")) EV = edge_values(Y1,1) check_edge_values(EV) -error("stop") +# error("stop") Dir = boundary(Tetrs) Y = BEAST.dual1forms(Tetrs, Faces, Dir) diff --git a/examples/ex_dual1forms_bis.jl b/examples/ex_dual1forms_bis.jl index fdafc17c..f7bac6fb 100644 --- a/examples/ex_dual1forms_bis.jl +++ b/examples/ex_dual1forms_bis.jl @@ -150,7 +150,7 @@ Faces = submesh(!in(Bnd), CompScienceMeshes.skeleton_fast(Tetrs,2)) F = 396 Face = cells(Faces)[F] -pos = cartesian(center(chart(Faces, F))) +pos = cartesian(CompScienceMeshes.center(chart(Faces, F))) idcs1 = v2t[Face[1],1:v2n[Face[1]]] idcs2 = v2t[Face[2],1:v2n[Face[2]]] @@ -179,7 +179,7 @@ port_edges = submesh(in(boundary(supp12)), port_edges) # Step 1: set port flux and extend to dual faces x0 = ones(length(port_edges)) / length(port_edges) for (i,edge) in enumerate(port_edges) - tgt = tangents(center(chart(port_edges, edge)),1) + tgt = tangents(CompScienceMeshes.center(chart(port_edges, edge)),1) if dot(normal(chart(Faces, F)), tgt) < 0 x0[i] *= -1 end diff --git a/examples/ex_duals.jl b/examples/ex_duals.jl index 664b21be..0febe78d 100644 --- a/examples/ex_duals.jl +++ b/examples/ex_duals.jl @@ -99,7 +99,7 @@ J = [Ipp Zpd; Zpd' Idd]; # P = inv(2J - C) * (2J + C); # Q = (2J - C) * inv(2J + C); -jg = assemble(BEAST.ScalarTrace(p -> point(1,0,0)), primal1) +jg = assemble(BEAST.ScalarTrace{eltype(x̂)}(p -> x̂), primal1) mG = zeros(numfunctions(dual1)) b = [jg; mG] diff --git a/examples/ex_fem.jl b/examples/ex_fem.jl index 79535964..01de6d35 100644 --- a/examples/ex_fem.jl +++ b/examples/ex_fem.jl @@ -104,7 +104,7 @@ A = A1 - A2 using LinearAlgebra # f = BEAST.ScalarTrace(x -> point(1,0,0) * exp(-norm(x)^2/4)) -f = BEAST.ScalarTrace(x -> point(1,0,0)) +f = BEAST.ScalarTrace{typeof(x̂)}(x -> x̂) b = assemble(f, X) u = A \ b diff --git a/examples/ex_globalmultitrace.jl b/examples/ex_globalmultitrace.jl new file mode 100644 index 00000000..4131af00 --- /dev/null +++ b/examples/ex_globalmultitrace.jl @@ -0,0 +1,104 @@ +using CompScienceMeshes +using BEAST +using LinearAlgebra +import Plotly + +# Exterior wavenumber +κ₀ = 3.0 + +# This is where the number of domains enters the problem description +κ = [1.5κ₀, 2.5κ₀] + +# Description of the domain boundaries +h = 0.15 +Γ1 = meshcuboid(0.5, 1.0, 1.0, h) +Γ2 = -Mesh([point(-x,y,z) for (x,y,z) in vertices(Γ1)], deepcopy(cells(Γ1))) +Γ = [Γ1, Γ2] + +# Beyond this point the problem description is completely independent +# of the number of domains and their relative positioning + +# Incident field +E = Maxwell3D.planewave(direction=(x̂+ẑ)/√2, polarization=ŷ, wavenumber=κ₀) +H = -1/(im*κ₀)*curl(E) + +# Definition of the boundary integral operators +T0 = Maxwell3D.singlelayer(wavenumber=κ₀) +K0 = Maxwell3D.doublelayer(wavenumber=κ₀) + +T = [Maxwell3D.singlelayer(wavenumber=κᵢ) for κᵢ ∈ κ] +K = [Maxwell3D.doublelayer(wavenumber=κᵢ) for κᵢ ∈ κ] + +@hilbertspace m j +@hilbertspace k l +A0 = K0[k,m] - T0[k,j] + T0[l,m] + K0[l,j] +A = [Kᵢ[k,m] - Tᵢ[k,j] + Tᵢ[l,m] + Kᵢ[l,j] for (Tᵢ,Kᵢ) ∈ zip(T,K)] +Adiag = BEAST.Variational.DirectProductKernel(A) + +N = BEAST.NCross() +Nᵢ = -0.5*N[k,m] - 0.5*N[l,j] +Ndiag = BEAST.Variational.BlockDiagKernel(Nᵢ) + +# This needs a clean interface but for now it is the +# only way to keep the number of domains a runtime variable +psyms = [Symbol(:p,i) for i in eachindex(κ)] +qsyms = [Symbol(:q,i) for i in eachindex(κ)] +p = [BEAST.Variational.HilbertVector(i,psyms,[]) for i ∈ eachindex(psyms)] +q = [BEAST.Variational.HilbertVector(i,qsyms,[]) for i ∈ eachindex(qsyms)] + +B = A0[p,q] + Adiag[p,q] + Ndiag[p,q] - Nᵢ[p,q] + +e = (n × E) × n +h = (n × H) × n +bᵢ = e[k] - h[l] +b = bᵢ[p] + +Xₕ = raviartthomas.(Γ) +Yₕ = raviartthomas.(Γ) + +Pₕ = [Xᵢ×Yᵢ for (Xᵢ,Yᵢ) ∈ zip(Xₕ,Yₕ)] +Qₕ = [Xᵢ×Yᵢ for (Xᵢ,Yᵢ) ∈ zip(Xₕ,Yₕ)] +deq = BEAST.discretise(B==b, (p .∈ Pₕ)..., (q .∈ Qₕ)...) +# u = solve(deq) +u = gmres(deq, tol=1e-4, maxiter=2500) + +fcrm = [facecurrents(u[qᵢ][m], Xᵢ)[1] for (qᵢ,Xᵢ) ∈ zip(q,Xₕ)] +fcrj = [facecurrents(u[qᵢ][j], Xᵢ)[1] for (qᵢ,Xᵢ) ∈ zip(q,Xₕ)] + +Plotly.plot([patch(Γᵢ, norm.(fcrᵢ), caxis=(0,2)) for (fcrᵢ,Γᵢ) in zip(fcrm,Γ)]) +Plotly.plot([patch(Γᵢ, norm.(fcrᵢ), caxis=(0,2)) for (fcrᵢ,Γᵢ) in zip(fcrj,Γ)]) + +function nearfield(um,Xm,uj,Xj,κ,η,points) + + K = BEAST.MWDoubleLayerField3D(wavenumber=κ) + T = BEAST.MWSingleLayerField3D(wavenumber=κ) + + Em = potential(K, points, um, Xm) + Ej = potential(T, points, uj, Xj) + E = -Em + η * Ej + + Hm = potential(T, points, um, Xm) + Hj = potential(K, points, uj, Xj) + H = 1/η*Hm + Hj + + return E, H +end + +Xs = range(-1.5,1.5,length=150) +Zs = range(-0.5,1.5,length=100) +pts = [point(x,0.5,z) for z in Zs, x in Xs] + +EHi = [nearfield(-u[qᵢ][m], Xᵢ, -u[qᵢ][j], Yᵢ, κᵢ, 1.0, pts) for (qᵢ,Xᵢ,Yᵢ,κᵢ) ∈ zip(q,Xₕ,Yₕ,κ)] +EH0 = [nearfield(u[qᵢ][m], Xᵢ, u[qᵢ][j], Yᵢ, κ₀, 1.0, pts) for (qᵢ,Xᵢ,Yᵢ) ∈ zip(q,Xₕ,Yₕ)] + +Eo = sum(getindex.(EH0,1)) + E.(pts) +Ho = sum(getindex.(EH0,2)) + H.(pts) + +Ei = getindex.(EHi,1) +Hi = getindex.(EHi,2) + +Etot = Eo + sum(Ei) +Htot = Ho + sum(Hi) + +import Plots +Plots.heatmap(Xs, Zs, clamp.(abs.(getindex.(Etot,2)),-2.0,2.0); colormap=:viridis) \ No newline at end of file diff --git a/examples/ex_interpolation.jl b/examples/ex_interpolation.jl index 815bbb97..927389c2 100644 --- a/examples/ex_interpolation.jl +++ b/examples/ex_interpolation.jl @@ -6,11 +6,11 @@ trias = CompScienceMeshes.meshrectangle(1.0,1.0,0.05) trias = CompScienceMeshes.meshcircle(1.0,0.5) tetrs = CompScienceMeshes.tetmeshcuboid(1,1,1,0.2) -f = BEAST.ScalarTrace(x -> sin(pi*x[1]*x[2])*sin(pi*x[2])) -f2 = BEAST.ScalarTrace(x -> (-x[2]*sin(2*pi*(x[1]^2+x[2]^2))*sin(atan(x[2],x[1])), x[1]*sin(2*pi*(x[1]^2+x[2]^2))*sin(atan(x[2],x[1])), 0) ) -g = BEAST.ScalarTrace(x -> (sin(pi*x[1])*cos(pi*x[2]), -cos(pi*x[1])*sin(pi*x[2]), 0 )) -g = BEAST.ScalarTrace(x -> (sin(pi*x[1])*cos(pi*x[2]), sin(pi*x[2])*cos(pi*x[2]), sin(pi*x[3])*cos(pi*x[2]))) -g2 = BEAST.ScalarTrace(x -> ( sin(pi*x[1])*cos(pi*x[3]), 0, -cos(pi*x[1])*sin(pi*x[3]))) +f = BEAST.ScalarTrace{Float64}(x -> sin(pi*x[1]*x[2])*sin(pi*x[2])) +f2 = BEAST.ScalarTrace{Float64}(x -> (-x[2]*sin(2*pi*(x[1]^2+x[2]^2))*sin(atan(x[2],x[1])), x[1]*sin(2*pi*(x[1]^2+x[2]^2))*sin(atan(x[2],x[1])), 0) ) +g = BEAST.ScalarTrace{Float64}(x -> (sin(pi*x[1])*cos(pi*x[2]), -cos(pi*x[1])*sin(pi*x[2]), 0 )) +g = BEAST.ScalarTrace{Float64}(x -> (sin(pi*x[1])*cos(pi*x[2]), sin(pi*x[2])*cos(pi*x[2]), sin(pi*x[3])*cos(pi*x[2]))) +g2 = BEAST.ScalarTrace{Float64}(x -> ( sin(pi*x[1])*cos(pi*x[3]), 0, -cos(pi*x[1])*sin(pi*x[3]))) N = 4 @@ -24,9 +24,9 @@ for n in 1:N h[n] = 0.5^(n) - trias2 = CompScienceMeshes.meshrectangle(1.0,1.0, h[n]) + trias = CompScienceMeshes.meshrectangle(1.0,1.0, h[n]) - trias = CompScienceMeshes.meshcircle(1.0, h[n], 3) + # trias = CompScienceMeshes.meshcircle(1.0, h[n], 3) Z = BEAST.lagrangec0d1(trias, dirichlet=false) Y = BEAST.brezzidouglasmarini(trias) @@ -75,12 +75,21 @@ for i in 1:N bndry = boundary(tetrs) faces = skeleton(tetrs, 2) - bndry_faces = [sort(c) for c in cells(skeleton(bndry, 2))] - function is_interior(faces) - !(sort(faces) in bndry_faces) - end + # onbnd1 = CompScienceMeshes.in(bnd_edges) + # onbnd0 = CompScienceMeshes.in(bnd_verts) - interior_faces = submesh(is_interior, faces) + # interior_edges = submesh(!onbnd1, all_edges) + # interior_verts = submesh(!onbnd0, all_verts) + + # bndry_faces = [sort(c) for c in cells(skeleton(bndry, 2))] + # function is_interior(faces) + # !(sort(faces) in bndry_faces) + # end + + onbnd = CompScienceMeshes.in(bndry) + interior_faces = submesh(!onbnd, faces) + + # interior_faces = submesh(is_interior, faces) W = BEAST.brezzidouglasmarini3d(tetrs, interior_faces) @@ -111,12 +120,15 @@ for i in 1:N bndry = boundary(tetrs) faces = skeleton(tetrs, 2) - bndry_faces = [sort(c) for c in cells(skeleton(bndry, 2))] - function is_interior(faces) - !(sort(faces) in bndry_faces) - end + # bndry_faces = [sort(c) for c in cells(skeleton(bndry, 2))] + # function is_interior(faces) + # !(sort(faces) in bndry_faces) + # end + + # interior_faces = submesh(is_interior, faces) - interior_faces = submesh(is_interior, faces) + onbnd = CompScienceMeshes.in(bndry) + interior_faces = submesh(!onbnd, faces) V = BEAST.nedelecd3d(tetrs, interior_faces) diff --git a/examples/junction_calderon.jl b/examples/junction_calderon.jl index c05cab54..bc4db4a8 100644 --- a/examples/junction_calderon.jl +++ b/examples/junction_calderon.jl @@ -2,7 +2,7 @@ using CompScienceMeshes using BEAST using LinearAlgebra -width, height, h = 1.0, 0.5, 0.1 +width, height, h = 1.0, 0.5, 0.05 ℑ₁ = meshrectangle(width, height, h) ℑ₂ = CompScienceMeshes.rotate(ℑ₁, 0.5π * x̂) ℑ₃ = CompScienceMeshes.rotate(ℑ₁, 1.0π * x̂) @@ -42,6 +42,13 @@ P = Dxy * Syy * Dyx u1, ch1 = solve(BEAST.GMRESSolver(Sxx,tol=2e-5, restart=250), ex) u2, ch2 = solve(BEAST.GMRESSolver(P*Sxx, tol=2e-5, restart=250), P*ex) +# error() + +# Q = P*Sxx +# S = BEAST.GMRESSolver(Q, tol=2e-5, restart=250) +# rhs = P*ex +# @run solve(S, rhs) + # Compute and visualise the far field Φ, Θ = [0.0], range(0,stop=π,length=100) pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] @@ -50,6 +57,6 @@ near2 = potential(MWFarField3D(wavenumber=κ), pts, u2, X) using Plots @show norm(u1-u2) -plot(title="far field") -plot!(Θ, real.(getindex.(near1,1)), label="no preconditioner") -scatter!(Θ, real.(getindex.(near2,1)), label="Calderon preconditioner") \ No newline at end of file +# Plots.plot(title="far field") +Plots.plot!(Θ, real.(getindex.(near1,1)), label="no preconditioner") +Plots.scatter!(Θ, real.(getindex.(near2,1)), label="Calderon preconditioner") \ No newline at end of file diff --git a/examples/nitsche.jl b/examples/nitsche.jl index d35e6cf7..9574b34e 100644 --- a/examples/nitsche.jl +++ b/examples/nitsche.jl @@ -40,8 +40,15 @@ X = X1 × X2 × X3 α, β = 1/(im*κ), log(abs(h))/(im*κ) Eq = @varform T[k,j] + α*S[trc(k), dvg(j)] + α*St[dvg(k), trc(j)] + β*I[trc(k), trc(j)] == e[k] eq = @discretise Eq j∈X k∈X + +# trcX = trc(X) +# dvgX = dvg(X) +# Q1 = assemble(Eq.lhs.terms[2].kernel, trcX, dvgX; threading=BEAST.Threading{:single}) +# Q2 = assemble(Eq.lhs.terms[3].kernel, dvgX, trcX; threading=BEAST.Threading{:single}) + u = solve(eq) + Θ, Φ = range(0.0,stop=π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] farfield = potential(MWFarField3D(wavenumber=κ), ffpoints, u, X) diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 24f38d61..088fcad0 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -1,15 +1,26 @@ using CompScienceMeshes, BEAST using LinearAlgebra -T = CompScienceMeshes.tetmeshsphere(1.0,0.12) -X = BEAST.nedelecc3d(T) -Γ = boundary(T) +ϵ0 = 8.854e-12 +μ0 = 4π*1e-7 +c = 1/√(ϵ0*μ0) +λ = 2.9979563769321627 +ω = 2π*c/λ + +Ω = CompScienceMeshes.tetmeshsphere(λ,0.1*λ) +Γ = boundary(Ω) X = raviartthomas(Γ) @show numfunctions(X) -κ, η = π, 1.0 -κ′, η′ = 2.0κ, η/2.0 +ϵr = 2.0 +μr = 1.0 + +κ, η = ω/c, √(μ0/ϵ0) +κ′, η′ = κ*√(ϵr*μr), η*√(μr/ϵr) + +# κ, η = π, 1.0 +# κ′, η′ = 2.0κ, η/2.0 T = Maxwell3D.singlelayer(wavenumber=κ) T′ = Maxwell3D.singlelayer(wavenumber=κ′) @@ -19,7 +30,8 @@ K′ = Maxwell3D.doublelayer(wavenumber=κ′) E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) H = -1/(im*κ*η)*curl(E) -e, h = (n × E) × n, (n × H) × n +e = (n × E) × n +h = (n × H) × n @hilbertspace j m @hilbertspace k l @@ -30,36 +42,6 @@ pmchwt = @discretise( (K+K′)[l,j] + (α*T+α′*T′)[l,m] == -e[k] - h[l], j∈X, m∈X, k∈X, l∈X) - -# Q = BEAST.sysmatrix(pmchwt) -# M = BEAST.convert_to_dense(Q) -# error() -# Q = assemble(T, X, X) -# error() -# b = BEAST.rhs(pmchwt) -# M = BEAST.convert_to_dense(Z) -# u = M \ b -# error() -# W = BEAST.assemble_hide(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict) - -# using BEAST.BlockArrays -# using BEAST.IterativeSolvers - -# u = PseudoBlockVector{ComplexF64}(undef, numfunctions.([X,X])) -# fill!(u,0) - -# error() -# u, ch = IterativeSolvers.gmres!(u, Z, b, log=true, maxiter=1000, -# restart=1000, reltol=1e-5, verbose=true) - -u = gmres(pmchwt) - -# Z = BEAST.sysmatrix(pmchwt) -# u = zeros(ComplexF64, axes(Z,2)) -# y = zeros(ComplexF64, axes(Z,1)) - -# @time for i in 1:300; BEAST.LinearMaps.mul!(y, Z, u); end - Θ, Φ = range(0.0,stop=2π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] @@ -72,13 +54,12 @@ using Plots Plots.plot(xlabel="theta") Plots.plot!(Θ,norm.(ff),label="far field",title="PMCHWT") -error() -#import Plotly -#using LinearAlgebra -#fcrj, _ = facecurrents(u[j],X) -#fcrm, _ = facecurrents(u[m],X) -#Plotly.plot(patch(Γ, norm.(fcrj))) -#Plotly.plot(patch(Γ, norm.(fcrm))) +import Plotly +using LinearAlgebra +fcrj, _ = facecurrents(u[j],X) +fcrm, _ = facecurrents(u[m],X) +Plotly.plot(patch(Γ, norm.(fcrj))) +Plotly.plot(patch(Γ, norm.(fcrm))) function nearfield(um,uj,Xm,Xj,κ,η,points, @@ -100,8 +81,8 @@ function nearfield(um,uj,Xm,Xj,κ,η,points, end -Z = range(-1,1,length=100) -Y = range(-1,1,length=100) +Z = range(-6,6,length=200) +Y = range(-4,4,length=200) nfpoints = [point(0,y,z) for z in Z, y in Y] import Base.Threads: @spawn @@ -117,11 +98,13 @@ H_tot = H_in + H_ex Plots.contour(real.(getindex.(E_tot,1))) Plots.contour(real.(getindex.(H_tot,2))) -Plots.heatmap(Z, Y, real.(getindex.(E_tot,1))) +Plots.heatmap(Z, Y, clamp.(real.(getindex.(E_tot,1)),-1.5,1.5)) +Plots.heatmap(Z, Y, clamp.(imag.(getindex.(E_tot,1)),-1.5,1.5)) Plots.heatmap(Z, Y, real.(getindex.(H_tot,2))) +Plots.heatmap(Z, Y, imag.(getindex.(H_tot,2))) Plots.plot(real.(getindex.(E_tot[:,51],1))) -Plots.plot!(real.(getindex.(H_tot[:,51],2))) +Plots.plot(real.(getindex.(H_tot[:,51],2))) # Compare the far field and the field far diff --git a/examples/projectors.jl b/examples/projectors.jl index 9b726e2a..99443fa7 100644 --- a/examples/projectors.jl +++ b/examples/projectors.jl @@ -99,43 +99,43 @@ plot!(Θ, norm.(near0)); scatter!(Θ, norm.(near1)) # scatter!(Θ, norm.(near2)) -error() +# error() -using LinearAlgebra -using Plots -plotly() -w0 = eigvals(Matrix(Sxx)) -w1 = eigvals(Matrix(sys)) -w2 = eigvals(Matrix(P * sys)) -plot(exp.(2pi*im*range(0,1,length=200))) -scatter!(w0) -scatter!(w1) -scatter!(w2) - - -function makesim(d::Dict) - @unpack h, κ = d - u1, ch1, u2, ch2, near1, near2, X = runsim(;h, κ) - fulld = merge(d, Dict( - "u1" => u1, - "u2" => u2, - "ch1" => ch1, - "ch2" => ch2, - "near1" => near1, - "near2" => near2 - )) -end - -method = splitext(basename(@__FILE__))[1] - - -params = @strdict h κ -dicts = dict_list(params) -for (i,d) in enumerate(dicts) - @show d - f = makesim(d) - @tagsave(datadir("simulations", method, savename(d,"jld2")), f) -end +# using LinearAlgebra +# using Plots +# plotly() +# w0 = eigvals(Matrix(SLxx)) +# w1 = eigvals(Matrix(sys)) +# w2 = eigvals(Matrix(P * sys)) +# plot(exp.(2pi*im*range(0,1,length=200))) +# scatter!(w0) +# scatter!(w1) +# scatter!(w2) + + +# function makesim(d::Dict) +# @unpack h, κ = d +# u1, ch1, u2, ch2, near1, near2, X = runsim(;h, κ) +# fulld = merge(d, Dict( +# "u1" => u1, +# "u2" => u2, +# "ch1" => ch1, +# "ch2" => ch2, +# "near1" => near1, +# "near2" => near2 +# )) +# end + +# method = splitext(basename(@__FILE__))[1] + + +# params = @strdict h κ +# dicts = dict_list(params) +# for (i,d) in enumerate(dicts) +# @show d +# f = makesim(d) +# @tagsave(datadir("simulations", method, savename(d,"jld2")), f) +# end #' Visualise the spectrum @@ -176,7 +176,7 @@ end # Q = HY * iN * HX -using AlgebraicMultigrid -A = poisson(100) -b = rand(100); -solve(A, b, RugeStubenAMG(), maxiter=1, abstol=1e-6) +# using AlgebraicMultigrid +# A = poisson(100) +# b = rand(100); +# solve(A, b, RugeStubenAMG(), maxiter=1, abstol=1e-6) diff --git a/examples/utils/edge_values.jl b/examples/utils/edge_values.jl index 5e7a03e1..8c8587a1 100644 --- a/examples/utils/edge_values.jl +++ b/examples/utils/edge_values.jl @@ -12,7 +12,7 @@ function edge_values(space, m) for k in nzrange(Conn, tet_id) edg_id = rowvals(Conn)[k] edge = chart(edgs, edg_id) - ctr = center(edge) + ctr = CompScienceMeshes.center(edge) tgt = tangents(ctr,1) ctr1 = neighborhood(tet, carttobary(tet, cartesian(ctr))) vals = rs(ctr1) diff --git a/src/BEAST.jl b/src/BEAST.jl index d59803d3..5d0eb99e 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -15,6 +15,9 @@ using FastGaussQuadrature using LinearMaps using LiftedMaps +using AbstractTrees +using NestedUnitRanges + import LinearAlgebra: cross, dot import LinearAlgebra: ×, ⋅ diff --git a/src/bases/basis.jl b/src/bases/basis.jl index 4252eb19..1ce43370 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -89,8 +89,9 @@ numfunctions(S::DirectProductSpace) = sum([numfunctions(s) for s in S.factors]) scalartype(s::DirectProductSpace{T}) where {T} = T geometry(x::DirectProductSpace) = weld(x.geo...) -children(x::AbstractSpace) = () -children(x::DirectProductSpace) = x.factors + +AbstractTrees.children(x::AbstractSpace) = () +AbstractTrees.children(x::DirectProductSpace) = x.factors Base.iterate(x::DirectProductSpace) = iterate(x.factors) Base.iterate(x::DirectProductSpace, state) = iterate(x.factors, state) @@ -196,7 +197,7 @@ function assemblydata(basis::Space; onlyactives=true) act_to_global = collect(1:num_cells) end - @assert num_active_cells != 0 + num_active_cells == 0 && return nothing elements = instantiate_charts(geo, num_active_cells, active) max_celltonum = maximum(celltonum) diff --git a/src/bases/bdm3dspace.jl b/src/bases/bdm3dspace.jl index bdfc8a05..cabb8774 100644 --- a/src/bases/bdm3dspace.jl +++ b/src/bases/bdm3dspace.jl @@ -26,7 +26,7 @@ function brezzidouglasmarini3d(mesh, faces) #println(cells(faces)) - for (i,face) in enumerate(cells(faces)) + for (i,face) in enumerate(faces) fns[3*(i-1)+1] = Vector{Shape{T}}() fns[3*(i-1)+2] = Vector{Shape{T}}() diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index c6397785..5eb0c456 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -171,7 +171,8 @@ Build lagrangec0d1 elements, including (dirichlet=false) or excluding (dirichlet """ function lagrangec0d1(mesh; dirichlet::Bool=true) if dirichlet == false - return lagrangec0d1(mesh, boundary(mesh)) + # return lagrangec0d1(mesh, boundary(mesh)) + return lagrangec0d1(mesh, skeleton(mesh,0)) else return lagrangec0d1_dirichlet(mesh) end diff --git a/src/helmholtz2d/helmholtzop.jl b/src/helmholtz2d/helmholtzop.jl index efd924d0..f5213d06 100644 --- a/src/helmholtz2d/helmholtzop.jl +++ b/src/helmholtz2d/helmholtzop.jl @@ -122,19 +122,27 @@ mutable struct PlaneWaveDirichlet{T,P} <: Functional direction::P end +scalartype(x::PlaneWaveDirichlet{T}) where {T} = complex(T) + mutable struct PlaneWaveNeumann{T,P} <: Functional wavenumber::T direction::P end -mutable struct ScalarTrace{F} <: Functional +scalartype(x::PlaneWaveNeumann{T}) where {T} = complex(T) + +mutable struct ScalarTrace{T,F} <: Functional field::F end +ScalarTrace(f::F) where {F} = ScalarTrace{scalartype(f), F}(f) +ScalarTrace{T}(f::F) where {T,F} = ScalarTrace{T,F}(f) + strace(f, mesh::Mesh) = ScalarTrace(f) (s::ScalarTrace)(x) = s.field(cartesian(x)) integrand(s::ScalarTrace, tx, fx) = dot(tx.value, fx) +scalartype(s::ScalarTrace{T}) where {T} = T shapevals(f::Functional, ϕ, ts) = shapevals(ValOnly(), ϕ, ts) diff --git a/src/helmholtz3d/hh3dexc.jl b/src/helmholtz3d/hh3dexc.jl index e52745b2..534dd87e 100644 --- a/src/helmholtz3d/hh3dexc.jl +++ b/src/helmholtz3d/hh3dexc.jl @@ -18,6 +18,8 @@ function (f::HH3DPlaneWave)(r) a * exp(-im*k*dot(d,r)) end +scalartype(f::HH3DPlaneWave{T}) where {T} = complex(T) + """ HH3DLinearPotential @@ -75,6 +77,8 @@ struct HH3DMonopole{T,P} amplitude::T end +scalartype(x::HH3DMonopole{T}) where {T} = complex(T) + function HH3DMonopole(;position=SVector(0.0,0.0,0.0), wavenumber=0.0, amplitude=1.0) w, a = promote(wavenumber, amplitude) HH3DMonopole(position, w, a) @@ -94,6 +98,8 @@ struct gradHH3DMonopole{T,P} amplitude::T end +scalartype(x::gradHH3DMonopole{T}) where {T} = complex(T) + function gradHH3DMonopole(;position=SVector(0.0,0.0,0.0), wavenumber=0.0, amplitude=1.0) w, a = promote(wavenumber, amplitude) gradHH3DMonopole(position, w, a) @@ -130,14 +136,18 @@ end integrand(::DirichletTrace, test_vals, field_vals) = dot(test_vals[1], field_vals) -struct NormalDerivative{F} <: Functional +struct NormalDerivative{T,F} <: Functional field::F end +NormalDerivative(f::F) where {F} = NormalDerivative{scalartype(f), F}(f) +NormalDerivative{T}(f::F) where {T,F} = NormalDerivative{T,F}(f) +scalartype(s::NormalDerivative{T}) where {T} = T + const ∂n = Val{:normalderivative} (::Type{Val{:normalderivative}})(f) = NormalDerivative(f) -function (f::NormalDerivative{T})(manipoint) where T<:HH3DPlaneWave +function (f::NormalDerivative{T,F})(manipoint) where {T,F<:HH3DPlaneWave} d = f.field.direction k = f.field.wavenumber a = f.field.amplitude @@ -146,13 +156,13 @@ function (f::NormalDerivative{T})(manipoint) where T<:HH3DPlaneWave -im*k*a * dot(d,n) * exp(-im*k*dot(d,r)) end -function (f::NormalDerivative{T})(manipoint) where T<:HH3DLinearPotential +function (f::NormalDerivative{T,F})(manipoint) where {T,F<:HH3DLinearPotential} gradient = f.field.amplitude * f.field.direction n = normal(manipoint) return dot(n, gradient) end -function (f::NormalDerivative{T})(manipoint) where T<:HH3DMonopole +function (f::NormalDerivative{T,F})(manipoint) where {T,F<:HH3DMonopole} m = f.field grad_m = grad(m) n = normal(manipoint) diff --git a/src/identityop.jl b/src/identityop.jl index 1e6ba7c6..d1a44411 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -42,7 +42,7 @@ function quaddata(op::LocalOperator, g::subReferenceSpace, f::subReferenceSpace, return qd, A end -const LinearRefSpaceTetr = Union{NDLCCRefSpace, NDLCDRefSpace} +const LinearRefSpaceTetr = Union{NDLCCRefSpace, NDLCDRefSpace, BDM3DRefSpace} defaultquadstrat(::LocalOperator, ::LinearRefSpaceTetr, ::LinearRefSpaceTetr) = SingleNumQStrat(3) function quaddata(op::LocalOperator, g::LinearRefSpaceTetr, f::LinearRefSpaceTetr, tels, bels, qs::SingleNumQStrat) diff --git a/src/integralop.jl b/src/integralop.jl index 577daf99..f4a61320 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -68,8 +68,14 @@ Computes the matrix of operator biop wrt the finite element spaces tfs and bfs function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; quadstrat=defaultquadstrat(biop, tfs, bfs)) - test_elements, tad, tcells = assemblydata(tfs) - bsis_elements, bad, bcells = assemblydata(bfs) + # test_elements, tad, tcells = assemblydata(tfs) + # bsis_elements, bad, bcells = assemblydata(bfs) + + tr = assemblydata(tfs); tr == nothing && return + br = assemblydata(bfs); br == nothing && return + + test_elements, tad, tcells = tr + bsis_elements, bad, bcells = br tshapes = refspace(tfs); num_tshapes = numfunctions(tshapes) bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes) diff --git a/src/interpolation.jl b/src/interpolation.jl index 212f442e..273fa6d0 100644 --- a/src/interpolation.jl +++ b/src/interpolation.jl @@ -99,7 +99,7 @@ function DofInterpolate(basis::BDMBasis, field) edge = simplex(basis.geo.vertices[[v1,v2]]...) t = tangents(center(edge),1) - tria = chart(basis.geo, cell) + tria = chart(basis.geo, cellid) n = normal(center(tria)) diff --git a/src/maxwell/mwexc.jl b/src/maxwell/mwexc.jl index 048d8b78..1bfba9f2 100644 --- a/src/maxwell/mwexc.jl +++ b/src/maxwell/mwexc.jl @@ -11,6 +11,8 @@ function PlaneWaveMW(d,p,k,a = 1) PlaneWaveMW{T,P}(d,p,k,a) end +scalartype(x::PlaneWaveMW{T,P}) where {T,P} = promote_type(complex(T), eltype(P)) + """ planewavemw3d(;direction, polarization, wavenumber[, amplitude=1]) @@ -128,10 +130,14 @@ mutable struct CrossTraceMW{F} <: Functional field::F end +scalartype(x::CrossTraceMW) = scalartype(x.field) + mutable struct TangTraceMW{F} <: Functional field::F end +scalartype(t::TangTraceMW) = scalartype(t.field) + cross(::NormalVector, p::Function) = CrossTraceMW(p) cross(::NormalVector, p::PlaneWaveMW) = CrossTraceMW(p) cross(::NormalVector, p::Dipole) = CrossTraceMW(p) @@ -154,10 +160,14 @@ end integrand(::TangTraceMW, gx, ϕx) = gx[1] ⋅ ϕx integrand(::CrossTraceMW, test_vals, field_val) = test_vals[1] ⋅ field_val -struct NDotTrace{F} <: Functional +struct NDotTrace{T,F} <: Functional field::F end +NDotTrace(f::F) where {F} = NDotTrace{scalartype(f), F}(f) +NDotTrace{T}(f::F) where {T,F} = NDotTrace{T,F}(f) +scalartype(s::NDotTrace{T}) where {T} = T + (ϕ::NDotTrace)(p) = dot(normal(p), ϕ.field(cartesian(p))) integrand(::NDotTrace, g, ϕ) = dot(g.value, ϕ) LinearAlgebra.dot(::NormalVector, f) = NDotTrace(f) diff --git a/src/maxwell/nxdbllayer.jl b/src/maxwell/nxdbllayer.jl index b51e7e2e..8772e354 100644 --- a/src/maxwell/nxdbllayer.jl +++ b/src/maxwell/nxdbllayer.jl @@ -71,10 +71,36 @@ end # return g ⋅ (n × (∇G × f)) # end -function (igd::Integrand{<:DoubleLayerRotatedMW3D})(u,v) +# function (igd::Integrand{<:DoubleLayerRotatedMW3D})(u,v) - x = neighborhood(igd.test_chart,u) - y = neighborhood(igd.trial_chart,v) +# x = neighborhood(igd.test_chart,u) +# y = neighborhood(igd.trial_chart,v) +# j = jacobian(x) * jacobian(y) +# nx = normal(x) + +# r = cartesian(x) - cartesian(y) +# R = norm(r) +# iR = 1/R +# γ = igd.operator.gamma +# G = exp(-γ*R)/(4π*R) +# K = -(γ + iR) * G * (iR * r) + +# f = igd.local_test_space(x) +# g = igd.local_trial_space(y) + +# fvalue = getvalue(f) +# gvalue = getvalue(g) + +# jKg = cross.(Ref(K), j*gvalue) +# jnxKg = cross.(Ref(nx), jKg) +# return _krondot(fvalue, jnxKg) +# end + + +function (igd::Integrand{<:DoubleLayerRotatedMW3D})(x,y,f,g) + + # x = neighborhood(igd.test_chart,u) + # y = neighborhood(igd.trial_chart,v) j = jacobian(x) * jacobian(y) nx = normal(x) @@ -85,8 +111,8 @@ function (igd::Integrand{<:DoubleLayerRotatedMW3D})(u,v) G = exp(-γ*R)/(4π*R) K = -(γ + iR) * G * (iR * r) - f = igd.local_test_space(x) - g = igd.local_trial_space(y) + # f = igd.local_test_space(x) + # g = igd.local_trial_space(y) fvalue = getvalue(f) gvalue = getvalue(g) diff --git a/src/operator.jl b/src/operator.jl index 769487f6..bfdb379b 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -14,8 +14,8 @@ abstract type AbstractOperator end """ abstract type Operator <: AbstractOperator end -mutable struct TransposedOperator <: Operator - op::Operator +mutable struct TransposedOperator <: AbstractOperator + op::AbstractOperator end scalartype(op::TransposedOperator) = scalartype(op.op) @@ -168,8 +168,8 @@ end (f::_OffsetStore)(v,m,n) = f.store(v,m + f.row_offset, n + f.col_offset) -function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, - threading::Type{Threading{:multi}} = Threading{:multi}; +function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, + store, threading::Type{Threading{:multi}}; quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) # @info "Multi-threaded assembly:" @@ -191,8 +191,8 @@ function assemble!(operator::Operator, test_functions::Space, trial_functions::S end -function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, - threading::Type{Threading{:single}}; +function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, + store, threading::Type{Threading{:single}}; quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) # @info "Single-threaded assembly" @@ -202,11 +202,12 @@ end -function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, store; +function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, + store, threading = Threading{:multi}; quadstrat=defaultquadstrat(op, tfs, bfs)) store1(v,m,n) = store(v,n,m) - assemble!(op.op, bfs, tfs, store1; quadstrat) + assemble!(op.op, bfs, tfs, store1, threading; quadstrat) end @@ -216,40 +217,46 @@ function assemble!(op::LinearCombinationOfOperators, tfs::AbstractSpace, bfs::Ab for (a,A,qs) in zip(op.coeffs, op.ops, quadstrat) store1(v,m,n) = store(a*v,m,n) - assemble!(A, tfs, bfs, store1; quadstrat=qs) + assemble!(A, tfs, bfs, store1, threading; quadstrat=qs) end end # Support for direct product spaces -function assemble!(op::Operator, tfs::DirectProductSpace, bfs::Space, store, threading = Threading{:multi}; +function assemble!(op::AbstractOperator, tfs::DirectProductSpace, bfs::Space, + store, threading = Threading{:multi}; quadstrat=defaultquadstrat(op, tfs[1], bfs)) + I = Int[0] for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) store1(v,m,n) = store(v,m + I[i], n) - assemble!(op, s, bfs, store1; quadstrat) + assemble!(op, s, bfs, store1, threading; quadstrat) end end -function assemble!(op::Operator, tfs::Space, bfs::DirectProductSpace, store, threading=Threading{:multi}; +function assemble!(op::AbstractOperator, tfs::Space, bfs::DirectProductSpace, + store, threading=Threading{:multi}; quadstrat=defaultquadstrat(op, tfs, bfs[1])) + J = Int[0] for s in bfs.factors push!(J, last(J) + numfunctions(s)) end for (j,s) in enumerate(bfs.factors) store1(v,m,n) = store(v,m,n + J[j]) - assemble!(op, tfs, s, store1; quadstrat) + assemble!(op, tfs, s, store1, threading; quadstrat) end end -function assemble!(op::Operator, tfs::DirectProductSpace, bfs::DirectProductSpace, store, threading=Threading{:multi}; +function assemble!(op::AbstractOperator, tfs::DirectProductSpace, bfs::DirectProductSpace, + store, threading=Threading{:multi}; quadstrat=defaultquadstrat(op, tfs[1], bfs[1])) + I = Int[0] for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) store1(v,m,n) = store(v,m + I[i],n) - assemble!(op, s, bfs, store1; quadstrat) + assemble!(op, s, bfs, store1, threading; quadstrat) end end @@ -264,6 +271,8 @@ diag(op::AbstractOperator) = BlockDiagonalOperator(op) scalartype(op::BlockDiagonalOperator) = scalartype(op.op) defaultquadstrat(op::BlockDiagonalOperator, U::DirectProductSpace, V::DirectProductSpace) = defaultquadstrat(op.op, U, V) +allocatestorage(op::BlockDiagonalOperator, X, Y, storage_trait, longdelays_trait) = + allocatestorage(op.op, X, Y, storage_trait, longdelays_trait) function assemble!(op::BlockDiagonalOperator, U::DirectProductSpace, V::DirectProductSpace, store, threading=Threading{:multi}; @@ -273,11 +282,9 @@ function assemble!(op::BlockDiagonalOperator, U::DirectProductSpace, V::DirectPr I = Int[0]; for u in U.factors push!(I, last(I) + numfunctions(u)) end J = Int[0]; for v in V.factors push!(J, last(J) + numfunctions(v)) end - k = 1 - for (u,v) in zip(U.factors, V.factors) + for (k,(u,v)) in enumerate(zip(U.factors, V.factors)) store1(v,m,n) = store(v, I[k] + m, J[k] + n) - assemble!(op.op, u, v, store1; quadstrat) - k += 1 + assemble!(op.op, u, v, store1, threading; quadstrat) end end @@ -293,7 +300,7 @@ defaultquadstrat(op::BlockFullOperators, U::DirectProductSpace, V::DirectProduct function assemble!(op::BlockFullOperators, U::DirectProductSpace, V::DirectProductSpace, - store, threading=Threading{:multi}; + store, threading; quadstrat = defaultquadstrat(op, U, V)) # @assert length(U.factors) == length(V.factors) @@ -310,7 +317,7 @@ function assemble!(op::BlockFullOperators, U::DirectProductSpace, V::DirectProdu for (k,u) in enumerate(U.factors) for (l,v) in enumerate(V.factors) store1(x,m,n) = store(x, I[k]+m, J[l]+n) - assemble!(op.op, u, v, store1; quadstrat) + assemble!(op.op, u, v, store1, threading; quadstrat) end end end \ No newline at end of file diff --git a/src/postproc.jl b/src/postproc.jl index 082ad4da..37ee64eb 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -100,12 +100,12 @@ function facecurrents(u, X1, Xs...) offset = 0 n = numfunctions(X1) - fcrs = [ facecurrents(u[offset + (1:n)], X1)[1] ] + fcrs = [ facecurrents(u[offset .+ (1:n)], X1)[1] ] offset += n for i in 1:length(Xs) n = numfunctions(Xs[i]) - push!(fcrs, facecurrents(u[offset + (1:n)], Xs[i])[1]) + push!(fcrs, facecurrents(u[offset .+ (1:n)], Xs[i])[1]) offset += n end diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index 0ec0207c..e5ac9599 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -31,17 +31,10 @@ operator(solver::GMRESSolver) = solver.linear_operator function solve(solver::GMRESSolver, b) - T = promote_type(eltype(solver), eltype(b)) - # x = PseudoBlockVector{T}(undef, BlockArrays.blocksizes(solver)[1]) x = similar(Array{T}, axes(solver)[2]) fill!(x,0) x, ch = solve!(x, solver, b) - - # op = operator(solver) - # x, ch = IterativeSolvers.gmres(op, b, log=true, maxiter=solver.maxiter, - # restart=solver.restart, reltol=solver.tol, verbose=solver.verbose) - # return x, ch end @@ -56,14 +49,9 @@ end function Base.:*(A::GMRESSolver, b::AbstractVector) T = promote_type(eltype(A), eltype(b)) - y = PseudoBlockVector{T}(undef, BlockArrays.blocksizes(A)[1]) + y = PseudoBlockVector{T}(undef, (axes(A,2),)) mul!(y, A, b) - - # x, ch = solve(solver, b) - # println("Number of iterations: ", ch.iters) - # ch.isconverged || error("Iterative solver did not converge.") - # return x end Base.size(solver::GMRESSolver) = reverse(size(solver.linear_operator)) @@ -80,55 +68,24 @@ LinearAlgebra.adjoint(A::GMRESSolver) = GMRESSolver(adjoint(A.linear_operator), LinearAlgebra.transpose(A::GMRESSolver) = GMRESSolver(transpose(A.linear_operator), A.maxiter, A.restart, A.tol, A.verbose) -# function LinearAlgebra.mul!( -# y::AbstractVecOrMat, -# transA::LinearMaps.AdjointMap{<:Any,<:GMRESSolver}, -# x::AbstractVector) - -# LinearMaps.check_dim_mul(y, transA, x) -# A = transA.lmap -# B = GMRESSolver(adjoint(A.linear_operator), A.maxiter, A.restart, A.tol, A.verbose) -# return mul!(y, B, x) -# end - - -# function LinearAlgebra.mul!( -# y::AbstractVecOrMat, -# transA::LinearMaps.TransposeMap{<:Any,<:GMRESSolver}, -# x::AbstractVector) - -# LinearMaps.check_dim_mul(y, transA, x) -# A = transA.lmap -# B = GMRESSolver(transpose(A.linear_operator), A.maxiter, A.restart, A.tol, A.verbose) -# return mul!(y, B, x) -# end - - function gmres(eq::DiscreteEquation; maxiter=0, restart=0, tol=0) - test_space_dict = eq.test_space_dict - trial_space_dict = eq.trial_space_dict - lhs = eq.equation.lhs rhs = eq.equation.rhs - b = assemble(rhs, test_space_dict) - Z = assemble(lhs, test_space_dict, trial_space_dict) + X = _spacedict_to_directproductspace(eq.test_space_dict) + Y = _spacedict_to_directproductspace(eq.trial_space_dict) - block_sizes = zeros(Int, length(trial_space_dict)) - for (p,x) in eq.trial_space_dict - block_sizes[p] = numfunctions(x) - end - - T = promote_type(eltype(Z), eltype(b)) - x = PseudoBlockVector{T}(undef, block_sizes) - fill!(x, 0) + b = assemble(rhs, X) + Z = assemble(lhs, X, Y) if tol == 0 invZ = GMRESSolver(Z, maxiter=maxiter, restart=restart) else invZ = GMRESSolver(Z, maxiter=maxiter, restart=restart, tol=tol) end - # x = invZ * b - mul!(x, invZ, b) + x = invZ * b + + ax = nestedrange(Y, 1, numfunctions) + return PseudoBlockVector(x, (ax,)) end diff --git a/src/solvers/lusolver.jl b/src/solvers/lusolver.jl index 588d81ed..b521506f 100644 --- a/src/solvers/lusolver.jl +++ b/src/solvers/lusolver.jl @@ -1,22 +1,22 @@ using LinearAlgebra -function convert_to_dense(A::LinearMaps.LinearCombination) - - # @info "convert matrix to dense..." - # T = eltype(A) - # # M = zeros(T, size(A)) - # # M = PseudoBlockMatrix{T}(undef, BlockArrays.blocksizes(A)...) - # # fill!(M,0) - # M = zeros(eltype(A), axes(A)) - # @show typeof(M) - # for map in A.maps - # mul!(M, 1, map, 1, 1) - # end - - # @info "matrix converted to dense" - # return M - return Matrix(A) -end +# function convert_to_dense(A::LinearMaps.LinearCombination) + +# # @info "convert matrix to dense..." +# # T = eltype(A) +# # # M = zeros(T, size(A)) +# # # M = PseudoBlockMatrix{T}(undef, BlockArrays.blocksizes(A)...) +# # # fill!(M,0) +# # M = zeros(eltype(A), axes(A)) +# # @show typeof(M) +# # for map in A.maps +# # mul!(M, 1, map, 1, 1) +# # end + +# # @info "matrix converted to dense" +# # return M +# return Matrix(A) +# end """ Solves a variational equation by simply creating the full system matrix @@ -36,8 +36,13 @@ function solve(eq) lhs = eq.equation.lhs rhs = eq.equation.rhs - b = assemble(rhs, test_space_dict) - Z = assemble(lhs, test_space_dict, trial_space_dict) + X = _spacedict_to_directproductspace(eq.test_space_dict) + Y = _spacedict_to_directproductspace(eq.trial_space_dict) + + b = assemble(rhs, X) + Z = assemble(lhs, X, X) + # b = assemble(rhs, test_space_dict) + # Z = assemble(lhs, test_space_dict, trial_space_dict) # M = convert_to_dense(Z) # println("Sysmatrix converted to dense.") @@ -49,7 +54,10 @@ function solve(eq) u = M \ Vector(b) println("done.") - return PseudoBlockVector(u, blocksizes(Z,2)) + # return u + # return PseudoBlockVector(u, blocksizes(Z,2)) + ax = nestedrange(Y, 1, numfunctions) + return PseudoBlockVector(u, (ax,)) end @@ -74,25 +82,25 @@ function td_solve(eq) marchonintime(iS, A, b, nt) end -function timeslice(A::BlockArray, k) +# function timeslice(A::BlockArray, k) - I = blocklengths(axes(A,1)) - J = blocklengths(axes(A,2)) +# I = blocklengths(axes(A,1)) +# J = blocklengths(axes(A,2)) - T = eltype(eltype(A)) - S = PseudoBlockArray{T}(undef, I, J) - fill!(S,0) +# T = eltype(eltype(A)) +# S = PseudoBlockArray{T}(undef, I, J) +# fill!(S,0) - for i in 1:blocksize(A,1) - for j in 1:blocksize(A,2) - isassigned(A.blocks, i, j) || continue - A[Block(i,j)] isa Zeros && continue - A[Block(i,j)] isa Fill && continue - Aij = A[Block(i,j)] - Ak = AbstractArray(A[Block(i,j)].convop)[:,:,k] - S[Block(i,j)] = Ak - end - end +# for i in 1:blocksize(A,1) +# for j in 1:blocksize(A,2) +# isassigned(A.blocks, i, j) || continue +# A[Block(i,j)] isa Zeros && continue +# A[Block(i,j)] isa Fill && continue +# Aij = A[Block(i,j)] +# Ak = AbstractArray(A[Block(i,j)].convop)[:,:,k] +# S[Block(i,j)] = Ak +# end +# end - return S -end +# return S +# end diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 03bff829..73e2e403 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -131,40 +131,86 @@ rhs(eq::DiscreteEquation) = assemble(eq.equation.rhs, eq.test_space_dict) assemble(dbf::DiscreteBilform; materialize=BEAST.assemble) = assemble(dbf.bilform, dbf.test_space_dict, dbf.trial_space_dict; materialize) assemble(dlf::DiscreteLinform) = assemble(dlf.linform, dlf.test_space_dict) + +function _spacedict_to_directproductspace(spacedict) + xfactors = Vector{AbstractSpace}(undef, length(spacedict)) + for (p,x) in spacedict xfactors[p] = x end + X = DirectProductSpace(xfactors) +end + function assemble(lform::LinForm, test_space_dict) - T=ComplexF64 - terms = lform.terms - blocksizes1 = Int[] - for p in 1:length(lform.test_space) - X = test_space_dict[p] - push!(blocksizes1, numfunctions(X)) - end - Z = zeros(T, sum(blocksizes1)) - B = PseudoBlockArray{T}(Z, blocksizes1) + # xfactors = Vector{AbstractSpace}(undef, length(test_space_dict)) + # for (p,x) in test_space_dict xfactors[p] = x end + # X = DirectProductSpace(xfactors) + X = _spacedict_to_directproductspace(test_space_dict) - for t in terms + return assemble(lform, X) +end + +scalartype(lf::LinForm) = scalartype(lf.terms...) +scalartype(lt::LinTerm) = scalartype(lt.coeff, lt.functional) + +function assemble(lform::LinForm, X::DirectProductSpace) + + @assert !isempty(lform.terms) + + M = length.(X.factors) + # U = BlockArrays.blockedrange(M) + U = NestedUnitRanges.nestedrange(X, 1, numfunctions) + + T = scalartype(lform, X) + Z = zeros(T, numfunctions(X)) + B = PseudoBlockArray{T}(Z, (U,)) + + for t in lform.terms - α = t.coeff - a = t.functional m = t.test_id - X = test_space_dict[m] - o = t.test_ops + x = X.factors[m] - # act with the various ops on X - for op in reverse(o) - Y = X; - X = op[end](op[1:end-1]..., Y) - end + for op in reverse(t.test_ops) x = op[end](op[1:end-1]..., x) end - b = assemble(a, X) - # B[I[m] : I[m+1]-1] = α * b - B[Block(m)] = α * b + b = assemble(t.functional, x) + B[Block(m)] = t.coeff * b end return B end +# function assemble(lform::LinForm, test_space_dict) +# T=ComplexF64 +# terms = lform.terms +# blocksizes1 = Int[] +# for p in 1:length(lform.test_space) +# X = test_space_dict[p] +# push!(blocksizes1, numfunctions(X)) +# end + +# Z = zeros(T, sum(blocksizes1)) +# B = PseudoBlockArray{T}(Z, blocksizes1) + +# for t in terms + +# α = t.coeff +# a = t.functional +# m = t.test_id +# X = test_space_dict[m] +# o = t.test_ops + +# # act with the various ops on X +# for op in reverse(o) +# Y = X; +# X = op[end](op[1:end-1]..., Y) +# end + +# b = assemble(a, X) +# # B[I[m] : I[m+1]-1] = α * b +# B[Block(m)] = α * b +# end + +# return B +# end + struct SpaceTimeData{T} <: AbstractArray{Vector{T},1} data::Array{T,2} @@ -259,14 +305,17 @@ end function assemble(bilform::BilForm, test_space_dict, trial_space_dict; materialize=BEAST.assemble) - xfactors = Vector{AbstractSpace}(undef, length(test_space_dict)) - yfactors = Vector{AbstractSpace}(undef, length(trial_space_dict)) + # xfactors = Vector{AbstractSpace}(undef, length(test_space_dict)) + # yfactors = Vector{AbstractSpace}(undef, length(trial_space_dict)) - for (p,x) in test_space_dict xfactors[p] = x end - for (q,y) in trial_space_dict yfactors[q] = y end + # for (p,x) in test_space_dict xfactors[p] = x end + # for (q,y) in trial_space_dict yfactors[q] = y end - X = DirectProductSpace(xfactors) - Y = DirectProductSpace(yfactors) + # X = DirectProductSpace(xfactors) + # Y = DirectProductSpace(yfactors) + + X = _spacedict_to_directproductspace(test_space_dict) + Y = _spacedict_to_directproductspace(trial_space_dict) return assemble(bilform, X, Y; materialize) end diff --git a/src/timedomain/tdtimeops.jl b/src/timedomain/tdtimeops.jl index f889fc4f..a088d9b7 100644 --- a/src/timedomain/tdtimeops.jl +++ b/src/timedomain/tdtimeops.jl @@ -164,7 +164,8 @@ function assemble!(operator::TensorOperator, testfns, trialfns, store, store(w*v,m,n,k) end end - assemble!(space_operator, space_testfns, space_trialfns, store1) + assemble!(space_operator, space_testfns, space_trialfns, + store1, threading) end diff --git a/src/utils/variational.jl b/src/utils/variational.jl index c088e1a1..df5fe833 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -421,4 +421,55 @@ macro varform(x) esc(y) end + +struct DirectProductKernel + bilforms +end + +function Base.getindex(A::DirectProductKernel, V::Vector{HilbertVector}, U::Vector{HilbertVector}) + terms = Vector{BilTerm}() + @assert length(V) == length(U) == length(A.bilforms) + + for (v,u,op) in zip(V,U, A.bilforms) + term = BilTerm(v.idx, u.idx, v.opstack, u.opstack, 1, op) + push!(terms, term) + end + + return BilForm(first(V).space, first(U).space, terms) +end + +struct BlockDiagKernel + bilform +end + +function Base.getindex(A::BlockDiagKernel, V::Vector{HilbertVector}, U::Vector{HilbertVector}) + terms = Vector{BilTerm}() + @assert length(V) == length(U) + + op = A.bilform + for (v,u) in zip(V,U) + term = BilTerm(v.idx, u.idx, v.opstack, u.opstack, 1, op) + push!(terms, term) + end + + return BilForm(first(V).space, first(U).space, terms) +end + + +struct OffDiagKernel + bilform +end + +function getindex(op::OffDiagKernel, V::Vector{HilbertVector}, U::Vector{HilbertVector}) + terms = Vector{BilTerm}() + for (i,v) in enumerate(V) + for (j,u) in enumerate(U) + i == j && continue + term = BilTerm(v.idx, u.idx, v.opstack, u.opstack, 1, op) + push!(terms, term) + end + end + return BilForm(first(V).space, first(U).space, terms) +end + end diff --git a/src/volumeintegral/vieexc.jl b/src/volumeintegral/vieexc.jl index d1f5e92b..dca5e685 100644 --- a/src/volumeintegral/vieexc.jl +++ b/src/volumeintegral/vieexc.jl @@ -5,6 +5,8 @@ mutable struct PlaneWaveVIE{T,P} <: Functional amplitude::T end + scalartype(x::PlaneWaveVIE{T,P}) where {T,P} = Complex{T} + function PlaneWaveVIE(d,p,k,a = 1) T = promote_type(eltype(d), eltype(p), typeof(k), typeof(a)) P = similar_type(typeof(d), T) From 6d8faf68aa73575e7a1885ee42324424374c6ff3 Mon Sep 17 00:00:00 2001 From: azuccott Date: Tue, 28 Mar 2023 19:17:39 +0200 Subject: [PATCH 225/528] curl of lagc0d2 pointwise curl of lagc0d2 and curl of lagc0d2 in terms of BDM functions --- src/bases/bdmdiv.jl | 2 ++ src/bases/lagrange.jl | 2 ++ src/bases/local/laglocal.jl | 50 +++++++++++++++++++++++++++++-------- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/bases/bdmdiv.jl b/src/bases/bdmdiv.jl index 76851f5d..8d2c6d30 100644 --- a/src/bases/bdmdiv.jl +++ b/src/bases/bdmdiv.jl @@ -4,6 +4,8 @@ struct BDMBasis{T,M,P} <: Space{T} pos::Vector{P} end +BDMBasis(geo, fns) = BDMBasis(geo, fns, Vector{vertextype(geo)}(undef,length(fns))) #? + refspace(s::BDMBasis{T}) where {T} = BDMRefSpace{T}() subset(s::BDMBasis,I) = BDMBasis(s.geo, s.fns[I], s.pos[I]) diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index c6397785..ffc3732b 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -529,6 +529,8 @@ gradient(space::LagrangeBasis{1,0}, geo, fns) = NDLCCBasis(geo, fns) curl(space::LagrangeBasis{1,0}, geo, fns) = RTBasis(geo, fns) +curl(space::LagrangeBasis{2,0}, geo, fns) = BDMBasis(geo, fns) #? + gradient(space::LagrangeBasis{1,0,<:CompScienceMeshes.AbstractMesh{<:Any,2}}, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, space.pos) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 63bd1091..b4d88555 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -173,18 +173,46 @@ function (f::LagrangeRefSpace{T,2,3})(t) where T j = jacobian(t) p = t.patch - # SVector( - # (value=u, curl=(p[3]-p[2])/j), - # (value=v, curl=(p[1]-p[3])/j), - # (value=w, curl=(p[2]-p[1])/j) - # ) + #curl=(p[3]-p[2])/j), + # (value=v, curl=(p[1]-p[3])/j), + # (value=w, curl=(p[2]-p[1])/j) + + SVector( + (value=u*(2*u-1), curl=(p[3]-p[2])*(4u-1)/j), + (value=v*(2*v-1), curl=(p[1]-p[3])*(4v-1)/j), + (value=w*(2*w-1), curl=(p[2]-p[1])*(4w-1)/j), + (value=4*v*w, curl=4*(w*(p[1]-p[3])+v*(p[2]-p[1]))/j), + (value=4*w*u, curl=4*(w*(p[3]-p[2])+u*(p[2]-p[1]))/j), + (value=4*u*v, curl=4*(u*(p[1]-p[3])+v*(p[3]-p[2]))/j), + ) +end +function (f::LagrangeRefSpace{T,1,3})(t, ::Type{Val{:withcurl}}) where T + # Evaluete quadratic Lagrange elements on a triange, together with their curl + j = jacobian(t) + u,v,w, = barycentric(t) + p = t.patch SVector( - (value=u*(2*u-1),), - (value=v*(2*v-1),), - (value=w*(2*w-1),), - (value=4*v*w,), - (value=4*w*u,), - (value=4*u*v,), + (value=u*(2*u-1), curl=(p[3]-p[2])*(4u-1)/j), + (value=v*(2*v-1), curl=(p[1]-p[3])*(4v-1)/j), + (value=w*(2*w-1), curl=(p[2]-p[1])*(4w-1)/j), + (value=4*v*w, curl=4*(w*(p[1]-p[3])+v*(p[2]-p[1]))/j), + (value=4*w*u, curl=4*(w*(p[3]-p[2])+u*(p[2]-p[1]))/j), + (value=4*u*v, curl=4*(u*(p[1]-p[3])+v*(p[3]-p[2]))/j), ) end + +function curl(ref::LagrangeRefSpace, sh, el) + if sh.refid < 4 + sh1 = Shape(sh.cellid, mod1(2*sh.refid+1,6), +sh.coeff) + sh2 = Shape(sh.cellid, mod1(2*sh.refid+2,6), -3*sh.coeff) + sh3 = Shape(sh.cellid, mod1(2*sh.refid+3,6), +3*sh.coeff) + sh4 = Shape(sh.cellid, mod1(2*sh.refid+4,6), -sh.coeff) + else + sh1 = Shape(sh.cellid, mod1(2*sh.refid+4,6), 0*sh.coeff) + sh2 = Shape(sh.cellid, mod1(2*sh.refid+5,6), -4*sh.coeff) + sh3 = Shape(sh.cellid, mod1(2*sh.refid+6,6), +4*sh.coeff) + sh4 = Shape(sh.cellid, mod1(2*sh.refid+7,6), 0*sh.coeff) + end + return [sh1, sh2, sh3, sh4] +end \ No newline at end of file From 5070eb139e7c912522cbef3543fc7468ce9440c1 Mon Sep 17 00:00:00 2001 From: azuccott Date: Tue, 28 Mar 2023 19:57:23 +0200 Subject: [PATCH 226/528] Update laglocal.jl --- src/bases/local/laglocal.jl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index b4d88555..4e1b6027 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -203,16 +203,17 @@ function (f::LagrangeRefSpace{T,1,3})(t, ::Type{Val{:withcurl}}) where T end function curl(ref::LagrangeRefSpace, sh, el) + z=zero(typeof(sh.coeff)) if sh.refid < 4 - sh1 = Shape(sh.cellid, mod1(2*sh.refid+1,6), +sh.coeff) - sh2 = Shape(sh.cellid, mod1(2*sh.refid+2,6), -3*sh.coeff) - sh3 = Shape(sh.cellid, mod1(2*sh.refid+3,6), +3*sh.coeff) - sh4 = Shape(sh.cellid, mod1(2*sh.refid+4,6), -sh.coeff) + sh1 = Shape(sh.cellid, mod1(2*sh.refid+1,6), -sh.coeff) + sh2 = Shape(sh.cellid, mod1(2*sh.refid+2,6), +3*sh.coeff) + sh3 = Shape(sh.cellid, mod1(2*sh.refid+3,6), -3*sh.coeff) + sh4 = Shape(sh.cellid, mod1(2*sh.refid+4,6), +sh.coeff) else - sh1 = Shape(sh.cellid, mod1(2*sh.refid+4,6), 0*sh.coeff) - sh2 = Shape(sh.cellid, mod1(2*sh.refid+5,6), -4*sh.coeff) - sh3 = Shape(sh.cellid, mod1(2*sh.refid+6,6), +4*sh.coeff) - sh4 = Shape(sh.cellid, mod1(2*sh.refid+7,6), 0*sh.coeff) + sh1 = Shape(sh.cellid, mod1(2*sh.refid+4,6), z*sh.coeff) + sh2 = Shape(sh.cellid, mod1(2*sh.refid+5,6), +4*sh.coeff) + sh3 = Shape(sh.cellid, mod1(2*sh.refid+6,6), -4*sh.coeff) + sh4 = Shape(sh.cellid, mod1(2*sh.refid+7,6), z*sh.coeff) end return [sh1, sh2, sh3, sh4] end \ No newline at end of file From 12bfaf9a9971882d4e752ff75b669a0e875bfbeb Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 29 Mar 2023 16:35:42 +0200 Subject: [PATCH 227/528] tighten compat for LiftedMaps and NURs --- Project.toml | 4 +- examples/ex_fem.jl | 98 +-------------------------------- examples/ex_globalmultitrace.jl | 48 ++++++++-------- src/utils/variational.jl | 6 ++ 4 files changed, 33 insertions(+), 123 deletions(-) diff --git a/Project.toml b/Project.toml index 0597b390..e7439328 100644 --- a/Project.toml +++ b/Project.toml @@ -41,9 +41,9 @@ FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4, 0.5" FillArrays = "0.11, 0.12, 0.13" IterativeSolvers = "0.9" -LiftedMaps = "0.5" +LiftedMaps = "0.5.1" LinearMaps = "3.7 - 3.9" -NestedUnitRanges = "0.1" +NestedUnitRanges = "0.2" SauterSchwab3D = "0.1" SauterSchwabQuadrature = "2.2.0" SparseMatrixDicts = "0.2" diff --git a/examples/ex_fem.jl b/examples/ex_fem.jl index 01de6d35..a7268119 100644 --- a/examples/ex_fem.jl +++ b/examples/ex_fem.jl @@ -1,82 +1,7 @@ using CompScienceMeshes using BEAST -# using Makeitso - using SparseArrays -function isdivconforming(space) - - geo = geometry(space) - mesh = geo - - edges = skeleton(mesh,1) - D = connectivity(edges, mesh, abs) - rows = rowvals(D) - vals = nonzeros(D) - - Flux = zeros(Float64, numcells(mesh),3) - TotalFlux = zeros(Float64, numcells(edges), numfunctions(space)) - - for i in 1 : numfunctions(space) - - fill!(Flux, 0) - bfs = BEAST.basisfunction(space,i) - for bf in bfs - c = bf.cellid - e = bf.refid - x = bf.coeff - Flux[c,e] += x - end - - for E in 1 : numcells(edges) - for j in nzrange(D,E) - F = rows[j] - e = vals[j] - @assert 1 <= e <= 3 - TotalFlux[E,i] += Flux[F,e] - end - end - end - - I = findall(abs.(TotalFlux) .> 1e-6) - @show length(I) - return TotalFlux, I -end - -import Plotly -function showfn(space,i) - geo = geometry(space) - T = coordtype(geo) - X = Dict{Int,T}() - Y = Dict{Int,T}() - Z = Dict{Int,T}() - U = Dict{Int,T}() - V = Dict{Int,T}() - W = Dict{Int,T}() - for sh in space.fns[i] - chrt = chart(geo, cells(geo)[sh.cellid]) - nbd = CompScienceMeshes.center(chrt) - vals = refspace(space)(nbd) - x,y,z = cartesian(nbd) - # @show vals[sh.refid].value - u,v,w = vals[sh.refid].value - # @show x, y, z - # @show u, v, w - X[sh.cellid] = x - Y[sh.cellid] = y - Z[sh.cellid] = z - U[sh.cellid] = get(U,sh.cellid,zero(T)) + sh.coeff * u - V[sh.cellid] = get(V,sh.cellid,zero(T)) + sh.coeff * v - W[sh.cellid] = get(W,sh.cellid,zero(T)) + sh.coeff * w - end - X = collect(values(X)) - Y = collect(values(Y)) - Z = collect(values(Z)) - U = collect(values(U)) - V = collect(values(V)) - W = collect(values(W)) - Plotly.cone(x=X,y=Y,z=Z,u=U,v=V,w=W) -end tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.15) @show numcells(tetrs) @@ -84,12 +9,6 @@ tetrs = CompScienceMeshes.tetmeshsphere(1.0, 0.15) bndry = boundary(tetrs) edges = skeleton(tetrs, 1) -# bndry_edges = [sort(c) for c in cells(skeleton(bndry, 1))] -# function is_interior(edge) -# !(sort(edge) in bndry_edges) -# end - -# interior_edges = submesh(is_interior, edges) interior_edges = submesh(!in(skeleton(bndry,1)), edges) @assert numcells(interior_edges) + numcells(skeleton(bndry,1)) == numcells(edges) @@ -103,7 +22,6 @@ A2 = assemble(Id, X, X) A = A1 - A2 using LinearAlgebra -# f = BEAST.ScalarTrace(x -> point(1,0,0) * exp(-norm(x)^2/4)) f = BEAST.ScalarTrace{typeof(x̂)}(x -> x̂) b = assemble(f, X) @@ -111,11 +29,11 @@ u = A \ b using Plots -pts = [point(0,t,0) for t in range(-2,2,length=200)]; +pts = [point(0.0,t,0.0) for t in range(-2,2,length=200)]; vals = BEAST.grideval(pts, u, X) vals2 = BEAST.grideval(pts, u, Y) vals2 = [point(0,1,0) × val for val in vals2] -plot(getindex.(real(vals),1), m=2) +plot(getindex.(real(vals),1), m=1) plot(norm.(vals2), m=2) # Inspect the value of a single basis function @@ -137,8 +55,6 @@ fce = simplex(tet.vertices[2], tet.vertices[3], tet.vertices[4]) nbd_rt = neighborhood(tet, [0.0, 1/3, 1/3]) vals_rt = refspace(Z)(nbd_rt) -# carttobary(fce, cartesian(nbd_rt)) - # dot(vals_rt[1].value, normal(fce)) * volume(fce) o, x, y, z = euclidianbasis(3) @@ -175,14 +91,4 @@ fcr1, geo1 = facecurrents(v, BEAST.ttrace(Xplus, bnd_tetrs)); fcr2, geo2 = facecurrents(u, BEAST.ttrace(curl(X), bnd_tetrs)); fcr3, geo3 = facecurrents(v, divergence(ttXplus)); - -# tetrs1 = skeleton(tetrs,1) -# tetrs2 = skeleton(tetrs,2) -# ttXplus = BEAST.ttrace(Xplus, bnd_tetrs) -# -# @assert dimension(geometry(ttXplus)) == 2 -# length(geometry(ttXplus)) == length(tetrs2) -# -# Conn = connectivity(tetrs1, tetrs2) -# fcr, geo = facecurrents(u, X) Plotly.plot(patch(geo, norm.(fcr))) diff --git a/examples/ex_globalmultitrace.jl b/examples/ex_globalmultitrace.jl index 4131af00..cc08189e 100644 --- a/examples/ex_globalmultitrace.jl +++ b/examples/ex_globalmultitrace.jl @@ -10,7 +10,7 @@ import Plotly κ = [1.5κ₀, 2.5κ₀] # Description of the domain boundaries -h = 0.15 +h = 0.08 Γ1 = meshcuboid(0.5, 1.0, 1.0, h) Γ2 = -Mesh([point(-x,y,z) for (x,y,z) in vertices(Γ1)], deepcopy(cells(Γ1))) Γ = [Γ1, Γ2] @@ -19,8 +19,8 @@ h = 0.15 # of the number of domains and their relative positioning # Incident field -E = Maxwell3D.planewave(direction=(x̂+ẑ)/√2, polarization=ŷ, wavenumber=κ₀) -H = -1/(im*κ₀)*curl(E) +Einc = Maxwell3D.planewave(direction=(x̂+ẑ)/√2, polarization=ŷ, wavenumber=κ₀) +Hinc = -1/(im*κ₀)*curl(Einc) # Definition of the boundary integral operators T0 = Maxwell3D.singlelayer(wavenumber=κ₀) @@ -29,27 +29,25 @@ K0 = Maxwell3D.doublelayer(wavenumber=κ₀) T = [Maxwell3D.singlelayer(wavenumber=κᵢ) for κᵢ ∈ κ] K = [Maxwell3D.doublelayer(wavenumber=κᵢ) for κᵢ ∈ κ] +# Definition of per-domain bilinear forms @hilbertspace m j @hilbertspace k l A0 = K0[k,m] - T0[k,j] + T0[l,m] + K0[l,j] A = [Kᵢ[k,m] - Tᵢ[k,j] + Tᵢ[l,m] + Kᵢ[l,j] for (Tᵢ,Kᵢ) ∈ zip(T,K)] -Adiag = BEAST.Variational.DirectProductKernel(A) - N = BEAST.NCross() Nᵢ = -0.5*N[k,m] - 0.5*N[l,j] -Ndiag = BEAST.Variational.BlockDiagKernel(Nᵢ) - -# This needs a clean interface but for now it is the -# only way to keep the number of domains a runtime variable -psyms = [Symbol(:p,i) for i in eachindex(κ)] -qsyms = [Symbol(:q,i) for i in eachindex(κ)] -p = [BEAST.Variational.HilbertVector(i,psyms,[]) for i ∈ eachindex(psyms)] -q = [BEAST.Variational.HilbertVector(i,qsyms,[]) for i ∈ eachindex(qsyms)] +# Building the global system +p = BEAST.hilbertspace(:p, length(κ)) +q = BEAST.hilbertspace(:q, length(κ)) +Adiag = BEAST.Variational.DirectProductKernel(A) +Ndiag = BEAST.Variational.BlockDiagKernel(Nᵢ) B = A0[p,q] + Adiag[p,q] + Ndiag[p,q] - Nᵢ[p,q] -e = (n × E) × n -h = (n × H) × n +# Also for the right hand side, first per domain contributions +# are constructed, followed by the global synthesis +e = (n × Einc) × n +h = (n × Hinc) × n bᵢ = e[k] - h[l] b = bᵢ[p] @@ -59,14 +57,14 @@ Yₕ = raviartthomas.(Γ) Pₕ = [Xᵢ×Yᵢ for (Xᵢ,Yᵢ) ∈ zip(Xₕ,Yₕ)] Qₕ = [Xᵢ×Yᵢ for (Xᵢ,Yᵢ) ∈ zip(Xₕ,Yₕ)] deq = BEAST.discretise(B==b, (p .∈ Pₕ)..., (q .∈ Qₕ)...) -# u = solve(deq) -u = gmres(deq, tol=1e-4, maxiter=2500) +u = solve(deq) +# u = gmres(deq, tol=1e-4, maxiter=2500) fcrm = [facecurrents(u[qᵢ][m], Xᵢ)[1] for (qᵢ,Xᵢ) ∈ zip(q,Xₕ)] fcrj = [facecurrents(u[qᵢ][j], Xᵢ)[1] for (qᵢ,Xᵢ) ∈ zip(q,Xₕ)] -Plotly.plot([patch(Γᵢ, norm.(fcrᵢ), caxis=(0,2)) for (fcrᵢ,Γᵢ) in zip(fcrm,Γ)]) -Plotly.plot([patch(Γᵢ, norm.(fcrᵢ), caxis=(0,2)) for (fcrᵢ,Γᵢ) in zip(fcrj,Γ)]) +Plotly.plot([patch(Γᵢ, norm.(fcrᵢ), caxis=(0,2)) for (fcrᵢ,Γᵢ) ∈ zip(fcrm,Γ)]) +Plotly.plot([patch(Γᵢ, norm.(fcrᵢ), caxis=(0,2)) for (fcrᵢ,Γᵢ) ∈ zip(fcrj,Γ)]) function nearfield(um,Xm,uj,Xj,κ,η,points) @@ -84,15 +82,15 @@ function nearfield(um,Xm,uj,Xj,κ,η,points) return E, H end -Xs = range(-1.5,1.5,length=150) -Zs = range(-0.5,1.5,length=100) +Xs = range(-1.5,1.5,length=300) +Zs = range(-1.5,1.5,length=200) pts = [point(x,0.5,z) for z in Zs, x in Xs] -EHi = [nearfield(-u[qᵢ][m], Xᵢ, -u[qᵢ][j], Yᵢ, κᵢ, 1.0, pts) for (qᵢ,Xᵢ,Yᵢ,κᵢ) ∈ zip(q,Xₕ,Yₕ,κ)] EH0 = [nearfield(u[qᵢ][m], Xᵢ, u[qᵢ][j], Yᵢ, κ₀, 1.0, pts) for (qᵢ,Xᵢ,Yᵢ) ∈ zip(q,Xₕ,Yₕ)] +EHi = [nearfield(-u[qᵢ][m], Xᵢ, -u[qᵢ][j], Yᵢ, κᵢ, 1.0, pts) for (qᵢ,Xᵢ,Yᵢ,κᵢ) ∈ zip(q,Xₕ,Yₕ,κ)] -Eo = sum(getindex.(EH0,1)) + E.(pts) -Ho = sum(getindex.(EH0,2)) + H.(pts) +Eo = sum(getindex.(EH0,1)) + Einc.(pts) +Ho = sum(getindex.(EH0,2)) + Hinc.(pts) Ei = getindex.(EHi,1) Hi = getindex.(EHi,2) @@ -101,4 +99,4 @@ Etot = Eo + sum(Ei) Htot = Ho + sum(Hi) import Plots -Plots.heatmap(Xs, Zs, clamp.(abs.(getindex.(Etot,2)),-2.0,2.0); colormap=:viridis) \ No newline at end of file +Plots.heatmap(Xs, Zs, clamp.(real.(getindex.(Etot,2)),-2.0,2.0); colormap=:viridis) \ No newline at end of file diff --git a/src/utils/variational.jl b/src/utils/variational.jl index df5fe833..d14396c2 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -108,6 +108,12 @@ end Base.Int(hv::HilbertVector) = hv.idx +function hilbertspace(s::Symbol, numcomponents::Int) + syms = [Symbol(s,i) for i in 1:numcomponents] + return [HilbertVector(i,syms,[]) for i in 1:numcomponents] +end + + Base.getindex(A::AbstractBlockArray, p::HilbertVector, q::HilbertVector) = A[Block(Int(p),Int(q))] Base.getindex(u::AbstractBlockArray, p::HilbertVector) = u[Block(Int(p))] From b2dd3ce0c12bbb2610419329dd9403d1c446680c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 29 Mar 2023 18:00:57 +0200 Subject: [PATCH 228/528] set version to v2.0.0 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index e7439328..2bf83c6a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.8.1" +version = "2.0.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From 896e33d287cfdc47b29297e711258541e4069583 Mon Sep 17 00:00:00 2001 From: azuccott Date: Thu, 30 Mar 2023 18:08:14 +0200 Subject: [PATCH 229/528] test curl lagc0d2 --- src/bases/bdmdiv.jl | 2 +- src/bases/lagrange.jl | 2 +- src/bases/local/laglocal.jl | 20 +++++++------ test/test_curl_lagc0d1_lagc0d2.jl | 47 +++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 test/test_curl_lagc0d1_lagc0d2.jl diff --git a/src/bases/bdmdiv.jl b/src/bases/bdmdiv.jl index 8d2c6d30..97736da7 100644 --- a/src/bases/bdmdiv.jl +++ b/src/bases/bdmdiv.jl @@ -4,7 +4,7 @@ struct BDMBasis{T,M,P} <: Space{T} pos::Vector{P} end -BDMBasis(geo, fns) = BDMBasis(geo, fns, Vector{vertextype(geo)}(undef,length(fns))) #? +BDMBasis(geo, fns) = BDMBasis(geo, fns, Vector{vertextype(geo)}(undef,length(fns))) refspace(s::BDMBasis{T}) where {T} = BDMRefSpace{T}() subset(s::BDMBasis,I) = BDMBasis(s.geo, s.fns[I], s.pos[I]) diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index ffc3732b..47ce55a6 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -529,7 +529,7 @@ gradient(space::LagrangeBasis{1,0}, geo, fns) = NDLCCBasis(geo, fns) curl(space::LagrangeBasis{1,0}, geo, fns) = RTBasis(geo, fns) -curl(space::LagrangeBasis{2,0}, geo, fns) = BDMBasis(geo, fns) #? +curl(space::LagrangeBasis{2,0}, geo, fns) = BDMBasis(geo, fns) gradient(space::LagrangeBasis{1,0,<:CompScienceMeshes.AbstractMesh{<:Any,2}}, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, space.pos) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 4e1b6027..bc0f6c60 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -6,6 +6,7 @@ mutable struct LagrangeRefSpace{T,Degree,Dim1,NF} <: RefSpace{T,NF} end numfunctions(s::LagrangeRefSpace{T,D,2}) where {T,D} = D+1 numfunctions(s::LagrangeRefSpace{T,0,3}) where {T} = 1 numfunctions(s::LagrangeRefSpace{T,1,3}) where {T} = 3 +numfunctions(s::LagrangeRefSpace{T,2,3}) where {T} = 6 valuetype(ref::LagrangeRefSpace{T}, charttype) where {T} = SVector{numfunctions(ref), Tuple{T,T}} @@ -65,7 +66,7 @@ function (f::LagrangeRefSpace{T,0,3})(t, ::Type{Val{:withcurl}}) where T end -function curl(ref::LagrangeRefSpace, sh, el) +function curl(ref::LagrangeRefSpace{T,1,3} where {T}, sh, el) sh1 = Shape(sh.cellid, mod1(sh.refid+1,3), -sh.coeff) sh2 = Shape(sh.cellid, mod1(sh.refid+2,3), +sh.coeff) return [sh1, sh2] @@ -187,7 +188,7 @@ function (f::LagrangeRefSpace{T,2,3})(t) where T ) end -function (f::LagrangeRefSpace{T,1,3})(t, ::Type{Val{:withcurl}}) where T +function (f::LagrangeRefSpace{T,2,3})(t, ::Type{Val{:withcurl}}) where T # Evaluete quadratic Lagrange elements on a triange, together with their curl j = jacobian(t) u,v,w, = barycentric(t) @@ -202,17 +203,18 @@ function (f::LagrangeRefSpace{T,1,3})(t, ::Type{Val{:withcurl}}) where T ) end -function curl(ref::LagrangeRefSpace, sh, el) +function curl(ref::LagrangeRefSpace{T,2,3} where {T}, sh, el) + #curl of lagc0d2 as combination of bdm functions z=zero(typeof(sh.coeff)) if sh.refid < 4 - sh1 = Shape(sh.cellid, mod1(2*sh.refid+1,6), -sh.coeff) - sh2 = Shape(sh.cellid, mod1(2*sh.refid+2,6), +3*sh.coeff) - sh3 = Shape(sh.cellid, mod1(2*sh.refid+3,6), -3*sh.coeff) - sh4 = Shape(sh.cellid, mod1(2*sh.refid+4,6), +sh.coeff) + sh1 = Shape(sh.cellid, mod1(2*sh.refid+1,6), +sh.coeff) + sh2 = Shape(sh.cellid, mod1(2*sh.refid+2,6), -3*sh.coeff) + sh3 = Shape(sh.cellid, mod1(2*sh.refid+3,6), +3*sh.coeff) + sh4 = Shape(sh.cellid, mod1(2*sh.refid+4,6), -sh.coeff) else sh1 = Shape(sh.cellid, mod1(2*sh.refid+4,6), z*sh.coeff) - sh2 = Shape(sh.cellid, mod1(2*sh.refid+5,6), +4*sh.coeff) - sh3 = Shape(sh.cellid, mod1(2*sh.refid+6,6), -4*sh.coeff) + sh2 = Shape(sh.cellid, mod1(2*sh.refid+5,6), -4*sh.coeff) + sh3 = Shape(sh.cellid, mod1(2*sh.refid+6,6), +4*sh.coeff) sh4 = Shape(sh.cellid, mod1(2*sh.refid+7,6), z*sh.coeff) end return [sh1, sh2, sh3, sh4] diff --git a/test/test_curl_lagc0d1_lagc0d2.jl b/test/test_curl_lagc0d1_lagc0d2.jl new file mode 100644 index 00000000..eae1b058 --- /dev/null +++ b/test/test_curl_lagc0d1_lagc0d2.jl @@ -0,0 +1,47 @@ +#test curl lagc0d2 + +using CompScienceMeshes +using BEAST + +using Test +for T in [Float32, Float64] + for j in [1,2,3] + local o, x, y, z = euclidianbasis(3,T) + triang = simplex(x,y,o) + ctr = center(triang) + + + + iref1 = BEAST.LagrangeRefSpace{T,2,3,6}() + oref1 = BEAST.BDMRefSpace{T}() + ishp1 = [BEAST.Shape{T}(1, j, 1.0), BEAST.Shape{T}(1, mod1(j+1,3)+3, 0.5), BEAST.Shape{T}(1, mod1(j+2,3)+3, 0.5)] + + # we use the property that a nodal lagc0d2 function plus 1/2 times and the two adiacent edge lagc0d2 functions is equal to the nodal lagc0d1 function at the same nodes, and the same must be true for their surfcurl + + outputbdm = [BEAST.curl(iref1, ishp1[1], triang), BEAST.curl(iref1, ishp1[2], triang), BEAST.curl(iref1, ishp1[3], triang)] + + global obdm = point(T,0,0,0) + + for i in [1,2,3] + for oshp in outputbdm[i] + obdm += oref1(ctr)[oshp.refid].value * oshp.coeff + end + end + + + iref2 = BEAST.LagrangeRefSpace{T,1,3,3}() + oref2 = BEAST.RTRefSpace{T}() + ishp2 = BEAST.Shape{T}(1, j, 1.0) + + outputrt = BEAST.curl(iref2, ishp2, triang) + + global ort = point(T,0,0,0) + + for oshp in outputrt + ort += oref2(ctr)[oshp.refid].value * oshp.coeff + end + + + @test ort ≈ obdm + end +end From d9fd92d6917ff0e8e885f9a4cf9044ca61c8b174 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Fri, 31 Mar 2023 00:42:30 +0000 Subject: [PATCH 230/528] CompatHelper: bump compat for FillArrays to 1, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 2bf83c6a..32cda58c 100644 --- a/Project.toml +++ b/Project.toml @@ -39,7 +39,7 @@ Compat = "2, 3, 4" ConvolutionOperators = "0.4" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4, 0.5" -FillArrays = "0.11, 0.12, 0.13" +FillArrays = "0.11, 0.12, 0.13, 1" IterativeSolvers = "0.9" LiftedMaps = "0.5.1" LinearMaps = "3.7 - 3.9" From 1fd5c55222204c6eb97827d94a2cc7f0aafef137 Mon Sep 17 00:00:00 2001 From: azuccott Date: Wed, 19 Apr 2023 18:20:13 +0200 Subject: [PATCH 231/528] Update test_curl_lagc0d1_lagc0d2.jl --- test/test_curl_lagc0d1_lagc0d2.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_curl_lagc0d1_lagc0d2.jl b/test/test_curl_lagc0d1_lagc0d2.jl index eae1b058..8cb2a329 100644 --- a/test/test_curl_lagc0d1_lagc0d2.jl +++ b/test/test_curl_lagc0d1_lagc0d2.jl @@ -16,7 +16,7 @@ for T in [Float32, Float64] oref1 = BEAST.BDMRefSpace{T}() ishp1 = [BEAST.Shape{T}(1, j, 1.0), BEAST.Shape{T}(1, mod1(j+1,3)+3, 0.5), BEAST.Shape{T}(1, mod1(j+2,3)+3, 0.5)] - # we use the property that a nodal lagc0d2 function plus 1/2 times and the two adiacent edge lagc0d2 functions is equal to the nodal lagc0d1 function at the same nodes, and the same must be true for their surfcurl + # we use the property that a nodal lagc0d2 function plus 1/2 times the two adiacent edge lagc0d2 functions is equal to the nodal lagc0d1 function at the same nodes, and the same must be true for their surfcurl outputbdm = [BEAST.curl(iref1, ishp1[1], triang), BEAST.curl(iref1, ishp1[2], triang), BEAST.curl(iref1, ishp1[3], triang)] From 5de971d77f784a878813cce2044f11fde6600b0c Mon Sep 17 00:00:00 2001 From: azuccott Date: Mon, 24 Apr 2023 11:58:46 +0200 Subject: [PATCH 232/528] adding ncrossbdm base --- src/bases/local/bdmlocal.jl | 3 +- src/bases/local/ncrossbdmlocal.jl | 20 ++++++++++++ src/bases/ncrossbdmspace.jl | 53 +++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/bases/local/ncrossbdmlocal.jl create mode 100644 src/bases/ncrossbdmspace.jl diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index cc6ff83a..69c29c5c 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -43,4 +43,5 @@ function dof_permutation(::BDMRefSpace, vert_permutation) return _dof_perms_bdm[i] end -dimtype(::BDMRefSpace, ::CompScienceMeshes.Simplex{U,2}) where {U} = Val{6} \ No newline at end of file +dimtype(::BDMRefSpace, ::CompScienceMeshes.Simplex{U,2}) where {U} = Val{6} + diff --git a/src/bases/local/ncrossbdmlocal.jl b/src/bases/local/ncrossbdmlocal.jl new file mode 100644 index 00000000..b3dd7b85 --- /dev/null +++ b/src/bases/local/ncrossbdmlocal.jl @@ -0,0 +1,20 @@ +struct NCrossBDMRefSpace{T} <: RefSpace{T,6} end + +function (f::NCrossBDMRefSpace)(p) + + u,v = parametric(p) + n = normal(p) + tu = tangents(p,1) + tv = tangents(p,2) + + j = jacobian(p) + d = 1/j + + return @SVector[ + (value= n × (-v*tu+v*tv)/j, curl=d), + (value= n × (u+v-1)*tu/j, curl=d), + (value= n × (u+v-1)*tv/j, curl=d), + (value= n × (u*tu-u*tv)/j, curl=d), + (value= n × (u*tu)/j, curl=d), + (value= n × (v*tv)/j, curl=d),] +end diff --git a/src/bases/ncrossbdmspace.jl b/src/bases/ncrossbdmspace.jl new file mode 100644 index 00000000..c93ae949 --- /dev/null +++ b/src/bases/ncrossbdmspace.jl @@ -0,0 +1,53 @@ +struct NCrossBDMBasis{T,M,P} <: Space{T} + geo::M + fns::Vector{Vector{Shape{T}}} + pos::Vector{P} +end + +NCrossBDMBasis(geo, fns) = BDMBasis(geo, fns, Vector{vertextype(geo)}(undef,length(fns))) + +refspace(s::NCrossBDMBasis{T}) where {T} = NCrossBDMRefSpace{T}() + +function ncrossbdm(mesh) + edges = skeleton(mesh, 1) + cps = cellpairs(mesh, edges, dropjunctionpair=true) + ids = findall(x -> x>0, cps[2,:]) + ncrossbdm(mesh, cps[:,ids]) +end + +function ncrossbdm(mesh, cellpairs::Array{Int,2}) + + @warn "brezzidouglasmarini(mesh, cellpairs) assumes mesh is oriented" + + @assert size(cellpairs,1) == 2 + + T = coordtype(mesh) + P = vertextype(mesh) + + S = Shape{T} + F = Vector{Shape{T}} + + nf = 2*size(cellpairs,2) + fns = Vector{F}(undef, nf) + pos = Vector{P}(undef, nf) + + for i in axes(cellpairs)[2] + c1, c2 = cellpairs[:,i] + cell1 = cells(mesh)[c1] + cell2 = cells(mesh)[c2] + e1, e2 = getcommonedge(cell1, cell2) + @assert e1*e2 < 0 + e1, e2 = abs(e1), abs(e2) + fns[2*(i-1)+1] = [ S(c1, 2*(e1-1)+1 ,+1.0), S(c2, 2*(e2-1)+2,-1.0)] + fns[2*(i-1)+2] = [ S(c1, 2*(e1-1)+2 ,+1.0), S(c2, 2*(e2-1)+1,-1.0)] + + v1 = cell1[mod1(e1+1,3)] + v2 = cell1[mod1(e1+2,3)] + edge = simplex(mesh.vertices[[v1,v2]]...) + cntr = cartesian(center(edge)) + pos[2*(i-1)+1] = cntr + pos[2*(i-1)+2] = cntr + end + + NCrossBDMBasis(mesh, fns, pos) +end \ No newline at end of file From 825eaf4298d54b9a110957d35c81283b736b88f7 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 2 May 2023 16:59:32 +0200 Subject: [PATCH 233/528] Reenable MT assembly for TD integral ops --- examples/ex_globalmultitrace.jl | 12 +++---- examples/pmchwt.jl | 2 ++ examples/tdpmchwt.jl | 6 ++-- examples/tdpmchwt_int.jl | 10 +++--- src/timedomain/tdintegralop.jl | 63 +++++++++++++++++++++++++-------- 5 files changed, 63 insertions(+), 30 deletions(-) diff --git a/examples/ex_globalmultitrace.jl b/examples/ex_globalmultitrace.jl index cc08189e..8b9a5efc 100644 --- a/examples/ex_globalmultitrace.jl +++ b/examples/ex_globalmultitrace.jl @@ -10,7 +10,7 @@ import Plotly κ = [1.5κ₀, 2.5κ₀] # Description of the domain boundaries -h = 0.08 +h = 0.125 Γ1 = meshcuboid(0.5, 1.0, 1.0, h) Γ2 = -Mesh([point(-x,y,z) for (x,y,z) in vertices(Γ1)], deepcopy(cells(Γ1))) Γ = [Γ1, Γ2] @@ -73,17 +73,15 @@ function nearfield(um,Xm,uj,Xj,κ,η,points) Em = potential(K, points, um, Xm) Ej = potential(T, points, uj, Xj) - E = -Em + η * Ej Hm = potential(T, points, um, Xm) Hj = potential(K, points, uj, Xj) - H = 1/η*Hm + Hj - - return E, H + + return -Em + η * Ej, 1/η*Hm + Hj end -Xs = range(-1.5,1.5,length=300) -Zs = range(-1.5,1.5,length=200) +Xs = range(-2.0,2.0,length=150) +Zs = range(-1.5,2.5,length=100) pts = [point(x,0.5,z) for z in Zs, x in Xs] EH0 = [nearfield(u[qᵢ][m], Xᵢ, u[qᵢ][j], Yᵢ, κ₀, 1.0, pts) for (qᵢ,Xᵢ,Yᵢ) ∈ zip(q,Xₕ,Yₕ)] diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 088fcad0..f6ad50a9 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -42,6 +42,8 @@ pmchwt = @discretise( (K+K′)[l,j] + (α*T+α′*T′)[l,m] == -e[k] - h[l], j∈X, m∈X, k∈X, l∈X) +u = solve(pmchwt) + Θ, Φ = range(0.0,stop=2π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] diff --git a/examples/tdpmchwt.jl b/examples/tdpmchwt.jl index fd9d9d7d..0cc4945b 100644 --- a/examples/tdpmchwt.jl +++ b/examples/tdpmchwt.jl @@ -10,7 +10,7 @@ using LinearAlgebra # fn = joinpath(dirname(pathof(CompScienceMeshes)),"geos/torus.geo") # Γ = CompScienceMeshes.meshgeo(fn; dim=2, h=1.0) -Γ = meshsphere(radius=1.0, h=0.35) +Γ = meshsphere(radius=1.0, h=0.25) X = raviartthomas(Γ) Y = buffachristiansen(Γ) @@ -42,8 +42,8 @@ H = direction × E # 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], # j∈X⊗T2, m∈Y⊗T2, k∈X⊗δ, l∈Y⊗δ) -BEAST.@defaultquadstrat (T, X⊗δ, X⊗T2) BEAST.OuterNumInnerAnalyticQStrat(3) -BEAST.@defaultquadstrat (K, X⊗δ, X⊗T2) BEAST.OuterNumInnerAnalyticQStrat(3) +BEAST.@defaultquadstrat (T, X⊗δ, X⊗T2) BEAST.OuterNumInnerAnalyticQStrat(7) +BEAST.@defaultquadstrat (K, X⊗δ, X⊗T2) BEAST.OuterNumInnerAnalyticQStrat(7) pmchwt = @discretise( 2.0T[k,j] + 2.0K[k,m] - diff --git a/examples/tdpmchwt_int.jl b/examples/tdpmchwt_int.jl index 1a1a0f6c..cdf1297f 100644 --- a/examples/tdpmchwt_int.jl +++ b/examples/tdpmchwt_int.jl @@ -10,15 +10,15 @@ using LinearAlgebra fn = joinpath(dirname(pathof(CompScienceMeshes)),"geos/torus.geo") Γ = CompScienceMeshes.meshgeo(fn; dim=2, h=1.0) -# Γ = meshsphere(1.0, h) +Γ = meshsphere(radius=1.0, h=0.25) X = raviartthomas(Γ) -Y = buffachristiansen(Γ, ibscaled=true) +Y = buffachristiansen(Γ, ibscaled=false) sol = 1.0 T = TDMaxwell3D.singlelayer(speedoflight=sol, numdiffs=0) K = TDMaxwell3D.doublelayer(speedoflight=sol, numdiffs=0) -Δt, Nt = 1.6, 300 +Δt, Nt = 0.1, 300 δ = timebasisdelta(Δt, Nt) T0 = timebasiscxd0(Δt, Nt) T1 = timebasisshiftedlagrange(Δt,Nt,1) @@ -47,8 +47,8 @@ pmchwt = @discretise( 2.0K[l,j] + 2.0T[l,m] == H[k] + E[l], j∈X⊗T1, m∈X⊗T1, k∈X⊗δ, l∈X⊗δ) -Z = BEAST.td_assemble(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict); -error() +# Z = BEAST.td_assemble(pmchwt.equation.lhs, pmchwt.test_space_dict, pmchwt.trial_space_dict); +# error() function cones(mesh, arrows; sizeref=2) diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 76be01f7..2e64cc29 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -120,11 +120,17 @@ function allocatestorage(op::RetardedPotential, testST, basisST, Z = ConvolutionOperators.ConvOp(data, K0, K1, tail, len) function store1(v,m,n,k) - if Z.k0[m,n] ≤ k ≤ Z.k1[m,n] - Z.data[k - Z.k0[m,n] + 1,m,n] += v - elseif k == Z.k1[m,n]+1 - Z.tail[m,n] += v - end + k0 = Z.k0[m,n] + k < k0 && return + k1 = Z.k1[m,n] + k > k1 + 1 && return + k > k1 && (Z.tail[m,n] += v; return) + Z.data[k - k0 + 1, m,n] += v + # if Z.k0[m,n] ≤ k ≤ Z.k1[m,n] + # Z.data[k - Z.k0[m,n] + 1,m,n] += v + # elseif k == Z.k1[m,n]+1 + # Z.tail[m,n] += v + # end end return ()->Z, store1 @@ -143,10 +149,37 @@ function assemble!(op::RetardedPotential, testST, trialST, store, threading=Threading{:multi}; quadstrat=defaultquadstrat(op, testST, trialST)) Y, S = spatialbasis(testST), temporalbasis(testST) + X, R = spatialbasis(trialST), temporalbasis(trialST) - P = Threads.nthreads() - @assert P >= 1 - splits = [round(Int,s) for s in range(0, stop=numfunctions(Y), length=P+1)] + T = Threads.nthreads() + M = length(spatialbasis(testST)) + N = length(spatialbasis(trialST)) + + P = max(1, floor(Int, sqrt(M*T/N))) + Q = max(1, floor(Int, sqrt(N*T/M))) + + rowsplits = [round(Int,s) for s in range(0, stop=M, length=P+1)] + colsplits = [round(Int,s) for s in range(0, stop=N, length=Q+1)] + + idcs = CartesianIndices((1:P, 1:Q)) + Threads.@threads for idx in idcs + i = idx[1] + j = idx[2] + + rlo, rhi = rowsplits[i]+1, rowsplits[i+1] + rlo <= rhi || continue + clo, chi = colsplits[j]+1, colsplits[j+1] + clo <= chi || continue + + Y_p = subset(Y, rlo:rhi) + X_q = subset(X, clo:chi) + + store1 = (v,m,n,k) -> store(v,rlo+m-1,clo+n-1,k) + assemble_chunk!(op, Y_p ⊗ S, X_q ⊗ R, store1) + end + + # P = Threads.nthreads() + # splits = [round(Int,s) for s in range(0, stop=numfunctions(Y), length=P+1)] # @info "Starting assembly with $P threads:" # Threads.@threads for i in 1:P @@ -157,7 +190,7 @@ function assemble!(op::RetardedPotential, testST, trialST, store, # assemble_chunk!(op, Y_p ⊗ S, trialST, store1) # end - assemble_chunk!(op, testST, trialST, store) + # assemble_chunk!(op, testST, trialST, store) end function assemble_chunk!(op::RetardedPotential, testST, trialST, store; @@ -216,9 +249,9 @@ function assemble_chunk!(op::RetardedPotential, testST, trialST, store; momintegrals!(z, op, U, V, W, τ, σ, ι, qr) # assemble in the global matrix - for i in 1 : udim - for j in 1 : vdim - for d in 1 : wdim + for d in 1 : wdim + for j in 1 : vdim + for i in 1 : udim v = z[i,j,d] @@ -232,9 +265,9 @@ function assemble_chunk!(op::RetardedPotential, testST, trialST, store; end # next κ end # next ν end # next μ - end # next d - end # next j - end #next i + end + end + end end # next r end # next q From 456da4634cf0f1085dee0349476f69a8a08f9151 Mon Sep 17 00:00:00 2001 From: Paula Respondek Date: Thu, 4 May 2023 09:10:01 +0200 Subject: [PATCH 234/528] Add SauterSchwabQR for HH3DDoubleLayer(Transposed) Functions momintegrals! and pulled_back_integrand were added for the Helmholtz3D doublelayer and transposed doublelayer to enable using SauterSchwab Quadrature for triangles with coinciding vertices for these operators. --- src/helmholtz3d/hh3d_sauterschwabqr.jl | 132 +++++++++++++++++- src/helmholtz3d/hh3dops.jl | 181 ++++++++++++++++++++++++- 2 files changed, 306 insertions(+), 7 deletions(-) diff --git a/src/helmholtz3d/hh3d_sauterschwabqr.jl b/src/helmholtz3d/hh3d_sauterschwabqr.jl index 5fffdb52..ea2298ec 100644 --- a/src/helmholtz3d/hh3d_sauterschwabqr.jl +++ b/src/helmholtz3d/hh3d_sauterschwabqr.jl @@ -68,6 +68,71 @@ function pulled_back_integrand(op::HH3DHyperSingularFDBIO, end end +function pulled_back_integrand(op::HH3DDoubleLayerFDBIO, + test_local_space::LagrangeRefSpace{<:Any,0}, + trial_local_space::LagrangeRefSpace{<:Any,1}, + test_chart, trial_chart) + + (u,v) -> begin + + x = neighborhood(test_chart,u) + y = neighborhood(trial_chart,v) + + ny = normal(y) + f = test_local_space(x) + g = trial_local_space(y) + + j = jacobian(x) * jacobian(y) + + α = op.alpha + γ = op.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + G = exp(-γ*R)/(4*π*R) + inv_R = 1/R + ∇G = -(γ + inv_R) * G * inv_R * r + αnyj∇G = dot(ny,-α*∇G*j) + + SMatrix{1,3}( + f[1].value * αnyj∇G * g[1].value, + f[1].value * αnyj∇G * g[2].value, + f[1].value * αnyj∇G * g[3].value) + end +end + +function pulled_back_integrand(op::HH3DDoubleLayerTransposedFDBIO, + test_local_space::LagrangeRefSpace{<:Any,1}, + trial_local_space::LagrangeRefSpace{<:Any,0}, + test_chart, trial_chart) + + (u,v) -> begin + + x = neighborhood(test_chart,u) + y = neighborhood(trial_chart,v) + + nx = normal(x) + f = test_local_space(x) + g = trial_local_space(y) + + j = jacobian(x) * jacobian(y) + + α = op.alpha + γ = op.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + G = exp(-γ*R)/(4*π*R) + inv_R = 1/R + ∇G = -(γ + inv_R) * G * inv_R * r + αnxj∇G = dot(nx,α*∇G*j) + + SMatrix{3,1}( + f[1].value * αnxj∇G * g[1].value, + f[2].value * αnxj∇G * g[1].value, + f[3].value * αnxj∇G * g[1].value) + end +end function momintegrals!(op::HH3DSingleLayerFDBIO, test_local_space::LagrangeRefSpace{<:Any,0}, trial_local_space::LagrangeRefSpace{<:Any,0}, @@ -128,4 +193,69 @@ function momintegrals!(op::HH3DHyperSingularFDBIO, end nothing -end \ No newline at end of file +end + +function momintegrals!(op::Helmholtz3DOp, + test_local_space::LagrangeRefSpace{<:Any,0}, trial_local_space::LagrangeRefSpace{<:Any,1}, + test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) + + I, J, K, L = SauterSchwabQuadrature.reorder( + test_triangular_element.vertices, + trial_triangular_element.vertices, strat) + + test_triangular_element = simplex( + test_triangular_element.vertices[I[1]], + test_triangular_element.vertices[I[2]], + test_triangular_element.vertices[I[3]]) + + trial_triangular_element = simplex( + trial_triangular_element.vertices[J[1]], + trial_triangular_element.vertices[J[2]], + trial_triangular_element.vertices[J[3]]) + + trial_sign = Combinatorics.levicivita(J) + + σ = trial_sign + + igd = pulled_back_integrand(op, test_local_space, trial_local_space, + test_triangular_element, trial_triangular_element) + G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) + + for i ∈ 1:3 + out[1,i] += G[L[i]] * σ + end + + nothing +end + +function momintegrals!(op::Helmholtz3DOp, + test_local_space::LagrangeRefSpace{<:Any,1}, trial_local_space::LagrangeRefSpace{<:Any,0}, + test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) + + I, J, K, L = SauterSchwabQuadrature.reorder( + test_triangular_element.vertices, + trial_triangular_element.vertices, strat) + + test_triangular_element = simplex( + test_triangular_element.vertices[I[1]], + test_triangular_element.vertices[I[2]], + test_triangular_element.vertices[I[3]]) + + trial_triangular_element = simplex( + trial_triangular_element.vertices[J[1]], + trial_triangular_element.vertices[J[2]], + trial_triangular_element.vertices[J[3]]) + + test_sign = Combinatorics.levicivita(I) + σ = test_sign + + igd = pulled_back_integrand(op, test_local_space, trial_local_space, + test_triangular_element, trial_triangular_element) + G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) + + for i ∈ 1:3 + out[i,1] += G[K[i]] * σ + end + + nothing +end diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index e3c5367a..f7471b24 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -40,12 +40,12 @@ struct HH3DSingleLayerSng{T,K} <: Helmholtz3DOp gamma::K end -struct HH3DDoubleLayer{T,K} <: Helmholtz3DOp +struct HH3DDoubleLayerFDBIO{T,K} <: Helmholtz3DOp alpha::T gamma::K end -struct HH3DDoubleLayerTransposed{T,K} <: Helmholtz3DOp +struct HH3DDoubleLayerTransposedFDBIO{T,K} <: Helmholtz3DOp alpha::T gamma::K end @@ -281,8 +281,92 @@ function quadrule(op::Helmholtz3DOp, i, test_element, j, trial_element, quadrature_data, qs::DoubleNumWiltonSauterQStrat) + return DoubleQuadRule( + quadrature_data[1][1,i], + quadrature_data[2][1,j]) +end + +function quadrule(op::HH3DDoubleLayerTransposedFDBIO, + test_refspace::LagrangeRefSpace{T,1} where T, + trial_refspace::LagrangeRefSpace{T,0} where T, + i, test_element, j, trial_element, quadrature_data, + qs::DoubleNumWiltonSauterQStrat) + + tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 + dmin2 = floatmax(eltype(eltype(test_element.vertices))) + + for t in test_element.vertices + for s in trial_element.vertices + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < tol) + end end + + hits == 3 && return SauterSchwabQuadrature.CommonFace(quadrature_data.gausslegendre[3]) + hits == 2 && return SauterSchwabQuadrature.CommonEdge(quadrature_data.gausslegendre[2]) + hits == 1 && return SauterSchwabQuadrature.CommonVertex(quadrature_data.gausslegendre[1]) + test_quadpoints = quadrature_data[1] trial_quadpoints = quadrature_data[2] + test_quadpoints = quadrature_data.test_qp + trial_quadpoints = quadrature_data.bsis_qp +#= h2 = volume(trial_element) + xtol2 = 0.2 * 0.2 + k2 = abs2(op.gamma) + + max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( + test_quadpoints[1,i], + DoubleQuadRule( + test_quadpoints[1,i], + trial_quadpoints[1,j])) =# + return DoubleQuadRule( + quadrature_data[1][1,i], + quadrature_data[2][1,j]) +end + +function quadrule(op::HH3DDoubleLayerFDBIO, + test_refspace::LagrangeRefSpace{T,0} where T, + trial_refspace::LagrangeRefSpace{T,1} where T, + i, test_element, j, trial_element, quadrature_data, + qs::DoubleNumWiltonSauterQStrat) + + tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 + dmin2 = floatmax(eltype(eltype(test_element.vertices))) + + for t in test_element.vertices + for s in trial_element.vertices + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < tol) + end end + + hits == 3 && return SauterSchwabQuadrature.CommonFace(quadrature_data.gausslegendre[3]) + hits == 2 && return SauterSchwabQuadrature.CommonEdge(quadrature_data.gausslegendre[2]) + hits == 1 && return SauterSchwabQuadrature.CommonVertex(quadrature_data.gausslegendre[1]) + test_quadpoints = quadrature_data[1] + trial_quadpoints = quadrature_data[2] + + test_quadpoints = quadrature_data.test_qp + trial_quadpoints = quadrature_data.bsis_qp + h2 = volume(trial_element) + xtol2 = 0.2 * 0.2 + k2 = abs2(op.gamma) + +#= max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( + test_quadpoints[1,i], + DoubleQuadRule( + test_quadpoints[1,i], + trial_quadpoints[1,j])) =# + return DoubleQuadRule( + quadrature_data[1][1,i], + quadrature_data[2][1,j]) +end + + +function quadrule(op::Helmholtz3DOp, + test_refspace::RefSpace, trial_refspace::RefSpace, + i, test_element, j, trial_element, quadrature_data, + qs::DoubleNumQStrat) return DoubleQuadRule( quadrature_data[1][1,i], @@ -304,6 +388,23 @@ end +function (igd::Integrand{<:HH3DHyperSingularFDBIO})(x,y,f,g) + α = igd.operator.alpha + β = igd.operator.alpha + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1 / R + green = exp(-γ*R)*(i4pi*iR) + nx = normal(x) + ny = normal(y) + + _integrands(f,g) do fi, gi + α*dot(nx,ny)*gi.value*fi.value*green + β*dot(gi.curl,fi.curl)*green + end +end + function integrand(op::HH3DHyperSingularFDBIO, kernel, test_values, test_element, trial_values, trial_element) @@ -331,6 +432,38 @@ HH3DSingleLayerFDBIO(gamma) = HH3DSingleLayerFDBIO(one(gamma), gamma) regularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerReg(op.alpha, op.gamma) singularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerSng(op.alpha, op.gamma) +function (igd::Integrand{<:HH3DSingleLayerFDBIO})(x,y,f,g) + α = igd.operator.alpha + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1 / R + green = exp(-γ*R)*(i4pi*iR) + + αG = α * green + + _integrands(f,g) do fi, gi + dot(gi.value, αG*fi.value) + end +end + +function (igd::Integrand{<:HH3DSingleLayerReg})(x,y,f,g) + α = igd.operator.alpha + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1 / R + green = (expm1(-γ*R) + γ*R - 0.5*γ^2*R^2) / (4pi*R) + αG = α * green + + _integrands(f,g) do fi, gi + dot(gi.value, αG*fi.value) + end +end + + function integrand(op::Union{HH3DSingleLayerFDBIO,HH3DSingleLayerReg}, kernel, test_values, test_element, trial_values, trial_element) @@ -367,7 +500,26 @@ end -function integrand(biop::HH3DDoubleLayer, +HH3DDoubleLayerFDBIO(gamma) = HH3DDoubleLayerFDBIO(one(gamma), gamma) + + +function (igd::Integrand{<:HH3DDoubleLayerFDBIO})(x,y,f,g) + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1/R + green = exp(-γ*R)*(iR*i4pi) + gradgreen = -(γ + iR) * green * (iR * r) + n = normal(y) + fvalue = getvalue(f) + gvalue = getvalue(g) + + return _krondot(fvalue,gvalue) * dot(n, -gradgreen) +end + + +function integrand(biop::HH3DDoubleLayerFDBIO, kernel, fp, mp, fq, mq) nq = normal(mq) @@ -376,7 +528,24 @@ end -function integrand(biop::HH3DDoubleLayerTransposed, + HH3DDoubleLayerTransposedFDBIO(gamma) = HH3DDoubleLayerTransposedFDBIO(one(gamma), gamma) + +function (igd::Integrand{<:HH3DDoubleLayerTransposedFDBIO})(x,y,f,g) + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1/R + green = exp(-γ*R)*(iR*i4pi) + gradgreen = -(γ + iR) * green * (iR * r) + n = normal(x) + fvalue = getvalue(f) + gvalue = getvalue(g) + + return _krondot(fvalue,gvalue) * dot(n, gradgreen) +end + +function integrand(biop::HH3DDoubleLayerTransposedFDBIO, kernel, fp, mp, fq, mq) np = normal(mp) @@ -429,10 +598,10 @@ module Helmholtz3D Mod.HH3DPlaneWave(direction, wavenumber) doublelayer(;gamma=error("gamma missing"), alpha=one(gamma)) = - Mod.HH3DDoubleLayer(alpha, gamma) + Mod.HH3DDoubleLayerFDBIO(alpha, gamma) doublelayer_transposed(;gamma=error("gamma missing"), alpha=one(gamma)) = - Mod.HH3DDoubleLayerTransposed(alpha, gamma) + Mod.HH3DDoubleLayerTransposedFDBIO(alpha, gamma) end export Helmholtz3D From 0253e957d7cd00873af91897e0379ff3e6f41388 Mon Sep 17 00:00:00 2001 From: Paula Respondek Date: Thu, 4 May 2023 09:14:50 +0200 Subject: [PATCH 235/528] Fix HH3D SingleLayer Wilton Integral The singular terms used for the regular and singular part of the singlelayer were not the same, the terms in the regular part were adapted. --- src/helmholtz3d/hh3dops.jl | 41 ++++++++++++-------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index f7471b24..fc3ee692 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -124,9 +124,13 @@ function quadrule(op::HH3DSingleLayerFDBIO, tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 + dmin2 = floatmax(eltype(eltype(test_element.vertices))) + for t in test_element.vertices for s in trial_element.vertices - norm(t-s) < tol && (hits +=1) + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < tol) end end hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) @@ -135,12 +139,14 @@ function quadrule(op::HH3DSingleLayerFDBIO, test_quadpoints = qd.test_qp trial_quadpoints = qd.bsis_qp - - # hits != 0 && return WiltonSERule( - # test_quadpoints[1,i], - # DoubleQuadRule( - # test_quadpoints[1,i], - # trial_quadpoints[1,j])) + h2 = volume(trial_element) + xtol2 = 0.2 * 0.2 + k2 = abs2(op.gamma) + max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( + test_quadpoints[1,i], + DoubleQuadRule( + test_quadpoints[1,i], + trial_quadpoints[1,j])) return DoubleQuadRule( qd.test_qp[1,i], @@ -154,27 +160,6 @@ function quadrule(op::HH3DSingleLayerFDBIO, i, test_element, j, trial_element, qd, qs::DoubleNumQStrat) - - tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 - for t in test_element.vertices - for s in trial_element.vertices - norm(t-s) < tol && (hits +=1) - end end - - - # hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) - # hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) - # hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - - test_quadpoints = qd.test_qp - trial_quadpoints = qd.bsis_qp - - hits != 0 && return WiltonSERule( - test_quadpoints[1,i], - DoubleQuadRule( - test_quadpoints[1,i], - trial_quadpoints[1,j])) - return DoubleQuadRule( qd.test_qp[1,i], qd.bsis_qp[1,j]) From f9a0c91e8e540bc5f40babcc5a61bf714256c83f Mon Sep 17 00:00:00 2001 From: Paula Respondek Date: Thu, 4 May 2023 12:51:30 +0200 Subject: [PATCH 236/528] Fix error in hypersingular integrand --- src/helmholtz3d/hh3dops.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index fc3ee692..19e99e8f 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -375,7 +375,7 @@ end function (igd::Integrand{<:HH3DHyperSingularFDBIO})(x,y,f,g) α = igd.operator.alpha - β = igd.operator.alpha + β = igd.operator.beta γ = igd.operator.gamma r = cartesian(x) - cartesian(y) From d3cd4c7685df3bf80f89b6a8e1f5fbfe8703b85f Mon Sep 17 00:00:00 2001 From: azuccott Date: Mon, 15 May 2023 13:08:02 +0200 Subject: [PATCH 237/528] Update ncrossbdmlocal.jl --- src/bases/local/ncrossbdmlocal.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bases/local/ncrossbdmlocal.jl b/src/bases/local/ncrossbdmlocal.jl index b3dd7b85..808830df 100644 --- a/src/bases/local/ncrossbdmlocal.jl +++ b/src/bases/local/ncrossbdmlocal.jl @@ -1,6 +1,6 @@ struct NCrossBDMRefSpace{T} <: RefSpace{T,6} end -function (f::NCrossBDMRefSpace)(p) +function (f::NCrossBDMRefSpace{T})(p) where T u,v = parametric(p) n = normal(p) From 2f3a15a4464e02ab6177a4e721263725f2b2140e Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 18 May 2023 12:07:46 +0000 Subject: [PATCH 238/528] add quadstrat using only doublenum and sauterschwab rules --- src/BEAST.jl | 11 +++++-- src/quaddata.jl | 56 ++++++++++++++++++------------------ src/quadrature/quaddata.jl | 21 ++++++++++++++ src/quadrature/quadrule.jl | 26 +++++++++++++++++ src/quadrature/quadstrats.jl | 9 ++++++ 5 files changed, 92 insertions(+), 31 deletions(-) create mode 100644 src/quadrature/quaddata.jl create mode 100644 src/quadrature/quadrule.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 5d0eb99e..cbbf4277 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -171,9 +171,6 @@ include("bases/tensorbasis.jl") include("operator.jl") include("quadrature/quadstrats.jl") -include("quadrature/double_quadrature.jl") -include("quadrature/singularity_extraction.jl") -include("quadrature/sauterschwabints.jl") include("excitation.jl") include("localop.jl") @@ -182,6 +179,14 @@ include("identityop.jl") include("integralop.jl") include("interpolation.jl") include("quaddata.jl") + +include("quadrature/quaddata.jl") +include("quadrature/quadrule.jl") + +include("quadrature/double_quadrature.jl") +include("quadrature/singularity_extraction.jl") +include("quadrature/sauterschwabints.jl") + include("postproc.jl") include("postproc/segcurrents.jl") diff --git a/src/quaddata.jl b/src/quaddata.jl index 8abd281c..5432a322 100644 --- a/src/quaddata.jl +++ b/src/quaddata.jl @@ -32,34 +32,34 @@ points and weights, geometric quantities, etc. The type of the returned quadrature rule will help in deciding which method of `momintegrals` to dispatch to. """ -function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd) - # defines coincidence of points - dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) +# function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd) +# # defines coincidence of points +# dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) - # decides on whether to use singularity extraction - xtol = 0.2 - k = norm(op.gamma) +# # decides on whether to use singularity extraction +# xtol = 0.2 +# k = norm(op.gamma) - hits = 0 - xmin = xtol - for t in τ.vertices - for s in σ.vertices - d = norm(t-s) - xmin = min(xmin, k*d) - if d < dtol - hits +=1 - break - end - end - end +# hits = 0 +# xmin = xtol +# for t in τ.vertices +# for s in σ.vertices +# d = norm(t-s) +# xmin = min(xmin, k*d) +# if d < dtol +# hits +=1 +# break +# end +# end +# end - hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) - hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) - hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - xmin < xtol && return DoubleQuadRule( - qd.tpoints[2,i], - qd.bpoints[2,j],) - return DoubleQuadRule( - qd.tpoints[1,i], - qd.bpoints[1,j],) -end +# hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) +# hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) +# hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) +# xmin < xtol && return DoubleQuadRule( +# qd.tpoints[2,i], +# qd.bpoints[2,j],) +# return DoubleQuadRule( +# qd.tpoints[1,i], +# qd.bpoints[1,j],) +# end diff --git a/src/quadrature/quaddata.jl b/src/quadrature/quaddata.jl new file mode 100644 index 00000000..a18241fa --- /dev/null +++ b/src/quadrature/quaddata.jl @@ -0,0 +1,21 @@ +# This file contains kernel independent implementations of quaddata for +# the various quadrature strategies defined in quadstrats.jl + +function quaddata(op::IntegralOperator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_charts, trial_charts, qs::DoubleNumSauterQstrat) + + T = coordtype(test_charts[1]) + + tqd = quadpoints(test_local_space, test_charts, (qs.outer_rule,)) + bqd = quadpoints(trial_local_space, trial_charts, (qs.inner_rule,)) + + leg = ( + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_vert,0,1)), + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_edge,0,1)), + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_face,0,1)), + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_tetr,0,1)), + ) + + return (tpoints=tqd, bpoints=bqd, gausslegendre=leg) +end \ No newline at end of file diff --git a/src/quadrature/quadrule.jl b/src/quadrature/quadrule.jl new file mode 100644 index 00000000..b4eda1b7 --- /dev/null +++ b/src/quadrature/quadrule.jl @@ -0,0 +1,26 @@ +# This file contains kernel independent implementations of quadrule for +# the various quadrature strategies defined in quadstrats.jl + +function quadrule(op::IntegralOperator, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd, + qs::DoubleNumSauterQstrat) + + T = eltype(eltype(τ.vertices)) + hits = 0 + dtol = 1.0e3 * eps(T) + dmin2 = floatmax(T) + for t in τ.vertices + for s in σ.vertices + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < dtol) + end + end + + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) + hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) + hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j],) +end \ No newline at end of file diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index 43c10700..40d4bddb 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -11,6 +11,15 @@ struct DoubleNumWiltonSauterQStrat{R,S} sauter_schwab_common_vert::S end +struct DoubleNumSauterQstrat{R,S} + outer_rule::R + inner_rule::R + sauter_schwab_common_tetr::S + sauter_schwab_common_face::S + sauter_schwab_common_edge::S + sauter_schwab_common_vert::S +end + struct DoubleNumQStrat{R} outer_rule::R inner_rule::R From e1ad9272998f98b1319dcf6e748b878d87ab8894 Mon Sep 17 00:00:00 2001 From: Simon Adrian Date: Fri, 21 Apr 2023 18:59:11 +0200 Subject: [PATCH 239/528] Add scalartype function for HH3DLinearPotential --- src/helmholtz3d/hh3dexc.jl | 8 +++++++- src/maxwell/mwexc.jl | 4 ++++ test/test_hh3dexc.jl | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/helmholtz3d/hh3dexc.jl b/src/helmholtz3d/hh3dexc.jl index 534dd87e..2dd7582c 100644 --- a/src/helmholtz3d/hh3dexc.jl +++ b/src/helmholtz3d/hh3dexc.jl @@ -41,6 +41,8 @@ function (f::HH3DLinearPotential)(r) return a * dot(d, r) end +scalartype(f::HH3DLinearPotential{T}) where {T} = complex(T) + struct gradHH3DLinearPotential{T,P} direction::P amplitude::T @@ -124,10 +126,14 @@ end dot(::NormalVector, m::gradHH3DMonopole) = NormalDerivative(HH3DMonopole(m.position, m.wavenumber, m.amplitude)) -mutable struct DirichletTrace{F} <: Functional +mutable struct DirichletTrace{T,F} <: Functional field::F end +DirichletTrace(f::F) where {F} = DirichletTrace{scalartype(f), F}(f) +DirichletTrace{T}(f::F) where {T,F} = DirichletTrace{T,F}(f) +scalartype(s::DirichletTrace{T}) where {T} = T + function (ϕ::DirichletTrace)(p) F = ϕ.field x = cartesian(p) diff --git a/src/maxwell/mwexc.jl b/src/maxwell/mwexc.jl index 1bfba9f2..5563a308 100644 --- a/src/maxwell/mwexc.jl +++ b/src/maxwell/mwexc.jl @@ -59,6 +59,8 @@ function DipoleMW(l,o,k) DipoleMW{T,P}(l,o,k) end +scalartype(x::DipoleMW{T,P}) where {T,P} = promote_type(complex(T), eltype(P)) + mutable struct curlDipoleMW{T,P} <: Dipole location::P orientation::P @@ -71,6 +73,8 @@ function curlDipoleMW(l,o,k) curlDipoleMW{T,P}(l,o,k) end +scalartype(x::curlDipoleMW{T,P}) where {T,P} = promote_type(complex(T), eltype(P)) + """ dipolemw3d(;location, orientation, wavenumber) diff --git a/test/test_hh3dexc.jl b/test/test_hh3dexc.jl index a42fdf39..93e1b40b 100644 --- a/test/test_hh3dexc.jl +++ b/test/test_hh3dexc.jl @@ -22,6 +22,8 @@ for T in [Float32, Float64] gradlp = grad(lp) @test gradlp(point(T,1,1,0)) == point(T, 0, 2, 0) + γnlp = dot(BEAST.NormalVector(), gradlp) + import BEAST.∂n p = ∂n(f) From 818db183035e362f66aa66c56f2349c3f0ffb034 Mon Sep 17 00:00:00 2001 From: Simon Adrian Date: Thu, 18 May 2023 20:25:56 +0200 Subject: [PATCH 240/528] Fixup of lusolver Adapted a unit test --- src/solvers/lusolver.jl | 2 +- test/test_dipole.jl | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/solvers/lusolver.jl b/src/solvers/lusolver.jl index b521506f..7c5ee2e2 100644 --- a/src/solvers/lusolver.jl +++ b/src/solvers/lusolver.jl @@ -40,7 +40,7 @@ function solve(eq) Y = _spacedict_to_directproductspace(eq.trial_space_dict) b = assemble(rhs, X) - Z = assemble(lhs, X, X) + Z = assemble(lhs, X, Y) # b = assemble(rhs, test_space_dict) # Z = assemble(lhs, test_space_dict, trial_space_dict) # M = convert_to_dense(Z) diff --git a/test/test_dipole.jl b/test/test_dipole.jl index 7f886d4c..3decdff5 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -4,6 +4,7 @@ using CompScienceMeshes using BEAST using StaticArrays using LinearAlgebra +using BlockArrays for U in [Float32,Float64] @@ -103,11 +104,15 @@ for U in [Float32,Float64] @test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 - K_bc = Matrix(assemble(𝓚,Y,X)) - G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) - h_bc = η*Vector(assemble(𝒉,Y)) - M_bc = -U(0.5)*G_nxbc_rt + K_bc - j_BCMFIE = M_bc\h_bc + @hilbertspace s + @hilbertspace t + + MFIE_discretebilform = @discretise( + -U(0.5)*𝓝[t, s] + 𝓚[t, s] == η*𝒉[t], + s∈X, t∈Y + ) + + j_BCMFIE = solve(MFIE_discretebilform)[Block(1)] nf_E_BCMFIE = potential(MWSingleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(wavenumber=k), pts, j_BCMFIE, X) ./ η From 60917bbb89cafd341d86d66d7406379a1bf73d3d Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 22 May 2023 10:43:04 +0000 Subject: [PATCH 241/528] facilitate assembly of TD systems --- src/bases/basis.jl | 1 + src/bases/tensorbasis.jl | 10 +++++++++- src/solvers/solver.jl | 30 ++++++++++++++++++++++-------- src/timedomain/tdintegralop.jl | 4 ++-- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/bases/basis.jl b/src/bases/basis.jl index 1ce43370..fbe0934c 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -86,6 +86,7 @@ end cross(a::Space{T}, b::Space{T}) where {T} = DirectProductSpace{T,Space{T}}(Space{T}[a,b]) cross(a::DirectProductSpace{T}, b::Space{T}) where {T} = DirectProductSpace{T,Space{T}}([a.factors; b]) numfunctions(S::DirectProductSpace) = sum([numfunctions(s) for s in S.factors]) +Base.length(S::DirectProductSpace) = numfunctions(S) scalartype(s::DirectProductSpace{T}) where {T} = T geometry(x::DirectProductSpace) = weld(x.geo...) diff --git a/src/bases/tensorbasis.jl b/src/bases/tensorbasis.jl index c19990f5..ca9547eb 100644 --- a/src/bases/tensorbasis.jl +++ b/src/bases/tensorbasis.jl @@ -1,11 +1,19 @@ -mutable struct SpaceTimeBasis{S,T} +struct SpaceTimeBasis{S,T,U} <: Space{U} space::S time::T end +function SpaceTimeBasis(space, time) + S = typeof(space) + T = typeof(time) + + U = scalartype(space,time) + return SpaceTimeBasis{S,T,U}(space,time) +end + spatialbasis(s::SpaceTimeBasis) = s.space temporalbasis(s::SpaceTimeBasis) = s.time diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 73e2e403..c7d8e578 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -220,6 +220,8 @@ Base.eltype(x::SpaceTimeData{T}) where {T} = Vector{T} Base.size(x::SpaceTimeData) = (size(x.data)[1],) Base.getindex(x::SpaceTimeData, i::Int) = x.data[i,:] +td_assemble(dlf::DiscreteLinform) = td_assemble(dlf.linform, dlf.test_space_dict) + function td_assemble(lform::LinForm, test_space_dict) terms = lform.terms @@ -362,17 +364,27 @@ end # z = assemble(bf, test_space_dict, trial_space_dict) # end +td_assemble(dbf::DiscreteBilform; materialize=BEAST.assemble) = + td_assemble(dbf.bilform, dbf.test_space_dict, dbf.trial_space_dict; materialize) -function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict) +function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict; + materialize=BEAST.assemble) - lhterms = bilform.terms + X = _spacedict_to_directproductspace(test_space_dict) + Y = _spacedict_to_directproductspace(trial_space_dict) - M = zeros(Int, length(test_space_dict)) - N = zeros(Int, length(trial_space_dict)) + return td_assemble(bilform, X, Y) +end - for (p,x) in test_space_dict M[p]=numfunctions(spatialbasis(x)) end - for (p,x) in trial_space_dict N[p]=numfunctions(spatialbasis(x)) end +function td_assemble(bilform::BilForm, + test_space_dict::DirectProductSpace, + trial_space_dict::DirectProductSpace) + + lhterms = bilform.terms + + M = [length(fct.space) for fct in test_space_dict.factors] + N = [length(fct.space) for fct in trial_space_dict.factors] row_axis = BlockArrays.blockedrange(M) col_axis = BlockArrays.blockedrange(N) @@ -384,13 +396,15 @@ function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict) a = t.coeff * t.kernel m = t.test_id - x = test_space_dict[m] + # x = test_space_dict[m] + x = test_space_dict.factors[m] for op in reverse(t.test_ops) x = op[end](op[1:end-1]..., x) end n = t.trial_id - y = trial_space_dict[n] + # y = trial_space_dict[n] + y = trial_space_dict.factors[n] for op in reverse(t.trial_ops) y = op[end](op[1:end-1]..., y) end diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 2e64cc29..77a9c2e7 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -145,8 +145,8 @@ function assemble!(op::LinearCombinationOfOperators, tfs::SpaceTimeBasis, bfs::S end end -function assemble!(op::RetardedPotential, testST, trialST, store, - threading=Threading{:multi}; quadstrat=defaultquadstrat(op, testST, trialST)) +function assemble!(op::RetardedPotential, testST::Space, trialST::Space, store, + threading::Type{Threading{:multi}}=Threading{:multi}; quadstrat=defaultquadstrat(op, testST, trialST)) Y, S = spatialbasis(testST), temporalbasis(testST) X, R = spatialbasis(trialST), temporalbasis(trialST) From 6a8223217c059710b558116275e646ab0afad1ab Mon Sep 17 00:00:00 2001 From: azuccott Date: Mon, 22 May 2023 14:41:35 +0200 Subject: [PATCH 242/528] ncrossbdm base added this commit adds the ncross bdm base. now the base is defined on both two separated files but also in the bdm files two (local and space). This should be corrected soon --- src/bases/bdmdiv.jl | 61 +++++++++++++++++++++++++++++++ src/bases/local/bdmlocal.jl | 26 ++++++++++++- src/bases/local/ncrossbdmlocal.jl | 4 +- src/bases/ncrossbdmspace.jl | 2 +- src/identityop.jl | 2 +- 5 files changed, 90 insertions(+), 5 deletions(-) diff --git a/src/bases/bdmdiv.jl b/src/bases/bdmdiv.jl index 97736da7..fd671514 100644 --- a/src/bases/bdmdiv.jl +++ b/src/bases/bdmdiv.jl @@ -52,3 +52,64 @@ function brezzidouglasmarini(mesh, cellpairs::Array{Int,2}) BDMBasis(mesh, fns, pos) end + + + +divergence(X::BDMBasis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) + +#NCrossBDMBasis + +struct NCrossBDMBasis{T,M,P} <: Space{T} + geo::M + fns::Vector{Vector{Shape{T}}} + pos::Vector{P} +end + +NCrossBDMBasis(geo, fns) = NCrossBDMBasis(geo, fns, Vector{vertextype(geo)}(undef,length(fns))) + +refspace(s::NCrossBDMBasis{T}) where {T} = NCrossBDMRefSpace{T}() +#subset(s::NCrossBDMBasis,I) = NCrossBDMBasis(s.geo, s.fns[I], s.pos[I]) + +function ncrossbdm(mesh) + edges = skeleton(mesh, 1) + cps = cellpairs(mesh, edges, dropjunctionpair=true) + ids = findall(x -> x>0, cps[2,:]) + ncrossbdm(mesh, cps[:,ids]) +end + +function ncrossbdm(mesh, cellpairs::Array{Int,2}) + + @warn "brezzidouglasmarini(mesh, cellpairs) assumes mesh is oriented" + + @assert size(cellpairs,1) == 2 + + T = coordtype(mesh) + P = vertextype(mesh) + + S = Shape{T} + F = Vector{Shape{T}} + + nf = 2*size(cellpairs,2) + fns = Vector{F}(undef, nf) + pos = Vector{P}(undef, nf) + + for i in axes(cellpairs)[2] + c1, c2 = cellpairs[:,i] + cell1 = cells(mesh)[c1] + cell2 = cells(mesh)[c2] + e1, e2 = getcommonedge(cell1, cell2) + @assert e1*e2 < 0 + e1, e2 = abs(e1), abs(e2) + fns[2*(i-1)+1] = [ S(c1, 2*(e1-1)+1 ,+1.0), S(c2, 2*(e2-1)+2,-1.0)] + fns[2*(i-1)+2] = [ S(c1, 2*(e1-1)+2 ,+1.0), S(c2, 2*(e2-1)+1,-1.0)] + + v1 = cell1[mod1(e1+1,3)] + v2 = cell1[mod1(e1+2,3)] + edge = simplex(mesh.vertices[[v1,v2]]...) + cntr = cartesian(center(edge)) + pos[2*(i-1)+1] = cntr + pos[2*(i-1)+2] = cntr + end + + NCrossBDMBasis(mesh, fns, pos) +end \ No newline at end of file diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index 69c29c5c..a11e47ed 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -19,7 +19,7 @@ function (f::BDMRefSpace)(p) (value=v*tv/j, divergence=d),] end - +divergence(ref::BDMRefSpace, sh, el) = Shape(sh.cellid, 1, sh.coeff/(2*volume(el))) const _vert_perms_bdm = [ (1,2,3), @@ -45,3 +45,27 @@ end dimtype(::BDMRefSpace, ::CompScienceMeshes.Simplex{U,2}) where {U} = Val{6} +#ncrossbdm + +struct NCrossBDMRefSpace{T} <: RefSpace{T,6} end + +function (f::NCrossBDMRefSpace{T})(p) where T + + u,v = parametric(p) + n = normal(p) + tu = tangents(p,1) + tv = tangents(p,2) + + j = jacobian(p) + d = 1/j + + return @SVector[ + (value= n × (-v*tu+v*tv)/j, curl=d), + (value= n × ((u+v-1)*tu)/j, curl=d), + (value= n × ((u+v-1)*tv) /j, curl=d), + (value= n × (u*tu-u*tv)/j, curl=d), + (value= n × (u*tu)/j, curl=d), + (value= n × (v*tv)/j, curl=d),] +end + +#dimtype(::NCrossBDMRefSpace, ::CompScienceMeshes.Simplex{U,2}) where {U} = Val{6} \ No newline at end of file diff --git a/src/bases/local/ncrossbdmlocal.jl b/src/bases/local/ncrossbdmlocal.jl index 808830df..b7dc7504 100644 --- a/src/bases/local/ncrossbdmlocal.jl +++ b/src/bases/local/ncrossbdmlocal.jl @@ -12,8 +12,8 @@ function (f::NCrossBDMRefSpace{T})(p) where T return @SVector[ (value= n × (-v*tu+v*tv)/j, curl=d), - (value= n × (u+v-1)*tu/j, curl=d), - (value= n × (u+v-1)*tv/j, curl=d), + (value= n × ((u+v-1)*tu)/j, curl=d), + (value= n × ((u+v-1)*tv) /j, curl=d), (value= n × (u*tu-u*tv)/j, curl=d), (value= n × (u*tu)/j, curl=d), (value= n × (v*tv)/j, curl=d),] diff --git a/src/bases/ncrossbdmspace.jl b/src/bases/ncrossbdmspace.jl index c93ae949..c5ed0364 100644 --- a/src/bases/ncrossbdmspace.jl +++ b/src/bases/ncrossbdmspace.jl @@ -4,7 +4,7 @@ struct NCrossBDMBasis{T,M,P} <: Space{T} pos::Vector{P} end -NCrossBDMBasis(geo, fns) = BDMBasis(geo, fns, Vector{vertextype(geo)}(undef,length(fns))) +NCrossBDMBasis(geo, fns) = NCrossBDMBasis(geo, fns, Vector{vertextype(geo)}(undef,length(fns))) refspace(s::NCrossBDMBasis{T}) where {T} = NCrossBDMRefSpace{T}() diff --git a/src/identityop.jl b/src/identityop.jl index 1e6ba7c6..dba41e2e 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -20,7 +20,7 @@ function _alloc_workspace(qd, g, f, tels, bels) A = Vector{typeof(a)}(undef,length(qd)) end -const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace, BDMRefSpace} +const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace, BDMRefSpace, NCrossBDMRefSpace} defaultquadstrat(::LocalOperator, ::LinearRefSpaceTriangle, ::LinearRefSpaceTriangle) = SingleNumQStrat(6) function quaddata(op::LocalOperator, g::LinearRefSpaceTriangle, f::LinearRefSpaceTriangle, tels, bels, qs::SingleNumQStrat) From 7e88766dda889977227f1857d1cf3c2d2972c436 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 24 May 2023 11:26:23 +0000 Subject: [PATCH 243/528] visualisation utils for basis functions --- Project.toml | 1 + src/BEAST.jl | 2 ++ src/bases/basis.jl | 38 +++++++++++++++++++++++++++++++ src/maxwell/timedomain/mwtdops.jl | 2 +- src/utils/plotlyglue.jl | 18 +++++++++++++++ 5 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/utils/plotlyglue.jl diff --git a/Project.toml b/Project.toml index 2bf83c6a..d85edcdf 100644 --- a/Project.toml +++ b/Project.toml @@ -20,6 +20,7 @@ LiftedMaps = "d22a30c1-52ac-4762-a8c9-5838452405e0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" NestedUnitRanges = "032820ab-dc03-4b49-91f4-7d58d4da98b3" +Requires = "ae029012-a4dd-5104-9daa-d747884805df" SauterSchwab3D = "0a13313b-1c00-422e-8263-562364ed9544" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" diff --git a/src/BEAST.jl b/src/BEAST.jl index cbbf4277..2d66bcee 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -245,6 +245,8 @@ include("solvers/solver.jl") include("solvers/lusolver.jl") include("solvers/itsolver.jl") +include("utils/plotlyglue.jl") + diff --git a/src/bases/basis.jl b/src/bases/basis.jl index fbe0934c..8b0dbcce 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -296,3 +296,41 @@ function addf!(fn::Vector{<:Shape}, x::Vector, space::Space, idcs::Vector{Int}) end end end + +function support(s::BEAST.AbstractSpace, index::Int) + geo = geometry(s) + s1 = subset(s,[index]) + charts, ad, activecells = BEAST.assemblydata(s1) + return geo[activecells] +end + +function functionvals(s::BEAST.Space, index::Int, n=3) + + s1 = subset(s,[index]) + charts, ad, a2g = BEAST.assemblydata(s1) + support = geometry(s)[a2g] + + vals = Any[] + ctrs = Any[] + refs = refspace(s) + for (p,ch) in enumerate(charts) + for i in 1:n-1 + for j in 1:n-1 + i+j < n || continue + val = zero(BEAST.valuetype(refs, typeof(ch))) + ct = CompScienceMeshes.neighborhood(ch, (i/n,j/n)) + fx = refs(ct) + for r in eachindex(fx) + for (m,a) in ad[p,r] + @assert m == 1 + val += a * fx[r].value + end + end + push!(ctrs, cartesian(ct)) + push!(vals, val) + end + end + end + + return ctrs, vals +end diff --git a/src/maxwell/timedomain/mwtdops.jl b/src/maxwell/timedomain/mwtdops.jl index 0c91094e..962e577f 100644 --- a/src/maxwell/timedomain/mwtdops.jl +++ b/src/maxwell/timedomain/mwtdops.jl @@ -101,7 +101,7 @@ end function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, store, - threading=Threading{:multi}; quadstrat=defaultquadstrat(dl,W,V)) + threading::Type{Threading{:multi}}; quadstrat=defaultquadstrat(dl,W,V)) X, T = spatialbasis(W), temporalbasis(W) Y, U = spatialbasis(V), temporalbasis(V) diff --git a/src/utils/plotlyglue.jl b/src/utils/plotlyglue.jl new file mode 100644 index 00000000..61aa84eb --- /dev/null +++ b/src/utils/plotlyglue.jl @@ -0,0 +1,18 @@ +using Requires + +export functionvals + +function __init__() + @require PlotlyJS="f0f68f2c-4968-5e81-91da-67840de0976a" begin + @eval function PlotlyJS.cone(xyz::Vector, uvw::Vector;kwargs...) + PlotlyJS.cone(; + x=getindex.(xyz,1), + y=getindex.(xyz,2), + z=getindex.(xyz,3), + u=getindex.(uvw,1), + v=getindex.(uvw,2), + w=getindex.(uvw,3), + kwargs...) + end + end +end \ No newline at end of file From 9e6226ada12e53fb4995c3bb387fbdf01064a401 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Thu, 25 May 2023 00:39:48 +0000 Subject: [PATCH 244/528] CompatHelper: add new compat entry for Requires at version 1, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index d85edcdf..bbe15231 100644 --- a/Project.toml +++ b/Project.toml @@ -45,6 +45,7 @@ IterativeSolvers = "0.9" LiftedMaps = "0.5.1" LinearMaps = "3.7 - 3.9" NestedUnitRanges = "0.2" +Requires = "1" SauterSchwab3D = "0.1" SauterSchwabQuadrature = "2.2.0" SparseMatrixDicts = "0.2" From f999f019344131a326c37ef49dc9b95895611a4d Mon Sep 17 00:00:00 2001 From: azuccott Date: Thu, 1 Jun 2023 10:31:01 +0200 Subject: [PATCH 245/528] ncrossbdm files now included. ncrossbdm files now included. tests still must be commited --- Project.toml | 1 + src/BEAST.jl | 2 ++ src/bases/bdmdiv.jl | 4 ++-- src/bases/local/bdmlocal.jl | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Project.toml b/Project.toml index 50977b3e..9a413c3e 100644 --- a/Project.toml +++ b/Project.toml @@ -18,6 +18,7 @@ IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LiftedMaps = "d22a30c1-52ac-4762-a8c9-5838452405e0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" +Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45" SauterSchwab3D = "0a13313b-1c00-422e-8263-562364ed9544" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" diff --git a/src/BEAST.jl b/src/BEAST.jl index d59803d3..6de1a7fe 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -143,6 +143,7 @@ include("bases/local/laglocal.jl") include("bases/local/rtlocal.jl") include("bases/local/ndlocal.jl") include("bases/local/bdmlocal.jl") +include("bases/local/ncrossbdmlocal.jl") include("bases/local/ndlcclocal.jl") include("bases/local/ndlcdlocal.jl") include("bases/local/bdm3dlocal.jl") @@ -153,6 +154,7 @@ include("bases/rtxspace.jl") include("bases/bcspace.jl") include("bases/ndspace.jl") include("bases/bdmdiv.jl") +include("bases/ncrossbdmspace.jl") include("bases/ndlccspace.jl") include("bases/ndlcdspace.jl") include("bases/dual3d.jl") diff --git a/src/bases/bdmdiv.jl b/src/bases/bdmdiv.jl index fd671514..cae34f36 100644 --- a/src/bases/bdmdiv.jl +++ b/src/bases/bdmdiv.jl @@ -58,7 +58,7 @@ end divergence(X::BDMBasis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) #NCrossBDMBasis - +#= struct NCrossBDMBasis{T,M,P} <: Space{T} geo::M fns::Vector{Vector{Shape{T}}} @@ -112,4 +112,4 @@ function ncrossbdm(mesh, cellpairs::Array{Int,2}) end NCrossBDMBasis(mesh, fns, pos) -end \ No newline at end of file +end=# \ No newline at end of file diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index a11e47ed..5d7240df 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -46,7 +46,7 @@ end dimtype(::BDMRefSpace, ::CompScienceMeshes.Simplex{U,2}) where {U} = Val{6} #ncrossbdm - +#= struct NCrossBDMRefSpace{T} <: RefSpace{T,6} end function (f::NCrossBDMRefSpace{T})(p) where T @@ -66,6 +66,6 @@ function (f::NCrossBDMRefSpace{T})(p) where T (value= n × (u*tu-u*tv)/j, curl=d), (value= n × (u*tu)/j, curl=d), (value= n × (v*tv)/j, curl=d),] -end +end=# #dimtype(::NCrossBDMRefSpace, ::CompScienceMeshes.Simplex{U,2}) where {U} = Val{6} \ No newline at end of file From 353b54c8154abd904b9ddb006f5f7e3e5bb7b3d9 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 2 Jun 2023 10:57:17 +0000 Subject: [PATCH 246/528] getindex for DirectProductSpace --- src/bases/basis.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bases/basis.jl b/src/bases/basis.jl index 8b0dbcce..40ddedf0 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -56,7 +56,7 @@ geometry(s::Space) = s.geo basisfunction(s::Space, i) = s.fns[i] numfunctions(space::Space) = length(space.fns) -mutable struct DirectProductSpace{T,S<:AbstractSpace} <: AbstractSpace +struct DirectProductSpace{T,S<:AbstractSpace} <: AbstractSpace factors::Vector{S} end @@ -66,6 +66,8 @@ function DirectProductSpace(factors::Vector{S}) where {S<:AbstractSpace} return DirectProductSpace{T,S}(factors) end +Base.getindex(dps::DirectProductSpace, i) = dps.factors[i] + defaultquadstrat(op, tfs::DirectProductSpace, bfs::DirectProductSpace) = defaultquadstrat(op, tfs.factors[1], bfs.factors[1]) defaultquadstrat(op, tfs::Space, bfs::DirectProductSpace) = defaultquadstrat(op, tfs, bfs.factors[1]) defaultquadstrat(op, tfs::DirectProductSpace, bfs::Space) = defaultquadstrat(op, tfs.factors[1], bfs) From 207c7b1fc19d417741ced031e0e5c392950a8bdc Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 2 Jun 2023 13:14:44 +0000 Subject: [PATCH 247/528] quadsrtat/data/rule for near/far potentials --- examples/pmchwt.jl | 19 ++++++++++------- src/BEAST.jl | 1 + src/helmholtz3d/hh3dnear.jl | 6 +++--- src/maxwell/farfield.jl | 15 ++++++++++--- src/maxwell/nearfield.jl | 6 ++++-- src/postproc.jl | 42 +++++++++++++++++-------------------- src/postproc/farfield.jl | 6 ++++++ 7 files changed, 56 insertions(+), 39 deletions(-) create mode 100644 src/postproc/farfield.jl diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index f6ad50a9..6148a8ea 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -52,6 +52,17 @@ ffm = potential(MWFarField3D(κ*im, η), ffpoints, u[m], X) ffj = potential(MWFarField3D(κ*im, η), ffpoints, u[j], X) ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) +# Compare the far field and the field far +using Plots +ffradius = 100.0 +E_far, H_far = nearfield(u[m],u[j],X,X,κ,η, ffradius .* ffpoints) +nxE_far = cross.(ffpoints, E_far) * (4π*ffradius) / exp(-im*κ*ffradius) +Et_far = -cross.(ffpoints, nxE_far) + +Plots.plot() +Plots.plot!(Θ, norm.(ff)/η ,label="far field") +Plots.scatter!(Θ, norm.(Et_far), label="field far") + using Plots Plots.plot(xlabel="theta") Plots.plot!(Θ,norm.(ff),label="far field",title="PMCHWT") @@ -109,12 +120,4 @@ Plots.plot(real.(getindex.(E_tot[:,51],1))) Plots.plot(real.(getindex.(H_tot[:,51],2))) -# Compare the far field and the field far -ffradius = 100.0 -E_far, H_far = nearfield(u[m],u[j],X,X,κ,η, ffradius .* ffpoints) -nxE_far = cross.(ffpoints, E_far) * (4π*ffradius) / exp(-im*κ*ffradius) -Et_far = -cross.(ffpoints, nxE_far) -Plots.plot() -Plots.plot!(Θ, norm.(ff),label="far field") -Plots.scatter!(Θ, norm.(Et_far), label="field far") diff --git a/src/BEAST.jl b/src/BEAST.jl index 2d66bcee..1e7e9c90 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -189,6 +189,7 @@ include("quadrature/sauterschwabints.jl") include("postproc.jl") include("postproc/segcurrents.jl") +include("postproc/farfield.jl") include("timedomain/tdintegralop.jl") include("timedomain/tdexcitation.jl") diff --git a/src/helmholtz3d/hh3dnear.jl b/src/helmholtz3d/hh3dnear.jl index 3ec763e5..fdb7062e 100644 --- a/src/helmholtz3d/hh3dnear.jl +++ b/src/helmholtz3d/hh3dnear.jl @@ -48,9 +48,9 @@ function HH3DDoubleLayerTransposedNear(;wavenumber=error("wavenumber is a requir end HH3DNear = Union{HH3DSingleLayerNear, HH3DDoubleLayerNear, HH3DDoubleLayerTransposedNear, HH3DHyperSingularNear} - -quaddata(op::HH3DNear,rs,els) = quadpoints(rs,els,(3,)) -quadrule(op::HH3DNear,refspace,p,y,q,el,qdata) = qdata[1,q] +defaultquadstrat(op::HH3DNear, basis) = SingleNumQStrat(3) +quaddata(op::HH3DNear,rs,els,qs::SingleNumQStrat) = quadpoints(rs,els,(qs.quad_rule,)) +quadrule(op::HH3DNear,refspace,p,y,q,el,qdata,qs::SingleNumQStrat) = qdata[1,q] function kernelvals(op::HH3DNear,y,p) diff --git a/src/maxwell/farfield.jl b/src/maxwell/farfield.jl index d5e41e4e..e97f0c26 100644 --- a/src/maxwell/farfield.jl +++ b/src/maxwell/farfield.jl @@ -1,4 +1,4 @@ -abstract type MWFarField end +abstract type MWFarField <: FarField end """ Operator to compute the far field of a current distribution. In particular, given the current distribution ``j`` this operator allows for the computation of @@ -78,8 +78,8 @@ end MWDoubleLayerFarField3D(op::MWDoubleLayer3D{T}) where {T} = MWDoubleLayerFarField3D(op.gamma, 1.0) -quaddata(op::MWFarField,rs,els) = quadpoints(rs,els,(3,)) -quadrule(op::MWFarField,refspace,p,y,q,el,qdata) = qdata[1,q] +# quaddata(op::MWFarField,rs,els) = quadpoints(rs,els,(3,)) +# quadrule(op::MWFarField,refspace,p,y,q,el,qdata) = qdata[1,q] kernelvals(op::MWFarField,y,p) = exp(op.gamma*dot(y,cartesian(p))) function integrand(op::MWFarField3D,krn,y,f,p) @@ -89,3 +89,12 @@ end function integrand(op::MWDoubleLayerFarField3D,krn,y,f,p) op.waveimpedance*(y × (krn * f[1])) end + +struct MWFarField3DDropConstant{K, U} <: MWFarField + gamma::K + coeff::U +end +kernelvals(op::MWFarField3DDropConstant,y,p) = expm1(op.gamma*dot(y,cartesian(p))) +function integrand(op::MWFarField3DDropConstant,krn,y,f,p) + op.coeff*(y × (krn * f[1])) × y +end \ No newline at end of file diff --git a/src/maxwell/nearfield.jl b/src/maxwell/nearfield.jl index 44f047f8..085a05b3 100644 --- a/src/maxwell/nearfield.jl +++ b/src/maxwell/nearfield.jl @@ -82,8 +82,10 @@ end MWDoubleLayerField3D(op::MWDoubleLayer3D) = MWDoubleLayerField3D(op.gamma) const MWField3D = Union{MWSingleLayerField3D,MWDoubleLayerField3D} -quaddata(op::MWField3D,rs,els) = quadpoints(rs,els,(2,)) -quadrule(op::MWField3D,refspace,p,y,q,el,qdata) = qdata[1,q] +defaultquadstrat(op::MWField3D, basis) = SingleNumQStrat(2) +quaddata(op::MWField3D,rs,els,qs::SingleNumQStrat) = quadpoints(rs,els,(qs.quad_rule,)) +quadrule(op::MWField3D,refspace,p,y,q,el,qdata,qs::SingleNumQStrat) = qdata[1,q] + function kernelvals(op::MWField3D,y,p) γ = op.gamma diff --git a/src/postproc.jl b/src/postproc.jl index 37ee64eb..961ad2e9 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -127,13 +127,13 @@ function facecurrents(u, X::DirectProductSpace) fcr, m end -function potential(op, points, coeffs, basis; type=SVector{3,ComplexF64}) - # T = SVector{3,ComplexF64} - # T = SVector{3,eltype(coeffs)} - T = type - ff = zeros(T, size(points)) +function potential(op, points, coeffs, basis; + type=SVector{3,ComplexF64}, + quadstrat=defaultquadstrat(op, basis)) + + ff = zeros(type, size(points)) store(v,m,n) = (ff[m] += v*coeffs[n]) - potential!(store, op, points, basis, type=T) + potential!(store, op, points, basis; type, quadstrat) return ff end @@ -145,39 +145,35 @@ function potential(op, points,coeffs, basis::SpaceTimeBasis) return ff end -function potential(op, points, coeffs, space::DirectProductSpace; type=SVector{3,ComplexF64}) - # T = SVector{3,ComplexF64} - # T = SVector{3,eltype(coeffs)} - T = type - ff = zeros(T, size(points)) - # @show size(ff) +function potential(op, points, coeffs, space::DirectProductSpace; + type=SVector{3,ComplexF64}, + quadstrat=defaultquadstrat(op,space)) + ff = zeros(type, size(points)) @assert length(coeffs) == numfunctions(space) offset = 0 for fct in space.factors store(v,m,n) = (ff[m] += v*coeffs[offset+n]) - potential!(store, op, points, fct, type=T) + potential!(store, op, points, fct; type, quadstrat) offset += numfunctions(fct) end ff end -function potential!(store, op, points, basis; type=SVector{3,ComplexF64}) +function potential!(store, op, points, basis; + type=SVector{3,ComplexF64}, + quadstrat=defaultquadstrat(op, basis)) - # T = SVector{3,ComplexF64} - T = type - # @show T - z = zeros(T,length(points)) + z = zeros(type,length(points)) els, ad = assemblydata(basis) rs = refspace(basis) - zlocal = Array{T}(undef,numfunctions(rs)) - qdata = quaddata(op,rs,els) + zlocal = Array{type}(undef,numfunctions(rs)) + qdata = quaddata(op,rs,els,quadstrat) - #println("Computing nearfield.") print("dots out of 10: ") todo, done, pctg = length(points), 0, 0 @@ -185,8 +181,8 @@ function potential!(store, op, points, basis; type=SVector{3,ComplexF64}) for (p,y) in enumerate(points) for (q,el) in enumerate(els) - fill!(zlocal,zero(T)) - qr = quadrule(op,rs,p,y,q,el,qdata) + fill!(zlocal,zero(type)) + qr = quadrule(op,rs,p,y,q,el,qdata,quadstrat) farfieldlocal!(zlocal,op,rs,y,el,qr) # assemble from local contributions diff --git a/src/postproc/farfield.jl b/src/postproc/farfield.jl new file mode 100644 index 00000000..141be8c2 --- /dev/null +++ b/src/postproc/farfield.jl @@ -0,0 +1,6 @@ +abstract type FarField end + +defaultquadstrat(op::FarField, basis) = SingleNumQStrat(3) + +quaddata(op::FarField,rs,els,qs::SingleNumQStrat) = quadpoints(rs,els,(qs.quad_rule,)) +quadrule(op::FarField,refspace,p,y,q,el,qdata,qs::SingleNumQStrat) = qdata[1,q] \ No newline at end of file From 2fc6e09084233e66d77e7d0b8066acfef7cd4b5d Mon Sep 17 00:00:00 2001 From: azuccott Date: Mon, 5 Jun 2023 15:26:17 +0200 Subject: [PATCH 248/528] Create test_ncrossbdm.jl --- test/test_ncrossbdm.jl | 47 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 test/test_ncrossbdm.jl diff --git a/test/test_ncrossbdm.jl b/test/test_ncrossbdm.jl new file mode 100644 index 00000000..bb6c9f53 --- /dev/null +++ b/test/test_ncrossbdm.jl @@ -0,0 +1,47 @@ +using Test +using BEAST +using CompScienceMeshes +#testing local value in the center of a triangle +for T in [Float64] + for j in [1,2,3,4,5,6] + local o, x, y, z = euclidianbasis(3,T) + triang = simplex(x,y,o) + ctr = center(triang) + n = normal(triang) + oref = BEAST.NCrossBDMRefSpace{T}() + oref2= BEAST.BDMRefSpace{T}() + oshp=BEAST.Shape(1,j,1.0) + oshp2=BEAST.Shape(1,j,1.0) + + o1 = oref(ctr)[oshp.refid].value * oshp.coeff + o2=n × oref2(ctr)[oshp2.refid].value * oshp2.coeff + @test o1 ≈ o2 + end +end + +#testing their connectivity on a mesh + +m=meshsphere(radius=1,h=1.0) +nodes = skeleton(m,0) +edges = skeleton(m,1) + + +Z = BEAST.brezzidouglasmarini(m) +NZ = BEAST.ncrossbdm(m) +for i=(1,11,36,52,111) + for j=(1,2) + @test Z.fns[i][j].coeff ≈ NZ.fns[i][j].coeff + @test Z.fns[i][j].refid ≈ NZ.fns[i][j].refid + @test Z.fns[i][j].cellid ≈ NZ.fns[i][j].cellid + end +end + +#testing the values on a mesh through Gram matrices + +Id = BEAST.Identity() +qs = BEAST.SingleNumQStrat(8) + +Gzz= assemble(Id,Z,Z,quadstrat=qs) +Gnznz= assemble(Id,NZ,NZ,quadstrat=qs) +@test Gzz ≈ Gnznz + From 3d4cccc0bec95526e7fc4ed4f36ded6e1494537c Mon Sep 17 00:00:00 2001 From: azuccott Date: Mon, 5 Jun 2023 15:33:54 +0200 Subject: [PATCH 249/528] Update Project.toml --- Project.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Project.toml b/Project.toml index 9a413c3e..50977b3e 100644 --- a/Project.toml +++ b/Project.toml @@ -18,7 +18,6 @@ IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LiftedMaps = "d22a30c1-52ac-4762-a8c9-5838452405e0" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" -Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45" SauterSchwab3D = "0a13313b-1c00-422e-8263-562364ed9544" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" From c9819f8e7ef4dc320cce3edc24cb74df925c6289 Mon Sep 17 00:00:00 2001 From: azuccott Date: Mon, 5 Jun 2023 15:38:43 +0200 Subject: [PATCH 250/528] deleting comments --- src/bases/bdmdiv.jl | 56 ------------------------------------- src/bases/local/bdmlocal.jl | 24 ---------------- 2 files changed, 80 deletions(-) diff --git a/src/bases/bdmdiv.jl b/src/bases/bdmdiv.jl index cae34f36..7865825d 100644 --- a/src/bases/bdmdiv.jl +++ b/src/bases/bdmdiv.jl @@ -57,59 +57,3 @@ end divergence(X::BDMBasis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) -#NCrossBDMBasis -#= -struct NCrossBDMBasis{T,M,P} <: Space{T} - geo::M - fns::Vector{Vector{Shape{T}}} - pos::Vector{P} -end - -NCrossBDMBasis(geo, fns) = NCrossBDMBasis(geo, fns, Vector{vertextype(geo)}(undef,length(fns))) - -refspace(s::NCrossBDMBasis{T}) where {T} = NCrossBDMRefSpace{T}() -#subset(s::NCrossBDMBasis,I) = NCrossBDMBasis(s.geo, s.fns[I], s.pos[I]) - -function ncrossbdm(mesh) - edges = skeleton(mesh, 1) - cps = cellpairs(mesh, edges, dropjunctionpair=true) - ids = findall(x -> x>0, cps[2,:]) - ncrossbdm(mesh, cps[:,ids]) -end - -function ncrossbdm(mesh, cellpairs::Array{Int,2}) - - @warn "brezzidouglasmarini(mesh, cellpairs) assumes mesh is oriented" - - @assert size(cellpairs,1) == 2 - - T = coordtype(mesh) - P = vertextype(mesh) - - S = Shape{T} - F = Vector{Shape{T}} - - nf = 2*size(cellpairs,2) - fns = Vector{F}(undef, nf) - pos = Vector{P}(undef, nf) - - for i in axes(cellpairs)[2] - c1, c2 = cellpairs[:,i] - cell1 = cells(mesh)[c1] - cell2 = cells(mesh)[c2] - e1, e2 = getcommonedge(cell1, cell2) - @assert e1*e2 < 0 - e1, e2 = abs(e1), abs(e2) - fns[2*(i-1)+1] = [ S(c1, 2*(e1-1)+1 ,+1.0), S(c2, 2*(e2-1)+2,-1.0)] - fns[2*(i-1)+2] = [ S(c1, 2*(e1-1)+2 ,+1.0), S(c2, 2*(e2-1)+1,-1.0)] - - v1 = cell1[mod1(e1+1,3)] - v2 = cell1[mod1(e1+2,3)] - edge = simplex(mesh.vertices[[v1,v2]]...) - cntr = cartesian(center(edge)) - pos[2*(i-1)+1] = cntr - pos[2*(i-1)+2] = cntr - end - - NCrossBDMBasis(mesh, fns, pos) -end=# \ No newline at end of file diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index 5d7240df..fa4b66c9 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -45,27 +45,3 @@ end dimtype(::BDMRefSpace, ::CompScienceMeshes.Simplex{U,2}) where {U} = Val{6} -#ncrossbdm -#= -struct NCrossBDMRefSpace{T} <: RefSpace{T,6} end - -function (f::NCrossBDMRefSpace{T})(p) where T - - u,v = parametric(p) - n = normal(p) - tu = tangents(p,1) - tv = tangents(p,2) - - j = jacobian(p) - d = 1/j - - return @SVector[ - (value= n × (-v*tu+v*tv)/j, curl=d), - (value= n × ((u+v-1)*tu)/j, curl=d), - (value= n × ((u+v-1)*tv) /j, curl=d), - (value= n × (u*tu-u*tv)/j, curl=d), - (value= n × (u*tu)/j, curl=d), - (value= n × (v*tv)/j, curl=d),] -end=# - -#dimtype(::NCrossBDMRefSpace, ::CompScienceMeshes.Simplex{U,2}) where {U} = Val{6} \ No newline at end of file From ab2dc497f781e064e5a798feaa6875622084ef1b Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 6 Jun 2023 09:12:20 +0000 Subject: [PATCH 251/528] itsolvers allows passing both abstol and reltol --- src/postproc/farfield.jl | 1 + src/solvers/itsolver.jl | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/postproc/farfield.jl b/src/postproc/farfield.jl index 141be8c2..d7af91f8 100644 --- a/src/postproc/farfield.jl +++ b/src/postproc/farfield.jl @@ -1,6 +1,7 @@ abstract type FarField end defaultquadstrat(op::FarField, basis) = SingleNumQStrat(3) +defaultquadstrat(op::FarField, basis::DirectProductSpace) = defaultquadstrat(op, basis.factors[1]) quaddata(op::FarField,rs,els,qs::SingleNumQStrat) = quadpoints(rs,els,(qs.quad_rule,)) quadrule(op::FarField,refspace,p,y,q,el,qdata,qs::SingleNumQStrat) = qdata[1,q] \ No newline at end of file diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index e5ac9599..e3be55ea 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -30,18 +30,18 @@ end operator(solver::GMRESSolver) = solver.linear_operator -function solve(solver::GMRESSolver, b) +function solve(solver::GMRESSolver, b; abstol=zero(real(eltype(b))), reltol=solver.tol) T = promote_type(eltype(solver), eltype(b)) x = similar(Array{T}, axes(solver)[2]) fill!(x,0) - x, ch = solve!(x, solver, b) + x, ch = solve!(x, solver, b; abstol, reltol) end -function solve!(x, solver::GMRESSolver, b) +function solve!(x, solver::GMRESSolver, b; abstol=zero(real(eltype(b))), reltol=solver.tol) op = operator(solver) x, ch = IterativeSolvers.gmres!(x, op, b, log=true, maxiter=solver.maxiter, - restart=solver.restart, reltol=solver.tol, verbose=solver.verbose) + restart=solver.restart, reltol=reltol, abstol=abstol, verbose=solver.verbose) return x, ch end From f0ff5d0f3580aef1ed40b9e43c754ad9ca2c3aa1 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 7 Jun 2023 10:47:30 +0000 Subject: [PATCH 252/528] quadstrat/rule/data for excitations in FD fix ambiguity in assembly of TD TensorOperator --- examples/pmchwt.jl | 35 ++++++++++++++++++---------------- src/excitation.jl | 27 ++++++++++++++------------ src/timedomain/tdtimeops.jl | 5 +++-- test/runtests.jl | 1 + test/test_td_tensoroperator.jl | 35 ++++++++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 30 deletions(-) create mode 100644 test/test_td_tensoroperator.jl diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 6148a8ea..4500ab56 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -1,6 +1,25 @@ using CompScienceMeshes, BEAST using LinearAlgebra + +function nearfield(um,uj,Xm,Xj,κ,η,points, + Einc=(x->point(0,0,0)), + Hinc=(x->point(0,0,0))) + + K = BEAST.MWDoubleLayerField3D(wavenumber=κ) + T = BEAST.MWSingleLayerField3D(wavenumber=κ) + + Em = potential(K, points, um, Xm) + Ej = potential(T, points, uj, Xj) + E = -Em + η * Ej + Einc.(points) + + Hm = potential(T, points, um, Xm) + Hj = potential(K, points, uj, Xj) + H = 1/η*Hm + Hj + Hinc.(points) + + return E, H +end + ϵ0 = 8.854e-12 μ0 = 4π*1e-7 c = 1/√(ϵ0*μ0) @@ -75,23 +94,7 @@ Plotly.plot(patch(Γ, norm.(fcrj))) Plotly.plot(patch(Γ, norm.(fcrm))) -function nearfield(um,uj,Xm,Xj,κ,η,points, - Einc=(x->point(0,0,0)), - Hinc=(x->point(0,0,0))) - K = BEAST.MWDoubleLayerField3D(wavenumber=κ) - T = BEAST.MWSingleLayerField3D(wavenumber=κ) - - Em = potential(K, points, um, Xm) - Ej = potential(T, points, uj, Xj) - E = -Em + η * Ej + Einc.(points) - - Hm = potential(T, points, um, Xm) - Hj = potential(K, points, uj, Xj) - H = 1/η*Hm + Hj + Hinc.(points) - - return E, H -end Z = range(-6,6,length=200) diff --git a/src/excitation.jl b/src/excitation.jl index c033b601..165894a7 100644 --- a/src/excitation.jl +++ b/src/excitation.jl @@ -2,8 +2,10 @@ abstract type Functional end -quaddata(fn::Functional, refs, cells) = quadpoints(refs, cells, [8]) -quadrule(fn::Functional, refs, p, cell, qd) = qd[1,p] +defaultquadstrat(fn::Functional, basis) = SingleNumQStrat(8) +quaddata(fn::Functional, refs, cells, qs::SingleNumQStrat) = + quadpoints(refs, cells, [qs.quad_rule]) +quadrule(fn::Functional, refs, p, cell, qd, qs::SingleNumQStrat) = qd[1,p] """ assemble(fn, tfs) @@ -11,38 +13,39 @@ quadrule(fn::Functional, refs, p, cell, qd) = qd[1,p] Assemble the vector of test coefficients corresponding to functional `fn` and test functions `tfs`. """ -function assemble(field::Functional, tfs; quaddata=quaddata, quadrule=quadrule) +function assemble(field::Functional, tfs; + quadstrat=defaultquadstrat(field, tfs)) R = scalartype(tfs) b = zeros(Complex{R}, numfunctions(tfs)) store(v,m) = (b[m] += v) - assemble!(field, tfs, store, quaddata=quaddata, quadrule=quadrule) + assemble!(field, tfs, store; quadstrat) return b end function assemble!(field::Functional, tfs::DirectProductSpace, store; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(field, tfs)) I = Int[0] for s in tfs.factors push!(I, last(I) + numfunctions(s)) end for (i,s) in enumerate(tfs.factors) store1(v,m) = store(v, m + I[i]) - assemble!(field, s, store1, quaddata=quaddata, quadrule=quadrule) + assemble!(field, s, store1; quadstrat) end end function assemble!(field::Functional, tfs::Space, store; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(field, tfs)) tels, tad = assemblydata(tfs) trefs = refspace(tfs) - qd = quaddata(field, trefs, tels) + qd = quaddata(field, trefs, tels, quadstrat) for (t, tcell) in enumerate(tels) # compute the testing with the reference elements - qr = quadrule(field, trefs, t, tcell, qd) + qr = quadrule(field, trefs, t, tcell, qd, quadstrat) blocal = celltestvalues(trefs, tcell, field, qr) for i in 1 : numfunctions(trefs) @@ -56,7 +59,7 @@ function assemble!(field::Functional, tfs::Space, store; end function assemble!(field::Functional, tfs::subdBasis, store; - quaddata=quaddata, quadrule=quadrule) + quadstrat=defaultquadstrat(field, tfs)) tels, tad = assemblydata(tfs) @@ -66,8 +69,8 @@ function assemble!(field::Functional, tfs::subdBasis, store; for (t, tcell) in enumerate(tels) # compute the testing with the reference elements - qr = quadrule(field, trefs, t, tcell, qd) - blocal = celltestvalues(trefs, tcell, field, qr) + qr = quadrule(field, trefs, t, tcell, qd, quadstrat) + blocal = celltestvalues(trefs, tcell, field, qr, quadstrat) for i in 1 : length(tad[t]) for (m,a) in tad[t][i] diff --git a/src/timedomain/tdtimeops.jl b/src/timedomain/tdtimeops.jl index a088d9b7..40eed25e 100644 --- a/src/timedomain/tdtimeops.jl +++ b/src/timedomain/tdtimeops.jl @@ -131,8 +131,9 @@ end # return MatrixConvolution(Z), (v,m,n,k)->(Z[m,n,k] += v) # end -function assemble!(operator::TensorOperator, testfns, trialfns, store, - threading = Threading{:multi}; quadstrat=defaultquadstrat(operator, testfns, trialfns)) +function assemble!(operator::TensorOperator, testfns::SpaceTimeBasis, trialfns::SpaceTimeBasis, + store, threading::Type{Threading{:multi}}; + quadstrat=defaultquadstrat(operator, testfns, trialfns)) space_operator = operator.spatial_factor time_operator = operator.temporal_factor diff --git a/test/runtests.jl b/test/runtests.jl index 84b3e55c..f5a0e4c4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -66,6 +66,7 @@ include("test_compressed_storage.jl") include("test_tdop_scaling.jl") include("test_tdrhs_scaling.jl") +include("test_td_tensoroperator.jl") include("test_variational.jl") diff --git a/test/test_td_tensoroperator.jl b/test/test_td_tensoroperator.jl new file mode 100644 index 00000000..db480dac --- /dev/null +++ b/test/test_td_tensoroperator.jl @@ -0,0 +1,35 @@ +using CompScienceMeshes +using BEAST + +using Test + +@testset "Assembly of TensorOperator wrt SpaceTimeBasis" begin + m = Mesh( + [ + point(0,0,0), + point(1,0,0), + point(1,1,0), + point(0,1,0) + ], + [ + index(1,2,3), + index(1,3,4) + ], + ) + + X = raviartthomas(m) + δ = timebasisdelta(0.01234, 10) + T = timebasisshiftedlagrange(0.1234, 10, 1) + U = X ⊗ T + V = X ⊗ δ + + Id = BEAST.Identity() + # Nx = BEAST.NCross() + op = Id ⊗ Id + Z = assemble(op, V, U) + A = BEAST.ConvolutionOperators.ConvOpAsArray(Z) + @test size(A) == (1,1,10) + for i in 2:10 + @test A[1,1,i] ≈ 0 atol=sqrt(eps(eltype(A))) + end +end \ No newline at end of file From 85877f258bb249176aeb0de3acd3ed758ce86959 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 7 Jun 2023 13:19:04 +0000 Subject: [PATCH 253/528] test env via Project.toml file --- Project.toml | 9 - test/Manifest.toml | 609 +++++++++++++++++++++++++++++++++++++++++++++ test/Project.toml | 12 + 3 files changed, 621 insertions(+), 9 deletions(-) create mode 100644 test/Manifest.toml create mode 100644 test/Project.toml diff --git a/Project.toml b/Project.toml index bbe15231..e765de34 100644 --- a/Project.toml +++ b/Project.toml @@ -54,13 +54,4 @@ StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" WiltonInts84 = "0.2.3" julia = "1.6" -[extras] -DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" -Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" -LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -[targets] -test = ["Pkg", "DelimitedFiles", "LinearAlgebra", "SparseArrays", "Test", "Distributed"] diff --git a/test/Manifest.toml b/test/Manifest.toml new file mode 100644 index 00000000..c7a420e8 --- /dev/null +++ b/test/Manifest.toml @@ -0,0 +1,609 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.9.0" +manifest_format = "2.0" +project_hash = "5eac3002246ba098277ed23880bcbc5af87a54be" + +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.1" + +[[deps.ArrayLayouts]] +deps = ["FillArrays", "LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "4efc22e4c299e49995a38d503d9dbb0544a37838" +uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" +version = "1.0.4" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[deps.Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[deps.BlockArrays]] +deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] +git-tree-sha1 = "14d688a2254ca2242d834176a385cc9b3bceeb02" +uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" +version = "0.16.28" + +[[deps.Bzip2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" +uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" +version = "1.0.8+0" + +[[deps.Cairo_jll]] +deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2" +uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" +version = "1.16.1+1" + +[[deps.ClusterTrees]] +deps = ["DelimitedFiles", "LinearAlgebra", "Pkg"] +git-tree-sha1 = "4114d60c95974edf9272d88571d14abeed598942" +uuid = "5100927d-02aa-593a-b4f9-7235df19f0db" +version = "0.2.1" + +[[deps.CollisionDetection]] +deps = ["Compat", "LinearAlgebra", "StaticArrays"] +git-tree-sha1 = "8d86c864d69f72e23adcd7c2014d205bf32c90a1" +uuid = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" +version = "0.1.5" + +[[deps.Combinatorics]] +git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" +uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" +version = "1.0.2" + +[[deps.CompScienceMeshes]] +deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays"] +git-tree-sha1 = "133f6ac95849d0f96ecc82378e625ffa3790b940" +uuid = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" +version = "0.5.2" + +[[deps.Compat]] +deps = ["UUIDs"] +git-tree-sha1 = "7a60c856b9fa189eb34f5f8a6f6b5529b7942957" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "4.6.1" +weakdeps = ["Dates", "LinearAlgebra"] + + [deps.Compat.extensions] + CompatLinearAlgebraExt = "LinearAlgebra" + +[[deps.CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "1.0.2+0" + +[[deps.DataStructures]] +deps = ["Compat", "InteractiveUtils", "OrderedCollections"] +git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.18.13" + +[[deps.Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[deps.DelimitedFiles]] +deps = ["Mmap"] +git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" +version = "1.9.1" + +[[deps.Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[deps.DocStringExtensions]] +deps = ["LibGit2"] +git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.9.3" + +[[deps.Downloads]] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.6.0" + +[[deps.Expat_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "bad72f730e9e91c08d9427d5e8db95478a3c323d" +uuid = "2e619515-83b5-522b-bb60-26c02a35a201" +version = "2.4.8+0" + +[[deps.FLTK_jll]] +deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "72a4842f93e734f378cf381dae2ca4542f019d23" +uuid = "4fce6fc7-ba6a-5f4c-898f-77e99806d6f8" +version = "1.3.8+0" + +[[deps.FastGaussQuadrature]] +deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] +git-tree-sha1 = "0f478d8bad6f52573fb7658a263af61f3d96e43a" +uuid = "442a2c76-b920-505d-bb47-c5924d526838" +version = "0.5.1" + +[[deps.FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" + +[[deps.FillArrays]] +deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"] +git-tree-sha1 = "589d3d3bff204bdd80ecc53293896b4f39175723" +uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" +version = "1.1.1" + +[[deps.Fontconfig_jll]] +deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" +uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" +version = "2.13.93+0" + +[[deps.FreeType2_jll]] +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "87eb71354d8ec1a96d4a7636bd57a7347dde3ef9" +uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" +version = "2.10.4+0" + +[[deps.GLU_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] +git-tree-sha1 = "65af046f4221e27fb79b28b6ca89dd1d12bc5ec7" +uuid = "bd17208b-e95e-5925-bf81-e2f59b3e5c61" +version = "9.0.1+0" + +[[deps.GMP_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" +version = "6.2.1+2" + +[[deps.Gettext_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" +uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" +version = "0.21.0+0" + +[[deps.Glib_jll]] +deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "d3b3624125c1474292d0d8ed0f65554ac37ddb23" +uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" +version = "2.74.0+2" + +[[deps.GmshTools]] +deps = ["Libdl", "gmsh_jll"] +git-tree-sha1 = "299aa66053646db77f8aa7fafcebe0f9e5c0d1dc" +uuid = "82e2f556-b1bd-5f1a-9576-f93c0da5f0ee" +version = "0.5.2" + +[[deps.HDF5_jll]] +deps = ["Artifacts", "JLLWrappers", "LibCURL_jll", "Libdl", "OpenSSL_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "4cc2bb72df6ff40b055295fdef6d92955f9dede8" +uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" +version = "1.12.2+2" + +[[deps.InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[deps.IrrationalConstants]] +git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.2.2" + +[[deps.JLLWrappers]] +deps = ["Preferences"] +git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.4.1" + +[[deps.JpegTurbo_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6f2675ef130a300a112286de91973805fcc5ffbc" +uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" +version = "2.1.91+0" + +[[deps.LLVMOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "f689897ccbe049adb19a065c495e75f372ecd42b" +uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" +version = "15.0.4+0" + +[[deps.LZO_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" +uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" +version = "2.10.1+0" + +[[deps.LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.3" + +[[deps.LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "7.84.0+0" + +[[deps.LibGit2]] +deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.10.2+0" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[deps.Libffi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" +uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" +version = "3.2.2+1" + +[[deps.Libgcrypt_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] +git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" +uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" +version = "1.8.7+0" + +[[deps.Libglvnd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] +git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" +uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" +version = "1.6.0+0" + +[[deps.Libgpg_error_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" +uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" +version = "1.42.0+0" + +[[deps.Libiconv_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c7cb1f5d892775ba13767a87c7ada0b980ea0a71" +uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" +version = "1.16.1+2" + +[[deps.Libmount_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73" +uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" +version = "2.35.0+0" + +[[deps.Libuuid_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066" +uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" +version = "2.36.0+0" + +[[deps.LinearAlgebra]] +deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[deps.LinearElasticity_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "71e8ee0f9fe0e86a8f8c7f28361e5118eab2f93f" +uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" +version = "5.0.0+0" + +[[deps.LogExpFunctions]] +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "c3ce8e7420b3a6e071e0fe4745f5d4300e37b13f" +uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" +version = "0.3.24" + + [deps.LogExpFunctions.extensions] + LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" + LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" + LogExpFunctionsInverseFunctionsExt = "InverseFunctions" + + [deps.LogExpFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" + +[[deps.Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[deps.METIS_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "1fd0a97409e418b78c53fac671cf4622efdf0f21" +uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" +version = "5.1.2+0" + +[[deps.MMG_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] +git-tree-sha1 = "70a59df96945782bb0d43b56d0fbfdf1ce2e4729" +uuid = "86086c02-e288-5929-a127-40944b0018b7" +version = "5.6.0+0" + +[[deps.Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.2+0" + +[[deps.Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[deps.MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2022.10.11" + +[[deps.NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" + +[[deps.OCCT_jll]] +deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "acc8099ae8ed10226dc8424fb256ec9fe367a1f0" +uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" +version = "7.6.2+2" + +[[deps.OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.21+4" + +[[deps.OpenLibm_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "05823500-19ac-5b8b-9628-191a04bc5112" +version = "0.8.1+0" + +[[deps.OpenSSL_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1aa4b74f80b01c6bc2b89992b861b5f210e665b5" +uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" +version = "1.1.21+0" + +[[deps.OpenSpecFun_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" +uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" +version = "0.5.5+0" + +[[deps.OrderedCollections]] +git-tree-sha1 = "d321bf2de576bf25ec4d3e4360faca399afca282" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.6.0" + +[[deps.PCRE2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" +version = "10.42.0+0" + +[[deps.Pixman_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29" +uuid = "30392449-352a-5448-841d-b1acce4e97dc" +version = "0.40.1+0" + +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.9.0" + +[[deps.Preferences]] +deps = ["TOML"] +git-tree-sha1 = "7eb1686b4f04b82f96ed7a4ea5890a4f0c7a09f1" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.4.0" + +[[deps.Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[deps.REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[deps.Random]] +deps = ["SHA", "Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[deps.Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.3.0" + +[[deps.SCOTCH_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "7110b749766853054ce8a2afaa73325d72d32129" +uuid = "a8d0f55d-b80e-548d-aff6-1a04c175f0f9" +version = "6.1.3+0" + +[[deps.SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" + +[[deps.SauterSchwabQuadrature]] +deps = ["FastGaussQuadrature", "LinearAlgebra", "StaticArrays"] +git-tree-sha1 = "80a8bf94c550ff26ef7778921b3e3833ac422be6" +uuid = "535c7bfe-2023-5c1d-b712-654ef9d93a38" +version = "2.2.1" + +[[deps.Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[deps.Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[deps.SparseArrays]] +deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.SpecialFunctions]] +deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "ef28127915f4229c971eb43f3fc075dd3fe91880" +uuid = "276daf66-3868-5448-9aa4-cd146d93841b" +version = "2.2.0" + + [deps.SpecialFunctions.extensions] + SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" + + [deps.SpecialFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + +[[deps.StaticArrays]] +deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] +git-tree-sha1 = "8982b3607a212b070a5e46eea83eb62b4744ae12" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.5.25" + +[[deps.StaticArraysCore]] +git-tree-sha1 = "6b7ba252635a5eff6a0b0664a41ee140a1c9e72a" +uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" +version = "1.4.0" + +[[deps.Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +version = "1.9.0" + +[[deps.SuiteSparse_jll]] +deps = ["Artifacts", "Libdl", "Pkg", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "5.10.1+6" + +[[deps.TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.3" + +[[deps.Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" + +[[deps.Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[deps.UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[deps.Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[deps.WiltonInts84]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "446d97faa3e974e8a4d406ecc873284f5aa9558a" +uuid = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" +version = "0.2.4" + +[[deps.XML2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "93c41695bc1c08c46c5899f4fe06d6ead504bb73" +uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" +version = "2.10.3+0" + +[[deps.XSLT_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" +uuid = "aed1982a-8fda-507f-9586-7b0439959a61" +version = "1.1.34+0" + +[[deps.Xorg_libX11_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527" +uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" +version = "1.6.9+4" + +[[deps.Xorg_libXau_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e" +uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" +version = "1.0.9+4" + +[[deps.Xorg_libXdmcp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4" +uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" +version = "1.1.3+4" + +[[deps.Xorg_libXext_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" +uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" +version = "1.3.4+4" + +[[deps.Xorg_libXfixes_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" +uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" +version = "5.0.3+4" + +[[deps.Xorg_libXft_jll]] +deps = ["Fontconfig_jll", "Libdl", "Pkg", "Xorg_libXrender_jll"] +git-tree-sha1 = "754b542cdc1057e0a2f1888ec5414ee17a4ca2a1" +uuid = "2c808117-e144-5220-80d1-69d4eaa9352c" +version = "2.3.3+1" + +[[deps.Xorg_libXinerama_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] +git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" +uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" +version = "1.1.4+4" + +[[deps.Xorg_libXrender_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96" +uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" +version = "0.9.10+4" + +[[deps.Xorg_libpthread_stubs_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb" +uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" +version = "0.1.0+3" + +[[deps.Xorg_libxcb_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] +git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6" +uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" +version = "1.13.0+3" + +[[deps.Xorg_xtrans_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845" +uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" +version = "1.4.0+3" + +[[deps.Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.2.13+0" + +[[deps.gmsh_jll]] +deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "d15409a4b9f1d14f1e1f9e910cd00f7d6695c261" +uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" +version = "4.11.1+0" + +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.7.0+0" + +[[deps.libpng_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c" +uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" +version = "1.6.38+0" + +[[deps.nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.48.0+0" + +[[deps.p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.4.0+0" diff --git a/test/Project.toml b/test/Project.toml new file mode 100644 index 00000000..9a7b4b64 --- /dev/null +++ b/test/Project.toml @@ -0,0 +1,12 @@ +[deps] +BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" +CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" +DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" +Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" From 8ffa0fa72807b9106baa54783da5c816aa111770 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 8 Jun 2023 11:58:43 +0200 Subject: [PATCH 254/528] gmres_ch returns convergence history --- examples/efie.jl | 2 +- src/solvers/itsolver.jl | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/efie.jl b/examples/efie.jl index fd2ad405..5ce77674 100644 --- a/examples/efie.jl +++ b/examples/efie.jl @@ -15,7 +15,7 @@ e = (n × E) × n @hilbertspace j @hilbertspace k efie = @discretise t[k,j]==e[k] j∈X k∈X -u = gmres(efie; restart=1500) +u, ch = BEAST.gmres_ch(efie; restart=1500) include("utils/postproc.jl") include("utils/plotresults.jl") diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index e5ac9599..8f7c3494 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -68,7 +68,8 @@ LinearAlgebra.adjoint(A::GMRESSolver) = GMRESSolver(adjoint(A.linear_operator), LinearAlgebra.transpose(A::GMRESSolver) = GMRESSolver(transpose(A.linear_operator), A.maxiter, A.restart, A.tol, A.verbose) -function gmres(eq::DiscreteEquation; maxiter=0, restart=0, tol=0) + +function gmres_ch(eq::DiscreteEquation; maxiter=0, restart=0, tol=0) lhs = eq.equation.lhs rhs = eq.equation.rhs @@ -84,8 +85,11 @@ function gmres(eq::DiscreteEquation; maxiter=0, restart=0, tol=0) else invZ = GMRESSolver(Z, maxiter=maxiter, restart=restart, tol=tol) end - x = invZ * b + x, ch = solve(invZ, b) + # x = invZ * b ax = nestedrange(Y, 1, numfunctions) - return PseudoBlockVector(x, (ax,)) + return PseudoBlockVector(x, (ax,)), ch end + +gmres(eq::DiscreteEquation; maxiter=0, restart=0, tol=0) = gmres_ch(eq; maxiter, restart, tol)[1] From afcf128c9960615b210d17b3704308a0eef80187 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Tue, 14 Dec 2021 15:27:59 +0100 Subject: [PATCH 255/528] dsvie --- examples/dsvie.jl | 73 ++++++ examples/pmchwt.jl | 13 +- src/BEAST.jl | 4 + src/operator.jl | 4 +- src/volumeintegral/sauterschwab_ints.jl | 102 +++++++- src/volumeintegral/vsie.jl | 236 +++++++++++++++++++ src/volumeintegral/vsieops.jl | 300 ++++++++++++++++++++++++ 7 files changed, 723 insertions(+), 9 deletions(-) create mode 100644 examples/dsvie.jl create mode 100644 src/volumeintegral/vsie.jl create mode 100644 src/volumeintegral/vsieops.jl diff --git a/examples/dsvie.jl b/examples/dsvie.jl new file mode 100644 index 00000000..ebb0ea10 --- /dev/null +++ b/examples/dsvie.jl @@ -0,0 +1,73 @@ +using CompScienceMeshes, BEAST +using LinearAlgebra +using Profile +using StaticArrays + + + +ntrc = X->ntrace(X,Γ) + +T = tetmeshsphere(1.0,0.5) +X = nedelecd3d(T) +Γ = boundary(T) +Y = raviartthomas(Γ) + +@show numfunctions(X) +@show numfunctions(Y) + + +κ, η = 1.0, 1.0 +κ′, η′ = √2.0κ, η/√2.0 +ϵ_r =2.0 + + +χ = x->(1.0-1.0/ϵ_r) + +#Volume-Volume +L,I,B = VIE.singlelayer(wavenumber=κ', tau=χ), Identity(), VIE.boundary(wavenumber=κ', tau=χ) +#Volume-Surface +Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ', tau=χ)), transpose(VSIE.boundary(wavenumber=κ', tau=χ)), transpose(VSIE.doublelayer(wavenumber=κ', tau=χ)) +Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ'), VSIE.boundary(wavenumber=κ'), VSIE.doublelayer(wavenumber=κ') +#Surface-Surface +T = Maxwell3D.singlelayer(wavenumber=κ) #Outside +T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside +K = Maxwell3D.doublelayer(wavenumber=κ) #Outside +K′ = Maxwell3D.doublelayer(wavenumber=κ′) #Inside + +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +H = -1/(im*κ*η)*curl(E) + +e, h = (n × E) × n, (n × H) × n + +@hilbertspace D j m +@hilbertspace k l o +β = 1.0/ϵ_r +α, α′ = 1/η, 1/η′ + +eq = @varform (β*I[k,D]-L[k,D]-B[ntrc(k),D] + Lt[k,j]+Bt[ntrc(k),j] + Kt[k,m] + + Ls[l,D]+Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + + Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) + + +dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y + +u = solve(dvsie) + + +#Post processing +Θ, Φ = range(0.0,stop=2π,length=100), 0.0 +ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] + +# Don't forget the far field comprises two contributions +ffm = potential(MWFarField3D(κ*im), ffpoints, u[m], Y) +ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], Y) +ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) + + +ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u[D], X) +ff2 = im*κ*ffd + +using Plots +plot(xlabel="theta") +plot!(Θ,norm.(ff),label="far field",title="DVSIE") +plot!(Θ,norm.(ff2),label="far field",title="DVSIE 2") diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index 4500ab56..ad804825 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -86,12 +86,13 @@ using Plots Plots.plot(xlabel="theta") Plots.plot!(Θ,norm.(ff),label="far field",title="PMCHWT") -import Plotly -using LinearAlgebra -fcrj, _ = facecurrents(u[j],X) -fcrm, _ = facecurrents(u[m],X) -Plotly.plot(patch(Γ, norm.(fcrj))) -Plotly.plot(patch(Γ, norm.(fcrm))) +error() +#import Plotly +#using LinearAlgebra +#fcrj, _ = facecurrents(u[j],X) +#fcrm, _ = facecurrents(u[m],X) +#Plotly.plot(patch(Γ, norm.(fcrj))) +#Plotly.plot(patch(Γ, norm.(fcrm))) diff --git a/src/BEAST.jl b/src/BEAST.jl index 3ca7aaa5..8aa377e7 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -84,6 +84,7 @@ export DoubleLayerRotatedMW3D export MWSingleLayerPotential3D export VIEOperator +export VSIEOperator export gmres export @hilbertspace, @varform, @discretise @@ -228,8 +229,11 @@ include("volumeintegral/vie.jl") include("volumeintegral/vieexc.jl") include("volumeintegral/vieops.jl") include("volumeintegral/farfield.jl") +include("volumeintegral/vsie.jl") +include("volumeintegral/vsieops.jl") include("volumeintegral/sauterschwab_ints.jl") + include("decoupled/dpops.jl") include("decoupled/potentials.jl") diff --git a/src/operator.jl b/src/operator.jl index bfdb379b..16c4cddf 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -202,8 +202,8 @@ end -function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, - store, threading = Threading{:multi}; +function assemble!(op::TransposedOperator, tfs::Space, bfs::Space, store, + threading::Type{Threading{:multi}} = Threading{:multi}; quadstrat=defaultquadstrat(op, tfs, bfs)) store1(v,m,n) = store(v,n,m) diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index b62b3e7f..2746604c 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -71,7 +71,7 @@ function reorder_dof(space::LagrangeRefSpace{Float64,0,3,1},I) return SVector{1,Int64}(1),SVector{1,Int64}(1) end - +#Method of moments volume integrals operators function momintegrals!(op::VIEOperator, test_local_space::RefSpace, trial_local_space::RefSpace, test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) @@ -168,3 +168,103 @@ function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::Do return z end + + +#Method of moments volume surface integrals operators +function momintegrals!(op::VSIEOperator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) + + #Find permutation of vertices to match location of singularity to SauterSchwab + J, I= SauterSchwab3D.reorder(strat.sing) + + #Get permutation and rel. orientatio of DoFs + K,O1 = reorder_dof(test_local_space, I) + L,O2 = reorder_dof(trial_local_space, J) + #Apply permuation to elements + + if length(I) == 4 + test_tetrahedron_element = simplex( + test_tetrahedron_element.vertices[I[1]], + test_tetrahedron_element.vertices[I[2]], + test_tetrahedron_element.vertices[I[3]], + test_tetrahedron_element.vertices[I[4]]) + elseif length(I) == 3 + test_tetrahedron_element = simplex( + test_tetrahedron_element.vertices[I[1]], + test_tetrahedron_element.vertices[I[2]], + test_tetrahedron_element.vertices[I[3]]) + end + + #test_tetrahedron_element = simplex(test_tetrahedron_element.vertices[I]...) + + if length(J) == 4 + trial_tetrahedron_element = simplex( + trial_tetrahedron_element.vertices[J[1]], + trial_tetrahedron_element.vertices[J[2]], + trial_tetrahedron_element.vertices[J[3]], + trial_tetrahedron_element.vertices[J[4]]) + elseif length(J) == 3 + trial_tetrahedron_element = simplex( + trial_tetrahedron_element.vertices[J[1]], + trial_tetrahedron_element.vertices[J[2]], + trial_tetrahedron_element.vertices[J[3]]) + end + + #trial_tetrahedron_element = simplex(trial_tetrahedron_element.vertices[J]...) + + #Define integral (returns a function that only needs barycentric coordinates) + igd = VSIEIntegrand(test_tetrahedron_element, trial_tetrahedron_element, + op, test_local_space, trial_local_space) + + #Evaluate integral + Q = SauterSchwab3D.sauterschwab_parameterized(igd, strat) + + #Undo permuation on DoFs + for j in 1 : length(L) + for i in 1 : length(K) + out[i,j] += Q[K[i],L[j]]*O1[i]*O2[j] + end + end + nothing +end + +function momintegrals!(biop::VSIEOperator, tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) + + # memory allocation here is a result from the type instability on strat + # which is on purpose, i.e. the momintegrals! method is chosen based + # on dynamic polymorphism. + womps = strat.outer_quad_points + wimps = strat.inner_quad_points + + M, N = size(z) + + for womp in womps + tgeo = womp.point + tvals = womp.value + jx = womp.weight + + for wimp in wimps + bgeo = wimp.point + bvals = wimp.value + jy = wimp.weight + + j = jx * jy + kernel = kernelvals(biop, tgeo, bgeo) + + + igd = integrand(biop, kernel, tvals, tgeo, bvals, bgeo) + + for m in 1 : length(tvals) + + for n in 1 : length(bvals) + + z[m,n] += j * igd[m,n] + end + end + end + end + + return z +end + diff --git a/src/volumeintegral/vsie.jl b/src/volumeintegral/vsie.jl new file mode 100644 index 00000000..b0dcc2b4 --- /dev/null +++ b/src/volumeintegral/vsie.jl @@ -0,0 +1,236 @@ +module VSIE + using ..BEAST + Mod = BEAST + + """ + singlelayer(;gamma, alpha, beta, tau) + singlelayer(;wavenumber, alpha, beta, tau) + + Bilinear form given by: + + ```math + α ∬_{Γ×Ω} j(x)⋅τ(y)⋅k(y) G_{γ}(x,y) + β ∬_{Γ×Ω} div j(x) τ(y) div k(y) G_{γ}(x,y) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast dyadic + """ + + function singlelayer(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = wavenumber*wavenumber) + beta == nothing && (beta = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIESingleLayer(gamma, alpha, beta, tau) + end + + function singlelayerT(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = wavenumber*wavenumber) + beta == nothing && (beta = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIESingleLayerT(gamma, alpha, beta, tau) + end + + """ + boundary(;gamma, alpha, tau) + boundary(;wavenumber, alpha, tau) + + Bilinear form given by: + + ```math + α ∬_{Γ×Ω} T_n j(x) grad G_{γ}(x,y)⋅τ(y) div k(y) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast dyadic + """ + + function boundary(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIEBoundary(gamma, alpha, tau) + end + + function boundaryT(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIEBoundaryT(gamma, alpha, tau) + end + + + + """ + doublelayer(;gamma, alpha, beta, tau) + doublelayer(;wavenumber, alpha, beta, tau) + + Bilinear form given by: + + ```math + α ∬_{Ω×Γ} j(x) ⋅ grad G_{γ}(x,y) × τ(y)⋅k(y) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast dyadic + """ + + function doublelayer(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIEDoubleLayer(gamma, alpha, tau) + end + + + function doublelayerT(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + beta=nothing, + tau=nothing) + + if (gamma == nothing) && (wavenumber == nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma != nothing) && (wavenumber != nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma == nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma != nothing + + alpha == nothing && (alpha = 1.0) + tau == nothing && (tau = x->1.0) + + Mod.VSIEDoubleLayerT(gamma, alpha, tau) + end + +end \ No newline at end of file diff --git a/src/volumeintegral/vsieops.jl b/src/volumeintegral/vsieops.jl new file mode 100644 index 00000000..1235cd7c --- /dev/null +++ b/src/volumeintegral/vsieops.jl @@ -0,0 +1,300 @@ +abstract type VSIEOperator <: IntegralOperator end +abstract type VolumeSurfaceOperator <: VSIEOperator end +abstract type BoundarySurfaceOperator <: VSIEOperator end + +struct KernelValsVSIE{T,U,P,Q,K} + gamma::U + vect::P + dist::T + green::U + gradgreen::Q + tau::K +end + +function kernelvals(viop::VSIEOperator, p ,q) + Y = viop.gamma; + r = cartesian(p)-cartesian(q) + R = norm(r) + yR = Y*R + + expn = exp(-yR) + green = expn / (4*pi*R) + gradgreen = - (Y +1/R)*green/R*r + + + tau = viop.tau(cartesian(q)) + + KernelValsVSIE(Y,r,R, green, gradgreen,tau) +end + +struct VSIESingleLayer{T,U,P} <: VolumeSurfaceOperator + gamma::T + α::U + β::U + tau::P +end + +struct VSIEBoundary{T,U,P} <: BoundarySurfaceOperator + gamma::T + α::U + tau::P +end + +struct VSIEDoubleLayer{T,U,P} <: VolumeSurfaceOperator + gamma::T + α::U + tau::P +end + + +struct VSIESingleLayerT{T,U,P} <: VolumeSurfaceOperator + gamma::T + α::U + β::U + tau::P +end + +struct VSIEBoundaryT{T,U,P} <: BoundarySurfaceOperator + gamma::T + α::U + tau::P +end + +struct VSIEDoubleLayerT{T,U,P} <: VolumeSurfaceOperator + gamma::T + α::U + tau::P +end + +scalartype(op::VSIEOperator) = typeof(op.gamma) + +export VSIE + +struct VSIEIntegrand{S,T,O,K,L} + test_tetrahedron_element::S + trial_triangle_element::T + op::O + test_local_space::K + trial_local_space::L +end + + +function (igd::VSIEIntegrand)(u,v) + + #mesh points + tgeo = neighborhood(igd.test_tetrahedron_element,v) + bgeo = neighborhood(igd.trial_triangle_element,u) + + #kernel values + kerneldata = kernelvals(igd.op,tgeo,bgeo) + + #values & grad/div/curl of local shape functions + tval = igd.test_local_space(tgeo) + bval = igd.trial_local_space(bgeo) + + #jacobian + j = jacobian(tgeo) * jacobian(bgeo) + + integrand(igd.op, kerneldata,tval,tgeo,bval,tgeo) * j +end + + +function integrand(viop::VSIESingleLayer, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:3] + fy = @SVector[bvals[i].value for i in 1:4] + + dgx = @SVector[tvals[i].divergence for i in 1:3] + dfy = @SVector[bvals[i].divergence for i in 1:4] + + G = kerneldata.green + + Ty = kerneldata.tau + + α = viop.α + β = viop.β + + @SMatrix[α * dot(gx[i],Ty*fy[j]) * G + β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] +end + +function integrand(viop::VSIEBoundary, kerneldata, tvals, tgeo, bvals, bgeo) + + dgx = @SVector[tvals[i].divergence for i in 1:3] + fy = @SVector[bvals[i].value for i in 1:1] + + G = kerneldata.green + + Tx = kerneldata.tau + + α = viop.α + + @SMatrix[α * Tx * dgx[i] * fy[j] * G for i in 1:3, j in 1:1] +end + +function integrand(viop::VSIESingleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:4] + fy = @SVector[bvals[i].value for i in 1:3] + + dgx = @SVector[tvals[i].divergence for i in 1:4] + dfy = @SVector[bvals[i].divergence for i in 1:3] + + G = kerneldata.green + + Ty = kerneldata.tau + + α = viop.α + β = viop.β + + @SMatrix[α * dot(gx[i],Ty*fy[j]) * G + β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] +end + + +function integrand(viop::VSIEBoundaryT, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:1] + dfy = @SVector[bvals[i].divergence for i in 1:3] + + G = kerneldata.green + + Tx = kerneldata.tau + + α = viop.α + + @SMatrix[α * Tx * gx[i] * dfy[j] * G for i in 1:3, j in 1:1] +end + + +function integrand(viop::VSIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) + gx = @SVector[tvals[i].value for i in 1:3] + fy = @SVector[bvals[i].value for i in 1:4] + + gradG = kerneldata.gradgreen + + Ty = kerneldata.tau + + α = viop.α + + @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:3, j in 1:4] +end + +function integrand(viop::VSIEDoubleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) + gx = @SVector[tvals[i].value for i in 1:4] + fy = @SVector[bvals[i].value for i in 1:3] + + gradG = kerneldata.gradgreen + + Ty = kerneldata.tau + + α = viop.α + + @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:4, j in 1:3] +end + + +defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) + + +function quaddata(op::VSIEOperator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_charts, trial_charts, qs::SauterSchwab3DQStrat) + + #The combinations of rules (6,7) and (5,7 are) BAAAADDDD + # they result in many near singularity evaluations with any + # resemblence of accuracy going down the drain! Simply don't! + # (same for (5,7) btw...). + t_qp = quadpoints(test_local_space, test_charts, (qs.outer_rule)) + b_qp = quadpoints(trial_local_space, trial_charts, (qs.inner_rule)) + + + sing_qp = (SauterSchwab3D._legendre(qs.sauter_schwab_1D,0,1), + SauterSchwab3D._shunnham2D(qs.sauter_schwab_2D), + SauterSchwab3D._shunnham3D(qs.sauter_schwab_3D), + SauterSchwab3D._shunnham4D(qs.sauter_schwab_4D),) + + + return (tpoints=t_qp, bpoints=b_qp, sing_qp=sing_qp) +end + +quadrule(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs) = qr_volume(op, g, f, i, τ, j, σ, qd, qs) + + +function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, + qs::SauterSchwab3DQStrat) + + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) + + hits = 0 + idx_t = Int64[] + idx_s = Int64[] + sizehint!(idx_t,4) + sizehint!(idx_s,4) + dmin2 = floatmax(eltype(eltype(τ.vertices))) + D = dimension(τ)+dimension(σ) + for (i,t) in enumerate(τ.vertices) + for (j,s) in enumerate(σ.vertices) + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + if d2 < dtol + push!(idx_t,i) + push!(idx_s,j) + hits +=1 + break + end + end + end + + #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) + + hits == 3 && return SauterSchwab3D.CommonFace5D_S(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) + hits == 2 && return SauterSchwab3D.CommonEdge5D_S(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) + hits == 1 && return SauterSchwab3D.CommonVertex5D_S(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),(qd.sing_qp[3],qd.sing_qp[2])) + + + return DoubleQuadRule( + qd[1][1,i], + qd[2][1,j]) + +end + +quadrule(op::BoundarySurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs) = qr_boundary(op, g, f, i, τ, j, σ, qd, qs) + +function qr_boundary(op::BoundarySurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, + qs::SauterSchwab3DQStrat) + + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) + + hits = 0 + idx_t = Int64[] + idx_s = Int64[] + sizehint!(idx_t,4) + sizehint!(idx_s,4) + dmin2 = floatmax(eltype(eltype(τ.vertices))) + D = dimension(τ)+dimension(σ) + for (i,t) in enumerate(τ.vertices) + for (j,s) in enumerate(σ.vertices) + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + if d2 < dtol + push!(idx_t,i) + push!(idx_s,j) + hits +=1 + break + end + end + end + + #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) + + + hits == 3 && return SauterSchwab3D.CommonFace4D_S(SauterSchwab3D.Singularity4DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[3])) + hits == 2 && return SauterSchwab3D.CommonEdge4D_S(SauterSchwab3D.Singularity4DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2])) + hits == 1 && return SauterSchwab3D.CommonVertex4D_S(SauterSchwab3D.Singularity4DPoint(idx_t,idx_s),(qd.sing_qp[2])) + + + return DoubleQuadRule( + qd[1][1,i], + qd[2][1,j]) + +end + From cffab1c981dbe28ed14a63b4fd18e362ba6711f4 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Sat, 18 Dec 2021 15:43:34 +0100 Subject: [PATCH 256/528] update vsie implementation --- examples/dsvie.jl | 38 +++++++++++++++++++++++------------ examples/dvie.jl | 7 ++++--- examples/pmchwt.jl | 9 ++++++--- src/volumeintegral/vieops.jl | 2 +- src/volumeintegral/vsie.jl | 4 ++-- src/volumeintegral/vsieops.jl | 2 +- 6 files changed, 39 insertions(+), 23 deletions(-) diff --git a/examples/dsvie.jl b/examples/dsvie.jl index ebb0ea10..fa12c2ef 100644 --- a/examples/dsvie.jl +++ b/examples/dsvie.jl @@ -7,7 +7,7 @@ using StaticArrays ntrc = X->ntrace(X,Γ) -T = tetmeshsphere(1.0,0.5) +T = tetmeshsphere(1.0,0.25) X = nedelecd3d(T) Γ = boundary(T) Y = raviartthomas(Γ) @@ -18,7 +18,8 @@ Y = raviartthomas(Γ) κ, η = 1.0, 1.0 κ′, η′ = √2.0κ, η/√2.0 -ϵ_r =2.0 +ϵ_r =3.0 +ϵ_b =2.0 χ = x->(1.0-1.0/ϵ_r) @@ -26,8 +27,8 @@ Y = raviartthomas(Γ) #Volume-Volume L,I,B = VIE.singlelayer(wavenumber=κ', tau=χ), Identity(), VIE.boundary(wavenumber=κ', tau=χ) #Volume-Surface -Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ', tau=χ)), transpose(VSIE.boundary(wavenumber=κ', tau=χ)), transpose(VSIE.doublelayer(wavenumber=κ', tau=χ)) -Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ'), VSIE.boundary(wavenumber=κ'), VSIE.doublelayer(wavenumber=κ') +Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ')), transpose(VSIE.boundary(wavenumber=κ')), transpose(VSIE.doublelayer(wavenumber=κ')) +Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ', tau=χ), VSIE.boundary(wavenumber=κ', tau=χ), VSIE.doublelayer(wavenumber=κ', tau=χ) #Surface-Surface T = Maxwell3D.singlelayer(wavenumber=κ) #Outside T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside @@ -41,17 +42,21 @@ e, h = (n × E) × n, (n × H) × n @hilbertspace D j m @hilbertspace k l o -β = 1.0/ϵ_r +β = 1.0/(ϵ_r*ϵ_b) +ν = 1/ϵ_b α, α′ = 1/η, 1/η′ +γ′ = im*η′/κ′ +ζ′ = im*η′*κ′ +δ′ = im*κ′/ϵ_b -eq = @varform (β*I[k,D]-L[k,D]-B[ntrc(k),D] + Lt[k,j]+Bt[ntrc(k),j] + Kt[k,m] + - Ls[l,D]+Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + - Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) +eq = @varform (β*I[k,D]-ν*L[k,D]-ν*B[ntrc(k),D] + η′*Lt[k,j]-γ′*Bt[ntrc(k),j] + Kt[k,m] + + -δ′*Ls[l,D]-ν*Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + + ζ′*Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y -u = solve(dvsie) +u_n = solve(dvsie) #Post processing @@ -59,15 +64,22 @@ u = solve(dvsie) ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] # Don't forget the far field comprises two contributions -ffm = potential(MWFarField3D(κ*im), ffpoints, u[m], Y) -ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], Y) +ffm = potential(MWFarField3D(κ*im), ffpoints, u_n[m], Y) +ffj = potential(MWFarField3D(κ*im), ffpoints, u_n[j], Y) ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) -ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u[D], X) +ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u_n[D], X) ff2 = im*κ*ffd using Plots plot(xlabel="theta") plot!(Θ,norm.(ff),label="far field",title="DVSIE") -plot!(Θ,norm.(ff2),label="far field",title="DVSIE 2") +plot!(Θ,√6.0*norm.(ff),label="far field",title="DVSIE 2") + +import Plotly +using LinearAlgebra +fcrj, _ = facecurrents(u_n[j],Y) +fcrm, _ = facecurrents(u_n[m],Y) +Plotly.plot(patch(Γ, norm.(fcrj))) +Plotly.plot(patch(Γ, norm.(fcrm))) \ No newline at end of file diff --git a/examples/dvie.jl b/examples/dvie.jl index cd5f1868..a14ac29c 100644 --- a/examples/dvie.jl +++ b/examples/dvie.jl @@ -4,7 +4,7 @@ using Profile using StaticArrays function tau(x::SVector{U,T}) where {U,T} - 1.0-1.0/4.0 + 1.0-1.0/9.0 end ntrc = X->ntrace(X,y) @@ -15,7 +15,7 @@ y = boundary(T) @show numfunctions(X) ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) -ϵ_r =4.0 +ϵ_r =25.0 χ = tau K, I, B = VIE.singlelayer(wavenumber=κ, tau=χ), Identity(), VIE.boundary(wavenumber=κ, tau=χ) E = VIE.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) @@ -41,6 +41,7 @@ using Plots plot(xlabel="theta") plot!(Θ, norm.(ff), label="far field", title="D-VIE") +#= #NearField Z = range(-1,1,length=100) Y = range(-1,1,length=100) @@ -52,6 +53,6 @@ Enear = reshape(Enear,100,100) contour(real.(getindex.(Enear,1))) heatmap(Z, Y, real.(getindex.(Enear,1)), clim=(0.0,1.0)) - +=# diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index ad804825..c20cb074 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -38,8 +38,8 @@ X = raviartthomas(Γ) κ, η = ω/c, √(μ0/ϵ0) κ′, η′ = κ*√(ϵr*μr), η*√(μr/ϵr) -# κ, η = π, 1.0 -# κ′, η′ = 2.0κ, η/2.0 +# κ, η = 1.0, 1.0 +# κ′, η′ = √6.0κ, η/√6.0 T = Maxwell3D.singlelayer(wavenumber=κ) T′ = Maxwell3D.singlelayer(wavenumber=κ′) @@ -86,7 +86,7 @@ using Plots Plots.plot(xlabel="theta") Plots.plot!(Θ,norm.(ff),label="far field",title="PMCHWT") -error() + #import Plotly #using LinearAlgebra #fcrj, _ = facecurrents(u[j],X) @@ -125,3 +125,6 @@ Plots.plot(real.(getindex.(H_tot[:,51],2))) +plot() +plot!(Θ, norm.(ff),label="far field") +scatter!(Θ, norm.(Et_far), label="field far") diff --git a/src/volumeintegral/vieops.jl b/src/volumeintegral/vieops.jl index 57a0ed5b..0879f9dd 100644 --- a/src/volumeintegral/vieops.jl +++ b/src/volumeintegral/vieops.jl @@ -178,7 +178,7 @@ function integrand(viop::VIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) end -defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) +defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) function quaddata(op::VIEOperator, diff --git a/src/volumeintegral/vsie.jl b/src/volumeintegral/vsie.jl index b0dcc2b4..57d2cb35 100644 --- a/src/volumeintegral/vsie.jl +++ b/src/volumeintegral/vsie.jl @@ -41,8 +41,8 @@ module VSIE @assert gamma != nothing - alpha == nothing && (alpha = wavenumber*wavenumber) - beta == nothing && (beta = 1.0) + alpha == nothing && (alpha = gamma) + beta == nothing && (beta = im*im/gamma) tau == nothing && (tau = x->1.0) Mod.VSIESingleLayer(gamma, alpha, beta, tau) diff --git a/src/volumeintegral/vsieops.jl b/src/volumeintegral/vsieops.jl index 1235cd7c..85222b23 100644 --- a/src/volumeintegral/vsieops.jl +++ b/src/volumeintegral/vsieops.jl @@ -192,7 +192,7 @@ function integrand(viop::VSIEDoubleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) end -defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) +defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) function quaddata(op::VSIEOperator, From 1193cb06dc2e8a8c2d117a27118e6ae029992852 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Wed, 16 Mar 2022 15:24:29 +0100 Subject: [PATCH 257/528] working volume surface formulation --- examples/dsvie.jl | 85 ---------- examples/dvie.jl | 46 ++++-- examples/dvsie.jl | 198 ++++++++++++++++++++++++ examples/evie.jl | 43 ++--- examples/pmchwt.jl | 64 ++++++-- src/BEAST.jl | 2 + src/volumeintegral/sauterschwab_ints.jl | 14 +- src/volumeintegral/vieexc.jl | 9 ++ src/volumeintegral/vieops.jl | 25 +-- src/volumeintegral/vsie.jl | 12 +- src/volumeintegral/vsieops.jl | 86 +++++++--- 11 files changed, 411 insertions(+), 173 deletions(-) delete mode 100644 examples/dsvie.jl create mode 100644 examples/dvsie.jl diff --git a/examples/dsvie.jl b/examples/dsvie.jl deleted file mode 100644 index fa12c2ef..00000000 --- a/examples/dsvie.jl +++ /dev/null @@ -1,85 +0,0 @@ -using CompScienceMeshes, BEAST -using LinearAlgebra -using Profile -using StaticArrays - - - -ntrc = X->ntrace(X,Γ) - -T = tetmeshsphere(1.0,0.25) -X = nedelecd3d(T) -Γ = boundary(T) -Y = raviartthomas(Γ) - -@show numfunctions(X) -@show numfunctions(Y) - - -κ, η = 1.0, 1.0 -κ′, η′ = √2.0κ, η/√2.0 -ϵ_r =3.0 -ϵ_b =2.0 - - -χ = x->(1.0-1.0/ϵ_r) - -#Volume-Volume -L,I,B = VIE.singlelayer(wavenumber=κ', tau=χ), Identity(), VIE.boundary(wavenumber=κ', tau=χ) -#Volume-Surface -Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ')), transpose(VSIE.boundary(wavenumber=κ')), transpose(VSIE.doublelayer(wavenumber=κ')) -Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ', tau=χ), VSIE.boundary(wavenumber=κ', tau=χ), VSIE.doublelayer(wavenumber=κ', tau=χ) -#Surface-Surface -T = Maxwell3D.singlelayer(wavenumber=κ) #Outside -T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside -K = Maxwell3D.doublelayer(wavenumber=κ) #Outside -K′ = Maxwell3D.doublelayer(wavenumber=κ′) #Inside - -E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) -H = -1/(im*κ*η)*curl(E) - -e, h = (n × E) × n, (n × H) × n - -@hilbertspace D j m -@hilbertspace k l o -β = 1.0/(ϵ_r*ϵ_b) -ν = 1/ϵ_b -α, α′ = 1/η, 1/η′ -γ′ = im*η′/κ′ -ζ′ = im*η′*κ′ -δ′ = im*κ′/ϵ_b - -eq = @varform (β*I[k,D]-ν*L[k,D]-ν*B[ntrc(k),D] + η′*Lt[k,j]-γ′*Bt[ntrc(k),j] + Kt[k,m] + - -δ′*Ls[l,D]-ν*Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + - ζ′*Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) - - -dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y - -u_n = solve(dvsie) - - -#Post processing -Θ, Φ = range(0.0,stop=2π,length=100), 0.0 -ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] - -# Don't forget the far field comprises two contributions -ffm = potential(MWFarField3D(κ*im), ffpoints, u_n[m], Y) -ffj = potential(MWFarField3D(κ*im), ffpoints, u_n[j], Y) -ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) - - -ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u_n[D], X) -ff2 = im*κ*ffd - -using Plots -plot(xlabel="theta") -plot!(Θ,norm.(ff),label="far field",title="DVSIE") -plot!(Θ,√6.0*norm.(ff),label="far field",title="DVSIE 2") - -import Plotly -using LinearAlgebra -fcrj, _ = facecurrents(u_n[j],Y) -fcrm, _ = facecurrents(u_n[m],Y) -Plotly.plot(patch(Γ, norm.(fcrj))) -Plotly.plot(patch(Γ, norm.(fcrm))) \ No newline at end of file diff --git a/examples/dvie.jl b/examples/dvie.jl index a14ac29c..26eb2774 100644 --- a/examples/dvie.jl +++ b/examples/dvie.jl @@ -1,21 +1,22 @@ using CompScienceMeshes, BEAST using LinearAlgebra using Profile +using TimerOutputs using StaticArrays function tau(x::SVector{U,T}) where {U,T} - 1.0-1.0/9.0 + 1.0-1.0/5.0 end ntrc = X->ntrace(X,y) -T = CompScienceMeshes.tetmeshsphere(1.0,0.25) +T = tetmeshsphere(1.0,0.2) X = nedelecd3d(T) y = boundary(T) @show numfunctions(X) ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) -ϵ_r =25.0 +ϵ_r =5.0 χ = tau K, I, B = VIE.singlelayer(wavenumber=κ, tau=χ), Identity(), VIE.boundary(wavenumber=κ, tau=χ) E = VIE.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) @@ -27,8 +28,8 @@ H = -1/(im*κ*η)*curl(E) eq = @varform α*I[j,k]-K[j,k]-B[ntrc(j),k] == E[j] dbvie = @discretise eq j∈X k∈X -u = solve(dbvie) - +u_m = solve(dbvie) +#= #postprocessing Φ, Θ = [0.0], range(0,stop=2π,length=100) pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] @@ -40,19 +41,42 @@ using Plots #Farfield plot(xlabel="theta") plot!(Θ, norm.(ff), label="far field", title="D-VIE") - -#= +=# +using Plots #NearField Z = range(-1,1,length=100) Y = range(-1,1,length=100) -nfpoints = [point(0,y,z) for y in Y, z in Z] +nfpoints = [point(0,y,z) for z in Z, y in Y] -Enear = BEAST.grideval(nfpoints,α.*u,X) +Enear = BEAST.grideval(nfpoints,α.*u_m,X) Enear = reshape(Enear,100,100) contour(real.(getindex.(Enear,1))) -heatmap(Z, Y, real.(getindex.(Enear,1)), clim=(0.0,1.0)) +heatmap(Z, Y, real.(getindex.(Enear,1))) -=# +plot!(Y[2:99],real.(getindex.(Enear[2:99,50],1)),label="D-VIE (simplex)", linestyle=:dash, linecolor=:darkorange4) +#plot!(Y[2:99],real.(getindex.(Enear_simplex[2:99,50],1)),label="D-VIE (simplex)", linestyle=:solid, linecolor=:darkorange3) + +#= +import Cairo +using DataFrames, Gadfly, RDatasets +D = dataset("datasets","HairEyeColor") +palette = ["skyblue","skyblue3"] + + +D = DataFrame( + Type=["D-VIE \n (1,190 Tets.)","D-VIE \n (1,190 Tets.)","D-VIE \n (1,190 Tets.)","D-VIE \n (1,190 Tets.)","E-VIE \n (1,190 Tets.)","E-VIE \n (1,190 Tets.)","E-VIE \n (1,190 Tets.)","E-VIE \n (1,190 Tets.)","D-VIE \n (2,706 Tets.)","D-VIE \n (2,706 Tets.)","D-VIE \n (2,706 Tets.)","D-VIE \n (2,706 Tets.)","E-VIE \n (2,706 Tets.)","E-VIE \n (2,706 Tets.)","E-VIE \n (2,706 Tets.)","E-VIE \n (2,706 Tets.)"], + Quad=["Simplex","Simplex","Classic","Classic","Simplex","Simplex","Classic","Classic","Simplex","Simplex","Classic","Classic","Simplex","Simplex","Classic","Classic"], + Sing=["Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular","Regular","Singular"], + Time=[24,4.77,23.1,27.9,41.9,8.49,41.5,45.7,125,13.1,125,68.6,222,20.5,222,115], +) + +p = Gadfly.plot(D, x=:Quad, y=:Time, color=:Sing, xgroup=:Type, + Geom.subplot_grid(Geom.bar(position=:stack)), + Scale.color_discrete_manual(palette...), + Guide.xlabel(""),Guide.ylabel("Assembly time [s]"), Guide.colorkey("Interaction"),Theme(major_label_font="Times",minor_label_font="Times",key_title_font="Times", key_label_font="Times",background_color=color("white"))) +# draw(PNG("haireyecolor.png", 6.6inch, 4inch), p) +draw(PNG(6.6inch, 4inch), p) +=# \ No newline at end of file diff --git a/examples/dvsie.jl b/examples/dvsie.jl new file mode 100644 index 00000000..dfa822c5 --- /dev/null +++ b/examples/dvsie.jl @@ -0,0 +1,198 @@ +using CompScienceMeshes, BEAST +using LinearAlgebra +using Profile +using LiftedMaps +using BlockArrays +using StaticArrays + + + +ntrc = X->ntrace(X,Γ) + +T = tetmeshsphere(1.0,0.3) +X = nedelecd3d(T) +Γ = boundary(T) +Y = raviartthomas(Γ) + +@show numfunctions(X) +@show numfunctions(Y) + +Z = BEAST.buffachristiansen2(Γ) + + +κ, η = 1.0, 1.0 +κ′, η′ = √2κ, η/√2 +ϵ_r =3 +ϵ_b =2.0 +κ_in, η_in = √6κ, η/√6 + + +#χ = x->(1.0-1.0/ϵ_r) + +function tau(x::SVector{U,T}) where {U,T} + 1.0-1.0/3.0 +end + +χ = tau + + + +N = NCross() +#Volume-Volume +L,I,B = VIE.singlelayer(wavenumber=κ′, tau=χ), Identity(), VIE.boundary(wavenumber=κ′, tau=χ) +#Volume-Surface +Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ′)), transpose(VSIE.boundary(wavenumber=κ′)), transpose(VSIE.doublelayer(wavenumber=κ′)) +#Kt = VSIE.doublelayerT(wavenumber=κ′) + +Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ′, tau=χ), VSIE.boundary(wavenumber=κ′, tau=χ), VSIE.doublelayer(wavenumber=κ′, tau=χ) +#Surface-Surface +T = Maxwell3D.singlelayer(wavenumber=κ) #Outside +T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside +K = Maxwell3D.doublelayer(wavenumber=κ) #Outside +K′ = Maxwell3D.doublelayer(wavenumber=κ′) #Inside + +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +H = -1/(im*κ*η)*curl(E) + +e, h = (n × E) × n, (n × H) × n + +@hilbertspace D j m +@hilbertspace k l o +β = 1/(ϵ_r*ϵ_b) +ν = 1/ϵ_b +α, α′ = 1/η, 1/η′ +γ′ = im*η′/κ′ +ζ′ = im*κ′/(ϵ_b*η′) +δ′ = im*κ′/ϵ_b + +#= +eq = @varform (β*I[k,D] + η′*Lt[k,j]-γ′*Bt[ntrc(k),j] - Kt[k,m] + + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) + +=# + +eq = @varform (β*I[k,D]-ν*L[k,D]-ν*B[ntrc(k),D] + η′*Lt[k,j]-γ′*Bt[ntrc(k),j] - Kt[k,m] + + -δ′*Ls[l,D]-ν*Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + + -ζ′*Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) + + +dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y + +u_n = solve(dvsie) + + +#= +#preconditioner +Mxx = assemble(I,X,X) +iMxx = inv(Matrix(Mxx)) + +Tzz = assemble(T,Z,Z); println("dual discretisation assembled.") +Nyz = Matrix(assemble(N,Y,Z)); println("duality form assembled.") + +iNyz = inv(Nyz); println("duality form inverted.") +NTN = iNyz' * Tzz * iNyz + +M = zeros(Int, 3) +N = zeros(Int, 3) + +M[1] = numfunctions(X) +N[1] = numfunctions(X) +M[2] = M[3] = numfunctions(Y) +N[2] = N[3] = numfunctions(Y) + +U = BlockArrays.blockedrange(M) +V = BlockArrays.blockedrange(N) + +precond = BEAST.ZeroMap{Float32}(U, V) + +z1 = LiftedMap(iMxx,Block(1),Block(1),U,V) +z2 = LiftedMap(NTN,Block(2),Block(2),U,V) +z3 = LiftedMap(NTN,Block(3),Block(3),U,V) +precond = precond +ϵ_r*z1 + z2 + z3 + +A_dvsie_precond = precond*A_dvsie + +#GMREs +import IterativeSolvers +cT = promote_type(eltype(A_dvsie), eltype(rhs)) +x = PseudoBlockVector{cT}(undef, M) +fill!(x, 0) +x, ch = IterativeSolvers.gmres!(x, A_dvsie, Rhs, log=true, reltol=1e-4) +fill!(x, 0) +x, ch = IterativeSolvers.gmres!(x, precond*A_dvsie, precond*Rhs, log=true, reltol=1e-8) +=# + +#Post processing +Θ, Φ = range(0.0,stop=2π,length=100), 0.0 +ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] + +# Don't forget the far field comprises two contributions +ffm = potential(MWFarField3D(κ*im), ffpoints, u_n[m], Y) +ffj = potential(MWFarField3D(κ*im), ffpoints, u_n[j], Y) +ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) + + +ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u_n[D], X) +ff2 = im*κ*ffd + +using Plots +plot(xlabel="theta") +plot!(Θ,norm.(ff),label="far field",title="DVSIE") +plot!(Θ,√6.0*norm.(ff),label="far field",title="DVSIE 2") + +import Plotly +using LinearAlgebra +fcrj, _ = facecurrents(u_n[j],Y) +fcrm, _ = facecurrents(u_n[m],Y) +vsie_j = Plotly.plot([patch(Γ, norm.(fcrj))], Plotly.Layout(title="j D-VSIE")) +vsie_m = Plotly.plot(patch(Γ, norm.(fcrm)), Plotly.Layout(title="m D-VSIE")) + +#NearField +function nearfield(um,uj,Xm,Xj,κ,η,points, + Einc=(x->point(0,0,0)), + Hinc=(x->point(0,0,0))) + + K = BEAST.MWDoubleLayerField3D(wavenumber=κ) + T = BEAST.MWSingleLayerField3D(wavenumber=κ) + + Em = potential(K, points, um, Xm) + Ej = potential(T, points, uj, Xj) + E = -Em + η*Ej + Einc.(points) + + Hm = potential(T, points, um, Xm) + Hj = potential(K, points, uj, Xj) + H = 1/η*Hm + Hj + Hinc.(points) + + return E, H +end + +Zz = range(-1,1,length=100) +Yy = range(-1,1,length=100) +nfpoints = [point(0.0,y,z) for z in Zz, y in Yy] + + +import Base.Threads: @spawn +task1 = @spawn nearfield(u_n[m],u_n[j],Y,Y,κ,η,nfpoints,E,H) +task2 = @spawn nearfield(-u_n[m],-u_n[j],Y,Y,κ_in,η_in,nfpoints) + +E_ex, H_ex = fetch(task1) +E_in, H_in = fetch(task2) + + + +Enear = BEAST.grideval(nfpoints,β.* u_n[D],X) +Enear = reshape(Enear,100,100) + +contour(real.(getindex.(Enear,1))) +heatmap(Zz, Yy, real.(getindex.(Enear,1))) + +heatmap(Zz, Yy, real.(getindex.(E_in,1))) +heatmap(Zz, Yy, real.(getindex.(E_ex,1))) + +contour(real.(getindex.(E_ex,1))) + + +Dd = range(1,100,step=1) +plot!(Yy,real.(getindex.(E_in[Dd,50],1))) +plot(collect(Yy)[2:99],real.(getindex.(Enear[2:99,50],1))) \ No newline at end of file diff --git a/examples/evie.jl b/examples/evie.jl index 2ef54e01..358512bb 100644 --- a/examples/evie.jl +++ b/examples/evie.jl @@ -3,19 +3,22 @@ using LinearAlgebra using Profile using StaticArrays -function tau(x::SVector{U,T}) where {U,T} - 4.0-1.0 -end + ttrc = X->ttrace(X,y) -T = CompScienceMeshes.tetmeshsphere(1.0,0.25) +T= tetmeshsphere(1.0,0.2) X = nedelecc3d(T) y = boundary(T) @show numfunctions(X) ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) -ϵ_r = 4.0 +ϵ_r = 5.0 + +function tau(x::SVector{U,T}) where {U,T} + 5.0 -1.0 +end + χ = tau K, I, B = VIE.singlelayer2(wavenumber=κ, tau=χ), Identity(), VIE.boundary2(wavenumber=κ, tau=χ) E = VIE.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) @@ -27,29 +30,31 @@ H = -1/(im*κ*η)*curl(E) eq = @varform α*I[j,k]-K[j,k]+B[ttrc(j),k] == E[j] evie = @discretise eq j∈X k∈X -u = solve(evie) +u_n = solve(evie) #postprocessing -Φ, Θ = [0.0], range(0,stop=2π,length=100) -pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ ] -ffe = potential(VIE.farfield(wavenumber=κ, tau=χ), pts, u, X) -ff = ffe - -using Plots +#Φ, Θ = [0.0], range(0,stop=2π,length=100) +#pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ ] +#ffe = potential(VIE.farfield(wavenumber=κ, tau=χ), pts, u_n, X) +#ff = ffe #Farfield -plot(xlabel="theta") -plot!(Θ, norm.(ff), label="far field", title="E-VIE") +#plot(xlabel="theta") +#plot!(Θ, norm.(ff), label="far field", title="E-VIE") + +using Plots #Nearfield Z = range(-1,1,length=100) Y = range(-1,1,length=100) -nfpoints = [point(0,y,z) for y in Y, z in Z] +nfpoints = [point(0,y,z) for z in Z, y in Y] + +Enear2 = BEAST.grideval(nfpoints,u_n,X) +Enear2 = reshape(Enear2,100,100) -Enear = BEAST.grideval(nfpoints,u,X) -Enear = reshape(Enear,100,100) +contour(real.(getindex.(Enear2,1))) +heatmap(Z, Y, real.(getindex.(Enear2,1))) -contour(real.(getindex.(Enear,1))) -heatmap(Z, Y, real.(getindex.(Enear,1)), clim=(0.0,1.0)) +plot!(Y[2:99],real.(getindex.(Enear2[2:99,50],1)),label="E-VIE (simplex)", linestyle=:dash, linecolor=:darkolivegreen4) diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index c20cb074..e3abae79 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -1,6 +1,11 @@ using CompScienceMeshes, BEAST using LinearAlgebra +using LiftedMaps +using BlockArrays +T = CompScienceMeshes.tetmeshsphere(1.0,0.12) +X = BEAST.nedelecc3d(T) +Γ = boundary(T) function nearfield(um,uj,Xm,Xj,κ,η,points, Einc=(x->point(0,0,0)), @@ -31,15 +36,13 @@ c = 1/√(ϵ0*μ0) Γ = boundary(Ω) X = raviartthomas(Γ) @show numfunctions(X) +Y = BEAST.buffachristiansen2(Γ) -ϵr = 2.0 -μr = 1.0 -κ, η = ω/c, √(μ0/ϵ0) -κ′, η′ = κ*√(ϵr*μr), η*√(μr/ϵr) +κ, η = 1.0, 1.0 +κ′, η′ = √5.0κ, η/√5.0 -# κ, η = 1.0, 1.0 -# κ′, η′ = √6.0κ, η/√6.0 +N = NCross() T = Maxwell3D.singlelayer(wavenumber=κ) T′ = Maxwell3D.singlelayer(wavenumber=κ′) @@ -63,6 +66,40 @@ pmchwt = @discretise( u = solve(pmchwt) +#preconditioner +#= +Tyy = assemble(T,Y,Y); println("dual discretisation assembled.") +Nxy = Matrix(assemble(N,X,Y)); println("duality form assembled.") + +iNxy = inv(Nxy); println("duality form inverted.") +NTN = iNxy' * Tyy * iNxy + +M = zeros(Int, 2) +N = zeros(Int, 2) + +M[1] = M[2] = numfunctions(X) +N[1] = N[2] = numfunctions(X) + +U = BlockArrays.blockedrange(M) +V = BlockArrays.blockedrange(N) + +precond = BEAST.ZeroMap{Float32}(U, V) + +z1 = LiftedMap(NTN,Block(1),Block(1),U,V) +z2 = LiftedMap(NTN,Block(2),Block(2),U,V) +precond = precond + z1 + z2 + +A_pmchwt_precond = precond*A_pmchwt + +#GMREs +import IterativeSolvers +cT = promote_type(eltype(A_pmchwt), eltype(rhs)) +x = PseudoBlockVector{cT}(undef, M) +fill!(x, 0) +x, ch = IterativeSolvers.gmres!(x, A_pmchwt, rhs, log=true, reltol=1e-6) +fill!(x, 0) +x, ch = IterativeSolvers.gmres!(x, precond*A_pmchwt, precond*rhs, log=true, reltol=1e-6) +=# Θ, Φ = range(0.0,stop=2π,length=100), 0.0 ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] @@ -87,12 +124,12 @@ Plots.plot(xlabel="theta") Plots.plot!(Θ,norm.(ff),label="far field",title="PMCHWT") -#import Plotly -#using LinearAlgebra -#fcrj, _ = facecurrents(u[j],X) -#fcrm, _ = facecurrents(u[m],X) -#Plotly.plot(patch(Γ, norm.(fcrj))) -#Plotly.plot(patch(Γ, norm.(fcrm))) +import Plotly +using LinearAlgebra +fcrj, _ = facecurrents(u[j],X) +fcrm, _ = facecurrents(u[m],X) +Plotly.plot(patch(Γ, norm.(fcrj)),Plotly.Layout(title="j PMCHWT")) +Plotly.plot(patch(Γ, norm.(fcrm)),Plotly.Layout(title="m PMCHWT")) @@ -121,10 +158,11 @@ Plots.heatmap(Z, Y, real.(getindex.(H_tot,2))) Plots.heatmap(Z, Y, imag.(getindex.(H_tot,2))) Plots.plot(real.(getindex.(E_tot[:,51],1))) -Plots.plot(real.(getindex.(H_tot[:,51],2))) +Plots.plot!(real.(getindex.(H_tot[:,51],2))) plot() plot!(Θ, norm.(ff),label="far field") scatter!(Θ, norm.(Et_far), label="field far") +=# \ No newline at end of file diff --git a/src/BEAST.jl b/src/BEAST.jl index 8aa377e7..c632084f 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -15,6 +15,8 @@ using FastGaussQuadrature using LinearMaps using LiftedMaps +using TimerOutputs + using AbstractTrees using NestedUnitRanges diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index 2746604c..3c140015 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -75,7 +75,7 @@ end function momintegrals!(op::VIEOperator, test_local_space::RefSpace, trial_local_space::RefSpace, test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) - + @timeit to "singular" begin #Find permutation of vertices to match location of singularity to SauterSchwab J, I= SauterSchwab3D.reorder(strat.sing) @@ -127,11 +127,13 @@ function momintegrals!(op::VIEOperator, out[i,j] += Q[K[i],L[j]]*O1[i]*O2[j] end end + + end nothing end function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) - + @timeit to "regular" begin # memory allocation here is a result from the type instability on strat # which is on purpose, i.e. the momintegrals! method is chosen based # on dynamic polymorphism. @@ -165,7 +167,8 @@ function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::Do end end end - + + end return z end @@ -238,6 +241,7 @@ function momintegrals!(biop::VSIEOperator, tshs, bshs, tcell, bcell, z, strat::D wimps = strat.inner_quad_points M, N = size(z) + for womp in womps tgeo = womp.point @@ -254,7 +258,7 @@ function momintegrals!(biop::VSIEOperator, tshs, bshs, tcell, bcell, z, strat::D igd = integrand(biop, kernel, tvals, tgeo, bvals, bgeo) - + for m in 1 : length(tvals) for n in 1 : length(bvals) @@ -265,6 +269,6 @@ function momintegrals!(biop::VSIEOperator, tshs, bshs, tcell, bcell, z, strat::D end end - return z + #return z end diff --git a/src/volumeintegral/vieexc.jl b/src/volumeintegral/vieexc.jl index dca5e685..d54f20d3 100644 --- a/src/volumeintegral/vieexc.jl +++ b/src/volumeintegral/vieexc.jl @@ -21,6 +21,15 @@ planewavevie(; ) = PlaneWaveVIE(direction, polarization, wavenumber, amplitude) function (e::PlaneWaveVIE)(p) + k = e.wavenumber + d = e.direction + u = e.polarisation + a = e.amplitude + x = p + a * exp(-im * k * dot(d, x)) * u + end + + function (e::PlaneWaveVIE)(p::CompScienceMeshes.MeshPointNM) k = e.wavenumber d = e.direction u = e.polarisation diff --git a/src/volumeintegral/vieops.jl b/src/volumeintegral/vieops.jl index 0879f9dd..611b2fe4 100644 --- a/src/volumeintegral/vieops.jl +++ b/src/volumeintegral/vieops.jl @@ -178,7 +178,7 @@ function integrand(viop::VIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) end -defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) +defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) function quaddata(op::VIEOperator, @@ -230,15 +230,18 @@ function qr_volume(op::VolumeOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, q end end - #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) - - + #Simplex-product quadrature rules hits == 4 && return SauterSchwab3D.CommonVolume6D_S(SauterSchwab3D.Singularity6DVolume(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[4])) hits == 3 && return SauterSchwab3D.CommonFace6D_S(SauterSchwab3D.Singularity6DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 2 && return SauterSchwab3D.CommonEdge6D_S(SauterSchwab3D.Singularity6DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3],qd.sing_qp[4])) hits == 1 && return SauterSchwab3D.CommonVertex6D_S(SauterSchwab3D.Singularity6DPoint(idx_t,idx_s),qd.sing_qp[3]) - - + #= + #Classic tensor-product quadrature rules + hits == 4 && return SauterSchwab3D.CommonVolume6D(SauterSchwab3D.Singularity6DVolume(idx_t,idx_s),qd.sing_qp[1]) + hits == 3 && return SauterSchwab3D.CommonFace6D(SauterSchwab3D.Singularity6DFace(idx_t,idx_s),qd.sing_qp[1]) + hits == 2 && return SauterSchwab3D.CommonEdge6D(SauterSchwab3D.Singularity6DEdge(idx_t,idx_s),qd.sing_qp[1]) + hits == 1 && return SauterSchwab3D.CommonVertex6D(SauterSchwab3D.Singularity6DPoint(idx_t,idx_s),qd.sing_qp[1]) + =# return DoubleQuadRule( qd[1][1,i], @@ -273,13 +276,15 @@ function qr_boundary(op::BoundaryOperator, g::RefSpace, f::RefSpace, i, τ, j, end end - #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) - - + hits == 3 && return SauterSchwab3D.CommonFace5D_S(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 2 && return SauterSchwab3D.CommonEdge5D_S(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 1 && return SauterSchwab3D.CommonVertex5D_S(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),(qd.sing_qp[3],qd.sing_qp[2])) - + #= + hits == 3 && return SauterSchwab3D.CommonFace5D(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),qd.sing_qp[1]) + hits == 2 && return SauterSchwab3D.CommonEdge5D(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),qd.sing_qp[1]) + hits == 1 && return SauterSchwab3D.CommonVertex5D(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),qd.sing_qp[1]) + =# return DoubleQuadRule( qd[1][1,i], diff --git a/src/volumeintegral/vsie.jl b/src/volumeintegral/vsie.jl index 57d2cb35..9947f567 100644 --- a/src/volumeintegral/vsie.jl +++ b/src/volumeintegral/vsie.jl @@ -41,14 +41,14 @@ module VSIE @assert gamma != nothing - alpha == nothing && (alpha = gamma) - beta == nothing && (beta = im*im/gamma) + alpha == nothing && (alpha = -gamma) + beta == nothing && (beta = -1/gamma) tau == nothing && (tau = x->1.0) Mod.VSIESingleLayer(gamma, alpha, beta, tau) end - function singlelayerT(; + function singlelayer2(; gamma=nothing, wavenumber=nothing, alpha=nothing, @@ -73,11 +73,11 @@ module VSIE @assert gamma != nothing - alpha == nothing && (alpha = wavenumber*wavenumber) - beta == nothing && (beta = 1.0) + alpha == nothing && (alpha = -gamma) + beta == nothing && (beta = -1/gamma) tau == nothing && (tau = x->1.0) - Mod.VSIESingleLayerT(gamma, alpha, beta, tau) + Mod.VSIESingleLayer2(gamma, alpha, beta, tau) end """ diff --git a/src/volumeintegral/vsieops.jl b/src/volumeintegral/vsieops.jl index 85222b23..7338e7d3 100644 --- a/src/volumeintegral/vsieops.jl +++ b/src/volumeintegral/vsieops.jl @@ -1,6 +1,9 @@ abstract type VSIEOperator <: IntegralOperator end abstract type VolumeSurfaceOperator <: VSIEOperator end abstract type BoundarySurfaceOperator <: VSIEOperator end +abstract type VSIEOperatorT <: VSIEOperator end +abstract type VolumeSurfaceOperatorT <: VolumeSurfaceOperator end +abstract type BoundarySurfaceOperatorT <: BoundarySurfaceOperator end struct KernelValsVSIE{T,U,P,Q,K} gamma::U @@ -20,7 +23,6 @@ function kernelvals(viop::VSIEOperator, p ,q) expn = exp(-yR) green = expn / (4*pi*R) gradgreen = - (Y +1/R)*green/R*r - tau = viop.tau(cartesian(q)) @@ -46,26 +48,26 @@ struct VSIEDoubleLayer{T,U,P} <: VolumeSurfaceOperator tau::P end - -struct VSIESingleLayerT{T,U,P} <: VolumeSurfaceOperator +#= +struct VSIESingleLayer2{T,U,P} <: VolumeSurfaceOperator gamma::T α::U β::U tau::P end -struct VSIEBoundaryT{T,U,P} <: BoundarySurfaceOperator +struct VSIEBoundaryT{T,U,P} <: BoundarySurfaceOperatorT gamma::T α::U tau::P end -struct VSIEDoubleLayerT{T,U,P} <: VolumeSurfaceOperator +struct VSIEDoubleLayerT{T,U,P} <: VolumeSurfaceOperatorT gamma::T α::U tau::P end - +=# scalartype(op::VSIEOperator) = typeof(op.gamma) export VSIE @@ -78,6 +80,15 @@ struct VSIEIntegrand{S,T,O,K,L} trial_local_space::L end +#= +struct VSIEIntegrandT{S,T,O,K,L} + test_tetrahedron_element::S + trial_triangle_element::T + op::O + test_local_space::K + trial_local_space::L +end +=# function (igd::VSIEIntegrand)(u,v) @@ -99,6 +110,28 @@ function (igd::VSIEIntegrand)(u,v) end +#= +function (igd::VSIEIntegrandT)(u,v) + + #mesh points + tgeo = neighborhood(igd.test_tetrahedron_element,u) + bgeo = neighborhood(igd.trial_triangle_element,v) + + #kernel values + kerneldata = kernelvals(igd.op,tgeo,bgeo) + + #values & grad/div/curl of local shape functions + tval = igd.test_local_space(tgeo) + bval = igd.trial_local_space(bgeo) + + #jacobian + j = jacobian(tgeo) * jacobian(bgeo) + + integrand(igd.op, kerneldata,tval,tgeo,bval,tgeo) * j +end +=# + + function integrand(viop::VSIESingleLayer, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:3] @@ -124,20 +157,21 @@ function integrand(viop::VSIEBoundary, kerneldata, tvals, tgeo, bvals, bgeo) G = kerneldata.green - Tx = kerneldata.tau + Ty = kerneldata.tau α = viop.α - @SMatrix[α * Tx * dgx[i] * fy[j] * G for i in 1:3, j in 1:1] + @SMatrix[α * Ty * dgx[i] * fy[j] * G for i in 1:3, j in 1:1] end -function integrand(viop::VSIESingleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) +#= +function integrand(viop::VSIESingleLayer2, kerneldata, tvals, tgeo, bvals, bgeo) - gx = @SVector[tvals[i].value for i in 1:4] - fy = @SVector[bvals[i].value for i in 1:3] + gx = @SVector[tvals[i].value for i in 1:3] + fy = @SVector[bvals[i].value for i in 1:4] - dgx = @SVector[tvals[i].divergence for i in 1:4] - dfy = @SVector[bvals[i].divergence for i in 1:3] + dgx = @SVector[tvals[i].divergence for i in 1:3] + dfy = @SVector[bvals[i].divergence for i in 1:4] G = kerneldata.green @@ -146,7 +180,7 @@ function integrand(viop::VSIESingleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) α = viop.α β = viop.β - @SMatrix[α * dot(gx[i],Ty*fy[j]) * G + β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] + @SMatrix[α * dot(gx[i],Ty*fy[j]) * G - β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] end @@ -163,7 +197,7 @@ function integrand(viop::VSIEBoundaryT, kerneldata, tvals, tgeo, bvals, bgeo) @SMatrix[α * Tx * gx[i] * dfy[j] * G for i in 1:3, j in 1:1] end - +=# function integrand(viop::VSIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:3] @@ -178,21 +212,23 @@ function integrand(viop::VSIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:3, j in 1:4] end +#= function integrand(viop::VSIEDoubleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:4] fy = @SVector[bvals[i].value for i in 1:3] + gradG = kerneldata.gradgreen - + Ty = kerneldata.tau α = viop.α - @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:4, j in 1:3] + @SMatrix[α * transpose(cross(fy[j],gx[i])) *gradG for i in 1:4, j in 1:3] end +=# - -defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) +defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) function quaddata(op::VSIEOperator, @@ -221,7 +257,9 @@ quadrule(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs::SauterSchwab3DQStrat) - + + @assert (length(τ.vertices)==3 && length(σ.vertices)==4) "Expected simplex wrong" + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) hits = 0 @@ -236,8 +274,8 @@ function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j d2 = LinearAlgebra.norm_sqr(t-s) dmin2 = min(dmin2, d2) if d2 < dtol - push!(idx_t,i) - push!(idx_s,j) + push!(idx_t,j) + push!(idx_s,i) hits +=1 break end @@ -249,8 +287,8 @@ function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j hits == 3 && return SauterSchwab3D.CommonFace5D_S(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 2 && return SauterSchwab3D.CommonEdge5D_S(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 1 && return SauterSchwab3D.CommonVertex5D_S(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),(qd.sing_qp[3],qd.sing_qp[2])) - - + #hits == 0 && return SauterSchwab3D.PositiveDistance5D_S(SauterSchwab3D.Singularity5DPositiveDistance(),(qd.sing_qp[3],qd.sing_qp[2])) + return DoubleQuadRule( qd[1][1,i], qd[2][1,j]) From 429d6dba0bf05b36332cd841c32000f57d7fe68a Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Wed, 16 Mar 2022 15:33:53 +0100 Subject: [PATCH 258/528] mixed mueller formulation --- examples/mueller.jl | 88 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 examples/mueller.jl diff --git a/examples/mueller.jl b/examples/mueller.jl new file mode 100644 index 00000000..5c95731a --- /dev/null +++ b/examples/mueller.jl @@ -0,0 +1,88 @@ +using CompScienceMeshes, BEAST + + +T = tetmeshsphere(1.0,0.3) +X = nedelecc3d(T) +Γ = boundary(T) + +X, Y = raviartthomas(Γ), buffachristiansen(Γ) + +ω = 1.0 +ϵ, ϵ′ = 1.0, 5.0 +μ, μ′ = 1.0, 1.0 +κ, η = ω*√(ϵ*μ), √(μ/ϵ) +κ′, η′ = ω*√(ϵ′*μ′), √(μ′/ϵ′) + + + +N = NCross() + +T = Maxwell3D.singlelayer(wavenumber=κ) +T′ = Maxwell3D.singlelayer(wavenumber=κ′) +K = Maxwell3D.doublelayer(wavenumber=κ) +K′ = Maxwell3D.doublelayer(wavenumber=κ′) + +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +H = -1/(im*κ*η)*curl(E) +e, h = (n × E) × n, (n × H) × n + +@hilbertspace j m +@hilbertspace k l + +α, α′ = μ/η, μ′/η′ +mueller = @discretise( + (ϵ*η*T-ϵ′*η′*T′)[k,j] - ((ϵ+ϵ′)/2*N+ϵ*K-ϵ′*K′)[k,m] + +((μ+μ′)/2*N+μ*K-μ′*K′)[l,j] + (α*T-α′*T′)[l,m] == -ϵ*e[k] - μ*h[l], +j∈X, m∈X, k∈Y, l∈Y) +u,A_mueller,rhs_mueller = solve(mueller) + +#postprocessing +using Plots +import Plotly +using LinearAlgebra +fcrj, _ = facecurrents(u[j],X) +fcrm, _ = facecurrents(u[m],X) +Plotly.plot(patch(Γ, norm.(fcrj)),Plotly.Layout(title="j Mueller")) +Plotly.plot(patch(Γ, norm.(fcrm)),Plotly.Layout(title="m Mueller")) + + +function nearfield(um,uj,Xm,Xj,κ,η,points, + Einc=(x->point(0,0,0)), + Hinc=(x->point(0,0,0))) + + K = BEAST.MWDoubleLayerField3D(wavenumber=κ) + T = BEAST.MWSingleLayerField3D(wavenumber=κ) + + Em = potential(K, points, um, Xm) + Ej = potential(T, points, uj, Xj) + E = -Em + η * Ej + Einc.(points) + + Hm = potential(T, points, um, Xm) + Hj = potential(K, points, uj, Xj) + H = 1/η*Hm + Hj + Hinc.(points) + + return E, H +end + + +Z = range(-1,1,length=100) +Y = range(-1,1,length=100) +nfpoints = [point(0,y,z) for z in Z, y in Y] + +import Base.Threads: @spawn +task1 = @spawn nearfield(u[m],u[j],X,X,κ,η,nfpoints,E,H) +task2 = @spawn nearfield(-u[m],-u[j],X,X,κ′,η′,nfpoints) + +E_ex, H_ex = fetch(task1) +E_in, H_in = fetch(task2) + +E_tot = E_in + E_ex +H_tot = H_in + H_ex + +contour(real.(getindex.(E_tot,1))) +contour(real.(getindex.(H_tot,2))) + +heatmap(Z, Y, real.(getindex.(E_tot,1)),title="E_tot Mueller") +#heatmap(Z, Y, real.(getindex.(H_tot,2))) +#heatmap(Z, Y, real.(getindex.(E_in,1))) +#heatmap(Z, Y, real.(getindex.(E_ex,1))) \ No newline at end of file From 570f17421e9f0a01364079f4385e3a9ebedacbac Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Wed, 6 Apr 2022 10:19:29 +0200 Subject: [PATCH 259/528] updated farfield --- examples/pmchwt.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index e3abae79..dd3a8f76 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -104,8 +104,8 @@ x, ch = IterativeSolvers.gmres!(x, precond*A_pmchwt, precond*rhs, log=true, rel ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] # Don't forget the far field comprises two contributions -ffm = potential(MWFarField3D(κ*im, η), ffpoints, u[m], X) -ffj = potential(MWFarField3D(κ*im, η), ffpoints, u[j], X) +ffm = potential(MWFarField3D(gamma=κ*im), ffpoints, u[m], X) +ffj = potential(MWFarField3D(gamma=κ*im), ffpoints, u[j], X) ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) # Compare the far field and the field far From b442e5a30c3131a53bc5ee1cc57825c7cda29cc9 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Tue, 28 Jun 2022 10:50:44 +0200 Subject: [PATCH 260/528] quadratic lagrange basisfunctions --- src/BEAST.jl | 7 +- src/bases/lagrange.jl | 134 ++++++++++++++++++++++++- src/bases/local/bdmlocal.jl | 4 + src/bases/local/laglocal.jl | 87 +++++++++++++++- src/bases/local/ndlocal.jl | 5 + src/maxwell/mwops.jl | 32 ++++++ src/maxwell/sauterschwabints_bdm_rt.jl | 95 ++++++++++++++++++ src/postproc/segcurrents.jl | 2 +- 8 files changed, 361 insertions(+), 5 deletions(-) create mode 100644 src/maxwell/sauterschwabints_bdm_rt.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index c632084f..1dd6b5a9 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -29,7 +29,7 @@ export dot export planewave export RefSpace, numfunctions, coordtype, scalartype, assemblydata, geometry, refspace, valuetype -export lagrangecxd0, lagrangec0d1, duallagrangec0d1 +export lagrangecxd0, lagrangec0d1, duallagrangec0d1, lagrangec0d2 export duallagrangecxd0 export lagdimension export restrict @@ -207,8 +207,11 @@ include("timedomain/zdomain.jl") # Support for Maxwell equations include("maxwell/mwexc.jl") include("maxwell/mwops.jl") -include("maxwell/nxdbllayer.jl") include("maxwell/wiltonints.jl") +include("maxwell/sauterschwabints_rt.jl") +include("maxwell/sauterschwabints_bdm.jl") +include("maxwell/sauterschwabints_bdm_rt.jl") +include("maxwell/nxdbllayer.jl") include("maxwell/nitsche.jl") include("maxwell/farfield.jl") include("maxwell/nearfield.jl") diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 790492e3..420fdf4f 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -525,6 +525,137 @@ function duallagrangec0d1(mesh, mesh2, pred, ::Type{Val{2}}) LagrangeBasis{1,0,NF}(geometry, fns, pos) end +""" + lagrangec0d2(mesh) -> basis + +Build lagrangec0d2 elements, including boundary vertices (meaning assuming a closed mesh). +""" + +function lagrangec0d2(mesh) + + #mark non connectet vertices + verts = skeleton(mesh, 0) + detached = trues(numvertices(mesh)) + for v in cells(verts) + detached[v] = false + end + + #identify + bnd = boundary(mesh) + bndverts = skeleton(bnd, 0) + notonbnd = trues(numvertices(mesh)) + for v in cells(bndverts) + notonbnd[v] = false + end + + vertexlist = findall(.!detached) + + edges = skeleton(mesh, 1; sort) + cps = cellpairs(mesh, edges, dropjunctionpair=true) + #only interior edges + ids = findall(x -> x>0, cps[2,:]) + + lagrangec0d2(mesh,vertexlist,cps,Val{dimension(mesh)+1}) +end + + +function lagrangec0d2(mesh, vertexlist::Vector, cellpairs::Array{Int,2}, ::Type{Val{3}}) + + T = coordtype(mesh) + U = universedimension(mesh) + + numvertices = length(vertexlist) + numedges = size(cellpairs,2) + + cellids, ncells = vertextocellmap(mesh) + + Cells = cells(mesh) + Verts = vertices(mesh) + + # create the local shapes + functions = Vector{Vector{Shape{Float64}}}(undef,numvertices+numedges) + positions = Vector{vertextype(mesh)}(undef,numvertices+numedges) + + #temp containers for vertex dof + fns = Vector{Vector{Shape{Float64}}}() + pos = Vector{vertextype(mesh)}() + + sizehint!(fns, length(vertexlist)) + sizehint!(pos, length(vertexlist)) + + #shape function associated with vetex dof + for v in vertexlist + + numshapes = ncells[v] + numshapes == 0 && continue + + shapes = Vector{Shape{Float64}}(undef,numshapes) + for s in 1: numshapes + c = cellids[v,s] + # cell = mesh.faces[c] + cell = Cells[c] + + localid = something(findfirst(isequal(v), cell),0) + @assert localid != 0 + + shapes[s] = Shape(c, localid, 1.0) + end + #functions[v] = shapes + #positions[v] = Verts[v] + + push!(fns, shapes) + push!(pos, mesh.vertices[v]) + end + + @assert length(fns) == numvertices + + for i in 1:numvertices + functions[i] = fns[i] + positions[i] = pos[i] + end + + #shape function associated with edge dof + for i in 1:numedges + #internal edge + if cellpairs[2,i] > 0 + c1 = cellpairs[1,i]; cell1 = Cells[c1] #mesh.faces[c1] + c2 = cellpairs[2,i]; cell2 = Cells[c2] #mesh.faces[c2] + e1, e2 = getcommonedge(cell1, cell2) + functions[numvertices+i] = [ + Shape(c1, abs(e1)+3, 1.0), + Shape(c2, abs(e2)+3, 1.0)] + isct = intersect(cell1, cell2) + @assert length(isct) == 2 + @assert !(cell1[abs(e1)] in isct) + @assert !(cell2[abs(e2)] in isct) + + v1 = cell1[mod1(abs(e1)+1,3)] + v2 = cell1[mod1(abs(e1)+2,3)] + + edge = simplex(mesh.vertices[[v1,v2]]...) + + positions[numvertices+i] = cartesian(center(edge)) + #boundary edge + else + c1 = cellpairs[1,i]; cell1 = Cells[c1] + e1 = cellpairs[2,i] + functions[numvertices+i] = [ + Shape(c1, abs(e1)+3, +1.0)] + + v1 = cell1[mod1(abs(e1)+1,3)] + v2 = cell1[mod1(abs(e1)+2,3)] + edge = simplex(mesh.vertices[[v1,v2]]...) + + positions[numvertices+i] = cartesian(center(edge)) + end + end + + + NF = 6 + LagrangeBasis{2,0,NF}(mesh, functions, positions) +end + + gradient(space::LagrangeBasis{1,0}, geo, fns) = NDLCCBasis(geo, fns) # gradient(space::LagrangeBasis{1,0}, geo::CompScienceMeshes.AbstractMesh{U,3} where {U}, fns) = NDBasis(geo, fns) @@ -535,10 +666,11 @@ curl(space::LagrangeBasis{2,0}, geo, fns) = BDMBasis(geo, fns) gradient(space::LagrangeBasis{1,0,<:CompScienceMeshes.AbstractMesh{<:Any,2}}, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, space.pos) - gradient(space::LagrangeBasis{1,0,<:CompScienceMeshes.AbstractMesh{<:Any,3}}, geo, fns) = NDBasis(geo, fns, space.pos) +curl(space::LagrangeBasis{2,0},geo, fns) = BDMBasis(geo, fns) + # # Sclar trace for Laggrange element based spaces # diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index fa4b66c9..15095ccb 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -1,5 +1,9 @@ struct BDMRefSpace{T} <: RefSpace{T,6} end +function valuetype(ref::BDMRefSpace{T}, charttype::Type) where {T} + SVector{universedimension(charttype),T} +end + function (f::BDMRefSpace)(p) u,v = parametric(p) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index bc0f6c60..666317db 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -11,6 +11,7 @@ numfunctions(s::LagrangeRefSpace{T,2,3}) where {T} = 6 valuetype(ref::LagrangeRefSpace{T}, charttype) where {T} = SVector{numfunctions(ref), Tuple{T,T}} + # Evaluate constant lagrange elements on anything (ϕ::LagrangeRefSpace{T,0})(tp) where {T} = SVector(((value=one(T), derivative=zero(T)),)) @@ -39,6 +40,25 @@ function (f::LagrangeRefSpace{T,1,3})(t) where T (value=w, curl=(p[2]-p[1])/j)) end +valuetype(ref::LagrangeRefSpace{T,1,3}, charttype) where {T} = + SVector{1,T} + +# Evaluate quadratic lagrange elements on a triangle +function (f::LagrangeRefSpace{T,2,3})(t) where T + u,v,w, = barycentric(t) + SVector( + (value=((2*u-1)*u),), + (value=((2*v-1)*v),), + (value=((2*w-1)*w),), + (value=(4*v*w),), + (value=(4*w*u),), + (value=(4*u*v),), + ) +end + +valuetype(ref::LagrangeRefSpace{T,2,3}, charttype) where {T} = + SVector{1, T} + """ f(tangent_space, Val{:withcurl}) @@ -72,6 +92,26 @@ function curl(ref::LagrangeRefSpace{T,1,3} where {T}, sh, el) return [sh1, sh2] end +function curl(ref::LagrangeRefSpace{T,2,3}, sh, el) where T + + j = 1.0 #volume(el) * factorial(dimension(el)) + + if sh.refid < 4 + sh1 = Shape(sh.cellid, mod1(2*sh.refid+1,6), -sh.coeff*j) + sh2 = Shape(sh.cellid, mod1(2*sh.refid+2,6), 3*sh.coeff*j) + sh3 = Shape(sh.cellid, mod1(2*sh.refid+3,6), -3*sh.coeff*j) + sh4 = Shape(sh.cellid, mod1(2*sh.refid+4,6), sh.coeff*j) + + return [sh1, sh2, sh3, sh4] + else + sh1 = Shape(sh.cellid, 2*mod1(sh.refid,3)-1, 4*sh.coeff*j) + sh2 = Shape(sh.cellid, 2*mod1(sh.refid,3), -4*sh.coeff*j) + + return [sh1, sh2] + end + +end + function gradient(ref::LagrangeRefSpace{T,1,4}, sh, tet) where {T} this_vert = tet.vertices[sh.refid] # other_verts = deleteat(tet.vertices, sh.refid) @@ -218,4 +258,49 @@ function curl(ref::LagrangeRefSpace{T,2,3} where {T}, sh, el) sh4 = Shape(sh.cellid, mod1(2*sh.refid+7,6), z*sh.coeff) end return [sh1, sh2, sh3, sh4] -end \ No newline at end of file +end + +function restrict(f::LagrangeRefSpace{T,2}, dom1, dom2) where T + + D = numfunctions(f) + Q = zeros(T, D, D) + + # for each point of the new domain + for i in 1:3 + + #vertices + v = dom2.vertices[i] + + # find the barycentric coordinates in dom1 + uvn = carttobary(dom1, v) + + # evaluate the shape functions in this point + x = neighborhood(dom1, uvn) + fx = f(x) + + for j in 1:D + Q[j,i] = fx[j][1] + end + + + #edges + # find the center of edge i of dom2 + a = dom2.vertices[mod1(i+1,3)] + b = dom2.vertices[mod1(i+2,3)] + v = (a + b) / 2 + + # find the barycentric coordinates in dom1 + uvn = carttobary(dom1, v) + + # evaluate the shape functions in this point + x = neighborhood(dom1, uvn) + fx = f(x) + + for j in 4:D + Q[j,i+3] = fx[j][1] + end + end + + return Q +end + diff --git a/src/bases/local/ndlocal.jl b/src/bases/local/ndlocal.jl index 9f0c196f..35ba9386 100644 --- a/src/bases/local/ndlocal.jl +++ b/src/bases/local/ndlocal.jl @@ -8,6 +8,11 @@ there is no well defined concept of adjacent-ness. """ mutable struct NDRefSpace{T} <: RefSpace{T,3} end +function valuetype(ref::NDRefSpace{T}, charttype::Type) where {T} + SVector{universedimension(charttype),T} +end + + function (ϕ::NDRefSpace)(nbd) u, v = parametric(nbd) diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 0cb87dec..3cc7caf9 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -163,6 +163,38 @@ function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::BDMRefSpace, i, τ, end +function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::RTRefSpace, i, τ, j, σ, qd, + qs::DoubleNumWiltonSauterQStrat) + + hits = 0 + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) + dmin2 = floatmax(eltype(eltype(τ.vertices))) + for t in τ.vertices + for s in σ.vertices + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < dtol) + end + end + + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) + hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) + hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + + h2 = volume(σ) + xtol2 = 0.2 * 0.2 + k2 = abs2(op.gamma) + # max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( + # qd.tpoints[2,i], + # DoubleQuadRule( + # qd.tpoints[2,i], + # qd.bpoints[2,j],),) + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j],) +end + + function qrib(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd) dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) diff --git a/src/maxwell/sauterschwabints_bdm_rt.jl b/src/maxwell/sauterschwabints_bdm_rt.jl new file mode 100644 index 00000000..b03d6ee8 --- /dev/null +++ b/src/maxwell/sauterschwabints_bdm_rt.jl @@ -0,0 +1,95 @@ + + +struct MWDL3DIntegrand3{C,O,L,M} + test_triangular_element::C + trial_triangular_element::C + op::O + test_local_space::L + trial_local_space::M +end + +function (igd::MWDL3DIntegrand3)(u,v) + + γ = igd.op.gamma + + x = neighborhood(igd.test_triangular_element,u) + y = neighborhood(igd.trial_triangular_element,v) + + r = cartesian(x) - cartesian(y) + R = norm(r) + G = exp(-γ*R)/(4π*R) + + GG = -(γ + 1/R) * G / R * r + + f = igd.test_local_space(x) #BDM + g = igd.trial_local_space(y) #RT + + j = jacobian(x) * jacobian(y) + + G = @SVector [cross(GG,(j*g[1].value)), cross(GG,(j*g[2].value)), cross(GG,(j*g[3].value))] + + + f1 = f[1].value + f2 = f[2].value + f3 = f[3].value + f4 = f[4].value + f5 = f[5].value + f6 = f[6].value + + SMatrix{6,3}( + dot(f1,G[1]), + dot(f2,G[1]), + dot(f3,G[1]), + dot(f4,G[1]), + dot(f5,G[1]), + dot(f6,G[1]), + dot(f1,G[2]), + dot(f2,G[2]), + dot(f3,G[2]), + dot(f4,G[2]), + dot(f5,G[2]), + dot(f6,G[2]), + dot(f1,G[3]), + dot(f2,G[3]), + dot(f3,G[3]), + dot(f4,G[3]), + dot(f5,G[3]), + dot(f6,G[3])) +end + + +function momintegrals!(op::MWDoubleLayer3D, + test_local_space::BDMRefSpace, trial_local_space::RTRefSpace, + test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) + + I, J, K, L = SauterSchwabQuadrature.reorder( + test_triangular_element.vertices, + trial_triangular_element.vertices, strat) + + test_triangular_element = simplex( + test_triangular_element.vertices[I[1]], + test_triangular_element.vertices[I[2]], + test_triangular_element.vertices[I[3]]) + + trial_triangular_element = simplex( + trial_triangular_element.vertices[J[1]], + trial_triangular_element.vertices[J[2]], + trial_triangular_element.vertices[J[3]]) + + igd = MWDL3DIntegrand3(test_triangular_element, trial_triangular_element, + op, test_local_space, trial_local_space) + Q = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) + + A = levicivita(K) == 1 ? @SVector[1,2] : @SVector[2,1] + + for j ∈ 1:3 + for i ∈ 1:3 + p = K[i] + for m in 1:2 + a = A[m] + out[2*(i-1)+m,j] += Q[2*(p-1)+a,L[j]] + end + end + end + nothing +end diff --git a/src/postproc/segcurrents.jl b/src/postproc/segcurrents.jl index 0d45d3ff..b2e5355d 100644 --- a/src/postproc/segcurrents.jl +++ b/src/postproc/segcurrents.jl @@ -45,7 +45,7 @@ function grideval(points, coeffs, basis; type=nothing) vals = refs(neighborhood(chart,u)) for r in 1 : numfunctions(refs) for (m,w) in ad[i, r] - values[j] += w * coeffs[m] * vals[r][1] + values[j] += w * coeffs[m] * P(vals[r][1]) end end end From aea0166d4c6d29474b819097640743fe279b9f8d Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Tue, 14 Dec 2021 15:27:59 +0100 Subject: [PATCH 261/528] dsvie --- examples/dsvie.jl | 73 +++++++++++++++++++++ examples/pmchwt.jl | 1 - src/volumeintegral/sauterschwab_ints.jl | 9 +-- src/volumeintegral/vsie.jl | 12 ++-- src/volumeintegral/vsieops.jl | 84 +++++++------------------ 5 files changed, 104 insertions(+), 75 deletions(-) create mode 100644 examples/dsvie.jl diff --git a/examples/dsvie.jl b/examples/dsvie.jl new file mode 100644 index 00000000..ebb0ea10 --- /dev/null +++ b/examples/dsvie.jl @@ -0,0 +1,73 @@ +using CompScienceMeshes, BEAST +using LinearAlgebra +using Profile +using StaticArrays + + + +ntrc = X->ntrace(X,Γ) + +T = tetmeshsphere(1.0,0.5) +X = nedelecd3d(T) +Γ = boundary(T) +Y = raviartthomas(Γ) + +@show numfunctions(X) +@show numfunctions(Y) + + +κ, η = 1.0, 1.0 +κ′, η′ = √2.0κ, η/√2.0 +ϵ_r =2.0 + + +χ = x->(1.0-1.0/ϵ_r) + +#Volume-Volume +L,I,B = VIE.singlelayer(wavenumber=κ', tau=χ), Identity(), VIE.boundary(wavenumber=κ', tau=χ) +#Volume-Surface +Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ', tau=χ)), transpose(VSIE.boundary(wavenumber=κ', tau=χ)), transpose(VSIE.doublelayer(wavenumber=κ', tau=χ)) +Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ'), VSIE.boundary(wavenumber=κ'), VSIE.doublelayer(wavenumber=κ') +#Surface-Surface +T = Maxwell3D.singlelayer(wavenumber=κ) #Outside +T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside +K = Maxwell3D.doublelayer(wavenumber=κ) #Outside +K′ = Maxwell3D.doublelayer(wavenumber=κ′) #Inside + +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +H = -1/(im*κ*η)*curl(E) + +e, h = (n × E) × n, (n × H) × n + +@hilbertspace D j m +@hilbertspace k l o +β = 1.0/ϵ_r +α, α′ = 1/η, 1/η′ + +eq = @varform (β*I[k,D]-L[k,D]-B[ntrc(k),D] + Lt[k,j]+Bt[ntrc(k),j] + Kt[k,m] + + Ls[l,D]+Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + + Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) + + +dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y + +u = solve(dvsie) + + +#Post processing +Θ, Φ = range(0.0,stop=2π,length=100), 0.0 +ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] + +# Don't forget the far field comprises two contributions +ffm = potential(MWFarField3D(κ*im), ffpoints, u[m], Y) +ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], Y) +ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) + + +ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u[D], X) +ff2 = im*κ*ffd + +using Plots +plot(xlabel="theta") +plot!(Θ,norm.(ff),label="far field",title="DVSIE") +plot!(Θ,norm.(ff2),label="far field",title="DVSIE 2") diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index dd3a8f76..d22649e3 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -165,4 +165,3 @@ Plots.plot!(real.(getindex.(H_tot[:,51],2))) plot() plot!(Θ, norm.(ff),label="far field") scatter!(Θ, norm.(Et_far), label="field far") -=# \ No newline at end of file diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index 3c140015..326f3ffa 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -75,7 +75,7 @@ end function momintegrals!(op::VIEOperator, test_local_space::RefSpace, trial_local_space::RefSpace, test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) - @timeit to "singular" begin + #Find permutation of vertices to match location of singularity to SauterSchwab J, I= SauterSchwab3D.reorder(strat.sing) @@ -127,13 +127,10 @@ function momintegrals!(op::VIEOperator, out[i,j] += Q[K[i],L[j]]*O1[i]*O2[j] end end - - end nothing end function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) - @timeit to "regular" begin # memory allocation here is a result from the type instability on strat # which is on purpose, i.e. the momintegrals! method is chosen based # on dynamic polymorphism. @@ -167,8 +164,7 @@ function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::Do end end end - - end + return z end @@ -241,7 +237,6 @@ function momintegrals!(biop::VSIEOperator, tshs, bshs, tcell, bcell, z, strat::D wimps = strat.inner_quad_points M, N = size(z) - for womp in womps tgeo = womp.point diff --git a/src/volumeintegral/vsie.jl b/src/volumeintegral/vsie.jl index 9947f567..b0dcc2b4 100644 --- a/src/volumeintegral/vsie.jl +++ b/src/volumeintegral/vsie.jl @@ -41,14 +41,14 @@ module VSIE @assert gamma != nothing - alpha == nothing && (alpha = -gamma) - beta == nothing && (beta = -1/gamma) + alpha == nothing && (alpha = wavenumber*wavenumber) + beta == nothing && (beta = 1.0) tau == nothing && (tau = x->1.0) Mod.VSIESingleLayer(gamma, alpha, beta, tau) end - function singlelayer2(; + function singlelayerT(; gamma=nothing, wavenumber=nothing, alpha=nothing, @@ -73,11 +73,11 @@ module VSIE @assert gamma != nothing - alpha == nothing && (alpha = -gamma) - beta == nothing && (beta = -1/gamma) + alpha == nothing && (alpha = wavenumber*wavenumber) + beta == nothing && (beta = 1.0) tau == nothing && (tau = x->1.0) - Mod.VSIESingleLayer2(gamma, alpha, beta, tau) + Mod.VSIESingleLayerT(gamma, alpha, beta, tau) end """ diff --git a/src/volumeintegral/vsieops.jl b/src/volumeintegral/vsieops.jl index 7338e7d3..1235cd7c 100644 --- a/src/volumeintegral/vsieops.jl +++ b/src/volumeintegral/vsieops.jl @@ -1,9 +1,6 @@ abstract type VSIEOperator <: IntegralOperator end abstract type VolumeSurfaceOperator <: VSIEOperator end abstract type BoundarySurfaceOperator <: VSIEOperator end -abstract type VSIEOperatorT <: VSIEOperator end -abstract type VolumeSurfaceOperatorT <: VolumeSurfaceOperator end -abstract type BoundarySurfaceOperatorT <: BoundarySurfaceOperator end struct KernelValsVSIE{T,U,P,Q,K} gamma::U @@ -23,6 +20,7 @@ function kernelvals(viop::VSIEOperator, p ,q) expn = exp(-yR) green = expn / (4*pi*R) gradgreen = - (Y +1/R)*green/R*r + tau = viop.tau(cartesian(q)) @@ -48,26 +46,26 @@ struct VSIEDoubleLayer{T,U,P} <: VolumeSurfaceOperator tau::P end -#= -struct VSIESingleLayer2{T,U,P} <: VolumeSurfaceOperator + +struct VSIESingleLayerT{T,U,P} <: VolumeSurfaceOperator gamma::T α::U β::U tau::P end -struct VSIEBoundaryT{T,U,P} <: BoundarySurfaceOperatorT +struct VSIEBoundaryT{T,U,P} <: BoundarySurfaceOperator gamma::T α::U tau::P end -struct VSIEDoubleLayerT{T,U,P} <: VolumeSurfaceOperatorT +struct VSIEDoubleLayerT{T,U,P} <: VolumeSurfaceOperator gamma::T α::U tau::P end -=# + scalartype(op::VSIEOperator) = typeof(op.gamma) export VSIE @@ -80,15 +78,6 @@ struct VSIEIntegrand{S,T,O,K,L} trial_local_space::L end -#= -struct VSIEIntegrandT{S,T,O,K,L} - test_tetrahedron_element::S - trial_triangle_element::T - op::O - test_local_space::K - trial_local_space::L -end -=# function (igd::VSIEIntegrand)(u,v) @@ -110,28 +99,6 @@ function (igd::VSIEIntegrand)(u,v) end -#= -function (igd::VSIEIntegrandT)(u,v) - - #mesh points - tgeo = neighborhood(igd.test_tetrahedron_element,u) - bgeo = neighborhood(igd.trial_triangle_element,v) - - #kernel values - kerneldata = kernelvals(igd.op,tgeo,bgeo) - - #values & grad/div/curl of local shape functions - tval = igd.test_local_space(tgeo) - bval = igd.trial_local_space(bgeo) - - #jacobian - j = jacobian(tgeo) * jacobian(bgeo) - - integrand(igd.op, kerneldata,tval,tgeo,bval,tgeo) * j -end -=# - - function integrand(viop::VSIESingleLayer, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:3] @@ -157,21 +124,20 @@ function integrand(viop::VSIEBoundary, kerneldata, tvals, tgeo, bvals, bgeo) G = kerneldata.green - Ty = kerneldata.tau + Tx = kerneldata.tau α = viop.α - @SMatrix[α * Ty * dgx[i] * fy[j] * G for i in 1:3, j in 1:1] + @SMatrix[α * Tx * dgx[i] * fy[j] * G for i in 1:3, j in 1:1] end -#= -function integrand(viop::VSIESingleLayer2, kerneldata, tvals, tgeo, bvals, bgeo) +function integrand(viop::VSIESingleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) - gx = @SVector[tvals[i].value for i in 1:3] - fy = @SVector[bvals[i].value for i in 1:4] + gx = @SVector[tvals[i].value for i in 1:4] + fy = @SVector[bvals[i].value for i in 1:3] - dgx = @SVector[tvals[i].divergence for i in 1:3] - dfy = @SVector[bvals[i].divergence for i in 1:4] + dgx = @SVector[tvals[i].divergence for i in 1:4] + dfy = @SVector[bvals[i].divergence for i in 1:3] G = kerneldata.green @@ -180,7 +146,7 @@ function integrand(viop::VSIESingleLayer2, kerneldata, tvals, tgeo, bvals, bgeo) α = viop.α β = viop.β - @SMatrix[α * dot(gx[i],Ty*fy[j]) * G - β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] + @SMatrix[α * dot(gx[i],Ty*fy[j]) * G + β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] end @@ -197,7 +163,7 @@ function integrand(viop::VSIEBoundaryT, kerneldata, tvals, tgeo, bvals, bgeo) @SMatrix[α * Tx * gx[i] * dfy[j] * G for i in 1:3, j in 1:1] end -=# + function integrand(viop::VSIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:3] @@ -212,21 +178,19 @@ function integrand(viop::VSIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:3, j in 1:4] end -#= function integrand(viop::VSIEDoubleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:4] fy = @SVector[bvals[i].value for i in 1:3] - gradG = kerneldata.gradgreen - + Ty = kerneldata.tau α = viop.α - @SMatrix[α * transpose(cross(fy[j],gx[i])) *gradG for i in 1:4, j in 1:3] + @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:4, j in 1:3] end -=# + defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) @@ -257,9 +221,7 @@ quadrule(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs::SauterSchwab3DQStrat) - - @assert (length(τ.vertices)==3 && length(σ.vertices)==4) "Expected simplex wrong" - + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) hits = 0 @@ -274,8 +236,8 @@ function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j d2 = LinearAlgebra.norm_sqr(t-s) dmin2 = min(dmin2, d2) if d2 < dtol - push!(idx_t,j) - push!(idx_s,i) + push!(idx_t,i) + push!(idx_s,j) hits +=1 break end @@ -287,8 +249,8 @@ function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j hits == 3 && return SauterSchwab3D.CommonFace5D_S(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 2 && return SauterSchwab3D.CommonEdge5D_S(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 1 && return SauterSchwab3D.CommonVertex5D_S(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),(qd.sing_qp[3],qd.sing_qp[2])) - #hits == 0 && return SauterSchwab3D.PositiveDistance5D_S(SauterSchwab3D.Singularity5DPositiveDistance(),(qd.sing_qp[3],qd.sing_qp[2])) - + + return DoubleQuadRule( qd[1][1,i], qd[2][1,j]) From a17bd5b31c60d832ad12e6f74c431cdf3baea537 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Sat, 18 Dec 2021 15:43:34 +0100 Subject: [PATCH 262/528] update vsie implementation --- examples/dsvie.jl | 38 +++++++++++++++++++++++------------ examples/dvie.jl | 17 ++++++++++++++++ examples/pmchwt.jl | 4 +--- src/volumeintegral/vieops.jl | 2 +- src/volumeintegral/vsie.jl | 4 ++-- src/volumeintegral/vsieops.jl | 2 +- 6 files changed, 47 insertions(+), 20 deletions(-) diff --git a/examples/dsvie.jl b/examples/dsvie.jl index ebb0ea10..fa12c2ef 100644 --- a/examples/dsvie.jl +++ b/examples/dsvie.jl @@ -7,7 +7,7 @@ using StaticArrays ntrc = X->ntrace(X,Γ) -T = tetmeshsphere(1.0,0.5) +T = tetmeshsphere(1.0,0.25) X = nedelecd3d(T) Γ = boundary(T) Y = raviartthomas(Γ) @@ -18,7 +18,8 @@ Y = raviartthomas(Γ) κ, η = 1.0, 1.0 κ′, η′ = √2.0κ, η/√2.0 -ϵ_r =2.0 +ϵ_r =3.0 +ϵ_b =2.0 χ = x->(1.0-1.0/ϵ_r) @@ -26,8 +27,8 @@ Y = raviartthomas(Γ) #Volume-Volume L,I,B = VIE.singlelayer(wavenumber=κ', tau=χ), Identity(), VIE.boundary(wavenumber=κ', tau=χ) #Volume-Surface -Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ', tau=χ)), transpose(VSIE.boundary(wavenumber=κ', tau=χ)), transpose(VSIE.doublelayer(wavenumber=κ', tau=χ)) -Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ'), VSIE.boundary(wavenumber=κ'), VSIE.doublelayer(wavenumber=κ') +Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ')), transpose(VSIE.boundary(wavenumber=κ')), transpose(VSIE.doublelayer(wavenumber=κ')) +Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ', tau=χ), VSIE.boundary(wavenumber=κ', tau=χ), VSIE.doublelayer(wavenumber=κ', tau=χ) #Surface-Surface T = Maxwell3D.singlelayer(wavenumber=κ) #Outside T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside @@ -41,17 +42,21 @@ e, h = (n × E) × n, (n × H) × n @hilbertspace D j m @hilbertspace k l o -β = 1.0/ϵ_r +β = 1.0/(ϵ_r*ϵ_b) +ν = 1/ϵ_b α, α′ = 1/η, 1/η′ +γ′ = im*η′/κ′ +ζ′ = im*η′*κ′ +δ′ = im*κ′/ϵ_b -eq = @varform (β*I[k,D]-L[k,D]-B[ntrc(k),D] + Lt[k,j]+Bt[ntrc(k),j] + Kt[k,m] + - Ls[l,D]+Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + - Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) +eq = @varform (β*I[k,D]-ν*L[k,D]-ν*B[ntrc(k),D] + η′*Lt[k,j]-γ′*Bt[ntrc(k),j] + Kt[k,m] + + -δ′*Ls[l,D]-ν*Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + + ζ′*Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y -u = solve(dvsie) +u_n = solve(dvsie) #Post processing @@ -59,15 +64,22 @@ u = solve(dvsie) ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] # Don't forget the far field comprises two contributions -ffm = potential(MWFarField3D(κ*im), ffpoints, u[m], Y) -ffj = potential(MWFarField3D(κ*im), ffpoints, u[j], Y) +ffm = potential(MWFarField3D(κ*im), ffpoints, u_n[m], Y) +ffj = potential(MWFarField3D(κ*im), ffpoints, u_n[j], Y) ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) -ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u[D], X) +ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u_n[D], X) ff2 = im*κ*ffd using Plots plot(xlabel="theta") plot!(Θ,norm.(ff),label="far field",title="DVSIE") -plot!(Θ,norm.(ff2),label="far field",title="DVSIE 2") +plot!(Θ,√6.0*norm.(ff),label="far field",title="DVSIE 2") + +import Plotly +using LinearAlgebra +fcrj, _ = facecurrents(u_n[j],Y) +fcrm, _ = facecurrents(u_n[m],Y) +Plotly.plot(patch(Γ, norm.(fcrj))) +Plotly.plot(patch(Γ, norm.(fcrm))) \ No newline at end of file diff --git a/examples/dvie.jl b/examples/dvie.jl index 26eb2774..9b71b065 100644 --- a/examples/dvie.jl +++ b/examples/dvie.jl @@ -5,7 +5,11 @@ using TimerOutputs using StaticArrays function tau(x::SVector{U,T}) where {U,T} +<<<<<<< HEAD 1.0-1.0/5.0 +======= + 1.0-1.0/9.0 +>>>>>>> 72ee5ec (update vsie implementation) end ntrc = X->ntrace(X,y) @@ -16,7 +20,11 @@ y = boundary(T) @show numfunctions(X) ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) +<<<<<<< HEAD ϵ_r =5.0 +======= +ϵ_r =25.0 +>>>>>>> 72ee5ec (update vsie implementation) χ = tau K, I, B = VIE.singlelayer(wavenumber=κ, tau=χ), Identity(), VIE.boundary(wavenumber=κ, tau=χ) E = VIE.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) @@ -41,8 +49,13 @@ using Plots #Farfield plot(xlabel="theta") plot!(Θ, norm.(ff), label="far field", title="D-VIE") +<<<<<<< HEAD =# using Plots +======= + +#= +>>>>>>> 72ee5ec (update vsie implementation) #NearField Z = range(-1,1,length=100) Y = range(-1,1,length=100) @@ -54,6 +67,7 @@ Enear = reshape(Enear,100,100) contour(real.(getindex.(Enear,1))) heatmap(Z, Y, real.(getindex.(Enear,1))) +<<<<<<< HEAD plot!(Y[2:99],real.(getindex.(Enear[2:99,50],1)),label="D-VIE (simplex)", linestyle=:dash, linecolor=:darkorange4) #plot!(Y[2:99],real.(getindex.(Enear_simplex[2:99,50],1)),label="D-VIE (simplex)", linestyle=:solid, linecolor=:darkorange3) @@ -63,6 +77,9 @@ import Cairo using DataFrames, Gadfly, RDatasets D = dataset("datasets","HairEyeColor") palette = ["skyblue","skyblue3"] +======= +=# +>>>>>>> 72ee5ec (update vsie implementation) D = DataFrame( diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index d22649e3..c2a37313 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -40,9 +40,7 @@ Y = BEAST.buffachristiansen2(Γ) κ, η = 1.0, 1.0 -κ′, η′ = √5.0κ, η/√5.0 - -N = NCross() +κ′, η′ = √6.0κ, η/√6.0 T = Maxwell3D.singlelayer(wavenumber=κ) T′ = Maxwell3D.singlelayer(wavenumber=κ′) diff --git a/src/volumeintegral/vieops.jl b/src/volumeintegral/vieops.jl index 611b2fe4..81b16b59 100644 --- a/src/volumeintegral/vieops.jl +++ b/src/volumeintegral/vieops.jl @@ -178,7 +178,7 @@ function integrand(viop::VIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) end -defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) +defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) function quaddata(op::VIEOperator, diff --git a/src/volumeintegral/vsie.jl b/src/volumeintegral/vsie.jl index b0dcc2b4..57d2cb35 100644 --- a/src/volumeintegral/vsie.jl +++ b/src/volumeintegral/vsie.jl @@ -41,8 +41,8 @@ module VSIE @assert gamma != nothing - alpha == nothing && (alpha = wavenumber*wavenumber) - beta == nothing && (beta = 1.0) + alpha == nothing && (alpha = gamma) + beta == nothing && (beta = im*im/gamma) tau == nothing && (tau = x->1.0) Mod.VSIESingleLayer(gamma, alpha, beta, tau) diff --git a/src/volumeintegral/vsieops.jl b/src/volumeintegral/vsieops.jl index 1235cd7c..85222b23 100644 --- a/src/volumeintegral/vsieops.jl +++ b/src/volumeintegral/vsieops.jl @@ -192,7 +192,7 @@ function integrand(viop::VSIEDoubleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) end -defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) +defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) function quaddata(op::VSIEOperator, From c1900dfd536b99b88ad18b31188424cf6bdb8573 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Wed, 16 Mar 2022 15:24:29 +0100 Subject: [PATCH 263/528] working volume surface formulation --- examples/dsvie.jl | 85 ------------------------ examples/dvie.jl | 17 ----- examples/pmchwt.jl | 9 ++- src/BEAST.jl | 2 + src/volumeintegral/sauterschwab_ints.jl | 8 ++- src/volumeintegral/vieops.jl | 2 +- src/volumeintegral/vsie.jl | 12 ++-- src/volumeintegral/vsieops.jl | 86 ++++++++++++++++++------- 8 files changed, 83 insertions(+), 138 deletions(-) delete mode 100644 examples/dsvie.jl diff --git a/examples/dsvie.jl b/examples/dsvie.jl deleted file mode 100644 index fa12c2ef..00000000 --- a/examples/dsvie.jl +++ /dev/null @@ -1,85 +0,0 @@ -using CompScienceMeshes, BEAST -using LinearAlgebra -using Profile -using StaticArrays - - - -ntrc = X->ntrace(X,Γ) - -T = tetmeshsphere(1.0,0.25) -X = nedelecd3d(T) -Γ = boundary(T) -Y = raviartthomas(Γ) - -@show numfunctions(X) -@show numfunctions(Y) - - -κ, η = 1.0, 1.0 -κ′, η′ = √2.0κ, η/√2.0 -ϵ_r =3.0 -ϵ_b =2.0 - - -χ = x->(1.0-1.0/ϵ_r) - -#Volume-Volume -L,I,B = VIE.singlelayer(wavenumber=κ', tau=χ), Identity(), VIE.boundary(wavenumber=κ', tau=χ) -#Volume-Surface -Lt,Bt,Kt = transpose(VSIE.singlelayer(wavenumber=κ')), transpose(VSIE.boundary(wavenumber=κ')), transpose(VSIE.doublelayer(wavenumber=κ')) -Ls,Bs,Ks = VSIE.singlelayer(wavenumber=κ', tau=χ), VSIE.boundary(wavenumber=κ', tau=χ), VSIE.doublelayer(wavenumber=κ', tau=χ) -#Surface-Surface -T = Maxwell3D.singlelayer(wavenumber=κ) #Outside -T′ = Maxwell3D.singlelayer(wavenumber=κ′) #Inside -K = Maxwell3D.doublelayer(wavenumber=κ) #Outside -K′ = Maxwell3D.doublelayer(wavenumber=κ′) #Inside - -E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) -H = -1/(im*κ*η)*curl(E) - -e, h = (n × E) × n, (n × H) × n - -@hilbertspace D j m -@hilbertspace k l o -β = 1.0/(ϵ_r*ϵ_b) -ν = 1/ϵ_b -α, α′ = 1/η, 1/η′ -γ′ = im*η′/κ′ -ζ′ = im*η′*κ′ -δ′ = im*κ′/ϵ_b - -eq = @varform (β*I[k,D]-ν*L[k,D]-ν*B[ntrc(k),D] + η′*Lt[k,j]-γ′*Bt[ntrc(k),j] + Kt[k,m] + - -δ′*Ls[l,D]-ν*Bs[l,ntrc(D)] + (η*T+η′*T′)[l,j] - (K+K′)[l,m] + - ζ′*Ks[o,D] + (K+K′)[o,j] + (α*T+α′*T′)[o,m] == -e[l] - h[o]) - - -dvsie = @discretise eq D∈X k∈X j∈Y m∈Y l∈Y o∈Y - -u_n = solve(dvsie) - - -#Post processing -Θ, Φ = range(0.0,stop=2π,length=100), 0.0 -ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] - -# Don't forget the far field comprises two contributions -ffm = potential(MWFarField3D(κ*im), ffpoints, u_n[m], Y) -ffj = potential(MWFarField3D(κ*im), ffpoints, u_n[j], Y) -ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) - - -ffd = potential(VIE.farfield(wavenumber=κ, tau=χ), ffpoints, u_n[D], X) -ff2 = im*κ*ffd - -using Plots -plot(xlabel="theta") -plot!(Θ,norm.(ff),label="far field",title="DVSIE") -plot!(Θ,√6.0*norm.(ff),label="far field",title="DVSIE 2") - -import Plotly -using LinearAlgebra -fcrj, _ = facecurrents(u_n[j],Y) -fcrm, _ = facecurrents(u_n[m],Y) -Plotly.plot(patch(Γ, norm.(fcrj))) -Plotly.plot(patch(Γ, norm.(fcrm))) \ No newline at end of file diff --git a/examples/dvie.jl b/examples/dvie.jl index 9b71b065..26eb2774 100644 --- a/examples/dvie.jl +++ b/examples/dvie.jl @@ -5,11 +5,7 @@ using TimerOutputs using StaticArrays function tau(x::SVector{U,T}) where {U,T} -<<<<<<< HEAD 1.0-1.0/5.0 -======= - 1.0-1.0/9.0 ->>>>>>> 72ee5ec (update vsie implementation) end ntrc = X->ntrace(X,y) @@ -20,11 +16,7 @@ y = boundary(T) @show numfunctions(X) ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) -<<<<<<< HEAD ϵ_r =5.0 -======= -ϵ_r =25.0 ->>>>>>> 72ee5ec (update vsie implementation) χ = tau K, I, B = VIE.singlelayer(wavenumber=κ, tau=χ), Identity(), VIE.boundary(wavenumber=κ, tau=χ) E = VIE.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) @@ -49,13 +41,8 @@ using Plots #Farfield plot(xlabel="theta") plot!(Θ, norm.(ff), label="far field", title="D-VIE") -<<<<<<< HEAD =# using Plots -======= - -#= ->>>>>>> 72ee5ec (update vsie implementation) #NearField Z = range(-1,1,length=100) Y = range(-1,1,length=100) @@ -67,7 +54,6 @@ Enear = reshape(Enear,100,100) contour(real.(getindex.(Enear,1))) heatmap(Z, Y, real.(getindex.(Enear,1))) -<<<<<<< HEAD plot!(Y[2:99],real.(getindex.(Enear[2:99,50],1)),label="D-VIE (simplex)", linestyle=:dash, linecolor=:darkorange4) #plot!(Y[2:99],real.(getindex.(Enear_simplex[2:99,50],1)),label="D-VIE (simplex)", linestyle=:solid, linecolor=:darkorange3) @@ -77,9 +63,6 @@ import Cairo using DataFrames, Gadfly, RDatasets D = dataset("datasets","HairEyeColor") palette = ["skyblue","skyblue3"] -======= -=# ->>>>>>> 72ee5ec (update vsie implementation) D = DataFrame( diff --git a/examples/pmchwt.jl b/examples/pmchwt.jl index c2a37313..624a97d7 100644 --- a/examples/pmchwt.jl +++ b/examples/pmchwt.jl @@ -40,7 +40,9 @@ Y = BEAST.buffachristiansen2(Γ) κ, η = 1.0, 1.0 -κ′, η′ = √6.0κ, η/√6.0 +κ′, η′ = √5.0κ, η/√5.0 + +N = NCross() T = Maxwell3D.singlelayer(wavenumber=κ) T′ = Maxwell3D.singlelayer(wavenumber=κ′) @@ -147,8 +149,8 @@ E_in, H_in = fetch(task2) E_tot = E_in + E_ex H_tot = H_in + H_ex -Plots.contour(real.(getindex.(E_tot,1))) -Plots.contour(real.(getindex.(H_tot,2))) +contour(real.(getindex.(E_tot,1))) +contour(real.(getindex.(H_tot,2))) Plots.heatmap(Z, Y, clamp.(real.(getindex.(E_tot,1)),-1.5,1.5)) Plots.heatmap(Z, Y, clamp.(imag.(getindex.(E_tot,1)),-1.5,1.5)) @@ -163,3 +165,4 @@ Plots.plot!(real.(getindex.(H_tot[:,51],2))) plot() plot!(Θ, norm.(ff),label="far field") scatter!(Θ, norm.(Et_far), label="field far") +=# \ No newline at end of file diff --git a/src/BEAST.jl b/src/BEAST.jl index 1dd6b5a9..14d9d6e9 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -270,4 +270,6 @@ export x̂, ŷ, ẑ const n = NormalVector() export n +const to = TimerOutput() + end # module diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index 326f3ffa..1c625925 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -75,7 +75,7 @@ end function momintegrals!(op::VIEOperator, test_local_space::RefSpace, trial_local_space::RefSpace, test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) - + @timeit to "singular" begin #Find permutation of vertices to match location of singularity to SauterSchwab J, I= SauterSchwab3D.reorder(strat.sing) @@ -127,6 +127,8 @@ function momintegrals!(op::VIEOperator, out[i,j] += Q[K[i],L[j]]*O1[i]*O2[j] end end + + end nothing end @@ -164,7 +166,8 @@ function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::Do end end end - + + end return z end @@ -237,6 +240,7 @@ function momintegrals!(biop::VSIEOperator, tshs, bshs, tcell, bcell, z, strat::D wimps = strat.inner_quad_points M, N = size(z) + for womp in womps tgeo = womp.point diff --git a/src/volumeintegral/vieops.jl b/src/volumeintegral/vieops.jl index 81b16b59..611b2fe4 100644 --- a/src/volumeintegral/vieops.jl +++ b/src/volumeintegral/vieops.jl @@ -178,7 +178,7 @@ function integrand(viop::VIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) end -defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) +defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) function quaddata(op::VIEOperator, diff --git a/src/volumeintegral/vsie.jl b/src/volumeintegral/vsie.jl index 57d2cb35..9947f567 100644 --- a/src/volumeintegral/vsie.jl +++ b/src/volumeintegral/vsie.jl @@ -41,14 +41,14 @@ module VSIE @assert gamma != nothing - alpha == nothing && (alpha = gamma) - beta == nothing && (beta = im*im/gamma) + alpha == nothing && (alpha = -gamma) + beta == nothing && (beta = -1/gamma) tau == nothing && (tau = x->1.0) Mod.VSIESingleLayer(gamma, alpha, beta, tau) end - function singlelayerT(; + function singlelayer2(; gamma=nothing, wavenumber=nothing, alpha=nothing, @@ -73,11 +73,11 @@ module VSIE @assert gamma != nothing - alpha == nothing && (alpha = wavenumber*wavenumber) - beta == nothing && (beta = 1.0) + alpha == nothing && (alpha = -gamma) + beta == nothing && (beta = -1/gamma) tau == nothing && (tau = x->1.0) - Mod.VSIESingleLayerT(gamma, alpha, beta, tau) + Mod.VSIESingleLayer2(gamma, alpha, beta, tau) end """ diff --git a/src/volumeintegral/vsieops.jl b/src/volumeintegral/vsieops.jl index 85222b23..7338e7d3 100644 --- a/src/volumeintegral/vsieops.jl +++ b/src/volumeintegral/vsieops.jl @@ -1,6 +1,9 @@ abstract type VSIEOperator <: IntegralOperator end abstract type VolumeSurfaceOperator <: VSIEOperator end abstract type BoundarySurfaceOperator <: VSIEOperator end +abstract type VSIEOperatorT <: VSIEOperator end +abstract type VolumeSurfaceOperatorT <: VolumeSurfaceOperator end +abstract type BoundarySurfaceOperatorT <: BoundarySurfaceOperator end struct KernelValsVSIE{T,U,P,Q,K} gamma::U @@ -20,7 +23,6 @@ function kernelvals(viop::VSIEOperator, p ,q) expn = exp(-yR) green = expn / (4*pi*R) gradgreen = - (Y +1/R)*green/R*r - tau = viop.tau(cartesian(q)) @@ -46,26 +48,26 @@ struct VSIEDoubleLayer{T,U,P} <: VolumeSurfaceOperator tau::P end - -struct VSIESingleLayerT{T,U,P} <: VolumeSurfaceOperator +#= +struct VSIESingleLayer2{T,U,P} <: VolumeSurfaceOperator gamma::T α::U β::U tau::P end -struct VSIEBoundaryT{T,U,P} <: BoundarySurfaceOperator +struct VSIEBoundaryT{T,U,P} <: BoundarySurfaceOperatorT gamma::T α::U tau::P end -struct VSIEDoubleLayerT{T,U,P} <: VolumeSurfaceOperator +struct VSIEDoubleLayerT{T,U,P} <: VolumeSurfaceOperatorT gamma::T α::U tau::P end - +=# scalartype(op::VSIEOperator) = typeof(op.gamma) export VSIE @@ -78,6 +80,15 @@ struct VSIEIntegrand{S,T,O,K,L} trial_local_space::L end +#= +struct VSIEIntegrandT{S,T,O,K,L} + test_tetrahedron_element::S + trial_triangle_element::T + op::O + test_local_space::K + trial_local_space::L +end +=# function (igd::VSIEIntegrand)(u,v) @@ -99,6 +110,28 @@ function (igd::VSIEIntegrand)(u,v) end +#= +function (igd::VSIEIntegrandT)(u,v) + + #mesh points + tgeo = neighborhood(igd.test_tetrahedron_element,u) + bgeo = neighborhood(igd.trial_triangle_element,v) + + #kernel values + kerneldata = kernelvals(igd.op,tgeo,bgeo) + + #values & grad/div/curl of local shape functions + tval = igd.test_local_space(tgeo) + bval = igd.trial_local_space(bgeo) + + #jacobian + j = jacobian(tgeo) * jacobian(bgeo) + + integrand(igd.op, kerneldata,tval,tgeo,bval,tgeo) * j +end +=# + + function integrand(viop::VSIESingleLayer, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:3] @@ -124,20 +157,21 @@ function integrand(viop::VSIEBoundary, kerneldata, tvals, tgeo, bvals, bgeo) G = kerneldata.green - Tx = kerneldata.tau + Ty = kerneldata.tau α = viop.α - @SMatrix[α * Tx * dgx[i] * fy[j] * G for i in 1:3, j in 1:1] + @SMatrix[α * Ty * dgx[i] * fy[j] * G for i in 1:3, j in 1:1] end -function integrand(viop::VSIESingleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) +#= +function integrand(viop::VSIESingleLayer2, kerneldata, tvals, tgeo, bvals, bgeo) - gx = @SVector[tvals[i].value for i in 1:4] - fy = @SVector[bvals[i].value for i in 1:3] + gx = @SVector[tvals[i].value for i in 1:3] + fy = @SVector[bvals[i].value for i in 1:4] - dgx = @SVector[tvals[i].divergence for i in 1:4] - dfy = @SVector[bvals[i].divergence for i in 1:3] + dgx = @SVector[tvals[i].divergence for i in 1:3] + dfy = @SVector[bvals[i].divergence for i in 1:4] G = kerneldata.green @@ -146,7 +180,7 @@ function integrand(viop::VSIESingleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) α = viop.α β = viop.β - @SMatrix[α * dot(gx[i],Ty*fy[j]) * G + β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] + @SMatrix[α * dot(gx[i],Ty*fy[j]) * G - β * (dgx[i] * Ty*dfy[j])*G for i in 1:3, j in 1:4] end @@ -163,7 +197,7 @@ function integrand(viop::VSIEBoundaryT, kerneldata, tvals, tgeo, bvals, bgeo) @SMatrix[α * Tx * gx[i] * dfy[j] * G for i in 1:3, j in 1:1] end - +=# function integrand(viop::VSIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:3] @@ -178,21 +212,23 @@ function integrand(viop::VSIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:3, j in 1:4] end +#= function integrand(viop::VSIEDoubleLayerT, kerneldata, tvals, tgeo, bvals, bgeo) gx = @SVector[tvals[i].value for i in 1:4] fy = @SVector[bvals[i].value for i in 1:3] + gradG = kerneldata.gradgreen - + Ty = kerneldata.tau α = viop.α - @SMatrix[α * dot(cross(Ty*fy[j],gx[i]),gradG) for i in 1:4, j in 1:3] + @SMatrix[α * transpose(cross(fy[j],gx[i])) *gradG for i in 1:4, j in 1:3] end +=# - -defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(4,4,4,4,4,4) +defaultquadstrat(op::VSIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) function quaddata(op::VSIEOperator, @@ -221,7 +257,9 @@ quadrule(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs::SauterSchwab3DQStrat) - + + @assert (length(τ.vertices)==3 && length(σ.vertices)==4) "Expected simplex wrong" + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) hits = 0 @@ -236,8 +274,8 @@ function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j d2 = LinearAlgebra.norm_sqr(t-s) dmin2 = min(dmin2, d2) if d2 < dtol - push!(idx_t,i) - push!(idx_s,j) + push!(idx_t,j) + push!(idx_s,i) hits +=1 break end @@ -249,8 +287,8 @@ function qr_volume(op::VolumeSurfaceOperator, g::RefSpace, f::RefSpace, i, τ, j hits == 3 && return SauterSchwab3D.CommonFace5D_S(SauterSchwab3D.Singularity5DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 2 && return SauterSchwab3D.CommonEdge5D_S(SauterSchwab3D.Singularity5DEdge(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) hits == 1 && return SauterSchwab3D.CommonVertex5D_S(SauterSchwab3D.Singularity5DPoint(idx_t,idx_s),(qd.sing_qp[3],qd.sing_qp[2])) - - + #hits == 0 && return SauterSchwab3D.PositiveDistance5D_S(SauterSchwab3D.Singularity5DPositiveDistance(),(qd.sing_qp[3],qd.sing_qp[2])) + return DoubleQuadRule( qd[1][1,i], qd[2][1,j]) From dd8269663b11e7be706da817a944baf406b151c1 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Fri, 7 Oct 2022 11:38:41 +0200 Subject: [PATCH 264/528] Remove TimerOutputs --- examples/dvie.jl | 8 +++++--- examples/dvsie.jl | 8 ++++---- src/BEAST.jl | 8 ++++---- src/volumeintegral/sauterschwab_ints.jl | 6 +++--- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/examples/dvie.jl b/examples/dvie.jl index 26eb2774..1b86906e 100644 --- a/examples/dvie.jl +++ b/examples/dvie.jl @@ -1,8 +1,10 @@ using CompScienceMeshes, BEAST + using LinearAlgebra -using Profile -using TimerOutputs +#using Profile +#using TimerOutputs using StaticArrays +using Plots function tau(x::SVector{U,T}) where {U,T} 1.0-1.0/5.0 @@ -42,7 +44,7 @@ using Plots plot(xlabel="theta") plot!(Θ, norm.(ff), label="far field", title="D-VIE") =# -using Plots + #NearField Z = range(-1,1,length=100) Y = range(-1,1,length=100) diff --git a/examples/dvsie.jl b/examples/dvsie.jl index dfa822c5..2249dc17 100644 --- a/examples/dvsie.jl +++ b/examples/dvsie.jl @@ -1,8 +1,8 @@ using CompScienceMeshes, BEAST using LinearAlgebra using Profile -using LiftedMaps -using BlockArrays +#using LiftedMaps +#using BlockArrays using StaticArrays @@ -128,8 +128,8 @@ x, ch = IterativeSolvers.gmres!(x, precond*A_dvsie, precond*Rhs, log=true, relt ffpoints = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] # Don't forget the far field comprises two contributions -ffm = potential(MWFarField3D(κ*im), ffpoints, u_n[m], Y) -ffj = potential(MWFarField3D(κ*im), ffpoints, u_n[j], Y) +ffm = potential(MWFarField3D(gamma=κ*im), ffpoints, u_n[m], Y) +ffj = potential(MWFarField3D(gamma=κ*im), ffpoints, u_n[j], Y) ff = -η*im*κ*ffj + im*κ*cross.(ffpoints, ffm) diff --git a/src/BEAST.jl b/src/BEAST.jl index 14d9d6e9..4264bdf1 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -15,7 +15,7 @@ using FastGaussQuadrature using LinearMaps using LiftedMaps -using TimerOutputs +#using TimerOutputs using AbstractTrees using NestedUnitRanges @@ -208,10 +208,10 @@ include("timedomain/zdomain.jl") include("maxwell/mwexc.jl") include("maxwell/mwops.jl") include("maxwell/wiltonints.jl") -include("maxwell/sauterschwabints_rt.jl") +include("maxwell/nxdbllayer.jl") include("maxwell/sauterschwabints_bdm.jl") include("maxwell/sauterschwabints_bdm_rt.jl") -include("maxwell/nxdbllayer.jl") +include("maxwell/sauterschwabints_rt.jl") include("maxwell/nitsche.jl") include("maxwell/farfield.jl") include("maxwell/nearfield.jl") @@ -270,6 +270,6 @@ export x̂, ŷ, ẑ const n = NormalVector() export n -const to = TimerOutput() +#const to = TimerOutput() end # module diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index 1c625925..f28559b0 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -75,7 +75,7 @@ end function momintegrals!(op::VIEOperator, test_local_space::RefSpace, trial_local_space::RefSpace, test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) - @timeit to "singular" begin + #@timeit to "singular" begin #Find permutation of vertices to match location of singularity to SauterSchwab J, I= SauterSchwab3D.reorder(strat.sing) @@ -128,7 +128,7 @@ function momintegrals!(op::VIEOperator, end end - end + #end #end of timeit nothing end @@ -167,7 +167,7 @@ function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::Do end end - end + #end #end of @timeit return z end From eed1e2c18a51ea4ca7bf48b532799755212f956e Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Mon, 3 Jul 2023 15:42:16 +0200 Subject: [PATCH 265/528] Clean up merge --- src/BEAST.jl | 4 ++-- src/bases/lagrange.jl | 2 +- src/bases/local/laglocal.jl | 7 ++++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/BEAST.jl b/src/BEAST.jl index 4264bdf1..a7dc0805 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -209,9 +209,9 @@ include("maxwell/mwexc.jl") include("maxwell/mwops.jl") include("maxwell/wiltonints.jl") include("maxwell/nxdbllayer.jl") -include("maxwell/sauterschwabints_bdm.jl") include("maxwell/sauterschwabints_bdm_rt.jl") -include("maxwell/sauterschwabints_rt.jl") +#include("maxwell/sauterschwabints_rt.jl") +#include("maxwell/sauterschwabints_bdm.jl") include("maxwell/nitsche.jl") include("maxwell/farfield.jl") include("maxwell/nearfield.jl") diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 420fdf4f..1263b2c8 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -661,7 +661,7 @@ gradient(space::LagrangeBasis{1,0}, geo, fns) = NDLCCBasis(geo, fns) curl(space::LagrangeBasis{1,0}, geo, fns) = RTBasis(geo, fns) -curl(space::LagrangeBasis{2,0}, geo, fns) = BDMBasis(geo, fns) +#curl(space::LagrangeBasis{2,0}, geo, fns) = BDMBasis(geo, fns) gradient(space::LagrangeBasis{1,0,<:CompScienceMeshes.AbstractMesh{<:Any,2}}, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, space.pos) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 666317db..31a0488f 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -42,7 +42,7 @@ end valuetype(ref::LagrangeRefSpace{T,1,3}, charttype) where {T} = SVector{1,T} - +#= # Evaluate quadratic lagrange elements on a triangle function (f::LagrangeRefSpace{T,2,3})(t) where T u,v,w, = barycentric(t) @@ -58,7 +58,7 @@ end valuetype(ref::LagrangeRefSpace{T,2,3}, charttype) where {T} = SVector{1, T} - +=# """ f(tangent_space, Val{:withcurl}) @@ -77,7 +77,6 @@ function (f::LagrangeRefSpace{T,1,3})(t, ::Type{Val{:withcurl}}) where T ) end - # Evaluate constant Lagrange elements on a triangle, with their curls function (f::LagrangeRefSpace{T,0,3})(t, ::Type{Val{:withcurl}}) where T i = one(T) @@ -92,6 +91,7 @@ function curl(ref::LagrangeRefSpace{T,1,3} where {T}, sh, el) return [sh1, sh2] end +#= function curl(ref::LagrangeRefSpace{T,2,3}, sh, el) where T j = 1.0 #volume(el) * factorial(dimension(el)) @@ -111,6 +111,7 @@ function curl(ref::LagrangeRefSpace{T,2,3}, sh, el) where T end end +=# function gradient(ref::LagrangeRefSpace{T,1,4}, sh, tet) where {T} this_vert = tet.vertices[sh.refid] From 478adb5db312c8695705a87666d17cca055f9777 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sun, 9 Jul 2023 15:50:31 +0200 Subject: [PATCH 266/528] add (p)cg solver --- src/helmholtz3d/hh3dexc.jl | 7 ++++ src/interpolation.jl | 6 ++-- src/postproc/segcurrents.jl | 3 +- src/solvers/itsolver.jl | 66 ++++++++++++++++++++++++++++++++++++- 4 files changed, 77 insertions(+), 5 deletions(-) diff --git a/src/helmholtz3d/hh3dexc.jl b/src/helmholtz3d/hh3dexc.jl index 2dd7582c..0de1215c 100644 --- a/src/helmholtz3d/hh3dexc.jl +++ b/src/helmholtz3d/hh3dexc.jl @@ -153,6 +153,13 @@ scalartype(s::NormalDerivative{T}) where {T} = T const ∂n = Val{:normalderivative} (::Type{Val{:normalderivative}})(f) = NormalDerivative(f) +function (f::NormalDerivative)(nbd) + x = cartesian(nbd) + g = gradient(f.field)(x) + n = normal(nbd) + return dot(n,g) +end + function (f::NormalDerivative{T,F})(manipoint) where {T,F<:HH3DPlaneWave} d = f.field.direction k = f.field.wavenumber diff --git a/src/interpolation.jl b/src/interpolation.jl index 273fa6d0..8aef944b 100644 --- a/src/interpolation.jl +++ b/src/interpolation.jl @@ -43,7 +43,7 @@ function DofInterpolate(basis::RTBasis, field) bfs = basis.fns[b] - shape = bfs[2] + shape = bfs[1] cellid = shape.cellid refid = shape.refid @@ -70,7 +70,7 @@ function DofInterpolate(basis::RTBasis, field) end - return res + return [r for r in res] end @@ -301,4 +301,4 @@ function EvalCenter(basis::Space, coeff) return res -end \ No newline at end of file +end diff --git a/src/postproc/segcurrents.jl b/src/postproc/segcurrents.jl index 0d45d3ff..a02e37c9 100644 --- a/src/postproc/segcurrents.jl +++ b/src/postproc/segcurrents.jl @@ -17,7 +17,7 @@ function octree(charts::Vector{S} where {S <: CompScienceMeshes.Simplex}) end """ - Hi + grideval(points, coeffs, basis; type=nothing) """ function grideval(points, coeffs, basis; type=nothing) @@ -48,6 +48,7 @@ function grideval(points, coeffs, basis; type=nothing) values[j] += w * coeffs[m] * vals[r][1] end end + continue end end return values diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index c08090e9..bc6a9fcf 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -2,7 +2,6 @@ import IterativeSolvers - struct GMRESSolver{L,T} <: LinearMap{T} linear_operator::L maxiter::Int @@ -93,3 +92,68 @@ function gmres_ch(eq::DiscreteEquation; maxiter=0, restart=0, tol=0) end gmres(eq::DiscreteEquation; maxiter=0, restart=0, tol=0) = gmres_ch(eq; maxiter, restart, tol)[1] + + + +struct CGSolver{L,M,T} <: LinearMap{T} + A::L + Pl::M + abstol::T + reltol::T + maxiter::Int + verbose::Bool +end + + +Base.size(solver::CGSolver) = reverse(size(solver.A)) +Base.axes(solver::CGSolver) = reverse(axes(solver.A)) +operator(solver::CGSolver) = solver.A + + +cg(A; + Pl = IterativeSolvers.Identity(), + abstol::Real = zero(real(eltype(A))), + reltol::Real = sqrt(eps(real(eltype(A)))), + maxiter::Int = size(A,2), + verbose::Bool = false) = CGSolver(A, Pl, abstol, reltol, maxiter, verbose) + + +function solve(solver::CGSolver, b) + T = promote_type(eltype(solver), eltype(b)) + x = similar(Vector{T}, size(solver)[2]) + fill!(x,0) + x, ch = solve!(x, solver, b) + z = similar(Array{T}, axes(solver)[2]) + copyto!(z,x) + return z, ch +end + + +function solve!(x, solver::CGSolver, b) + op = operator(solver) + x, ch = IterativeSolvers.cg!(x, op, b; + Pl = solver.Pl, + abstol = solver.abstol, + reltol = solver.reltol, + maxiter = solver.maxiter, + verbose = solver.verbose, + log = true) + return x, ch +end + +function LinearAlgebra.mul!(y::AbstractVecOrMat, solver::CGSolver, x::AbstractVector) + fill!(y,0) + y, ch = solve!(y, solver, x) + return y +end + + +struct Preconditioner{L,T} <: LinearMap{T} + A::L +end + +Preconditioner(A::L) where {L} = Preconditioner{L,eltype(A)}(A) + +function LinearAlgebra.ldiv!(y, P::Preconditioner, x) + mul!(y, P.A, x) +end \ No newline at end of file From 18f7ebd7013a9a07ba3d0215d1396e2794ee0ceb Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 10 Jul 2023 15:07:00 +0200 Subject: [PATCH 267/528] Rank 1 matrix LinearMap added --- src/BEAST.jl | 1 + src/utils/rank1map.jl | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/utils/rank1map.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 3ca7aaa5..bb9f8bbe 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -135,6 +135,7 @@ include("utils/specialfns.jl") include("utils/combinatorics.jl") include("utils/linearspace.jl") include("utils/zeromap.jl") +include("utils/rank1map.jl") include("bases/basis.jl") include("bases/lincomb.jl") diff --git a/src/utils/rank1map.jl b/src/utils/rank1map.jl new file mode 100644 index 00000000..f33603b6 --- /dev/null +++ b/src/utils/rank1map.jl @@ -0,0 +1,23 @@ +import LinearMaps + +struct Rank1Map{T,U,V} <: LinearMap{T} + u::U + v::V +end + +Rank1Map{T}(u::U, v::V) where {T,U,V} = Rank1Map{T,U,V}(u,v) +LinearMaps.MulStyle(A::Rank1Map) = LinearMaps.FiveArg() + +Base.size(A::Rank1Map) = (length(A.u), length(A.v),) +Base.axes(A::Rank1Map) = (axes(A.u)..., axes(A.v)...) + +function LinearMaps._unsafe_mul!(y::AbstractVector, L::Rank1Map, x::AbstractVector, α::Number=true, β::Number=false) + y .*= β + y .+= α .* L.u .* dot(L.v, x) +end + +function LinearMaps._unsafe_mul!(Y::AbstractMatrix, L::Rank1Map, c::Number, a::Number=true, b::Number=false) + rmul!(Y, b) + Y .+= (L.u * L.v') .* c .* α + return Y +end From 48765b614b72018b81224128e95e1e3a732157ed Mon Sep 17 00:00:00 2001 From: azuccott Date: Mon, 17 Jul 2023 16:34:39 +0200 Subject: [PATCH 268/528] adding quadrule for assemble(nedelec space,double layer, raviarthomas) --- src/bases/local/ndlocal.jl | 23 +++++++++++++++++++++++ src/maxwell/mwops.jl | 32 ++++++++++++++++++++++++++++++++ src/maxwell/nxdbllayer.jl | 5 +++-- src/quadrature/quadrule.jl | 24 ++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/bases/local/ndlocal.jl b/src/bases/local/ndlocal.jl index 9f0c196f..f4308306 100644 --- a/src/bases/local/ndlocal.jl +++ b/src/bases/local/ndlocal.jl @@ -67,3 +67,26 @@ function restrict(ϕ::NDRefSpace{T}, dom1, dom2) where T return Q end + +const _vert_perms_nd = [ + (1,2,3), + (2,3,1), + (3,1,2), + (2,1,3), + (1,3,2), + (3,2,1), +] +const _dof_perms_nd = [ + (1,2,3), + (3,1,2), + (2,3,1), + (2,1,3), + (1,3,2), + (3,2,1), +] + +function dof_permutation(::NDRefSpace, vert_permutation) + i = findfirst(==(tuple(vert_permutation...)), _vert_perms_nd) + @assert i != nothing + return _dof_perms_nd[i] +end \ No newline at end of file diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 0cb87dec..21b2b061 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -136,6 +136,38 @@ function quadrule(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j qd.bpoints[1,j],) end +function quadrule(op::MaxwellOperator3D, g::NDRefSpace, f::RTRefSpace, i, τ, j, σ, qd, + qs::DoubleNumWiltonSauterQStrat) + +T = eltype(eltype(τ.vertices)) +hits = 0 +dtol = 1.0e3 * eps(T) +dmin2 = floatmax(T) +for t in τ.vertices + for s in σ.vertices + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < dtol) + end +end + +hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) +hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) +hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + +h2 = volume(σ) +xtol2 = 0.2 * 0.2 +k2 = abs2(op.gamma) +max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( + qd.tpoints[2,i], + DoubleQuadRule( + qd.tpoints[2,i], + qd.bpoints[2,j],),) +return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j],) +end + function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::BDMRefSpace, i, τ, j, σ, qd, qs::DoubleNumWiltonSauterQStrat) diff --git a/src/maxwell/nxdbllayer.jl b/src/maxwell/nxdbllayer.jl index 8772e354..e8fab643 100644 --- a/src/maxwell/nxdbllayer.jl +++ b/src/maxwell/nxdbllayer.jl @@ -101,7 +101,7 @@ function (igd::Integrand{<:DoubleLayerRotatedMW3D})(x,y,f,g) # x = neighborhood(igd.test_chart,u) # y = neighborhood(igd.trial_chart,v) - j = jacobian(x) * jacobian(y) + # j = jacobian(x) * jacobian(y) nx = normal(x) r = cartesian(x) - cartesian(y) @@ -117,7 +117,8 @@ function (igd::Integrand{<:DoubleLayerRotatedMW3D})(x,y,f,g) fvalue = getvalue(f) gvalue = getvalue(g) - jKg = cross.(Ref(K), j*gvalue) + #jKg = cross.(Ref(K), j*gvalue) + jKg = cross.(Ref(K), gvalue) jnxKg = cross.(Ref(nx), jKg) return _krondot(fvalue, jnxKg) end diff --git a/src/quadrature/quadrule.jl b/src/quadrature/quadrule.jl index b4eda1b7..12cd7de1 100644 --- a/src/quadrature/quadrule.jl +++ b/src/quadrature/quadrule.jl @@ -20,6 +20,30 @@ function quadrule(op::IntegralOperator, g::RTRefSpace, f::RTRefSpace, i, τ, j, hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j],) +end + +function quadrule(op::IntegralOperator, g::RTRefSpace, f::NDRefSpace, i, τ, j, σ, qd, + qs::DoubleNumSauterQstrat) + + T = eltype(eltype(τ.vertices)) + hits = 0 + dtol = 1.0e3 * eps(T) + dmin2 = floatmax(T) + for t in τ.vertices + for s in σ.vertices + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < dtol) + end + end + + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) + hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) + hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + return DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j],) From d508af7629e6f6889994ceb945a568842276e6c9 Mon Sep 17 00:00:00 2001 From: azuccott Date: Mon, 31 Jul 2023 18:22:47 +0200 Subject: [PATCH 269/528] update maxwell operator to any linear basis on triangles --- src/maxwell/mwops.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 21b2b061..a92d2b1e 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -104,7 +104,9 @@ end regularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DReg(op.gamma) singularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DSng(op.gamma) -function quadrule(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd, +const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace, BDMRefSpace, NCrossBDMRefSpace} + +function quadrule(op::MaxwellOperator3D, g::LinearRefSpaceTriangle, f::LinearRefSpaceTriangle, i, τ, j, σ, qd, qs::DoubleNumWiltonSauterQStrat) T = eltype(eltype(τ.vertices)) @@ -195,6 +197,7 @@ function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::BDMRefSpace, i, τ, end + function qrib(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd) dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) From abb2686e185f0433053f0c5bd56c9ca9b5b0f1fc Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 3 Aug 2023 12:43:05 +0000 Subject: [PATCH 270/528] support for rank one operators defined by 2 traces --- src/BEAST.jl | 1 + src/dyadicop.jl | 46 +++++++++++++++++++++++++++++++++++++++++++ src/utils/rank1map.jl | 4 ++-- test/runtests.jl | 1 + test/test_dyadicop.jl | 33 +++++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/dyadicop.jl create mode 100644 test/test_dyadicop.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index bb9f8bbe..d207b9c3 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -180,6 +180,7 @@ include("localop.jl") include("multiplicativeop.jl") include("identityop.jl") include("integralop.jl") +include("dyadicop.jl") include("interpolation.jl") include("quaddata.jl") diff --git a/src/dyadicop.jl b/src/dyadicop.jl new file mode 100644 index 00000000..5306d397 --- /dev/null +++ b/src/dyadicop.jl @@ -0,0 +1,46 @@ +struct DyadicOp{T,F,G} <: Operator + α::T + f::F + g::G +end + +function DyadicOp(f::F, g::G) where {F,G} + T = scalartype(f,g) + DyadicOp{T,F,G}(one(T), f, g) +end + +Base.:*(b::Number, op::DyadicOp) = DyadicOp(b * op.α, op.f, op.g) + +scalartype(op::DyadicOp) = promote_type(scalartype(op.α), scalartype(op.f), scalartype(op.g)) +defaultquadstrat(op::DyadicOp, testspace, refspace) = nothing + + +function allocatestorage(op::DyadicOp, test_functions, trial_functions, + storage_trait, longdelays_trait) + + T = scalartype(op, test_functions, trial_functions) + M = length(test_functions) + N = length(trial_functions) + + u = zeros(T,M) + v = zeros(T,N) + + A = BEAST.Rank1Map{T}(u,v) + store() = A + freeze() = A + + return freeze, store +end + + +function assemble!(biop::DyadicOp, tfs::Space, bfs::Space, store, + threading::Type{BEAST.Threading{:multi}}; + quadstrat=defaultquadstrat(biop, tfs, bfs)) + + u = assemble(biop.f, tfs) + v = assemble(biop.g, bfs) + + A = store() + A.u .= biop.α * u + A.v .= adjoint.(v) +end \ No newline at end of file diff --git a/src/utils/rank1map.jl b/src/utils/rank1map.jl index f33603b6..5a366a96 100644 --- a/src/utils/rank1map.jl +++ b/src/utils/rank1map.jl @@ -16,8 +16,8 @@ function LinearMaps._unsafe_mul!(y::AbstractVector, L::Rank1Map, x::AbstractVect y .+= α .* L.u .* dot(L.v, x) end -function LinearMaps._unsafe_mul!(Y::AbstractMatrix, L::Rank1Map, c::Number, a::Number=true, b::Number=false) - rmul!(Y, b) +function LinearMaps._unsafe_mul!(Y::AbstractMatrix, L::Rank1Map, c::Number, α::Number=true, β::Number=false) + rmul!(Y, β) Y .+= (L.u * L.v') .* c .* α return Y end diff --git a/test/runtests.jl b/test/runtests.jl index f5a0e4c4..f635e5b9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -62,6 +62,7 @@ include("test_tdassembly.jl") include("test_tdhhdbl.jl") include("test_tdmwdbl.jl") include("test_compressed_storage.jl") +include("test_dyadicop.jl") # include("test_matrixconv.jl") include("test_tdop_scaling.jl") diff --git a/test/test_dyadicop.jl b/test/test_dyadicop.jl new file mode 100644 index 00000000..adf93ba2 --- /dev/null +++ b/test/test_dyadicop.jl @@ -0,0 +1,33 @@ +using Test + +using LinearAlgebra +using CompScienceMeshes +using BEAST + +@testset begin + m = meshsphere(radius=1.0, h=0.35) + X = lagrangec0d1(m) + + F = x -> 1.0 + f = BEAST.ScalarTrace{Float64}(F) + g = BEAST.ScalarTrace{Float64}(F) + + op = 2*BEAST.DyadicOp(f,g) + A = assemble(op, X, X) + + b = assemble(f,X) + c = assemble(f,X) + + M = Matrix(A) + @test norm(M - 2*b*c') ≤ 1e-8 + + @hilbertspace j[1:2] + @hilbertspace k[1:2] + + X2 = X × X + A2 = assemble(@discretise(BEAST.diag(op)[k,j], k∈X2, j∈X2)) + M2 = Matrix(A2) + + nX = length(X) + @test size(M2) == (2*nX, 2*nX) +end From 42046f1625a70dd468293f1070290d17668d4a18 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 10 Aug 2023 16:02:19 +0200 Subject: [PATCH 271/528] fix assembly for tensorproduct TD ops --- src/timedomain/tdintegralop.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 77a9c2e7..ff74175e 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -139,9 +139,9 @@ end function assemble!(op::LinearCombinationOfOperators, tfs::SpaceTimeBasis, bfs::SpaceTimeBasis, store, threading=Threading{:multi}; quadstrat=defaultquadstrat(op, tfs, bfs)) - for (a,A) in zip(op.coeffs, op.ops) + for (a,A,qs) in zip(op.coeffs, op.ops, quadstrat) store1(v,m,n,k) = store(a*v,m,n,k) - assemble!(A, tfs, bfs, store1) + assemble!(A, tfs, bfs, store1, threading; quadstrat=qs) end end From a231dc30f2dd945aea2615d42fcb9bcd149b5f21 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Tue, 18 Jul 2023 13:47:03 +0200 Subject: [PATCH 272/528] HH3D: allow gamma/wavenumber to be nothing With this change, we can dispatch on Laplace-Type kernels as 'Nothing' is a distinct type (while gamma=0.0 could not be distinguished from a Yukawa-Type solution) --- src/BEAST.jl | 2 + src/helmholtz3d/hh3d_sauterschwabqr.jl | 18 +-- src/helmholtz3d/hh3dops.jl | 165 +++++++++++++++++-------- 3 files changed, 124 insertions(+), 61 deletions(-) diff --git a/src/BEAST.jl b/src/BEAST.jl index d207b9c3..89ffeba1 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -61,6 +61,8 @@ export DirichletTrace export HH3DMonopole, gradHH3DMonopole export HH3DLinearPotential +export alpha, beta, gamma + export HH3DSingleLayerNear export HH3DDoubleLayerNear export HH3DDoubleLayerTransposedNear diff --git a/src/helmholtz3d/hh3d_sauterschwabqr.jl b/src/helmholtz3d/hh3d_sauterschwabqr.jl index ea2298ec..665e1b24 100644 --- a/src/helmholtz3d/hh3d_sauterschwabqr.jl +++ b/src/helmholtz3d/hh3d_sauterschwabqr.jl @@ -13,8 +13,8 @@ function pulled_back_integrand(op::HH3DSingleLayerFDBIO, j = jacobian(x) * jacobian(y) - α = op.alpha - γ = op.gamma + α = alpha(op) + γ = gamma(op) R = norm(cartesian(x)-cartesian(y)) G = exp(-γ*R)/(4*π*R) @@ -43,9 +43,9 @@ function pulled_back_integrand(op::HH3DHyperSingularFDBIO, j = jacobian(x) * jacobian(y) - α = op.alpha - β = op.beta - γ = op.gamma + α = alpha(op) + β = beta(op) + γ = gamma(op) R = norm(cartesian(x)-cartesian(y)) G = exp(-γ*R)/(4*π*R) @@ -84,8 +84,8 @@ function pulled_back_integrand(op::HH3DDoubleLayerFDBIO, j = jacobian(x) * jacobian(y) - α = op.alpha - γ = op.gamma + α = alpha(op) + γ = gamma(op) r = cartesian(x) - cartesian(y) R = norm(r) @@ -117,8 +117,8 @@ function pulled_back_integrand(op::HH3DDoubleLayerTransposedFDBIO, j = jacobian(x) * jacobian(y) - α = op.alpha - γ = op.gamma + α = alpha(op) + γ = gamma(op) r = cartesian(x) - cartesian(y) R = norm(r) diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index 19e99e8f..abe2d633 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -1,13 +1,13 @@ -abstract type Helmholtz3DOp <: MaxwellOperator3D end -abstract type Helmholtz3DOpReg <: MaxwellOperator3DReg end +abstract type Helmholtz3DOp{T,K} <: MaxwellOperator3D end +abstract type Helmholtz3DOpReg{T,K} <: MaxwellOperator3DReg end """ ``` ∫_Γ dx ∫_Γ dy \\left(α G g(x) n_x ⋅ n_y f(y) + β G \\mbox{curl} g(x) ⋅ \\mbox{curl} f(y) \\right) ``` with ``G(x,y) = \\frac{e^{-γ |x-y|}}{4 π |x-y|}`` """ -struct HH3DHyperSingularFDBIO{T,K} <: Helmholtz3DOp +struct HH3DHyperSingularFDBIO{T,K} <: Helmholtz3DOp{T,K} "coefficient of the weakly singular term" alpha::T "coefficient of the hyper singular term" @@ -17,7 +17,14 @@ struct HH3DHyperSingularFDBIO{T,K} <: Helmholtz3DOp end HH3DHyperSingularFDBIO(gamma) = HH3DHyperSingularFDBIO(gamma^2, one(gamma), gamma) -scalartype(op::HH3DHyperSingularFDBIO) = promote_type(typeof(op.alpha), typeof(op.beta), typeof(op.gamma)) + +scalartype(op::Helmholtz3DOp{T,K}) where {T, K <: Nothing} = T +scalartype(op::Helmholtz3DOp{T,K}) where {T, K} = promote_type(T, K) + +alpha(op::Union{Helmholtz3DOp{T,K},Helmholtz3DOpReg{T,K}}) where {T, K} = op.alpha +beta(op::HH3DHyperSingularFDBIO{T,K}) where {T, K} = op.beta +gamma(op::Union{Helmholtz3DOp{T,K}, Helmholtz3DOpReg{T,K}}) where {T, K <: Nothing} = T(0) +gamma(op::Union{Helmholtz3DOp{T,K}, Helmholtz3DOpReg{T,K}}) where {T, K} = op.gamma """ ```math @@ -25,27 +32,27 @@ a(u,v) = α ∬_{Γ×Γ} u(x) G_{γ}(|x-y|) v(y) ``` with ``G_{γ}(r) = \\frac{e^{-γr}}{4πr}``. """ -struct HH3DSingleLayerFDBIO{T,K} <: Helmholtz3DOp +struct HH3DSingleLayerFDBIO{T,K} <: Helmholtz3DOp{T,K} alpha::T gamma::K end -struct HH3DSingleLayerReg{T,K} <: Helmholtz3DOpReg +struct HH3DSingleLayerReg{T,K} <: Helmholtz3DOpReg{T,K} alpha::T gamma::K end -struct HH3DSingleLayerSng{T,K} <: Helmholtz3DOp +struct HH3DSingleLayerSng{T,K} <: Helmholtz3DOp{T,K} alpha::T gamma::K end -struct HH3DDoubleLayerFDBIO{T,K} <: Helmholtz3DOp +struct HH3DDoubleLayerFDBIO{T,K} <: Helmholtz3DOp{T,K} alpha::T gamma::K end -struct HH3DDoubleLayerTransposedFDBIO{T,K} <: Helmholtz3DOp +struct HH3DDoubleLayerTransposedFDBIO{T,K} <: Helmholtz3DOp{T,K} alpha::T gamma::K end @@ -141,7 +148,7 @@ function quadrule(op::HH3DSingleLayerFDBIO, trial_quadpoints = qd.bsis_qp h2 = volume(trial_element) xtol2 = 0.2 * 0.2 - k2 = abs2(op.gamma) + k2 = abs2(gamma(op)) max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( test_quadpoints[1,i], DoubleQuadRule( @@ -297,7 +304,7 @@ function quadrule(op::HH3DDoubleLayerTransposedFDBIO, trial_quadpoints = quadrature_data.bsis_qp #= h2 = volume(trial_element) xtol2 = 0.2 * 0.2 - k2 = abs2(op.gamma) + k2 = abs2(gamma(op)) max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( test_quadpoints[1,i], @@ -335,7 +342,7 @@ function quadrule(op::HH3DDoubleLayerFDBIO, trial_quadpoints = quadrature_data.bsis_qp h2 = volume(trial_element) xtol2 = 0.2 * 0.2 - k2 = abs2(op.gamma) + k2 = abs2(gamma(op)) #= max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( test_quadpoints[1,i], @@ -374,9 +381,9 @@ end function (igd::Integrand{<:HH3DHyperSingularFDBIO})(x,y,f,g) - α = igd.operator.alpha - β = igd.operator.beta - γ = igd.operator.gamma + α = alpha(igd.operator) + β = beta(igd.operator) + γ = gamma(igd.operator) r = cartesian(x) - cartesian(y) R = norm(r) @@ -394,8 +401,8 @@ end function integrand(op::HH3DHyperSingularFDBIO, kernel, test_values, test_element, trial_values, trial_element) - α = op.alpha - β = op.beta + α = alpha(op) + β = beta(op) G = kernel.green @@ -414,12 +421,12 @@ end HH3DSingleLayerFDBIO(gamma) = HH3DSingleLayerFDBIO(one(gamma), gamma) -regularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerReg(op.alpha, op.gamma) -singularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerSng(op.alpha, op.gamma) +regularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerReg(alpha(op), gamma(op)) +singularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerSng(alpha(op), gamma(op)) function (igd::Integrand{<:HH3DSingleLayerFDBIO})(x,y,f,g) - α = igd.operator.alpha - γ = igd.operator.gamma + α = alpha(igd.operator) + γ = gamma(igd.operator) r = cartesian(x) - cartesian(y) R = norm(r) @@ -434,8 +441,8 @@ function (igd::Integrand{<:HH3DSingleLayerFDBIO})(x,y,f,g) end function (igd::Integrand{<:HH3DSingleLayerReg})(x,y,f,g) - α = igd.operator.alpha - γ = igd.operator.gamma + α = alpha(igd.operator) + γ = gamma(igd.operator) r = cartesian(x) - cartesian(y) R = norm(r) @@ -452,7 +459,7 @@ end function integrand(op::Union{HH3DSingleLayerFDBIO,HH3DSingleLayerReg}, kernel, test_values, test_element, trial_values, trial_element) - α = op.alpha + α = alpha(op) G = kernel.green g = test_values.value @@ -467,8 +474,8 @@ function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, trial_refspace::LagrangeRefSpace{T,0} where {T}, test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) - γ = op.gamma - α = op.alpha + γ = gamma(op) + α = alpha(op) s1, s2, s3 = trial_element.vertices @@ -489,7 +496,7 @@ HH3DDoubleLayerFDBIO(gamma) = HH3DDoubleLayerFDBIO(one(gamma), gamma) function (igd::Integrand{<:HH3DDoubleLayerFDBIO})(x,y,f,g) - γ = igd.operator.gamma + γ = gamma(igd.operator) r = cartesian(x) - cartesian(y) R = norm(r) @@ -516,7 +523,7 @@ end HH3DDoubleLayerTransposedFDBIO(gamma) = HH3DDoubleLayerTransposedFDBIO(one(gamma), gamma) function (igd::Integrand{<:HH3DDoubleLayerTransposedFDBIO})(x,y,f,g) - γ = igd.operator.gamma + γ = gamma(igd.operator) r = cartesian(x) - cartesian(y) R = norm(r) @@ -544,19 +551,78 @@ module Helmholtz3D const Mod = BEAST function singlelayer(; + alpha=nothing, gamma=nothing, - wavenumber=nothing, - alpha=nothing) + wavenumber=nothing + ) - if (gamma == nothing) && (wavenumber == nothing) - error("Supply one of (not both) gamma or wavenumber") + alpha, gamma = parameter_handler(alpha, gamma, wavenumber) + + return Mod.HH3DSingleLayerFDBIO(alpha,gamma) + end + + function doublelayer(; + alpha=nothing, + gamma=nothing, + wavenumber=nothing + ) + + alpha, gamma = parameter_handler(alpha, gamma, wavenumber) + + return Mod.HH3DDoubleLayerFDBIO(alpha, gamma) + end + + function doublelayer_transposed(; + alpha=nothing, + gamma=nothing, + wavenumber=nothing + ) + + alpha, gamma = parameter_handler(alpha, gamma, wavenumber) + + return Mod.HH3DDoubleLayerTransposedFDBIO(alpha, gamma) + end + + function hypersingular(; + alpha=nothing, + beta=nothing, + gamma=nothing, + wavenumber=nothing + ) + + gamma, wavenumber = gamma_wavenumber_handler(gamma, wavenumber) + + if alpha === nothing + if gamma !== nothing + alpha = gamma^2 + else + alpha = 0.0 # In the long run, this should probably be rather 'nothing' + end end - if (gamma != nothing) && (wavenumber != nothing) + if beta === nothing + if gamma !== nothing + beta = one(gamma) + else + beta = one(alpha) + end + end + + return Mod.HH3DHyperSingularFDBIO(alpha, beta, gamma) + end + + planewave(; + direction=error("direction is a required argument"), + wavenumber=error("wavenumber is a required arguement"), + amplitude=one(eltype(direction))) = + Mod.HH3DPlaneWave(direction, wavenumber) + + function gamma_wavenumber_handler(gamma, wavenumber) + if (gamma !== nothing) && (wavenumber !== nothing) error("Supply one of (not both) gamma or wavenumber") end - if gamma == nothing + if gamma === nothing && (wavenumber !== nothing) if iszero(real(wavenumber)) gamma = -imag(wavenumber) else @@ -564,29 +630,24 @@ module Helmholtz3D end end - @assert gamma != nothing - alpha == nothing && (alpha = one(real(typeof(gamma)))) - - Mod.HH3DSingleLayerFDBIO(alpha,gamma) + return gamma, wavenumber end - hypersingular(; - gamma=error("propagation constant is a required argument"), - alpha=gamma^2, - beta=one(gamma)) = - Mod.HH3DHyperSingularFDBIO(alpha, beta, gamma) + function parameter_handler(alpha, gamma, wavenumber) - planewave(; - direction=error("direction is a required argument"), - wavenumber=error("wavenumber is a required arguement"), - amplitude=one(eltype(direction))) = - Mod.HH3DPlaneWave(direction, wavenumber) + gamma, wavenumber = gamma_wavenumber_handler(gamma, wavenumber) - doublelayer(;gamma=error("gamma missing"), alpha=one(gamma)) = - Mod.HH3DDoubleLayerFDBIO(alpha, gamma) + if alpha === nothing + if gamma !== nothing + alpha = one(real(typeof(gamma))) + else + # We are dealing with a static problem. Default to double precision. + alpha = 1.0 + end + end - doublelayer_transposed(;gamma=error("gamma missing"), alpha=one(gamma)) = - Mod.HH3DDoubleLayerTransposedFDBIO(alpha, gamma) + return alpha, gamma + end end export Helmholtz3D From b55125375cf50c85b9937c524673dc8dfefa3c0a Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Tue, 18 Jul 2023 17:55:05 +0200 Subject: [PATCH 273/528] HH3D: Consolidate interfaces, resembling Maxwell structure The public interfaces to the Helmholtz operators has been moved to a file helmholtz3d.jl resembling the maxwell.jl file. The right-hand sides now store gamma and not the wavenumber. They now correctly allow to discretize real-valued RHS if gamma is real-valued. --- src/BEAST.jl | 1 + src/helmholtz3d/helmholtz3d.jl | 134 +++++++++++++++++++++++++++++++++ src/helmholtz3d/hh3dexc.jl | 71 ++++++----------- src/helmholtz3d/hh3dops.jl | 108 -------------------------- src/operator.jl | 18 ++++- test/test_hh3d_nearfield.jl | 8 +- test/test_hh3dexc.jl | 4 +- 7 files changed, 181 insertions(+), 163 deletions(-) create mode 100644 src/helmholtz3d/helmholtz3d.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 89ffeba1..8495e908 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -226,6 +226,7 @@ include("helmholtz3d/nitsche.jl") include("helmholtz3d/hh3dnear.jl") include("helmholtz3d/hh3dfar.jl") include("helmholtz3d/hh3d_sauterschwabqr.jl") +include("helmholtz3d/helmholtz3d.jl") #suport for Volume Integral equation include("volumeintegral/vie.jl") diff --git a/src/helmholtz3d/helmholtz3d.jl b/src/helmholtz3d/helmholtz3d.jl new file mode 100644 index 00000000..2538ec37 --- /dev/null +++ b/src/helmholtz3d/helmholtz3d.jl @@ -0,0 +1,134 @@ +module Helmholtz3D + + using ..BEAST + const Mod = BEAST + + function singlelayer(; + alpha=nothing, + gamma=nothing, + wavenumber=nothing + ) + + alpha, gamma = parameter_handler(alpha, gamma, wavenumber) + + return Mod.HH3DSingleLayerFDBIO(alpha,gamma) + end + + function doublelayer(; + alpha=nothing, + gamma=nothing, + wavenumber=nothing + ) + + alpha, gamma = parameter_handler(alpha, gamma, wavenumber) + + return Mod.HH3DDoubleLayerFDBIO(alpha, gamma) + end + + function doublelayer_transposed(; + alpha=nothing, + gamma=nothing, + wavenumber=nothing + ) + + alpha, gamma = parameter_handler(alpha, gamma, wavenumber) + + return Mod.HH3DDoubleLayerTransposedFDBIO(alpha, gamma) + end + + function hypersingular(; + alpha=nothing, + beta=nothing, + gamma=nothing, + wavenumber=nothing + ) + + gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) + + if alpha === nothing + if gamma !== nothing + alpha = gamma^2 + else + alpha = 0.0 # In the long run, this should probably be rather 'nothing' + end + end + + if beta === nothing + if gamma !== nothing + beta = one(gamma) + else + beta = one(alpha) + end + end + + return Mod.HH3DHyperSingularFDBIO(alpha, beta, gamma) + end + + function planewave(; + direction=error("direction is a required argument"), + gamma=nothing, + wavenumber=nothing, + amplitude=one(eltype(direction))) + + gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) + + # Note: Unlike for the operators, there seems little benefit in + # explicitly declaring a Laplace-Type excitation. + + gamma === nothing && (gamma = zero(amplitude)) + + return Mod.HH3DPlaneWave(direction, amplitude, gamma) + end + + function linearpotential(; direction=SVector(1, 0, 0), amplitude=1.0) + return Mod.HH3DLinearPotential(direction ./ norm(direction), amplitude) + end + + function grad_linearpotential(; direction=SVector(0.0, 0.0, 0.0), amplitude=1.0) + return Mod.gradHH3DLinearPotential(direction, amplitude) + end + + function monopole(; + position=SVector(0.0, 0.0, 0.0), + gamma=nothing, + wavenumber=nothing, + amplitude=1.0 + ) + + gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) + gamma === nothing && (gamma = zero(amplitude)) + + return Mod.HH3DMonopole(position, gamma, amplitude) + end + + function grad_monopole(; + position=SVector(0.0, 0.0, 0.0), + gamma=nothing, + wavenumber=nothing, + amplitude=1.0 + ) + + gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) + gamma === nothing && (gamma = zero(amplitude)) + + return Mod.gradHH3DMonopole(position, gamma, amplitude) + end + + function parameter_handler(alpha, gamma, wavenumber) + + gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) + + if alpha === nothing + if gamma !== nothing + alpha = one(real(typeof(gamma))) + else + # We are dealing with a static problem. Default to double precision. + alpha = 1.0 + end + end + + return alpha, gamma + end +end + +export Helmholtz3D \ No newline at end of file diff --git a/src/helmholtz3d/hh3dexc.jl b/src/helmholtz3d/hh3dexc.jl index 0de1215c..ceac5a0e 100644 --- a/src/helmholtz3d/hh3dexc.jl +++ b/src/helmholtz3d/hh3dexc.jl @@ -1,24 +1,17 @@ - - -struct HH3DPlaneWave{T,P} +struct HH3DPlaneWave{P,K,T} direction::P - wavenumber::T + gamma::K amplitude::T end -function HH3DPlaneWave(direction, wavenumber; amplitude=1) - w, a = promote(wavenumber, amplitude) - HH3DPlaneWave(direction, w, a) -end - function (f::HH3DPlaneWave)(r) d = f.direction - k = f.wavenumber + γ = f.gamma a = f.amplitude - a * exp(-im*k*dot(d,r)) + return a * exp(-γ*dot(d,r)) end -scalartype(f::HH3DPlaneWave{T}) where {T} = complex(T) +scalartype(f::HH3DPlaneWave{P,K,T}) where {P,K,T} = promote_type(eltype(P), K, T) """ HH3DLinearPotential @@ -26,14 +19,12 @@ scalartype(f::HH3DPlaneWave{T}) where {T} = complex(T) A potential that linearly increases in `direction` with scaling coefficient `amplitude`. Its negative gradient will be a uniform vector field pointing in the opposite direction. """ -struct HH3DLinearPotential{T,P} +struct HH3DLinearPotential{P,T} direction::P amplitude::T end -function HH3DLinearPotential(; direction=SVector(1,0,0), amplitude=1.0) - HH3DLinearPotential(direction ./ norm(direction), amplitude) -end +scalartype(f::HH3DLinearPotential{P,T}) where {P,T} = promote_type(eltype(P), T) function (f::HH3DLinearPotential)(r) d = f.direction @@ -41,17 +32,11 @@ function (f::HH3DLinearPotential)(r) return a * dot(d, r) end -scalartype(f::HH3DLinearPotential{T}) where {T} = complex(T) - struct gradHH3DLinearPotential{T,P} direction::P amplitude::T end -function gradHH3DLinearPotential(;direction=SVector(0.0,0.0,0.0), amplitude=1.0) - gradHH3DLinearPotential(direction, amplitude) -end - function (f::gradHH3DLinearPotential)(r) d = f.direction a = f.amplitude @@ -73,58 +58,48 @@ dot(::NormalVector, m::gradHH3DLinearPotential) = NormalDerivative(HH3DLinearPot Potential of a monopole-type point source (e.g., of an electric charge) """ -struct HH3DMonopole{T,P} +struct HH3DMonopole{P,K,T} position::P - wavenumber::T + gamma::K amplitude::T end -scalartype(x::HH3DMonopole{T}) where {T} = complex(T) - -function HH3DMonopole(;position=SVector(0.0,0.0,0.0), wavenumber=0.0, amplitude=1.0) - w, a = promote(wavenumber, amplitude) - HH3DMonopole(position, w, a) -end +scalartype(x::HH3DMonopole{P,K,T}) where {P,K,T} = promote_type(eltype(P), K, T) function (f::HH3DMonopole)(r) - k = f.wavenumber + γ = f.gamma p = f.position a = f.amplitude - return a*exp(-im * k * norm(r - p)) / (norm(r - p)) + return a*exp(-γ * norm(r - p)) / (norm(r - p)) end -struct gradHH3DMonopole{T,P} +struct gradHH3DMonopole{P,K,T} position::P - wavenumber::T + gamma::K amplitude::T end -scalartype(x::gradHH3DMonopole{T}) where {T} = complex(T) - -function gradHH3DMonopole(;position=SVector(0.0,0.0,0.0), wavenumber=0.0, amplitude=1.0) - w, a = promote(wavenumber, amplitude) - gradHH3DMonopole(position, w, a) -end +scalartype(x::gradHH3DMonopole{P,K,T}) where {P,K,T} = promote_type(eltype(P), K, T) function (f::gradHH3DMonopole)(r) a = f.amplitude - k = f.wavenumber + γ = f.gamma p = f.position vecR = r - p R = norm(vecR) - return -a * vecR * exp(-im * k * R) / R^2 * (im * k + 1 / R) + return -a * vecR * exp(-γ * R) / R^2 * (γ + 1 / R) end function grad(m::HH3DMonopole) - return gradHH3DMonopole(m.position, m.wavenumber, m.amplitude) + return gradHH3DMonopole(m.position, m.gamma, m.amplitude) end -*(a::Number, m::HH3DMonopole) = HH3DMonopole(m.position, m.wavenumber, a * m.amplitude) -*(a::Number, m::gradHH3DMonopole) = gradHH3DMonopole(m.position, m.wavenumber, a * m.amplitude) +*(a::Number, m::HH3DMonopole) = HH3DMonopole(m.position, m.gamma, a * m.amplitude) +*(a::Number, m::gradHH3DMonopole) = gradHH3DMonopole(m.position, m.gamma, a * m.amplitude) -dot(::NormalVector, m::gradHH3DMonopole) = NormalDerivative(HH3DMonopole(m.position, m.wavenumber, m.amplitude)) +dot(::NormalVector, m::gradHH3DMonopole) = NormalDerivative(HH3DMonopole(m.position, m.gamma, m.amplitude)) mutable struct DirichletTrace{T,F} <: Functional field::F @@ -162,11 +137,11 @@ end function (f::NormalDerivative{T,F})(manipoint) where {T,F<:HH3DPlaneWave} d = f.field.direction - k = f.field.wavenumber + γ = f.field.gamma a = f.field.amplitude n = normal(manipoint) r = cartesian(manipoint) - -im*k*a * dot(d,n) * exp(-im*k*dot(d,r)) + return -γ*a * dot(d,n) * exp(-γ*dot(d,r)) end function (f::NormalDerivative{T,F})(manipoint) where {T,F<:HH3DLinearPotential} diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index abe2d633..51931678 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -543,111 +543,3 @@ function integrand(biop::HH3DDoubleLayerTransposedFDBIO, np = normal(mp) fp[1] * dot(np, kernel.gradgreen) * fq[1] end - - -module Helmholtz3D - - using ..BEAST - const Mod = BEAST - - function singlelayer(; - alpha=nothing, - gamma=nothing, - wavenumber=nothing - ) - - alpha, gamma = parameter_handler(alpha, gamma, wavenumber) - - return Mod.HH3DSingleLayerFDBIO(alpha,gamma) - end - - function doublelayer(; - alpha=nothing, - gamma=nothing, - wavenumber=nothing - ) - - alpha, gamma = parameter_handler(alpha, gamma, wavenumber) - - return Mod.HH3DDoubleLayerFDBIO(alpha, gamma) - end - - function doublelayer_transposed(; - alpha=nothing, - gamma=nothing, - wavenumber=nothing - ) - - alpha, gamma = parameter_handler(alpha, gamma, wavenumber) - - return Mod.HH3DDoubleLayerTransposedFDBIO(alpha, gamma) - end - - function hypersingular(; - alpha=nothing, - beta=nothing, - gamma=nothing, - wavenumber=nothing - ) - - gamma, wavenumber = gamma_wavenumber_handler(gamma, wavenumber) - - if alpha === nothing - if gamma !== nothing - alpha = gamma^2 - else - alpha = 0.0 # In the long run, this should probably be rather 'nothing' - end - end - - if beta === nothing - if gamma !== nothing - beta = one(gamma) - else - beta = one(alpha) - end - end - - return Mod.HH3DHyperSingularFDBIO(alpha, beta, gamma) - end - - planewave(; - direction=error("direction is a required argument"), - wavenumber=error("wavenumber is a required arguement"), - amplitude=one(eltype(direction))) = - Mod.HH3DPlaneWave(direction, wavenumber) - - function gamma_wavenumber_handler(gamma, wavenumber) - if (gamma !== nothing) && (wavenumber !== nothing) - error("Supply one of (not both) gamma or wavenumber") - end - - if gamma === nothing && (wavenumber !== nothing) - if iszero(real(wavenumber)) - gamma = -imag(wavenumber) - else - gamma = im*wavenumber - end - end - - return gamma, wavenumber - end - - function parameter_handler(alpha, gamma, wavenumber) - - gamma, wavenumber = gamma_wavenumber_handler(gamma, wavenumber) - - if alpha === nothing - if gamma !== nothing - alpha = one(real(typeof(gamma))) - else - # We are dealing with a static problem. Default to double precision. - alpha = 1.0 - end - end - - return alpha, gamma - end -end - -export Helmholtz3D diff --git a/src/operator.jl b/src/operator.jl index bfdb379b..3f371f40 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -320,4 +320,20 @@ function assemble!(op::BlockFullOperators, U::DirectProductSpace, V::DirectProdu assemble!(op.op, u, v, store1, threading; quadstrat) end end -end \ No newline at end of file +end + +function gamma_wavenumber_handler(gamma, wavenumber) + if (gamma !== nothing) && (wavenumber !== nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma === nothing && (wavenumber !== nothing) + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + return gamma, wavenumber +end diff --git a/test/test_hh3d_nearfield.jl b/test/test_hh3d_nearfield.jl index b39e8b11..7cdb04ea 100644 --- a/test/test_hh3d_nearfield.jl +++ b/test/test_hh3d_nearfield.jl @@ -26,8 +26,8 @@ using Test pos1 = SVector(r * 1.5, 0.0, 0.0) # positioning of point charges pos2 = SVector(-r * 1.5, 0.0, 0.0) - charge1 = HH3DMonopole(position=pos1, amplitude=q/(4*π*ϵ), wavenumber=k) - charge2 = HH3DMonopole(position=pos2, amplitude=-q/(4*π*ϵ), wavenumber=k) + charge1 = Helmholtz3D.monopole(position=pos1, amplitude=q/(4*π*ϵ), wavenumber=k) + charge2 = Helmholtz3D.monopole(position=pos2, amplitude=-q/(4*π*ϵ), wavenumber=k) # Potential of point charges @@ -89,8 +89,8 @@ using Test pos1 = SVector(r * 0.5, 0.0, 0.0) pos2 = SVector(-r * 0.5, 0.0, 0.0) - charge1 = HH3DMonopole(position=pos1, amplitude=q/(4*π*ϵ), wavenumber=k) - charge2 = HH3DMonopole(position=pos2, amplitude=-q/(4*π*ϵ), wavenumber=k) + charge1 = Helmholtz3D.monopole(position=pos1, amplitude=q/(4*π*ϵ), wavenumber=k) + charge2 = Helmholtz3D.monopole(position=pos2, amplitude=-q/(4*π*ϵ), wavenumber=k) gD0 = assemble(DirichletTrace(charge1), X0) + assemble(DirichletTrace(charge2), X0) gD1 = assemble(DirichletTrace(charge1), X1) + assemble(DirichletTrace(charge2), X1) diff --git a/test/test_hh3dexc.jl b/test/test_hh3dexc.jl index 93e1b40b..aa9e8c03 100644 --- a/test/test_hh3dexc.jl +++ b/test/test_hh3dexc.jl @@ -8,7 +8,7 @@ for T in [Float32, Float64] local κ = T(2π) direction = point(T,0,0,1) - local f = BEAST.HH3DPlaneWave(direction, κ) + local f = BEAST.HH3DPlaneWave(direction, im*κ, T(1.0)) v1 = f(point(T,0,0,0)) v2 = f(point(T,0,0,0.5)) @@ -16,7 +16,7 @@ for T in [Float32, Float64] @test v1 ≈ +1 @test v2 ≈ -1 - lp = HH3DLinearPotential(direction=point(T,0,1,0), amplitude=2.0) + lp = HH3DLinearPotential(point(T,0,1,0), 2.0) @test lp(point(T,1,1,0)) == T(2.0) gradlp = grad(lp) From 02e6de71892d32ed5768969990cf159d92378277 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Thu, 20 Jul 2023 09:19:01 +0200 Subject: [PATCH 274/528] Adapt Maxwell operators to allow detection of static kernels --- src/decoupled/dpops.jl | 2 +- src/helmholtz3d/helmholtz3d.jl | 21 ++------- src/helmholtz3d/hh3dops.jl | 9 +--- src/helmholtz3d/nitsche.jl | 2 +- src/maxwell/maxwell.jl | 44 +++++------------- src/maxwell/mwops.jl | 85 ++++++++++++++++++++++++++-------- src/maxwell/nitsche.jl | 2 +- src/maxwell/nxdbllayer.jl | 11 +++-- src/operator.jl | 16 ------- 9 files changed, 91 insertions(+), 101 deletions(-) diff --git a/src/decoupled/dpops.jl b/src/decoupled/dpops.jl index 3bcbd068..cfae115d 100644 --- a/src/decoupled/dpops.jl +++ b/src/decoupled/dpops.jl @@ -1,4 +1,4 @@ -struct CurlSingleLayerDP3D{T,U} <: MaxwellOperator3D +struct CurlSingleLayerDP3D{T,U} <: MaxwellOperator3D{U,T} gamma::T alpha::U end diff --git a/src/helmholtz3d/helmholtz3d.jl b/src/helmholtz3d/helmholtz3d.jl index 2538ec37..3d76332e 100644 --- a/src/helmholtz3d/helmholtz3d.jl +++ b/src/helmholtz3d/helmholtz3d.jl @@ -9,7 +9,7 @@ module Helmholtz3D wavenumber=nothing ) - alpha, gamma = parameter_handler(alpha, gamma, wavenumber) + alpha, gamma = Mod.operator_parameter_handler(alpha, gamma, wavenumber) return Mod.HH3DSingleLayerFDBIO(alpha,gamma) end @@ -20,7 +20,7 @@ module Helmholtz3D wavenumber=nothing ) - alpha, gamma = parameter_handler(alpha, gamma, wavenumber) + alpha, gamma = Mod.operator_parameter_handler(alpha, gamma, wavenumber) return Mod.HH3DDoubleLayerFDBIO(alpha, gamma) end @@ -31,7 +31,7 @@ module Helmholtz3D wavenumber=nothing ) - alpha, gamma = parameter_handler(alpha, gamma, wavenumber) + alpha, gamma = Mod.operator_parameter_handler(alpha, gamma, wavenumber) return Mod.HH3DDoubleLayerTransposedFDBIO(alpha, gamma) end @@ -114,21 +114,6 @@ module Helmholtz3D return Mod.gradHH3DMonopole(position, gamma, amplitude) end - function parameter_handler(alpha, gamma, wavenumber) - - gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) - - if alpha === nothing - if gamma !== nothing - alpha = one(real(typeof(gamma))) - else - # We are dealing with a static problem. Default to double precision. - alpha = 1.0 - end - end - - return alpha, gamma - end end export Helmholtz3D \ No newline at end of file diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index 51931678..9b9bec15 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -1,6 +1,6 @@ -abstract type Helmholtz3DOp{T,K} <: MaxwellOperator3D end -abstract type Helmholtz3DOpReg{T,K} <: MaxwellOperator3DReg end +abstract type Helmholtz3DOp{T,K} <: MaxwellOperator3D{T,K} end +abstract type Helmholtz3DOpReg{T,K} <: MaxwellOperator3DReg{T,K} end """ ``` ∫_Γ dx ∫_Γ dy \\left(α G g(x) n_x ⋅ n_y f(y) + β G \\mbox{curl} g(x) ⋅ \\mbox{curl} f(y) \\right) @@ -18,13 +18,8 @@ end HH3DHyperSingularFDBIO(gamma) = HH3DHyperSingularFDBIO(gamma^2, one(gamma), gamma) -scalartype(op::Helmholtz3DOp{T,K}) where {T, K <: Nothing} = T -scalartype(op::Helmholtz3DOp{T,K}) where {T, K} = promote_type(T, K) - alpha(op::Union{Helmholtz3DOp{T,K},Helmholtz3DOpReg{T,K}}) where {T, K} = op.alpha beta(op::HH3DHyperSingularFDBIO{T,K}) where {T, K} = op.beta -gamma(op::Union{Helmholtz3DOp{T,K}, Helmholtz3DOpReg{T,K}}) where {T, K <: Nothing} = T(0) -gamma(op::Union{Helmholtz3DOp{T,K}, Helmholtz3DOpReg{T,K}}) where {T, K} = op.gamma """ ```math diff --git a/src/helmholtz3d/nitsche.jl b/src/helmholtz3d/nitsche.jl index 78fa8d5d..dc10fdef 100644 --- a/src/helmholtz3d/nitsche.jl +++ b/src/helmholtz3d/nitsche.jl @@ -1,6 +1,6 @@ -mutable struct NitscheHH3{T} <: MaxwellOperator3D +mutable struct NitscheHH3{T} <: MaxwellOperator3D{T,T} gamma::T end diff --git a/src/maxwell/maxwell.jl b/src/maxwell/maxwell.jl index 9ab88702..6e114c74 100644 --- a/src/maxwell/maxwell.jl +++ b/src/maxwell/maxwell.jl @@ -21,26 +21,13 @@ module Maxwell3D alpha=nothing, beta=nothing) - if (gamma == nothing) && (wavenumber == nothing) - error("Supply one of (not both) gamma or wavenumber") - end - - if (gamma != nothing) && (wavenumber != nothing) - error("Supply one of (not both) gamma or wavenumber") - end - - if gamma == nothing - if iszero(real(wavenumber)) - gamma = -imag(wavenumber) - else - gamma = im*wavenumber - end - end + + gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) - @assert gamma != nothing + @assert gamma !== nothing - alpha == nothing && (alpha = -gamma) - beta == nothing && (beta = -1/gamma) + alpha === nothing && (alpha = -gamma) + beta === nothing && (beta = -1/gamma) Mod.MWSingleLayer3D(gamma, alpha, beta) end @@ -55,33 +42,26 @@ module Maxwell3D Bilinear form given by: ```math - ∬_{Γ^2} k(x) ⋅ (∇G_γ(x-y) × j(y)) + α ∬_{Γ^2} k(x) ⋅ (∇G_γ(x-y) × j(y)) ``` with ``G_γ = e^{-γ|x-y|} / 4π|x-y|`` """ function doublelayer(; + alpha=nothing, gamma=nothing, wavenumber=nothing) - if (gamma == nothing) && (wavenumber == nothing) - error("Supply one of (not both) gamma or wavenumber") - end - - if (gamma != nothing) && (wavenumber != nothing) - error("Supply one of (not both) gamma or wavenumber") - end + gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) - if gamma == nothing - if iszero(real(wavenumber)) - gamma = -imag(wavenumber) + if alpha === nothing + if gamma !== nothing + alpha = one(gamma) else - gamma = im*wavenumber + alpha = 1.0 # Default to double precision end end - @assert gamma != nothing - Mod.MWDoubleLayer3D(gamma) end diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 0cb87dec..7a0b2799 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -1,5 +1,11 @@ -abstract type MaxwellOperator3D <: IntegralOperator end -abstract type MaxwellOperator3DReg <: MaxwellOperator3D end +abstract type MaxwellOperator3D{T,K} <: IntegralOperator end +abstract type MaxwellOperator3DReg{T,K} <: MaxwellOperator3D{T,K} end + +scalartype(op::MaxwellOperator3D{T,K}) where {T, K <: Nothing} = T +scalartype(op::MaxwellOperator3D{T,K}) where {T, K} = promote_type(T, K) + +gamma(op::MaxwellOperator3D{T,K}) where {T, K <: Nothing} = T(0) +gamma(op::MaxwellOperator3D{T,K}) where {T, K} = op.gamma struct KernelValsMaxwell3D{T,U,P,Q} "gamma = im * wavenumber" @@ -13,7 +19,7 @@ end const inv_4pi = 1/(4pi) function kernelvals(biop::MaxwellOperator3D, p, q) - γ = biop.gamma + γ = gamma(biop) r = cartesian(p) - cartesian(q) T = eltype(r) R = norm(r) @@ -29,7 +35,7 @@ function kernelvals(biop::MaxwellOperator3D, p, q) end -struct MWSingleLayer3D{T,U} <: MaxwellOperator3D +struct MWSingleLayer3D{T,U} <: MaxwellOperator3D{T,U} gamma::T α::U β::U @@ -43,20 +49,18 @@ MWHyperSingular(gamma) = MWSingleLayer3D(gamma, 0, 1) export Maxwell3D -struct MWSingleLayer3DReg{T,U} <: MaxwellOperator3DReg +struct MWSingleLayer3DReg{T,U} <: MaxwellOperator3DReg{T,U} gamma::T α::U β::U end -struct MWSingleLayer3DSng{T,U} <: MaxwellOperator3D +struct MWSingleLayer3DSng{T,U} <: MaxwellOperator3D{T,U} gamma::T α::U β::U end -scalartype(op::MaxwellOperator3D) = typeof(op.gamma) - regularpart(op::MWSingleLayer3D) = MWSingleLayer3DReg(op.gamma, op.α, op.β) singularpart(op::MWSingleLayer3D) = MWSingleLayer3DSng(op.gamma, op.α, op.β) @@ -89,20 +93,25 @@ function quaddata(op::MaxwellOperator3D, return (tpoints=tqd, bpoints=bqd, gausslegendre=leg) end -struct MWDoubleLayer3D{T} <: MaxwellOperator3D - gamma::T +struct MWDoubleLayer3D{T,K} <: MaxwellOperator3D{T,K} + alpha::T + gamma::K end -struct MWDoubleLayer3DSng{T} <: MaxwellOperator3D - gamma::T +struct MWDoubleLayer3DSng{T,K} <: MaxwellOperator3D{T,K} + alpha::T + gamma::K end -struct MWDoubleLayer3DReg{T} <: MaxwellOperator3DReg - gamma::T +struct MWDoubleLayer3DReg{T,K} <: MaxwellOperator3DReg{T,K} + alpha::T + gamma::K end -regularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DReg(op.gamma) -singularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DSng(op.gamma) +MWDoubleLayer3D(gamma) = MWDoubleLayer3D(1.0, gamma) # For legacy purposes + +regularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DReg(op.alpha, op.gamma) +singularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DSng(op.alpha, op.gamma) function quadrule(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd, qs::DoubleNumWiltonSauterQStrat) @@ -125,7 +134,7 @@ function quadrule(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j h2 = volume(σ) xtol2 = 0.2 * 0.2 - k2 = abs2(op.gamma) + k2 = abs2(gamma(op)) max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( qd.tpoints[2,i], DoubleQuadRule( @@ -156,7 +165,7 @@ function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::BDMRefSpace, i, τ, h2 = volume(σ) xtol2 = 0.2 * 0.2 - k2 = abs2(op.gamma) + k2 = abs2(gamma(op)) return DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j],) @@ -168,7 +177,7 @@ function qrib(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) xtol = 0.2 - k = norm(op.gamma) + k = norm(gamma(op)) hits = 0 xmin = xtol @@ -209,7 +218,7 @@ function qrdf(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, # decides on whether to use singularity extraction xtol = 0.2 - k = norm(op.gamma) + k = norm(gamma(op)) hits = 0 xmin = xtol @@ -318,4 +327,40 @@ function (igd::Integrand{<:MWDoubleLayer3DReg})(x,y,f,g) return _krondot(fvalue, G) end +################################################################################ +# +# Handling of operator parameters (Helmholtz and Maxwell) +# +################################################################################ + +function gamma_wavenumber_handler(gamma, wavenumber) + if (gamma !== nothing) && (wavenumber !== nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma === nothing && (wavenumber !== nothing) + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + return gamma, wavenumber +end + +function operator_parameter_handler(alpha, gamma, wavenumber) + + gamma, wavenumber = gamma_wavenumber_handler(gamma, wavenumber) + + if alpha === nothing + if gamma !== nothing + alpha = one(real(typeof(gamma))) + else + # We are dealing with a static problem. Default to double precision. + alpha = 1.0 + end + end + return alpha, gamma +end \ No newline at end of file diff --git a/src/maxwell/nitsche.jl b/src/maxwell/nitsche.jl index d99135c9..e159c2a8 100644 --- a/src/maxwell/nitsche.jl +++ b/src/maxwell/nitsche.jl @@ -8,7 +8,7 @@ Describe a single layer operator from the surface to a line. = ∫_γ dx v(x) ∫_Γ dy \frac{e^{-ikR}}{4πR} u(y) ``` """ -mutable struct SingleLayerTrace{T} <: MaxwellOperator3D +mutable struct SingleLayerTrace{T} <: MaxwellOperator3D{T,T} gamma::T end diff --git a/src/maxwell/nxdbllayer.jl b/src/maxwell/nxdbllayer.jl index 8772e354..5343d986 100644 --- a/src/maxwell/nxdbllayer.jl +++ b/src/maxwell/nxdbllayer.jl @@ -1,11 +1,12 @@ -mutable struct DoubleLayerRotatedMW3D{T} <: MaxwellOperator3D +mutable struct DoubleLayerRotatedMW3D{T,K} <: MaxwellOperator3D{T,K} "im times the wavenumber" - gamma::T + alpha::T + gamma::K end -LinearAlgebra.cross(::NormalVector, a::MWDoubleLayer3D) = DoubleLayerRotatedMW3D(a.gamma) +LinearAlgebra.cross(::NormalVector, a::MWDoubleLayer3D) = DoubleLayerRotatedMW3D(a.alpha, a.gamma) # defaultquadstrat(::DoubleLayerRotatedMW3D, tfs, bfs) = DoubleNumQStrat(2,3) @@ -55,7 +56,7 @@ function quadrule(op::DoubleLayerRotatedMW3D, g::RTRefSpace, f::RTRefSpace, i, h2 = volume(σ) xtol2 = 0.2 * 0.2 - k2 = abs2(op.gamma) + k2 = abs2(gamma(op)) return DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j],) @@ -107,7 +108,7 @@ function (igd::Integrand{<:DoubleLayerRotatedMW3D})(x,y,f,g) r = cartesian(x) - cartesian(y) R = norm(r) iR = 1/R - γ = igd.operator.gamma + γ = gamma(igd.operator) G = exp(-γ*R)/(4π*R) K = -(γ + iR) * G * (iR * r) diff --git a/src/operator.jl b/src/operator.jl index 3f371f40..71c815e2 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -321,19 +321,3 @@ function assemble!(op::BlockFullOperators, U::DirectProductSpace, V::DirectProdu end end end - -function gamma_wavenumber_handler(gamma, wavenumber) - if (gamma !== nothing) && (wavenumber !== nothing) - error("Supply one of (not both) gamma or wavenumber") - end - - if gamma === nothing && (wavenumber !== nothing) - if iszero(real(wavenumber)) - gamma = -imag(wavenumber) - else - gamma = im*wavenumber - end - end - - return gamma, wavenumber -end From 76832668d981db4faf21636794c1868b7b378bef Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Thu, 20 Jul 2023 09:38:22 +0200 Subject: [PATCH 275/528] Fixup: remove unnecessary getter functions --- src/helmholtz3d/hh3d_sauterschwabqr.jl | 10 +++++----- src/helmholtz3d/hh3dops.jl | 23 ++++++++++------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/helmholtz3d/hh3d_sauterschwabqr.jl b/src/helmholtz3d/hh3d_sauterschwabqr.jl index 665e1b24..ad5e910b 100644 --- a/src/helmholtz3d/hh3d_sauterschwabqr.jl +++ b/src/helmholtz3d/hh3d_sauterschwabqr.jl @@ -13,7 +13,7 @@ function pulled_back_integrand(op::HH3DSingleLayerFDBIO, j = jacobian(x) * jacobian(y) - α = alpha(op) + α = op.alpha γ = gamma(op) R = norm(cartesian(x)-cartesian(y)) G = exp(-γ*R)/(4*π*R) @@ -43,8 +43,8 @@ function pulled_back_integrand(op::HH3DHyperSingularFDBIO, j = jacobian(x) * jacobian(y) - α = alpha(op) - β = beta(op) + α = op.alpha + β = op.beta γ = gamma(op) R = norm(cartesian(x)-cartesian(y)) G = exp(-γ*R)/(4*π*R) @@ -84,7 +84,7 @@ function pulled_back_integrand(op::HH3DDoubleLayerFDBIO, j = jacobian(x) * jacobian(y) - α = alpha(op) + α = op.alpha γ = gamma(op) r = cartesian(x) - cartesian(y) @@ -117,7 +117,7 @@ function pulled_back_integrand(op::HH3DDoubleLayerTransposedFDBIO, j = jacobian(x) * jacobian(y) - α = alpha(op) + α = op.alpha γ = gamma(op) r = cartesian(x) - cartesian(y) diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index 9b9bec15..6571a7ca 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -18,9 +18,6 @@ end HH3DHyperSingularFDBIO(gamma) = HH3DHyperSingularFDBIO(gamma^2, one(gamma), gamma) -alpha(op::Union{Helmholtz3DOp{T,K},Helmholtz3DOpReg{T,K}}) where {T, K} = op.alpha -beta(op::HH3DHyperSingularFDBIO{T,K}) where {T, K} = op.beta - """ ```math a(u,v) = α ∬_{Γ×Γ} u(x) G_{γ}(|x-y|) v(y) @@ -376,8 +373,8 @@ end function (igd::Integrand{<:HH3DHyperSingularFDBIO})(x,y,f,g) - α = alpha(igd.operator) - β = beta(igd.operator) + α = igd.operator.alpha + β = igd.operator.beta γ = gamma(igd.operator) r = cartesian(x) - cartesian(y) @@ -396,8 +393,8 @@ end function integrand(op::HH3DHyperSingularFDBIO, kernel, test_values, test_element, trial_values, trial_element) - α = alpha(op) - β = beta(op) + α = op.alpha + β = op.beta G = kernel.green @@ -416,11 +413,11 @@ end HH3DSingleLayerFDBIO(gamma) = HH3DSingleLayerFDBIO(one(gamma), gamma) -regularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerReg(alpha(op), gamma(op)) -singularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerSng(alpha(op), gamma(op)) +regularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerReg(op.alpha, gamma(op)) +singularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerSng(op.alpha, gamma(op)) function (igd::Integrand{<:HH3DSingleLayerFDBIO})(x,y,f,g) - α = alpha(igd.operator) + α = igd.operator.alpha γ = gamma(igd.operator) r = cartesian(x) - cartesian(y) @@ -436,7 +433,7 @@ function (igd::Integrand{<:HH3DSingleLayerFDBIO})(x,y,f,g) end function (igd::Integrand{<:HH3DSingleLayerReg})(x,y,f,g) - α = alpha(igd.operator) + α = igd.operator.alpha γ = gamma(igd.operator) r = cartesian(x) - cartesian(y) @@ -454,7 +451,7 @@ end function integrand(op::Union{HH3DSingleLayerFDBIO,HH3DSingleLayerReg}, kernel, test_values, test_element, trial_values, trial_element) - α = alpha(op) + α = op.alpha G = kernel.green g = test_values.value @@ -470,7 +467,7 @@ function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) γ = gamma(op) - α = alpha(op) + α = op.alpha s1, s2, s3 = trial_element.vertices From fb2486a9fa82b07ea6e7cb1a44d2262b11fabdb2 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Thu, 20 Jul 2023 10:26:09 +0200 Subject: [PATCH 276/528] Use gamma approach also for Maxwell excitations --- src/maxwell/mwexc.jl | 94 ++++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/src/maxwell/mwexc.jl b/src/maxwell/mwexc.jl index 5563a308..b25f1489 100644 --- a/src/maxwell/mwexc.jl +++ b/src/maxwell/mwexc.jl @@ -1,79 +1,87 @@ mutable struct PlaneWaveMW{T,P} direction::P polarisation::P - wavenumber::T + gamma::T amplitude::T end -function PlaneWaveMW(d,p,k,a = 1) - T = promote_type(eltype(d), eltype(p), typeof(k), typeof(a)) +function PlaneWaveMW(d,p,γ,a = 1) + T = promote_type(eltype(d), eltype(p), typeof(γ), typeof(a)) P = similar_type(typeof(d), T) - PlaneWaveMW{T,P}(d,p,k,a) + PlaneWaveMW{T,P}(d,p,γ,a) end -scalartype(x::PlaneWaveMW{T,P}) where {T,P} = promote_type(complex(T), eltype(P)) +scalartype(x::PlaneWaveMW{T,P}) where {T,P} = promote_type(T, eltype(P)) """ - planewavemw3d(;direction, polarization, wavenumber[, amplitude=1]) + planewavemw3d(;direction, polarization, wavenumber, gamma[, amplitude=1]) Create a plane wave solution to Maxwell's equations. """ -planewavemw3d(; +function planewavemw3d(; direction = error("missing arguement `direction`"), polarization = error("missing arguement `polarization`"), - wavenumber = error("missing arguement `wavenumber`"), + wavenumber = nothing, + gamma = nothing, amplitude = 1, - ) = PlaneWaveMW(direction, polarization, wavenumber, amplitude) + ) + + gamma, wavenumber = gamma_wavenumber_handler(gamma, wavenumber) + gamma === nothing && (gamma = zero(eltype(direction))) + + return PlaneWaveMW(direction, polarization, wavenumber, amplitude) + +end function (e::PlaneWaveMW)(x) - k = e.wavenumber + γ = e.gamma d = e.direction u = e.polarisation a = e.amplitude - a * exp(-im * k * dot(d, x)) * u + a * exp(-γ * dot(d, x)) * u end function curl(field::PlaneWaveMW) - k = field.wavenumber + γ = field.gamma d = field.direction u = field.polarisation a = field.amplitude v = d × u - b = -a * im * k - PlaneWaveMW(d, v, k, b) + b = -a * γ + PlaneWaveMW(d, v, γ, b) end -*(a::Number, e::PlaneWaveMW) = PlaneWaveMW(e.direction, e.polarisation, e.wavenumber, a*e.amplitude) +*(a::Number, e::PlaneWaveMW) = PlaneWaveMW(e.direction, e.polarisation, e.gamma, a*e.amplitude) abstract type Dipole end mutable struct DipoleMW{T,P} <: Dipole location::P orientation::P - wavenumber::T + gamma::T end -function DipoleMW(l,o,k) - T = promote_type(eltype(l), eltype(o), typeof(k)) +function DipoleMW(l,o,γ) + T = promote_type(eltype(l), eltype(o), typeof(γ)) P = similar_type(typeof(l), T) - DipoleMW{T,P}(l,o,k) + DipoleMW{T,P}(l,o,γ) end -scalartype(x::DipoleMW{T,P}) where {T,P} = promote_type(complex(T), eltype(P)) +scalartype(x::DipoleMW{T,P}) where {T,P} = promote_type(T, eltype(P)) mutable struct curlDipoleMW{T,P} <: Dipole location::P orientation::P - wavenumber::T + gamma::T end -function curlDipoleMW(l,o,k) - T = promote_type(eltype(l), eltype(o), typeof(k)) +function curlDipoleMW(l,o,γ) + T = promote_type(eltype(l), eltype(o), typeof(γ)) P = similar_type(typeof(l), T) - curlDipoleMW{T,P}(l,o,k) + curlDipoleMW{T,P}(l,o,γ) end -scalartype(x::curlDipoleMW{T,P}) where {T,P} = promote_type(complex(T), eltype(P)) +scalartype(x::curlDipoleMW{T,P}) where {T,P} = promote_type(T, eltype(P)) """ dipolemw3d(;location, orientation, wavenumber) @@ -82,53 +90,61 @@ Create an electric dipole solution to Maxwell's equations representing the elect field part. Implementation is based on (9.18) of Jackson's “Classical electrodynamics”, with the notable difference that the ``\exp(ikr)`` is used. """ -dipolemw3d(; +function dipolemw3d(; location = error("missing arguement `location`"), orientation = error("missing arguement `orientation`"), - wavenumber = error("missing arguement `wavenumber`"), - ) = DipoleMW(location, orientation, wavenumber) + wavenumber = nothing, + gamma = nothing + ) + + gamma, wavenumber = gamma_wavenumber_handler(gamma, wavenumber) + gamma === nothing && (gamma = zero(eltype(orientation))) + + return DipoleMW(location, orientation, gamma) + +end function (d::DipoleMW)(x; isfarfield=false) - k = d.wavenumber + γ = d.gamma x_0 = d.location p = d.orientation if isfarfield - # postfactor (4*π*im)/k to be consistent with BEAST far field computation + # postfactor (4*π*im)/k = (-4*π)/γ to be consistent with BEAST far field computation # and, of course, adapted phase factor exp(im*k*dot(n,x_0)) with # respect to (9.19) of Jackson's Classical Electrodynamics r = norm(x) n = x/r - return cross(cross(n,1/(4*π)*(k^2*cross(cross(n,p),n))*exp(im*k*dot(n,x_0))*(4*π*im)/k),n) + return cross(cross(n,1/(4*π)*(-γ^2*cross(cross(n,p),n))*exp(γ*dot(n,x_0))*(-4*π)/γ),n) else r = norm(x-x_0) n = (x - x_0)/r - return 1/(4*π)*exp(-im*k*r)*(k^2/r*cross(cross(n,p),n) + - (1/r^3 + im*k/r^2)*(3*n*dot(n,p) - p)) + return 1/(4*π)*exp(-γ*r)*(-γ^2/r*cross(cross(n,p),n) + + (1/r^3 + γ/r^2)*(3*n*dot(n,p) - p)) end end function (d::curlDipoleMW)(x; isfarfield=false) - k = d.wavenumber + γ = d.gamma x_0 = d.location p = d.orientation if isfarfield # postfactor (4*π*im)/k to be consistent with BEAST far field computation r = norm(x) n = x/r - return (-im*k)*k^2/(4*π)*cross(n,p)*(4*π*im)/k*exp(im*k*dot(n,x_0)) + return (-γ)*(-γ^2)/(4*π)*cross(n,p)*(-4*π)/γ*exp(γ*dot(n,x_0)) else r = norm(x-x_0) n = (x - x_0)/r - return -im*(k^3)/(4*π)*cross(n,p)*exp(-im*k*r)/r*(1 + 1/(im*k*r)) + return γ^3/(4*π)*cross(n,p)*exp(-γ*r)/r*(1 + 1/(γ*r)) end end function curl(d::DipoleMW) - return curlDipoleMW(d.location, d.orientation, d.wavenumber) + return curlDipoleMW(d.location, d.orientation, d.gamma) end -*(a::Number, d::DipoleMW) = DipoleMW(d.location, a .* d.orientation, d.wavenumber) -*(a::Number, d::curlDipoleMW) = curlDipoleMW(d.location, a .* d.orientation, d.wavenumber) +*(a::Number, d::DipoleMW) = DipoleMW(d.location, a .* d.orientation, d.gamma) +*(a::Number, d::curlDipoleMW) = curlDipoleMW(d.location, a .* d.orientation, d.gamma) mutable struct CrossTraceMW{F} <: Functional field::F From 216a3e42ad3b70e0d3d5da4ba64f5a67cd00172a Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Fri, 21 Jul 2023 15:38:19 +0200 Subject: [PATCH 277/528] Bugfixes regarding missing dependency and alpha prefactor - Alpha factor was missing for double - By shifting some rhs constructors for HH3D into the Helmholtz3D submodule LinearAlgebra package needs to be reloaded --- src/helmholtz3d/helmholtz3d.jl | 1 + src/helmholtz3d/hh3dops.jl | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/helmholtz3d/helmholtz3d.jl b/src/helmholtz3d/helmholtz3d.jl index 3d76332e..831a34c9 100644 --- a/src/helmholtz3d/helmholtz3d.jl +++ b/src/helmholtz3d/helmholtz3d.jl @@ -2,6 +2,7 @@ module Helmholtz3D using ..BEAST const Mod = BEAST + using LinearAlgebra function singlelayer(; alpha=nothing, diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index 6571a7ca..a02cfcd9 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -489,17 +489,19 @@ HH3DDoubleLayerFDBIO(gamma) = HH3DDoubleLayerFDBIO(one(gamma), gamma) function (igd::Integrand{<:HH3DDoubleLayerFDBIO})(x,y,f,g) γ = gamma(igd.operator) + α = igd.operator.alpha r = cartesian(x) - cartesian(y) R = norm(r) iR = 1/R green = exp(-γ*R)*(iR*i4pi) gradgreen = -(γ + iR) * green * (iR * r) + αgradgreen = α * gradgreen n = normal(y) fvalue = getvalue(f) gvalue = getvalue(g) - return _krondot(fvalue,gvalue) * dot(n, -gradgreen) + return _krondot(fvalue,gvalue) * dot(n, -αgradgreen) end @@ -516,17 +518,19 @@ end function (igd::Integrand{<:HH3DDoubleLayerTransposedFDBIO})(x,y,f,g) γ = gamma(igd.operator) + α = igd.operator.alpha r = cartesian(x) - cartesian(y) R = norm(r) iR = 1/R green = exp(-γ*R)*(iR*i4pi) gradgreen = -(γ + iR) * green * (iR * r) + αgradgreen = α * gradgreen n = normal(x) fvalue = getvalue(f) gvalue = getvalue(g) - return _krondot(fvalue,gvalue) * dot(n, gradgreen) + return _krondot(fvalue,gvalue) * dot(n, αgradgreen) end function integrand(biop::HH3DDoubleLayerTransposedFDBIO, From 989f28519c604d155945c54550a8ca8a77e5926d Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Sat, 12 Aug 2023 16:02:51 +0200 Subject: [PATCH 278/528] Use dynamic vectors for building linear combination of sys matrices When linear maps are added via '+', a tuple is created. For large DirectProductSpaces, this results in prohibitively long compile times with little benefit since the cost for a matrix-vector product will dominate. Instead, LinearCombinations can be created with vectors and long compile times can be avoided. --- src/solvers/solver.jl | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index c7d8e578..0154434e 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -333,7 +333,10 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; U = BlockArrays.blockedrange(M) V = BlockArrays.blockedrange(N) - Z = ZeroMap{Float32}(U,V) + lincombv = LinearMap[] + + T = Int32 + for term in bf.terms x = X.factors[term.test_id] for op in reverse(term.test_ops) @@ -349,10 +352,15 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; I = Block(term.test_id) J = Block(term.trial_id) - Z = Z + term.coeff * LiftedMap(z, I, J, U, V) + + Smap = term.coeff * LiftedMap(z, I, J, U, V) + + T = promote_type(T, eltype(Smap)) + + push!(lincombv, Smap) end - return Z + return LinearMaps.LinearCombination{T}(lincombv) end From d2159d004b14ef6a1168deecf3271b920e7fb971 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Fri, 18 Aug 2023 11:57:44 +0200 Subject: [PATCH 279/528] Do not export alpha, gamma, beta --- src/BEAST.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/BEAST.jl b/src/BEAST.jl index 8495e908..227362c0 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -61,8 +61,6 @@ export DirichletTrace export HH3DMonopole, gradHH3DMonopole export HH3DLinearPotential -export alpha, beta, gamma - export HH3DSingleLayerNear export HH3DDoubleLayerNear export HH3DDoubleLayerTransposedNear From 243179e783337e705b31416e1f9da156dd1ce4a3 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 22 Aug 2023 15:07:05 +0000 Subject: [PATCH 280/528] scalar finite elements support SauterSchwab quad --- src/bases/local/laglocal.jl | 37 ++++++++++++++++++++++++++++++ src/helmholtz3d/hh3dops.jl | 17 ++++++++++++++ src/maxwell/mwops.jl | 5 ++++ src/quadrature/quadrule.jl | 25 ++++++++++++++++++++ src/quadrature/sauterschwabints.jl | 8 ++++++- 5 files changed, 91 insertions(+), 1 deletion(-) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index bc0f6c60..29bad742 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -218,4 +218,41 @@ function curl(ref::LagrangeRefSpace{T,2,3} where {T}, sh, el) sh4 = Shape(sh.cellid, mod1(2*sh.refid+7,6), z*sh.coeff) end return [sh1, sh2, sh3, sh4] +end + + +const _vert_perms_lag = [ + (1,2,3), + (2,3,1), + (3,1,2), + (2,1,3), + (1,3,2), + (3,2,1), +] + +const _dof_perms_lag0 = [ + (1), + (1), + (1), + (1), + (1), + (1), +] +const _dof_perms_lag1 = [ + (1,2,3), + (3,1,2), + (2,3,1), + (2,1,3), + (1,3,2), + (3,2,1), +] + +function dof_permutation(::LagrangeRefSpace{<:Any,0}, vert_permutation) + i = findfirst(==(tuple(vert_permutation...)), _vert_perms_lag) + return _dof_perms_lag0[i] +end + +function dof_permutation(::LagrangeRefSpace{<:Any,1}, vert_permutation) + i = findfirst(==(tuple(vert_permutation...)), _vert_perms_lag) + return _dof_perms_lag1[i] end \ No newline at end of file diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index 19e99e8f..606506e2 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -16,6 +16,10 @@ struct HH3DHyperSingularFDBIO{T,K} <: Helmholtz3DOp gamma::K end +function sign_upon_permutation(op::HH3DHyperSingularFDBIO, I, J) + return Combinatorics.levicivita(I) * Combinatorics.levicivita(J) +end + HH3DHyperSingularFDBIO(gamma) = HH3DHyperSingularFDBIO(gamma^2, one(gamma), gamma) scalartype(op::HH3DHyperSingularFDBIO) = promote_type(typeof(op.alpha), typeof(op.beta), typeof(op.gamma)) @@ -40,16 +44,29 @@ struct HH3DSingleLayerSng{T,K} <: Helmholtz3DOp gamma::K end +function sign_upon_permutation(op::HH3DSingleLayerFDBIO, I, J) + return 1 +end + struct HH3DDoubleLayerFDBIO{T,K} <: Helmholtz3DOp alpha::T gamma::K end +function sign_upon_permutation(op::HH3DDoubleLayerFDBIO, I, J) + return Combinatorics.levicivita(J) +end + struct HH3DDoubleLayerTransposedFDBIO{T,K} <: Helmholtz3DOp alpha::T gamma::K end +function sign_upon_permutation(op::HH3DDoubleLayerTransposedFDBIO, I, J) + return Combinatorics.levicivita(I) +end + + defaultquadstrat(::Helmholtz3DOp, ::LagrangeRefSpace, ::LagrangeRefSpace) = DoubleNumWiltonSauterQStrat(2,3,2,3,4,4,4,4) function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 0cb87dec..ce59be91 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -35,6 +35,9 @@ struct MWSingleLayer3D{T,U} <: MaxwellOperator3D β::U end +scalartype(op::MWSingleLayer3D{T,U}) where {T,U} = promote_type(T,U) +sign_upon_permutation(op::MWSingleLayer3D, I, J) = 1 + MWSingleLayer3D(gamma) = MWSingleLayer3D(gamma, -gamma, -1/(gamma)) MWWeaklySingular(gamma) = MWSingleLayer3D(gamma, 1, 0) MWHyperSingular(gamma) = MWSingleLayer3D(gamma, 0, 1) @@ -93,6 +96,8 @@ struct MWDoubleLayer3D{T} <: MaxwellOperator3D gamma::T end +sign_upon_permutation(op::MWDoubleLayer3D, I, J) = 1 + struct MWDoubleLayer3DSng{T} <: MaxwellOperator3D gamma::T end diff --git a/src/quadrature/quadrule.jl b/src/quadrature/quadrule.jl index b4eda1b7..8307a966 100644 --- a/src/quadrature/quadrule.jl +++ b/src/quadrature/quadrule.jl @@ -20,6 +20,31 @@ function quadrule(op::IntegralOperator, g::RTRefSpace, f::RTRefSpace, i, τ, j, hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j],) +end + + +function quadrule(op::IntegralOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd, + qs::DoubleNumSauterQstrat) + + T = eltype(eltype(τ.vertices)) + hits = 0 + dtol = 1.0e3 * eps(T) + dmin2 = floatmax(T) + for t in τ.vertices + for s in σ.vertices + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < dtol) + end + end + + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) + hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) + hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + return DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j],) diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index 8d68152f..459a36ca 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -82,6 +82,11 @@ function (igd::Integrand)(x,y,f,g) end + +# function sign_upon_permutation(op, I, J) +# return 1 +# end + function momintegrals!(op::Operator, test_local_space::RefSpace, trial_local_space::RefSpace, test_chart, trial_chart, out, rule::SauterSchwabStrategy) @@ -102,12 +107,13 @@ function momintegrals!(op::Operator, igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, rule) + σ = sign_upon_permutation(op, I, J) K = dof_permutation(test_local_space, I) L = dof_permutation(trial_local_space, J) for i in 1:numfunctions(test_local_space) for j in 1:numfunctions(trial_local_space) - out[i,j] = G[K[i],L[j]] + out[i,j] = σ * G[K[i],L[j]] end end nothing From 37fef1705af97341ac447a04407c28cd1fced78c Mon Sep 17 00:00:00 2001 From: Paula Respondek Date: Tue, 30 May 2023 12:59:52 +0200 Subject: [PATCH 281/528] Add innerintegrals! function for HH3D Operators This allows a singularity extraction strategy for the doublelayer, doublelayertransposed and hypersingular operator for triangles close together (for specific pairings of patch and pyramid functions). --- src/BEAST.jl | 3 +- src/helmholtz3d/hh3dops.jl | 56 ++++- src/helmholtz3d/wiltonints.jl | 182 ++++++++++++++++ test/runtests.jl | 1 + test/test_hh3dints.jl | 379 ++++++++++++++++++++++++++++++++++ 5 files changed, 614 insertions(+), 7 deletions(-) create mode 100644 src/helmholtz3d/wiltonints.jl create mode 100644 test/test_hh3dints.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 227362c0..31d3886e 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -215,7 +215,7 @@ include("maxwell/spotential.jl") include("maxwell/maxwell.jl") include("maxwell/sourcefield.jl") -# Support for the Helmholtz equatio +# Support for the Helmholtz equation include("helmholtz2d/helmholtzop.jl") include("helmholtz3d/hh3dexc.jl") @@ -225,6 +225,7 @@ include("helmholtz3d/hh3dnear.jl") include("helmholtz3d/hh3dfar.jl") include("helmholtz3d/hh3d_sauterschwabqr.jl") include("helmholtz3d/helmholtz3d.jl") +include("helmholtz3d/wiltonints.jl") #suport for Volume Integral equation include("volumeintegral/vie.jl") diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index fec3b0b8..603efacb 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -20,6 +20,23 @@ function sign_upon_permutation(op::HH3DHyperSingularFDBIO, I, J) return Combinatorics.levicivita(I) * Combinatorics.levicivita(J) end +struct HH3DHyperSingularReg{T,K} <: Helmholtz3DOpReg + "coefficient of the weakly singular term" + alpha::T + "coefficient of the hyper singular term" + beta::T + "`im*κ` with `κ` the wave number" + gamma::K +end + +struct HH3DHyperSingularSng{T,K} <: Helmholtz3DOp + "coefficient of the weakly singular term" + alpha::T + "coefficient of the hyper singular term" + beta::T + "`im*κ` with `κ` the wave number" + gamma::K +end HH3DHyperSingularFDBIO(gamma) = HH3DHyperSingularFDBIO(gamma^2, one(gamma), gamma) """ @@ -65,6 +82,17 @@ function sign_upon_permutation(op::HH3DDoubleLayerFDBIO, I, J) end struct HH3DDoubleLayerTransposedFDBIO{T,K} <: Helmholtz3DOp{T,K} +struct HH3DDoubleLayerReg{T,K} <: Helmholtz3DOpReg + alpha::T + gamma::K +end + +struct HH3DDoubleLayerSng{T,K} <: Helmholtz3DOp + alpha::T + gamma::K +end + +struct HH3DDoubleLayerTransposedFDBIO{T,K} <: Helmholtz3DOp alpha::T gamma::K end @@ -73,6 +101,15 @@ function sign_upon_permutation(op::HH3DDoubleLayerTransposedFDBIO, I, J) return Combinatorics.levicivita(I) end +struct HH3DDoubleLayerTransposedReg{T,K} <: Helmholtz3DOpReg + alpha::T + gamma::K +end + +struct HH3DDoubleLayerTransposedSng{T,K} <: Helmholtz3DOp + alpha::T + gamma::K +end defaultquadstrat(::Helmholtz3DOp, ::LagrangeRefSpace, ::LagrangeRefSpace) = DoubleNumWiltonSauterQStrat(2,3,2,3,4,4,4,4) @@ -189,6 +226,8 @@ function quadrule(op::HH3DSingleLayerFDBIO, qd.bsis_qp[1,j]) end +regularpart(op::HH3DHyperSingularFDBIO) = HH3DHyperSingularReg(op.alpha, op.beta, op.gamma) +singularpart(op::HH3DHyperSingularFDBIO) = HH3DHyperSingularSng(op.alpha, op.beta, op.gamma) function quadrule(op::HH3DHyperSingularFDBIO, test_refspace::LagrangeRefSpace{T,1} where T, @@ -474,15 +513,15 @@ end function integrand(op::Union{HH3DSingleLayerFDBIO,HH3DSingleLayerReg}, - kernel, test_values, test_element, trial_values, trial_element) + kernel, test_values, test_element, trial_values, trial_element) - α = op.alpha - G = kernel.green +α = op.alpha +G = kernel.green - g = test_values.value - f = trial_values.value +g = test_values.value +f = trial_values.value - α*dot(g, G*f) +α*dot(g, G*f) end @@ -511,6 +550,8 @@ end HH3DDoubleLayerFDBIO(gamma) = HH3DDoubleLayerFDBIO(one(gamma), gamma) +regularpart(op::HH3DDoubleLayerFDBIO) = HH3DDoubleLayerReg(op.alpha, op.gamma) +singularpart(op::HH3DDoubleLayerFDBIO) = HH3DDoubleLayerSng(op.alpha, op.gamma) function (igd::Integrand{<:HH3DDoubleLayerFDBIO})(x,y,f,g) γ = gamma(igd.operator) @@ -541,6 +582,9 @@ end HH3DDoubleLayerTransposedFDBIO(gamma) = HH3DDoubleLayerTransposedFDBIO(one(gamma), gamma) +regularpart(op::HH3DDoubleLayerTransposedFDBIO) = HH3DDoubleLayerTransposedReg(op.alpha, op.gamma) +singularpart(op::HH3DDoubleLayerTransposedFDBIO) = HH3DDoubleLayerTransposedSng(op.alpha, op.gamma) + function (igd::Integrand{<:HH3DDoubleLayerTransposedFDBIO})(x,y,f,g) γ = gamma(igd.operator) α = igd.operator.alpha diff --git a/src/helmholtz3d/wiltonints.jl b/src/helmholtz3d/wiltonints.jl new file mode 100644 index 00000000..44949462 --- /dev/null +++ b/src/helmholtz3d/wiltonints.jl @@ -0,0 +1,182 @@ +# single layer +function (igd::Integrand{<:HH3DSingleLayerReg})(x,y,f,g) + α = igd.operator.alpha + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1 / R + green = (expm1(-γ*R) #= + γ*R =# - 0.5*γ^2*R^2) / (4pi*R) + αG = α * green + + _integrands(f,g) do fi, gi + dot(gi.value, αG*fi.value) + end +end + +function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, + test_refspace::LagrangeRefSpace{T,0} where {T}, + trial_refspace::LagrangeRefSpace{T,0} where {T}, + test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) + +γ = op.gamma +α = op.alpha + +s1, s2, s3 = trial_element.vertices + +x = cartesian(test_neighborhood) +n = normalize((s1-s3)×(s2-s3)) +ρ = x - dot(x - s1, n) * n + +scal, vec = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) +∫G = (scal[2]#= - γ*scal[3] =# + 0.5*γ^2*scal[4]) / (4π) + +zlocal[1,1] += α * ∫G * dx +return nothing +end + + + +# double layer transposed +function (igd::Integrand{<:HH3DDoubleLayerTransposedReg})(x,y,f,g) + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1/R + γR = γ*R + expo = exp(-γR) + + gradgreen = ( -(γR + 1)*expo + (1 - 0.5*γR^2) ) * (i4pi*iR^3) * r + + n = normal(x) + fvalue = getvalue(f) + gvalue = getvalue(g) + + return _krondot(fvalue,gvalue) * dot(n, gradgreen) +end + +function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, + test_refspace::LagrangeRefSpace{T,1,3,3} where {T}, + trial_refspace::LagrangeRefSpace{T,0,3,1} where {T}, + test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) + γ = op.gamma + α = op.alpha + + s1, s2, s3 = trial_element.vertices + t1, t2, t3 = test_elements.vertices + x = cartesian(test_neighborhood) + n = normalize((t1-t3)×(t2-t3)) + ρ = x - dot(x - s1, n) * n + + scal, vec, grad = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) + + ∫∇G = -(grad[1]+0.5*γ^2*grad[3])/(4π) + ∫n∇G = dot(n,∫∇G) + Atot = 1/2*norm(cross(t3-t1,t3-t2)) + for i in 1:numfunctions(test_refspace) + Ai = 1/2*norm(cross(test_elements.vertices[mod1(i-1,3)]-x,test_elements.vertices[mod1(i+1,3)]-x)) + g = Ai/Atot + for j in 1:numfunctions(trial_refspace) + zlocal[i,j] += α * ∫n∇G * g * dx + end + end + + return nothing +end + +# double layer +function (igd::Integrand{<:HH3DDoubleLayerReg})(x,y,f,g) + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1/R + γR = γ*R + expo = exp(-γR) + + gradgreen = ( -(γR + 1)*expo + (1 - 0.5*γR^2) ) * (i4pi*iR^3) * r + + n = normal(y) + fvalue = getvalue(f) + gvalue = getvalue(g) + + return _krondot(fvalue,gvalue) * dot(n, -gradgreen) + +end + +function innerintegrals!(op::HH3DDoubleLayerSng, p, + g::LagrangeRefSpace{T,0} where {T}, + f::LagrangeRefSpace{T,1} where {T}, + t, s, z, quadrature_rule::WiltonSERule, dx) + γ = op.gamma + α = op.alpha + + s1, s2, s3 = s.vertices + + x = cartesian(p) + n = normalize((s1-s3)×(s2-s3)) + ρ = x - dot(x - s1, n) * n + + _, _, _, grad = WiltonInts84.higherorder(s1,s2,s3,x,3) + + ∫∇G = (-grad[1] - 0.5*γ^2*grad[2]) / (4π) + + +for i in 1:numfunctions(g) + for j in 1:numfunctions(f) + z[i,j] += α * dot(n,∫∇G[j]) * dx + end +end + + return nothing +end + + +function (igd::Integrand{<:HH3DHyperSingularReg})(x,y,f,g) + α = igd.operator.alpha + β = igd.operator.beta + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1 / R + green = (expm1(-γ*R) - 0.5*γ^2*R^2) / (4pi*R) + nx = normal(x) + ny = normal(y) + + _integrands(f,g) do fi, gi + α*dot(nx,ny)*gi.value*fi.value*green + β*dot(gi.curl,fi.curl)*green + end +end + +function innerintegrals!(op::HH3DHyperSingularSng, p, + g::LagrangeRefSpace{T,1} where {T}, + f::LagrangeRefSpace{T,1} where {T}, + t, s, z, quadrature_rule::WiltonSERule, dx) + α = op.alpha + β = op.beta + γ = op.gamma + s1, s2, s3 = s.vertices + t1, t2, t3 = t.vertices + x = cartesian(p) + nx = normalize((s1-s3)×(s2-s3)) + ny = normalize((t1-t3)×(t2-t3)) + + ∫Rⁿ, ∫RⁿN = WiltonInts84.higherorder(s1,s2,s3,x,3) + greenconst = (∫Rⁿ[2] + 0.5*γ^2*∫Rⁿ[3]) / (4π) + greenlinear = (∫RⁿN[2] + 0.5*γ^2*∫RⁿN[3] ) / (4π) + + jt = volume(t) * factorial(dimension(t)) + js = volume(s) * factorial(dimension(s)) + curlt = [(t3-t2)/jt,(t1-t3)/jt,(t2-t1)/jt] + curls = [(s3-s2)/js,(s1-s3)/js,(s2-s1)/js] + Atot = 1/2*norm(cross(t3-t1,t3-t2)) + for i in 1:numfunctions(g) + Ai = 1/2*norm(cross(t.vertices[mod1(i-1,3)]-x,t.vertices[mod1(i+1,3)]-x)) + g = Ai/Atot + for j in 1:numfunctions(f) + z[i,j] += β * dot(curlt[i],curls[j])*greenconst*dx + α*dot(nx,ny) * greenlinear[j]*g*dx + end + end +end diff --git a/test/runtests.jl b/test/runtests.jl index f635e5b9..cf2ca17a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -50,6 +50,7 @@ include("test_dipole.jl") include("test_wiltonints.jl") include("test_sauterschwabints.jl") +include("test_hh3dints.jl") include("test_ss_nested_meshes.jl") include("test_nitsche.jl") include("test_nitschehh3d.jl") diff --git a/test/test_hh3dints.jl b/test/test_hh3dints.jl new file mode 100644 index 00000000..f5985f0f --- /dev/null +++ b/test/test_hh3dints.jl @@ -0,0 +1,379 @@ +using Test +using LinearAlgebra + +using BEAST, CompScienceMeshes, SauterSchwabQuadrature, StaticArrays + +T=Float64 + + +# operators +op1 = Helmholtz3D.singlelayer(gamma=T(2.0)) +op2 = Helmholtz3D.doublelayer(gamma=T(2.0)) +op3 = Helmholtz3D.doublelayer_transposed(gamma=T(2.0)) +op4 = Helmholtz3D.hypersingular(gamma=T(2.0)) +## Common face case: +@testset "Common Face" begin + # triangles +t1 = simplex( + T.(@SVector [0.180878, -0.941848, -0.283207]), + T.(@SVector [0.0, -0.980785, -0.19509]), + T.(@SVector [0.0, -0.92388, -0.382683])) + + lag0 = BEAST.LagrangeRefSpace{T,0,3,1}() # patch basis + lag1 = BEAST.LagrangeRefSpace{T,1,3,3}() #pyramid basis + + tqd0 = BEAST.quadpoints(lag0, [t1], (12,)) #test quadrature data + tqd1 = BEAST.quadpoints(lag1, [t1], (12,)) + + bqd0 = BEAST.quadpoints(lag0, [t1], (13,)) #basis quadrature data + bqd1 = BEAST.quadpoints(lag1, [t1], (13,)) + + SE_strategy = BEAST.WiltonSERule( #wilton + tqd0[1,1], + BEAST.DoubleQuadRule( + tqd0[1,1], + bqd0[1,1], + ), + ) + + SS_strategy = SauterSchwabQuadrature.CommonFace(BEAST._legendre(8,T(0.0),T(1.0))) #sauter + + Double_strategy = BEAST.DoubleQuadRule( #doublequadstrat + tqd0[1,1], + bqd0[1,1] + ) + + z_se = [0.0im] + z_ss = [0.0im] + z_double = [0.0im] + + BEAST.momintegrals!(op1, lag0, lag0, t1, t1, z_se, SE_strategy) + BEAST.momintegrals!(op1, lag0, lag0, t1, t1, z_ss, SS_strategy) + BEAST.momintegrals!(op1, lag0, lag0, t1, t1, z_double, Double_strategy) + + @test z_se ≈ z_ss rtol=1e-5 + @test z_double ≈ z_ss rtol = 1e-1 + @test norm(z_se-z_ss) < norm(z_double-z_ss) +end +# common vertex case +@testset "Common vertex" begin + t1 = simplex( + T.(@SVector [0.180878, -0.941848, -0.283207]), + T.(@SVector [0.0, -0.980785, -0.19509]), + T.(@SVector [0.0, -0.92388, -0.382683])) + t2 = simplex( + T.(@SVector [0.373086, -0.881524, -0.289348]), + T.(@SVector [0.180878, -0.941848, -0.283207]), + T.(@SVector [0.294908, -0.944921, -0.141962])) + lag0 = BEAST.LagrangeRefSpace{T,0,3,1}() # patch basis + lag1 = BEAST.LagrangeRefSpace{T,1,3,3}() #pyramid basis + + tqd0 = BEAST.quadpoints(lag0, [t1,t2], (12,)) #test quadrature data + tqd1 = BEAST.quadpoints(lag1, [t1,t2], (12,)) + + bqd0 = BEAST.quadpoints(lag0, [t1,t2], (13,)) #basis quadrature data + bqd1 = BEAST.quadpoints(lag1, [t1,t2], (13,)) + #single layer + SE_strategy = BEAST.WiltonSERule( + tqd0[1,1], + BEAST.DoubleQuadRule( + tqd0[1,1], + bqd0[1,2])) + + SS_strategy = SauterSchwabQuadrature.CommonVertex(BEAST._legendre(8,T(0.0),T(1.0))) + + Double_strategy = BEAST.DoubleQuadRule( #doublequadstrat + tqd0[1,1], + bqd0[1,2] + ) + z_cv_se = zeros(3,1) + z_cv_ss = zeros(3,1) + z_cv_double = zeros(3,1) + + BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_cv_double, Double_strategy) + + @test z_cv_double ≈ z_cv_ss rtol = 1e-4 + @test z_cv_se ≈ z_cv_ss rtol=1e-4 + #@test norm(z_cv_ss-z_cv_se)/norm(z_cv_ss) < norm(z_cv_ss-z_cv_double)/norm(z_cv_ss) + #double layer + SE_strategy = BEAST.WiltonSERule( + tqd0[1,1], + BEAST.DoubleQuadRule( + tqd0[1,1], + bqd1[1,2])) + + SS_strategy = SauterSchwabQuadrature.CommonVertex(BEAST._legendre(8,T(0.0),T(1.0))) + + Double_strategy = BEAST.DoubleQuadRule( #doublequadstrat + tqd0[1,1], + bqd1[1,2] + ) + z_cv_se = zeros(1,3) + z_cv_ss = zeros(1,3) + z_cv_double = zeros(1,3) + + BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_cv_double, Double_strategy) + + @test z_cv_double ≈ z_cv_ss rtol = 1e-4 + @test z_cv_se ≈ z_cv_ss rtol=1e-4 + @test norm(z_cv_ss-z_cv_se)/norm(z_cv_ss) < norm(z_cv_ss-z_cv_double)/norm(z_cv_ss) + + #double layer transposed + SE_strategy = BEAST.WiltonSERule( + tqd1[1,1], + BEAST.DoubleQuadRule( + tqd1[1,1], + bqd0[1,2])) + + SS_strategy = SauterSchwabQuadrature.CommonVertex(BEAST._legendre(8,T(0.0),T(1.0))) + + Double_strategy = BEAST.DoubleQuadRule( #doublequadstrat + tqd1[1,1], + bqd0[1,2] + ) + z_cv_se = zeros(3,1) + z_cv_ss = zeros(3,1) + z_cv_double = zeros(3,1) + + BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_cv_double, Double_strategy) + + @test z_cv_double ≈ z_cv_ss rtol = 1e-4 + @test z_cv_se ≈ z_cv_ss rtol=1e-4 + @test norm(z_cv_ss-z_cv_se)/norm(z_cv_ss) < norm(z_cv_ss-z_cv_double)/norm(z_cv_ss) + + #hypersingular + SE_strategy = BEAST.WiltonSERule( + tqd1[1,1], + BEAST.DoubleQuadRule( + tqd1[1,1], + bqd1[1,2])) + + SS_strategy = SauterSchwabQuadrature.CommonVertex(BEAST._legendre(8,T(0.0),T(1.0))) + + Double_strategy = BEAST.DoubleQuadRule( #doublequadstrat + tqd1[1,1], + bqd1[1,2] + ) + z_cv_se = zeros(3,3) + z_cv_ss = zeros(3,3) + z_cv_double = zeros(3,3) + + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_cv_double, Double_strategy) + + @test z_cv_double ≈ z_cv_ss rtol = 1e-4 + @test z_cv_se ≈ z_cv_ss rtol=1e-4 + #@test norm(z_cv_ss-z_cv_se)/norm(z_cv_ss) < norm(z_cv_ss-z_cv_double)/norm(z_cv_ss) + +end + +@testset "Common edge" begin + # common edge + t1 = simplex( + T.(@SVector [0.180878, -0.941848, -0.283207]), + T.(@SVector [0.0, -0.980785, -0.19509]), + T.(@SVector [0.0, -0.92388, -0.382683]) + ) + t2 = simplex( + T.(@SVector [0.180878, -0.941848, -0.283207]), + T.(@SVector [0.158174, -0.881178, -0.44554]), + T.(@SVector [0.0, -0.92388, -0.382683]) + ) + + lag0 = BEAST.LagrangeRefSpace{T,0,3,1}() # patch basis + lag1 = BEAST.LagrangeRefSpace{T,1,3,3}() #pyramid basis + + tqd0 = BEAST.quadpoints(lag0, [t1,t2], (12,)) #test quadrature data + tqd1 = BEAST.quadpoints(lag1, [t1,t2], (12,)) + + bqd0 = BEAST.quadpoints(lag0, [t1,t2], (13,)) #basis quadrature data + bqd1 = BEAST.quadpoints(lag1, [t1,t2], (13,)) + # singlelayer + SE_strategy = BEAST.WiltonSERule( + tqd0[1,1], + BEAST.DoubleQuadRule( + tqd0[1,1], + bqd0[1,2])) + + SS_strategy = SauterSchwabQuadrature.CommonEdge(BEAST._legendre(8,T(0.0),T(1.0))) + + Double_strategy = BEAST.DoubleQuadRule( #doublequadstrat + tqd0[1,1], + bqd0[1,2] + ) + z_ce_se = zeros(3,1) + z_ce_ss = zeros(3,1) + z_ce_double = zeros(3,1) + + BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_se ≈ z_ce_ss rtol=1e-4 + @test z_ce_double ≈ z_ce_ss rtol = 1e-3 + @test norm(z_ce_ss-z_ce_se)/norm(z_ce_ss) < norm(z_ce_ss-z_ce_double)/norm(z_ce_ss) + + # doublelayer + SE_strategy = BEAST.WiltonSERule( + tqd0[1,1], + BEAST.DoubleQuadRule( + tqd0[1,1], + bqd1[1,2])) + SS_strategy = SauterSchwabQuadrature.CommonEdge(BEAST._legendre(8,T(0.0),T(1.0))) + Double_strategy = BEAST.DoubleQuadRule( + tqd0[1,1], + bqd1[1,2]) + z_ce_se = zeros(1,3) + z_ce_ss = zeros(1,3) + z_ce_double = zeros(1,3) + BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_se ≈ z_ce_ss rtol = 1e-4 + @test z_ce_ss ≈ z_ce_double rtol = 1e-1 + @test norm(z_ce_ss-z_ce_se)/norm(z_ce_ss) < norm(z_ce_ss-z_ce_double)/norm(z_ce_ss) + + # doublelayer transposed + SE_strategy = BEAST.WiltonSERule( + tqd1[1,1], + BEAST.DoubleQuadRule( + tqd1[1,1], + bqd0[1,2])) + SS_strategy = SauterSchwabQuadrature.CommonEdge(BEAST._legendre(12,T(0.0),T(1.0))) + Double_strategy = BEAST.DoubleQuadRule( + tqd1[1,1], + bqd0[1,2]) + z_ce_se = zeros(3,1) + z_ce_ss = zeros(3,1) + z_ce_double = zeros(3,1) + + BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_se ≈ z_ce_ss rtol=1e-2 + @test z_ce_ss ≈ z_ce_double rtol = 1e-1 + @test norm(z_ce_ss-z_ce_se)/norm(z_ce_ss) < norm(z_ce_ss-z_ce_double)/norm(z_ce_ss) + + # hypersingular + + SE_strategy = BEAST.WiltonSERule( + tqd1[1,1], + BEAST.DoubleQuadRule( + tqd1[1,1], + bqd1[1,2])) + SS_strategy = SauterSchwabQuadrature.CommonEdge(BEAST._legendre(12,T(0.0),T(1.0))) + Double_strategy = BEAST.DoubleQuadRule( + tqd1[1,1], + bqd1[1,2]) + + z_ce_se = zeros(3,3) + z_ce_ss = zeros(3,3) + z_ce_double = zeros(3,3) + + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_ce_double, Double_strategy) + @test z_ce_se ≈ z_ce_ss rtol = 1e-5 + @test z_ce_double ≈ z_ce_double rtol = 1e-5 + @test norm(z_ce_se-z_ce_ss) < norm(z_ce_double-z_ce_ss) + +end + +@testset "Seperated triangles" begin + t1 = simplex( + T.(@SVector [0.180878, -0.941848, -0.283207]), + T.(@SVector [0.0, -0.980785, -0.19509]), + T.(@SVector [0.0, -0.92388, -0.382683])) + t2 = simplex( + T.(@SVector [0.373086, -0.881524, -1.289348]), + T.(@SVector [0.180878, -0.941848, -1.283207]), + T.(@SVector [0.294908, -0.944921, -1.141962])) + + lag0 = BEAST.LagrangeRefSpace{T,0,3,1}() # patch basis + lag1 = BEAST.LagrangeRefSpace{T,1,3,3}() #pyramid basis + + tqd0 = BEAST.quadpoints(lag0, [t1,t2], (12,)) #test quadrature data + tqd1 = BEAST.quadpoints(lag1, [t1,t2], (12,)) + + bqd0 = BEAST.quadpoints(lag0, [t1,t2], (13,)) #basis quadrature data + bqd1 = BEAST.quadpoints(lag1, [t1,t2], (13,)) + # singlelayer + SE_strategy = BEAST.WiltonSERule( + tqd0[1,1], + BEAST.DoubleQuadRule( + tqd0[1,1], + bqd0[1,2])) + + Double_strategy = BEAST.DoubleQuadRule( #doublequadstrat + tqd0[1,1], + bqd0[1,2] + ) + z_ce_se = zeros(3,1) + z_ce_double = zeros(3,1) + + BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_se ≈ z_ce_double rtol=1e-7 + + # doublelayer + SE_strategy = BEAST.WiltonSERule( + tqd0[1,1], + BEAST.DoubleQuadRule( + tqd0[1,1], + bqd1[1,2])) + + Double_strategy = BEAST.DoubleQuadRule( + tqd0[1,1], + bqd1[1,2]) + z_ce_se = zeros(1,3) + z_ce_double = zeros(1,3) + BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_se ≈ z_ce_double rtol = 1e-8 + + # doublelayer transposed + SE_strategy = BEAST.WiltonSERule( + tqd1[1,1], + BEAST.DoubleQuadRule( + tqd1[1,1], + bqd0[1,2])) + Double_strategy = BEAST.DoubleQuadRule( + tqd1[1,1], + bqd0[1,2]) + z_ce_se = zeros(3,1) + z_ce_double = zeros(3,1) + + BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_se ≈ z_ce_double rtol=1e-7 + + # hypersingular + SE_strategy = BEAST.WiltonSERule( + tqd1[1,1], + BEAST.DoubleQuadRule( + tqd1[1,1], + bqd1[1,2])) + Double_strategy = BEAST.DoubleQuadRule( + tqd1[1,1], + bqd1[1,2]) + + z_ce_se = zeros(3,3) + z_ce_double = zeros(3,3) + + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_ce_double, Double_strategy) + @test z_ce_se ≈ z_ce_double rtol = 1e-7 + +end From aee84fa897f0f9955c68c2dce0674147c1f159e1 Mon Sep 17 00:00:00 2001 From: Paula Respondek Date: Mon, 19 Jun 2023 13:50:19 +0200 Subject: [PATCH 282/528] Adapt SauterSchwab and Wilton ints for HH3D Added functions so that all combinations of patch and pyramid functions can be used with Sauter integrals for the common face/edge/certex case and a singularity extraction rule for the near singular case. Not clear why DoubleQuadStrat performs better for common vertex than singularity extraction. For triangles close together singularity extraction appears to perform better as is expected. --- src/helmholtz3d/hh3d_sauterschwabqr.jl | 84 ++-- src/helmholtz3d/hh3dops.jl | 45 +- src/helmholtz3d/wiltonints.jl | 260 +++++++++- test/test_hh3dints.jl | 630 ++++++++++++++++++++----- 4 files changed, 833 insertions(+), 186 deletions(-) diff --git a/src/helmholtz3d/hh3d_sauterschwabqr.jl b/src/helmholtz3d/hh3d_sauterschwabqr.jl index ad5e910b..1b59a052 100644 --- a/src/helmholtz3d/hh3d_sauterschwabqr.jl +++ b/src/helmholtz3d/hh3d_sauterschwabqr.jl @@ -1,6 +1,6 @@ function pulled_back_integrand(op::HH3DSingleLayerFDBIO, - test_local_space::LagrangeRefSpace{<:Any,0}, - trial_local_space::LagrangeRefSpace{<:Any,0}, + test_local_space::LagrangeRefSpace, + trial_local_space::LagrangeRefSpace, test_chart, trial_chart) (u,v) -> begin @@ -20,14 +20,14 @@ function pulled_back_integrand(op::HH3DSingleLayerFDBIO, αjG = α*G*j - SA[f[1].value * αjG * g[1].value] + SMatrix{length(f),length(g)}((f[j].value * αjG * g[i].value for i in 1:length(g) for j in 1:length(f) )...) end end function pulled_back_integrand(op::HH3DHyperSingularFDBIO, - test_local_space::LagrangeRefSpace{<:Any,1}, - trial_local_space::LagrangeRefSpace{<:Any,1}, + test_local_space::LagrangeRefSpace, + trial_local_space::LagrangeRefSpace, test_chart, trial_chart) (u,v) -> begin @@ -52,25 +52,16 @@ function pulled_back_integrand(op::HH3DHyperSingularFDBIO, αjG = ny*α*G*j βjG = β*G*j - A = SA[ αjG*g[1].value, αjG*g[2].value, αjG*g[3].value] - B = SA[ βjG*g[1].curl, βjG*g[2].curl, βjG*g[3].curl ] - - SMatrix{3,3}(( - dot(nx*f[1].value, A[1]) + dot(f[1].curl, B[1]), - dot(nx*f[2].value, A[1]) + dot(f[2].curl, B[1]), - dot(nx*f[3].value, A[1]) + dot(f[3].curl, B[1]), - dot(nx*f[1].value, A[2]) + dot(f[1].curl, B[2]), - dot(nx*f[2].value, A[2]) + dot(f[2].curl, B[2]), - dot(nx*f[3].value, A[2]) + dot(f[3].curl, B[2]), - dot(nx*f[1].value, A[3]) + dot(f[1].curl, B[3]), - dot(nx*f[2].value, A[3]) + dot(f[2].curl, B[3]), - dot(nx*f[3].value, A[3]) + dot(f[3].curl, B[3]))) + A = SA[(αjG*g[i].value for i in 1:length(g))...] + B = SA[(βjG*g[i].curl for i in 1:length(g))...] + + SMatrix{length(f),length(g)}((((dot(nx*f[j].value,A[i])+dot(f[j].curl,B[i])) for i in 1:length(g) for j in 1:length(f))...)) end end function pulled_back_integrand(op::HH3DDoubleLayerFDBIO, - test_local_space::LagrangeRefSpace{<:Any,0}, - trial_local_space::LagrangeRefSpace{<:Any,1}, + test_local_space::LagrangeRefSpace, + trial_local_space::LagrangeRefSpace, test_chart, trial_chart) (u,v) -> begin @@ -94,16 +85,13 @@ function pulled_back_integrand(op::HH3DDoubleLayerFDBIO, ∇G = -(γ + inv_R) * G * inv_R * r αnyj∇G = dot(ny,-α*∇G*j) - SMatrix{1,3}( - f[1].value * αnyj∇G * g[1].value, - f[1].value * αnyj∇G * g[2].value, - f[1].value * αnyj∇G * g[3].value) + SMatrix{length(f),length(g)}((f[j].value * αnyj∇G * g[i].value for i in 1:length(g) for j in 1:length(f))...) end end function pulled_back_integrand(op::HH3DDoubleLayerTransposedFDBIO, - test_local_space::LagrangeRefSpace{<:Any,1}, - trial_local_space::LagrangeRefSpace{<:Any,0}, + test_local_space::LagrangeRefSpace, + trial_local_space::LagrangeRefSpace, test_chart, trial_chart) (u,v) -> begin @@ -127,14 +115,11 @@ function pulled_back_integrand(op::HH3DDoubleLayerTransposedFDBIO, ∇G = -(γ + inv_R) * G * inv_R * r αnxj∇G = dot(nx,α*∇G*j) - SMatrix{3,1}( - f[1].value * αnxj∇G * g[1].value, - f[2].value * αnxj∇G * g[1].value, - f[3].value * αnxj∇G * g[1].value) + SMatrix{length(f),length(g)}((f[j].value * αnxj∇G * g[i].value for i in 1:length(g) for j in 1:length(f))...) end end -function momintegrals!(op::HH3DSingleLayerFDBIO, +function momintegrals!(op::Helmholtz3DOp, test_local_space::LagrangeRefSpace{<:Any,0}, trial_local_space::LagrangeRefSpace{<:Any,0}, test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) @@ -152,19 +137,21 @@ function momintegrals!(op::HH3DSingleLayerFDBIO, trial_triangular_element.vertices[J[2]], trial_triangular_element.vertices[J[3]]) - # igd = MWSL3DIntegrand(test_triangular_element, trial_triangular_element, - # op, test_local_space, trial_local_space) + test_sign = Combinatorics.levicivita(I) + trial_sign = Combinatorics.levicivita(J) + σ = momintegrals_sign(op, test_sign, trial_sign) + igd = pulled_back_integrand(op, test_local_space, trial_local_space, test_triangular_element, trial_triangular_element) G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) - out[1,1] += G[1,1] + out[1,1] += G[1,1] * σ nothing end -function momintegrals!(op::HH3DHyperSingularFDBIO, - test_local_space::LagrangeRefSpace{<:Any,1}, trial_local_space::LagrangeRefSpace{<:Any,1}, +function momintegrals!(op::Helmholtz3DOp, + test_local_space::LagrangeRefSpace, trial_local_space::LagrangeRefSpace, test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) I, J, K, L = SauterSchwabQuadrature.reorder( @@ -183,13 +170,14 @@ function momintegrals!(op::HH3DHyperSingularFDBIO, test_sign = Combinatorics.levicivita(I) trial_sign = Combinatorics.levicivita(J) - σ = test_sign * trial_sign + + σ = momintegrals_sign(op, test_sign, trial_sign) igd = pulled_back_integrand(op, test_local_space, trial_local_space, test_triangular_element, trial_triangular_element) G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) for j ∈ 1:3, i ∈ 1:3 - out[i,j] += G[K[i],L[j]] * σ + out[i,j] += G[K[i],L[j]] * σ end nothing @@ -213,9 +201,10 @@ function momintegrals!(op::Helmholtz3DOp, trial_triangular_element.vertices[J[2]], trial_triangular_element.vertices[J[3]]) + test_sign = Combinatorics.levicivita(I) trial_sign = Combinatorics.levicivita(J) - σ = trial_sign + σ = momintegrals_sign(op, test_sign, trial_sign) igd = pulled_back_integrand(op, test_local_space, trial_local_space, test_triangular_element, trial_triangular_element) @@ -247,7 +236,9 @@ function momintegrals!(op::Helmholtz3DOp, trial_triangular_element.vertices[J[3]]) test_sign = Combinatorics.levicivita(I) - σ = test_sign + trial_sign = Combinatorics.levicivita(J) + + σ = momintegrals_sign(op, test_sign, trial_sign) igd = pulled_back_integrand(op, test_local_space, trial_local_space, test_triangular_element, trial_triangular_element) @@ -259,3 +250,16 @@ function momintegrals!(op::Helmholtz3DOp, nothing end + +function momintegrals_sign(op::HH3DSingleLayerFDBIO, test_sign, trial_sign) + return 1 +end +function momintegrals_sign(op::HH3DDoubleLayerFDBIO, test_sign, trial_sign) + return trial_sign +end +function momintegrals_sign(op::HH3DDoubleLayerTransposedFDBIO, test_sign, trial_sign) + return test_sign +end +function momintegrals_sign(op::HH3DHyperSingularFDBIO, test_sign, trial_sign) + return test_sign * trial_sign +end diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index 603efacb..b4e95e89 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -177,9 +177,9 @@ end -function quadrule(op::HH3DSingleLayerFDBIO, - test_refspace::LagrangeRefSpace{T,0} where T, - trial_refspace::LagrangeRefSpace{T,0} where T, +function quadrule(op::Helmholtz3DOp, + test_refspace::LagrangeRefSpace, + trial_refspace::LagrangeRefSpace, i, test_element, j, trial_element, qd, qs::DoubleNumWiltonSauterQStrat) @@ -229,9 +229,9 @@ end regularpart(op::HH3DHyperSingularFDBIO) = HH3DHyperSingularReg(op.alpha, op.beta, op.gamma) singularpart(op::HH3DHyperSingularFDBIO) = HH3DHyperSingularSng(op.alpha, op.beta, op.gamma) -function quadrule(op::HH3DHyperSingularFDBIO, - test_refspace::LagrangeRefSpace{T,1} where T, - trial_refspace::LagrangeRefSpace{T,1} where T, +#= function quadrule(op::HH3DHyperSingularFDBIO, + test_refspace::LagrangeRefSpace, + trial_refspace::LagrangeRefSpace, i, test_element, j, trial_element, qd, qs::DoubleNumWiltonSauterQStrat) @@ -245,19 +245,21 @@ function quadrule(op::HH3DHyperSingularFDBIO, hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - # test_quadpoints = qd.test_qp - # trial_quadpoints = qd.bsis_qp - - # hits != 0 && return WiltonSERule( - # test_quadpoints[1,i], - # DoubleQuadRule( - # test_quadpoints[1,i], - # trial_quadpoints[1,j])) + test_quadpoints = qd.test_qp + trial_quadpoints = qd.bsis_qp + h2 = volume(trial_element) + xtol2 = 0.2 * 0.2 + k2 = abs2(op.gamma) + max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( + test_quadpoints[1,i], + DoubleQuadRule( + test_quadpoints[1,i], + trial_quadpoints[1,j])) return DoubleQuadRule( qd.test_qp[1,i], qd.bsis_qp[1,j]) -end +end =# function quadrule(op::HH3DHyperSingularFDBIO, @@ -334,7 +336,7 @@ function quadrule(op::Helmholtz3DOp, quadrature_data[2][1,j]) end -function quadrule(op::HH3DDoubleLayerTransposedFDBIO, +#= function quadrule(op::HH3DDoubleLayerTransposedFDBIO, test_refspace::LagrangeRefSpace{T,1} where T, trial_refspace::LagrangeRefSpace{T,0} where T, i, test_element, j, trial_element, quadrature_data, @@ -358,7 +360,7 @@ function quadrule(op::HH3DDoubleLayerTransposedFDBIO, trial_quadpoints = quadrature_data[2] test_quadpoints = quadrature_data.test_qp trial_quadpoints = quadrature_data.bsis_qp -#= h2 = volume(trial_element) + h2 = volume(trial_element) xtol2 = 0.2 * 0.2 k2 = abs2(gamma(op)) @@ -366,13 +368,14 @@ function quadrule(op::HH3DDoubleLayerTransposedFDBIO, test_quadpoints[1,i], DoubleQuadRule( test_quadpoints[1,i], - trial_quadpoints[1,j])) =# + trial_quadpoints[1,j])) + return DoubleQuadRule( quadrature_data[1][1,i], quadrature_data[2][1,j]) -end +end =# -function quadrule(op::HH3DDoubleLayerFDBIO, +#= function quadrule(op::HH3DDoubleLayerFDBIO, test_refspace::LagrangeRefSpace{T,0} where T, trial_refspace::LagrangeRefSpace{T,1} where T, i, test_element, j, trial_element, quadrature_data, @@ -408,7 +411,7 @@ function quadrule(op::HH3DDoubleLayerFDBIO, return DoubleQuadRule( quadrature_data[1][1,i], quadrature_data[2][1,j]) -end +end =# function quadrule(op::Helmholtz3DOp, diff --git a/src/helmholtz3d/wiltonints.jl b/src/helmholtz3d/wiltonints.jl index 44949462..c1cf7386 100644 --- a/src/helmholtz3d/wiltonints.jl +++ b/src/helmholtz3d/wiltonints.jl @@ -6,7 +6,7 @@ function (igd::Integrand{<:HH3DSingleLayerReg})(x,y,f,g) r = cartesian(x) - cartesian(y) R = norm(r) iR = 1 / R - green = (expm1(-γ*R) #= + γ*R =# - 0.5*γ^2*R^2) / (4pi*R) + green = (expm1(-γ*R) - 0.5*γ^2*R^2) / (4pi*R) αG = α * green _integrands(f,g) do fi, gi @@ -29,13 +29,95 @@ n = normalize((s1-s3)×(s2-s3)) ρ = x - dot(x - s1, n) * n scal, vec = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) -∫G = (scal[2]#= - γ*scal[3] =# + 0.5*γ^2*scal[4]) / (4π) +∫G = (scal[2] + 0.5*γ^2*scal[4]) / (4π) zlocal[1,1] += α * ∫G * dx return nothing end +# single layer with patch basis and pyramid testing +function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, + test_refspace::LagrangeRefSpace{T,1} where {T}, + trial_refspace::LagrangeRefSpace{T,0} where {T}, + test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) + +γ = op.gamma +α = op.alpha + +s1, s2, s3 = trial_element.vertices +t1, t2, t3 = test_elements.vertices + + +x = cartesian(test_neighborhood) +n = normalize((s1-s3)×(s2-s3)) +ρ = x - dot(x - s1, n) * n + +scal, vec = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) +∫G = (scal[2] + 0.5*γ^2*scal[4]) / (4π) +Atot = 1/2*norm(cross(t3-t1,t3-t2)) +for i in 1:numfunctions(test_refspace) + Ai = 1/2*norm(cross(test_elements.vertices[mod1(i-1,3)]-x,test_elements.vertices[mod1(i+1,3)]-x)) + g = Ai/Atot + for j in 1:numfunctions(trial_refspace) + zlocal[i,j] += α * ∫G * g * dx + end +end +return nothing +end + +# single layer with pyramid basis and patch testing +function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, + test_refspace::LagrangeRefSpace{T,0} where {T}, + trial_refspace::LagrangeRefSpace{T,1} where {T}, + test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) + +γ = op.gamma +α = op.alpha + +s1, s2, s3 = trial_element.vertices + +x = cartesian(test_neighborhood) +n = normalize((s1-s3)×(s2-s3)) +ρ = x - dot(x - s1, n) * n + +∫Rⁿ, ∫RⁿN = WiltonInts84.higherorder(s1,s2,s3,x,3) +∫G = (∫RⁿN[2] + 0.5*γ^2*∫RⁿN[3]) / (4π) + +for i in 1:numfunctions(trial_refspace) +zlocal[1,i] += α * ∫G[i] * dx +end +return nothing +end + +#single layer with pyramid basis and pyramid testing +function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, + test_refspace::LagrangeRefSpace{T,1} where {T}, + trial_refspace::LagrangeRefSpace{T,1} where {T}, + test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) + +γ = op.gamma +α = op.alpha + +s1, s2, s3 = trial_element.vertices +t1, t2, t3 = test_elements.vertices + +x = cartesian(test_neighborhood) +n = normalize((s1-s3)×(s2-s3)) +ρ = x - dot(x - s1, n) * n + +∫Rⁿ, ∫RⁿN = WiltonInts84.higherorder(s1,s2,s3,x,3) +∫G = (∫RⁿN[2] + 0.5*γ^2*∫RⁿN[3]) / (4π) +Atot = 1/2*norm(cross(t3-t1,t3-t2)) +for i in 1:numfunctions(test_refspace) + Ai = 1/2*norm(cross(test_elements.vertices[mod1(i-1,3)]-x,test_elements.vertices[mod1(i+1,3)]-x)) + g = Ai/Atot + for j in 1:numfunctions(trial_refspace) + zlocal[i,j] += α * ∫G[j] * g * dx + end +end +return nothing +end # double layer transposed function (igd::Integrand{<:HH3DDoubleLayerTransposedReg})(x,y,f,g) @@ -56,6 +138,33 @@ function (igd::Integrand{<:HH3DDoubleLayerTransposedReg})(x,y,f,g) return _krondot(fvalue,gvalue) * dot(n, gradgreen) end +#double layer transposed with patch basis and pyramid testing +function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, + test_refspace::LagrangeRefSpace{T,0,3,1} where {T}, + trial_refspace::LagrangeRefSpace{T,0,3,1} where {T}, + test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) + γ = op.gamma + α = op.alpha + + s1, s2, s3 = trial_element.vertices + t1, t2, t3 = test_elements.vertices + x = cartesian(test_neighborhood) + n = normalize((t1-t3)×(t2-t3)) + ρ = x - dot(x - s1, n) * n + + scal, vec, grad = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) + + ∫∇G = -(grad[1]+0.5*γ^2*grad[3])/(4π) + ∫n∇G = dot(n,∫∇G) + for i in 1:numfunctions(test_refspace) + for j in 1:numfunctions(trial_refspace) + zlocal[i,j] += α * ∫n∇G * dx + end + end + + return nothing +end +#double layer transposed with patch basis and pyramid testing function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, test_refspace::LagrangeRefSpace{T,1,3,3} where {T}, trial_refspace::LagrangeRefSpace{T,0,3,1} where {T}, @@ -84,7 +193,62 @@ function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, return nothing end +#double layer transposed with pyramid basis and patch testing +function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, + test_refspace::LagrangeRefSpace{T,0,3,1} where {T}, + trial_refspace::LagrangeRefSpace{T,1,3,3} where {T}, + test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) + γ = op.gamma + α = op.alpha + + s1, s2, s3 = trial_element.vertices + t1, t2, t3 = test_elements.vertices + x = cartesian(test_neighborhood) + n = normalize((t1-t3)×(t2-t3)) + ρ = x - dot(x - s1, n) * n + + _, _, _, grad = WiltonInts84.higherorder(s1,s2,s3,x,3) + + ∫∇G = -(grad[1] + 0.5*γ^2*grad[2]) / (4π) + for i in 1:numfunctions(test_refspace) + for j in 1:numfunctions(trial_refspace) + ∫n∇G = dot(n,∫∇G[j]) + zlocal[i,j] += α * ∫n∇G * dx + end + end + return nothing +end +#double layer transposed with pyramid basis and pyramid testing +function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, + test_refspace::LagrangeRefSpace{T,1,3,3} where {T}, + trial_refspace::LagrangeRefSpace{T,1,3,3} where {T}, + test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) + γ = op.gamma + α = op.alpha + + s1, s2, s3 = trial_element.vertices + t1, t2, t3 = test_elements.vertices + x = cartesian(test_neighborhood) + n = normalize((t1-t3)×(t2-t3)) + ρ = x - dot(x - s1, n) * n + + _, _, _, grad = WiltonInts84.higherorder(s1,s2,s3,x,3) + + ∫∇G = -(grad[1] + 0.5*γ^2*grad[2]) / (4π) + + Atot = 1/2*norm(cross(t3-t1,t3-t2)) + for i in 1:numfunctions(test_refspace) + Ai = 1/2*norm(cross(test_elements.vertices[mod1(i-1,3)]-x,test_elements.vertices[mod1(i+1,3)]-x)) + g = Ai/Atot + for j in 1:numfunctions(trial_refspace) + ∫n∇G = dot(n,∫∇G[j]) + zlocal[i,j] += α * ∫n∇G * g * dx + end + end + + return nothing +end # double layer function (igd::Integrand{<:HH3DDoubleLayerReg})(x,y,f,g) γ = igd.operator.gamma @@ -104,7 +268,93 @@ function (igd::Integrand{<:HH3DDoubleLayerReg})(x,y,f,g) return _krondot(fvalue,gvalue) * dot(n, -gradgreen) end +#double layer with pyramid basis and pyramid testing +function innerintegrals!(op::HH3DDoubleLayerSng, p, + g::LagrangeRefSpace{T,1} where {T}, + f::LagrangeRefSpace{T,1} where {T}, + t, s, z, quadrature_rule::WiltonSERule, dx) + γ = op.gamma + α = op.alpha + + s1, s2, s3 = s.vertices + t1, t2, t3 = t.vertices + + x = cartesian(p) + n = normalize((s1-s3)×(s2-s3)) + ρ = x - dot(x - s1, n) * n + + _, _, _, grad = WiltonInts84.higherorder(s1,s2,s3,x,3) + + ∫∇G = -(grad[1] + 0.5*γ^2*grad[2]) / (4π) + + Atot = 1/2*norm(cross(t3-t1,t3-t2)) + for i in 1:numfunctions(g) + Ai = 1/2*norm(cross(t.vertices[mod1(i-1,3)]-x,t.vertices[mod1(i+1,3)]-x)) + g = Ai/Atot + for j in 1:numfunctions(f) + z[i,j] += α * dot(n,-∫∇G[j]) * g * dx + end + end + + return nothing +end +#double layer with patch basis and pyramid testing +function innerintegrals!(op::HH3DDoubleLayerSng, p, + g::LagrangeRefSpace{T,0} where {T}, + f::LagrangeRefSpace{T,0} where {T}, + t, s, z, quadrature_rule::WiltonSERule, dx) + γ = op.gamma + α = op.alpha + + s1, s2, s3 = s.vertices + t1, t2, t3 = t.vertices + + x = cartesian(p) + n = normalize((s1-s3)×(s2-s3)) + ρ = x - dot(x - s1, n) * n + + scal, vec, grad = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) + + ∫∇G = -(grad[1]+0.5*γ^2*grad[3])/(4π) + + for i in 1:numfunctions(g) + for j in 1:numfunctions(f) + z[i,j] += α * dot(n,-∫∇G) * dx #why the minus? + end + end + return nothing +end +#double layer with patch basis and pyramid testing +function innerintegrals!(op::HH3DDoubleLayerSng, p, + g::LagrangeRefSpace{T,1} where {T}, + f::LagrangeRefSpace{T,0} where {T}, + t, s, z, quadrature_rule::WiltonSERule, dx) + γ = op.gamma + α = op.alpha + + s1, s2, s3 = s.vertices + t1, t2, t3 = t.vertices + + x = cartesian(p) + n = normalize((s1-s3)×(s2-s3)) + ρ = x - dot(x - s1, n) * n + + scal, vec, grad = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) + + ∫∇G = -(grad[1]+0.5*γ^2*grad[3])/(4π) + + Atot = 1/2*norm(cross(t3-t1,t3-t2)) +for i in 1:numfunctions(g) + Ai = 1/2*norm(cross(t.vertices[mod1(i-1,3)]-x,t.vertices[mod1(i+1,3)]-x)) + g = Ai/Atot + for j in 1:numfunctions(f) + z[i,j] += α * dot(n,-∫∇G) * g * dx + end +end + return nothing +end +#double layer with pyramid basis and patch testing function innerintegrals!(op::HH3DDoubleLayerSng, p, g::LagrangeRefSpace{T,0} where {T}, f::LagrangeRefSpace{T,1} where {T}, @@ -120,12 +370,12 @@ function innerintegrals!(op::HH3DDoubleLayerSng, p, _, _, _, grad = WiltonInts84.higherorder(s1,s2,s3,x,3) - ∫∇G = (-grad[1] - 0.5*γ^2*grad[2]) / (4π) + ∫∇G = -(grad[1] + 0.5*γ^2*grad[2]) / (4π) for i in 1:numfunctions(g) - for j in 1:numfunctions(f) - z[i,j] += α * dot(n,∫∇G[j]) * dx + for j in 1:numfunctions(f) + z[i,j] += α * dot(n,-∫∇G[j]) * dx end end diff --git a/test/test_hh3dints.jl b/test/test_hh3dints.jl index f5985f0f..a441a647 100644 --- a/test/test_hh3dints.jl +++ b/test/test_hh3dints.jl @@ -7,10 +7,10 @@ T=Float64 # operators -op1 = Helmholtz3D.singlelayer(gamma=T(2.0)) -op2 = Helmholtz3D.doublelayer(gamma=T(2.0)) -op3 = Helmholtz3D.doublelayer_transposed(gamma=T(2.0)) -op4 = Helmholtz3D.hypersingular(gamma=T(2.0)) +op1 = Helmholtz3D.singlelayer(gamma=T(1.0)im+T(1.0)) +op2 = Helmholtz3D.doublelayer(gamma=T(1.0)im+T(1.0)) +op3 = Helmholtz3D.doublelayer_transposed(gamma=T(1.0)im+T(1.0)) +op4 = Helmholtz3D.hypersingular(gamma=T(1.0)im+T(1.0)) ## Common face case: @testset "Common Face" begin # triangles @@ -22,11 +22,11 @@ t1 = simplex( lag0 = BEAST.LagrangeRefSpace{T,0,3,1}() # patch basis lag1 = BEAST.LagrangeRefSpace{T,1,3,3}() #pyramid basis - tqd0 = BEAST.quadpoints(lag0, [t1], (12,)) #test quadrature data - tqd1 = BEAST.quadpoints(lag1, [t1], (12,)) + tqd0 = BEAST.quadpoints(lag0, [t1], (13,)) #test quadrature data + tqd1 = BEAST.quadpoints(lag1, [t1], (13,)) - bqd0 = BEAST.quadpoints(lag0, [t1], (13,)) #basis quadrature data - bqd1 = BEAST.quadpoints(lag1, [t1], (13,)) + bqd0 = BEAST.quadpoints(lag0, [t1], (12,)) #basis quadrature data + bqd1 = BEAST.quadpoints(lag1, [t1], (12,)) SE_strategy = BEAST.WiltonSERule( #wilton tqd0[1,1], @@ -36,8 +36,8 @@ t1 = simplex( ), ) - SS_strategy = SauterSchwabQuadrature.CommonFace(BEAST._legendre(8,T(0.0),T(1.0))) #sauter - + SS_strategy = SauterSchwabQuadrature.CommonFace(BEAST._legendre(12,T(0.0),T(1.0))) #sauter +#single layer with patch-patch Double_strategy = BEAST.DoubleQuadRule( #doublequadstrat tqd0[1,1], bqd0[1,1] @@ -54,8 +54,84 @@ t1 = simplex( @test z_se ≈ z_ss rtol=1e-5 @test z_double ≈ z_ss rtol = 1e-1 @test norm(z_se-z_ss) < norm(z_double-z_ss) + + #single layer with patch-pyramid + SE_strategy = BEAST.WiltonSERule( #wilton + tqd1[1,1], + BEAST.DoubleQuadRule( + tqd1[1,1], + bqd0[1,1], + ),) + Double_strategy = BEAST.DoubleQuadRule( + tqd1[1,1], + bqd0[1,1], + ) + + z_se = zeros(complex(T),3,1) + z_ss = zeros(complex(T),3,1) + z_double = zeros(complex(T),3,1) + BEAST.momintegrals!(op1, lag1, lag0, t1, t1, z_ss, SS_strategy) + BEAST.momintegrals!(op1, lag1, lag0, t1, t1, z_se, SE_strategy) + BEAST.momintegrals!(op1, lag1, lag0, t1, t1, z_double, Double_strategy) + + @test z_ss ≈ z_se rtol = 1e-5 + @test z_ss ≈ z_double rtol = 1e-1 + @test norm(z_ss-z_se) < norm(z_ss-z_double) + +#single layer with pyramid-patch + SE_strategy = BEAST.WiltonSERule( #wilton + tqd0[1,1], + BEAST.DoubleQuadRule( + tqd0[1,1], + bqd1[1,1], + ),) + Double_strategy = BEAST.DoubleQuadRule( + tqd0[1,1], + bqd1[1,1], + ) + + z_se = zeros(complex(T),1,3) + z_ss = zeros(complex(T),1,3) + z_double = zeros(complex(T),1,3) + BEAST.momintegrals!(op1, lag0, lag1, t1, t1, z_ss, SS_strategy) + BEAST.momintegrals!(op1, lag0, lag1, t1, t1, z_se, SE_strategy) + BEAST.momintegrals!(op1, lag0, lag1, t1, t1, z_double, Double_strategy) + + @test z_ss ≈ z_se rtol = 1e-5 + @test z_ss ≈ z_double rtol = 1e-1 + @test norm(z_ss-z_se) < norm(z_ss-z_double) + + #single layer with pyramid pyramid + SE_strategy = BEAST.WiltonSERule( #wilton + tqd1[1,1], + BEAST.DoubleQuadRule( + tqd1[1,1], + bqd1[1,1], + ),) + Double_strategy = BEAST.DoubleQuadRule( + tqd1[1,1], + bqd1[1,1], + ) + + z_se = zeros(complex(T),3,3) + z_ss = zeros(complex(T),3,3) + z_double = zeros(complex(T),3,3) + BEAST.momintegrals!(op1, lag1, lag1, t1, t1, z_ss, SS_strategy) + BEAST.momintegrals!(op1, lag1, lag1, t1, t1, z_se, SE_strategy) + BEAST.momintegrals!(op1, lag1, lag1, t1, t1, z_double, Double_strategy) + + @test z_ss ≈ z_se rtol = 1e-5 + @test z_ss ≈ z_double rtol = 1e-1 + @test norm(z_ss-z_se) < norm(z_ss-z_double) + end # common vertex case + +# In the common vertex case, for the single layer and hypersingular, +# the double quadrature apparently is better than the wilton method. +# It is not clear to me why this is. If someone knows why this happens +# I would be grateful for an explanation. + @testset "Common vertex" begin t1 = simplex( T.(@SVector [0.180878, -0.941848, -0.283207]), @@ -73,7 +149,7 @@ end bqd0 = BEAST.quadpoints(lag0, [t1,t2], (13,)) #basis quadrature data bqd1 = BEAST.quadpoints(lag1, [t1,t2], (13,)) - #single layer +#patch patch SE_strategy = BEAST.WiltonSERule( tqd0[1,1], BEAST.DoubleQuadRule( @@ -86,9 +162,11 @@ end tqd0[1,1], bqd0[1,2] ) - z_cv_se = zeros(3,1) - z_cv_ss = zeros(3,1) - z_cv_double = zeros(3,1) + #single layer + + z_cv_se = zeros(complex(T),1,1) + z_cv_ss = zeros(complex(T),1,1) + z_cv_double = zeros(complex(T),1,1) BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_cv_se, SE_strategy) BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_cv_ss, SS_strategy) @@ -96,33 +174,96 @@ end @test z_cv_double ≈ z_cv_ss rtol = 1e-4 @test z_cv_se ≈ z_cv_ss rtol=1e-4 - #@test norm(z_cv_ss-z_cv_se)/norm(z_cv_ss) < norm(z_cv_ss-z_cv_double)/norm(z_cv_ss) - #double layer + @test norm(z_cv_ss-z_cv_se)/norm(z_cv_ss) < norm(z_cv_ss-z_cv_double)/norm(z_cv_ss) broken = true + #doublelayer + z_cv_se = zeros(complex(T),1,1) + z_cv_ss = zeros(complex(T),1,1) + z_cv_double = zeros(complex(T),1,1) + + BEAST.momintegrals!(op2, lag0, lag0, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op2, lag0, lag0, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op2, lag0, lag0, t1, t2, z_cv_double, Double_strategy) + + @test z_cv_double ≈ z_cv_ss rtol = 1e-4 + @test z_cv_se ≈ z_cv_double rtol=1e-4 + @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) + + # doublelayer_transposed + z_cv_se = zeros(complex(T),1,1) + z_cv_ss = zeros(complex(T),1,1) + z_cv_double = zeros(complex(T),1,1) + + BEAST.momintegrals!(op3, lag0, lag0, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op3, lag0, lag0, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op3, lag0, lag0, t1, t2, z_cv_double, Double_strategy) + + @test z_cv_double ≈ z_cv_ss rtol = 1e-4 + @test z_cv_se ≈ z_cv_ss rtol=1e-4 + @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) + +#pyramid pyramid + # singlelayer SE_strategy = BEAST.WiltonSERule( - tqd0[1,1], + tqd1[1,1], BEAST.DoubleQuadRule( - tqd0[1,1], + tqd1[1,1], bqd1[1,2])) - - SS_strategy = SauterSchwabQuadrature.CommonVertex(BEAST._legendre(8,T(0.0),T(1.0))) - Double_strategy = BEAST.DoubleQuadRule( #doublequadstrat - tqd0[1,1], + tqd1[1,1], bqd1[1,2] ) - z_cv_se = zeros(1,3) - z_cv_ss = zeros(1,3) - z_cv_double = zeros(1,3) + z_cv_se = zeros(complex(T),3,3) + z_cv_ss = zeros(complex(T),3,3) + z_cv_double = zeros(complex(T),3,3) - BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_cv_se, SE_strategy) - BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_cv_ss, SS_strategy) - BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_cv_double, Double_strategy) + BEAST.momintegrals!(op1, lag1, lag1, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op1, lag1, lag1, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op1, lag1, lag1, t1, t2, z_cv_double, Double_strategy) + + @test z_cv_double ≈ z_cv_ss rtol = 1e-4 + @test z_cv_se ≈ z_cv_double rtol=1e-4 + @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) broken = true + + #doublelayer + z_cv_se = zeros(complex(T),3,3) + z_cv_ss = zeros(complex(T),3,3) + z_cv_double = zeros(complex(T),3,3) + + BEAST.momintegrals!(op2, lag1, lag1, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op2, lag1, lag1, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op2, lag1, lag1, t1, t2, z_cv_double, Double_strategy) @test z_cv_double ≈ z_cv_ss rtol = 1e-4 @test z_cv_se ≈ z_cv_ss rtol=1e-4 - @test norm(z_cv_ss-z_cv_se)/norm(z_cv_ss) < norm(z_cv_ss-z_cv_double)/norm(z_cv_ss) + @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) - #double layer transposed + #doublelayer_transposed + z_cv_se = zeros(complex(T),3,3) + z_cv_ss = zeros(complex(T),3,3) + z_cv_double = zeros(complex(T),3,3) + + BEAST.momintegrals!(op3, lag1, lag1, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op3, lag1, lag1, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op3, lag1, lag1, t1, t2, z_cv_double, Double_strategy) + + @test z_cv_double ≈ z_cv_ss rtol = 1e-3 + @test z_cv_double ≈ z_cv_se rtol = 1e-5 + @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) + + #hypersingular + z_cv_se = zeros(complex(T),3,3) + z_cv_ss = zeros(complex(T),3,3) + z_cv_double = zeros(complex(T),3,3) + + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_cv_double, Double_strategy) + + @test z_cv_double ≈ z_cv_ss rtol = 1e-4 + @test z_cv_se ≈ z_cv_ss rtol=1e-4 + @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) broken = true + +#pyramid test patch basis SE_strategy = BEAST.WiltonSERule( tqd1[1,1], BEAST.DoubleQuadRule( @@ -135,9 +276,36 @@ end tqd1[1,1], bqd0[1,2] ) - z_cv_se = zeros(3,1) - z_cv_ss = zeros(3,1) - z_cv_double = zeros(3,1) + #singlelayer + z_cv_se = zeros(complex(T),3,1) + z_cv_ss = zeros(complex(T),3,1) + z_cv_double = zeros(complex(T),3,1) + + BEAST.momintegrals!(op1, lag1, lag0, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op1, lag1, lag0, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op1, lag1, lag0, t1, t2, z_cv_double, Double_strategy) + + @test z_cv_double ≈ z_cv_ss rtol = 1e-4 + @test z_cv_se ≈ z_cv_ss rtol=1e-4 + @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) broken = true + + #doublelayer + z_cv_se = zeros(complex(T),3,1) + z_cv_ss = zeros(complex(T),3,1) + z_cv_double = zeros(complex(T),3,1) + + BEAST.momintegrals!(op2, lag1, lag0, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op2, lag1, lag0, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op2, lag1, lag0, t1, t2, z_cv_double, Double_strategy) + + @test z_cv_double ≈ z_cv_ss rtol = 1e-4 + @test z_cv_se ≈ z_cv_ss rtol=1e-4 + @test norm(z_cv_ss-z_cv_se)/norm(z_cv_ss) < norm(z_cv_ss-z_cv_double)/norm(z_cv_ss) + #doublelayer_transposed + + z_cv_se = zeros(complex(T),3,1) + z_cv_ss = zeros(complex(T),3,1) + z_cv_double = zeros(complex(T),3,1) BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_cv_se, SE_strategy) BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_cv_ss, SS_strategy) @@ -145,32 +313,59 @@ end @test z_cv_double ≈ z_cv_ss rtol = 1e-4 @test z_cv_se ≈ z_cv_ss rtol=1e-4 - @test norm(z_cv_ss-z_cv_se)/norm(z_cv_ss) < norm(z_cv_ss-z_cv_double)/norm(z_cv_ss) + @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) - #hypersingular + #pyramid basis patch test SE_strategy = BEAST.WiltonSERule( - tqd1[1,1], + tqd0[1,1], BEAST.DoubleQuadRule( - tqd1[1,1], + tqd0[1,1], bqd1[1,2])) SS_strategy = SauterSchwabQuadrature.CommonVertex(BEAST._legendre(8,T(0.0),T(1.0))) Double_strategy = BEAST.DoubleQuadRule( #doublequadstrat - tqd1[1,1], + tqd0[1,1], bqd1[1,2] ) - z_cv_se = zeros(3,3) - z_cv_ss = zeros(3,3) - z_cv_double = zeros(3,3) + #singlelayer + z_cv_se = zeros(complex(T),1,3) + z_cv_ss = zeros(complex(T),1,3) + z_cv_double = zeros(complex(T),1,3) - BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_cv_se, SE_strategy) - BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_cv_ss, SS_strategy) - BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_cv_double, Double_strategy) + BEAST.momintegrals!(op1, lag0, lag1, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op1, lag0, lag1, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op1, lag0, lag1, t1, t2, z_cv_double, Double_strategy) + + @test z_cv_double ≈ z_cv_ss rtol = 1e-4 + @test z_cv_se ≈ z_cv_ss rtol=1e-4 + @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) broken = true + + #double layer + z_cv_se = zeros(complex(T),1,3) + z_cv_ss = zeros(complex(T),1,3) + z_cv_double = zeros(complex(T),1,3) + + BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_cv_double, Double_strategy) + + @test z_cv_double ≈ z_cv_ss rtol = 1e-4 + @test z_cv_se ≈ z_cv_ss rtol=1e-4 + @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) + + #double layer transposed + z_cv_se = zeros(complex(T),1,3) + z_cv_ss = zeros(complex(T),1,3) + z_cv_double = zeros(complex(T),1,3) + + BEAST.momintegrals!(op3, lag0, lag1, t1, t2, z_cv_se, SE_strategy) + BEAST.momintegrals!(op3, lag0, lag1, t1, t2, z_cv_ss, SS_strategy) + BEAST.momintegrals!(op3, lag0, lag1, t1, t2, z_cv_double, Double_strategy) @test z_cv_double ≈ z_cv_ss rtol = 1e-4 @test z_cv_se ≈ z_cv_ss rtol=1e-4 - #@test norm(z_cv_ss-z_cv_se)/norm(z_cv_ss) < norm(z_cv_ss-z_cv_double)/norm(z_cv_ss) + @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) end @@ -182,8 +377,8 @@ end T.(@SVector [0.0, -0.92388, -0.382683]) ) t2 = simplex( - T.(@SVector [0.180878, -0.941848, -0.283207]), T.(@SVector [0.158174, -0.881178, -0.44554]), + T.(@SVector [0.180878, -0.941848, -0.283207]), T.(@SVector [0.0, -0.92388, -0.382683]) ) @@ -195,7 +390,8 @@ end bqd0 = BEAST.quadpoints(lag0, [t1,t2], (13,)) #basis quadrature data bqd1 = BEAST.quadpoints(lag1, [t1,t2], (13,)) - # singlelayer + + #patch patch SE_strategy = BEAST.WiltonSERule( tqd0[1,1], BEAST.DoubleQuadRule( @@ -208,95 +404,223 @@ end tqd0[1,1], bqd0[1,2] ) - z_ce_se = zeros(3,1) - z_ce_ss = zeros(3,1) - z_ce_double = zeros(3,1) + #single layer + + z_ce_se = zeros(complex(T),1,1) + z_ce_ss = zeros(complex(T),1,1) + z_ce_double = zeros(complex(T),1,1) BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_ce_se, SE_strategy) BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_ce_ss, SS_strategy) BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_ce_double, Double_strategy) - @test z_ce_se ≈ z_ce_ss rtol=1e-4 @test z_ce_double ≈ z_ce_ss rtol = 1e-3 - @test norm(z_ce_ss-z_ce_se)/norm(z_ce_ss) < norm(z_ce_ss-z_ce_double)/norm(z_ce_ss) + @test z_ce_se ≈ z_ce_ss rtol=1e-4 + @test norm(z_ce_ss-z_ce_se) < norm(z_ce_ss-z_ce_double) - # doublelayer + #doublelayer + z_ce_se = zeros(complex(T),1,1) + z_ce_ss = zeros(complex(T),1,1) + z_ce_double = zeros(complex(T),1,1) + + BEAST.momintegrals!(op2, lag0, lag0, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op2, lag0, lag0, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op2, lag0, lag0, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_ss ≈ z_ce_double rtol = 1e-1 + @test z_ce_se ≈ z_ce_ss rtol = 1e-2 + @test norm(z_ce_ss-z_ce_se) < norm(z_ce_ss-z_ce_double) + + # doublelayer_transposed + z_ce_se = zeros(complex(T),1,1) + z_ce_ss = zeros(complex(T),1,1) + z_ce_double = zeros(complex(T),1,1) + + BEAST.momintegrals!(op3, lag0, lag0, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op3, lag0, lag0, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op3, lag0, lag0, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_double ≈ z_ce_ss rtol = 1e-1 + @test z_ce_se ≈ z_ce_ss rtol=1e-2 + @test norm(z_ce_ss-z_ce_se) < norm(z_ce_ss-z_ce_double) + +#pyramid pyramid + # singlelayer SE_strategy = BEAST.WiltonSERule( - tqd0[1,1], + tqd1[1,1], BEAST.DoubleQuadRule( - tqd0[1,1], + tqd1[1,1], bqd1[1,2])) - SS_strategy = SauterSchwabQuadrature.CommonEdge(BEAST._legendre(8,T(0.0),T(1.0))) - Double_strategy = BEAST.DoubleQuadRule( - tqd0[1,1], - bqd1[1,2]) - z_ce_se = zeros(1,3) - z_ce_ss = zeros(1,3) - z_ce_double = zeros(1,3) - BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_ce_se, SE_strategy) - BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_ce_ss, SS_strategy) - BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_ce_double, Double_strategy) + Double_strategy = BEAST.DoubleQuadRule( #doublequadstrat + tqd1[1,1], + bqd1[1,2] + ) + z_ce_se = zeros(complex(T),3,3) + z_ce_ss = zeros(complex(T),3,3) + z_ce_double = zeros(complex(T),3,3) - @test z_ce_se ≈ z_ce_ss rtol = 1e-4 - @test z_ce_ss ≈ z_ce_double rtol = 1e-1 + BEAST.momintegrals!(op1, lag1, lag1, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op1, lag1, lag1, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op1, lag1, lag1, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_double ≈ z_ce_ss rtol = 1e-3 + @test z_ce_se ≈ z_ce_double rtol=1e-3 + @test norm(z_ce_ss-z_ce_se) < norm(z_ce_ss-z_ce_double) + + #doublelayer + z_ce_se = zeros(complex(T),3,3) + z_ce_ss = zeros(complex(T),3,3) + z_ce_double = zeros(complex(T),3,3) + + BEAST.momintegrals!(op2, lag1, lag1, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op2, lag1, lag1, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op2, lag1, lag1, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_double ≈ z_ce_ss rtol = 1e-1 + @test z_ce_se ≈ z_ce_ss rtol=1e-4 @test norm(z_ce_ss-z_ce_se)/norm(z_ce_ss) < norm(z_ce_ss-z_ce_double)/norm(z_ce_ss) - # doublelayer transposed + #doublelayer_transposed + z_ce_se = zeros(complex(T),3,3) + z_ce_ss = zeros(complex(T),3,3) + z_ce_double = zeros(complex(T),3,3) + + BEAST.momintegrals!(op3, lag1, lag1, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op3, lag1, lag1, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op3, lag1, lag1, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_double ≈ z_ce_ss rtol = 1e-1 + @test z_ce_ss ≈ z_ce_se rtol = 1e-2 + @test norm(z_ce_ss-z_ce_se) < norm(z_ce_ss-z_ce_double) + + #hypersingular + z_ce_se = zeros(complex(T),3,3) + z_ce_ss = zeros(complex(T),3,3) + z_ce_double = zeros(complex(T),3,3) + + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_double ≈ z_ce_ss rtol = 1e-3 + @test z_ce_se ≈ z_ce_ss rtol=1e-4 + @test norm(z_ce_ss-z_ce_se) < norm(z_ce_ss-z_ce_double) + +#pyramid test patch basis SE_strategy = BEAST.WiltonSERule( - tqd1[1,1], - BEAST.DoubleQuadRule( - tqd1[1,1], - bqd0[1,2])) - SS_strategy = SauterSchwabQuadrature.CommonEdge(BEAST._legendre(12,T(0.0),T(1.0))) - Double_strategy = BEAST.DoubleQuadRule( tqd1[1,1], - bqd0[1,2]) - z_ce_se = zeros(3,1) - z_ce_ss = zeros(3,1) - z_ce_double = zeros(3,1) + BEAST.DoubleQuadRule( + tqd1[1,1], + bqd0[1,2])) + + SS_strategy = SauterSchwabQuadrature.CommonEdge(BEAST._legendre(8,T(0.0),T(1.0))) + + Double_strategy = BEAST.DoubleQuadRule( #doublequadstrat + tqd1[1,1], + bqd0[1,2] + ) + #singlelayer + z_ce_se = zeros(complex(T),3,1) + z_ce_ss = zeros(complex(T),3,1) + z_ce_double = zeros(complex(T),3,1) + + BEAST.momintegrals!(op1, lag1, lag0, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op1, lag1, lag0, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op1, lag1, lag0, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_double ≈ z_ce_ss rtol = 1e-3 + @test z_ce_se ≈ z_ce_ss rtol=1e-4 + @test norm(z_ce_ss-z_ce_se) < norm(z_ce_ss-z_ce_double) + + #doublelayer + z_ce_se = zeros(complex(T),3,1) + z_ce_ss = zeros(complex(T),3,1) + z_ce_double = zeros(complex(T),3,1) + + BEAST.momintegrals!(op2, lag1, lag0, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op2, lag1, lag0, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op2, lag1, lag0, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_double ≈ z_ce_ss rtol = 1e-1 + @test z_ce_se ≈ z_ce_ss rtol=1e-4 + @test norm(z_ce_ss-z_ce_se)/norm(z_ce_ss) < norm(z_ce_ss-z_ce_double)/norm(z_ce_ss) + #doublelayer_transposed + + z_ce_se = zeros(complex(T),3,1) + z_ce_ss = zeros(complex(T),3,1) + z_ce_double = zeros(complex(T),3,1) BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_ce_se, SE_strategy) BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_ce_ss, SS_strategy) BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_ce_double, Double_strategy) + @test z_ce_double ≈ z_ce_ss rtol = 1e-1 @test z_ce_se ≈ z_ce_ss rtol=1e-2 - @test z_ce_ss ≈ z_ce_double rtol = 1e-1 @test norm(z_ce_ss-z_ce_se)/norm(z_ce_ss) < norm(z_ce_ss-z_ce_double)/norm(z_ce_ss) - # hypersingular - + #pyramid basis patch test SE_strategy = BEAST.WiltonSERule( - tqd1[1,1], - BEAST.DoubleQuadRule( - tqd1[1,1], - bqd1[1,2])) - SS_strategy = SauterSchwabQuadrature.CommonEdge(BEAST._legendre(12,T(0.0),T(1.0))) - Double_strategy = BEAST.DoubleQuadRule( - tqd1[1,1], - bqd1[1,2]) + tqd0[1,1], + BEAST.DoubleQuadRule( + tqd0[1,1], + bqd1[1,2])) - z_ce_se = zeros(3,3) - z_ce_ss = zeros(3,3) - z_ce_double = zeros(3,3) + SS_strategy = SauterSchwabQuadrature.CommonEdge(BEAST._legendre(8,T(0.0),T(1.0))) - BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_ce_se, SE_strategy) - BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_ce_ss, SS_strategy) - BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_ce_double, Double_strategy) - @test z_ce_se ≈ z_ce_ss rtol = 1e-5 - @test z_ce_double ≈ z_ce_double rtol = 1e-5 - @test norm(z_ce_se-z_ce_ss) < norm(z_ce_double-z_ce_ss) + Double_strategy = BEAST.DoubleQuadRule( #doublequadstrat + tqd0[1,1], + bqd1[1,2] + ) + #singlelayer + z_ce_se = zeros(complex(T),1,3) + z_ce_ss = zeros(complex(T),1,3) + z_ce_double = zeros(complex(T),1,3) + + BEAST.momintegrals!(op1, lag0, lag1, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op1, lag0, lag1, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op1, lag0, lag1, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_double ≈ z_ce_ss rtol = 1e-3 + @test z_ce_se ≈ z_ce_ss rtol=1e-4 + @test norm(z_ce_ss-z_ce_se)/norm(z_ce_ss) < norm(z_ce_ss-z_ce_double)/norm(z_ce_ss) + + #double layer + z_ce_se = zeros(complex(T),1,3) + z_ce_ss = zeros(complex(T),1,3) + z_ce_double = zeros(complex(T),1,3) + + BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_double ≈ z_ce_ss rtol = 1e-1 + @test z_ce_se ≈ z_ce_ss rtol=1e-4 + @test norm(z_ce_ss-z_ce_se)/norm(z_ce_ss) < norm(z_ce_ss-z_ce_double)/norm(z_ce_ss) + + #double layer transposed + z_ce_se = zeros(complex(T),1,3) + z_ce_ss = zeros(complex(T),1,3) + z_ce_double = zeros(complex(T),1,3) + + BEAST.momintegrals!(op3, lag0, lag1, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op3, lag0, lag1, t1, t2, z_ce_ss, SS_strategy) + BEAST.momintegrals!(op3, lag0, lag1, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_double ≈ z_ce_ss rtol = 1e-1 + @test z_ce_se ≈ z_ce_ss rtol=1e-1 + @test norm(z_ce_ss-z_ce_se)/norm(z_ce_ss) < norm(z_ce_ss-z_ce_double)/norm(z_ce_ss) end @testset "Seperated triangles" begin t1 = simplex( - T.(@SVector [0.180878, -0.941848, -0.283207]), T.(@SVector [0.0, -0.980785, -0.19509]), + T.(@SVector [0.180878, -0.941848, -0.283207]), T.(@SVector [0.0, -0.92388, -0.382683])) t2 = simplex( - T.(@SVector [0.373086, -0.881524, -1.289348]), - T.(@SVector [0.180878, -0.941848, -1.283207]), - T.(@SVector [0.294908, -0.944921, -1.141962])) + T.(@SVector [0.0, -0.980785, -0.230102]), + T.(@SVector [0.180878, -0.941848, -0.383207]), + T.(@SVector [0.0, -0.92388, -0.411962])) lag0 = BEAST.LagrangeRefSpace{T,0,3,1}() # patch basis lag1 = BEAST.LagrangeRefSpace{T,1,3,3}() #pyramid basis @@ -317,14 +641,50 @@ end tqd0[1,1], bqd0[1,2] ) - z_ce_se = zeros(3,1) - z_ce_double = zeros(3,1) + z_ce_se = zeros(complex(T),1,1) + z_ce_double = zeros(complex(T),1,1) BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_ce_se, SE_strategy) BEAST.momintegrals!(op1, lag0, lag0, t1, t2, z_ce_double, Double_strategy) - @test z_ce_se ≈ z_ce_double rtol=1e-7 + @test z_ce_se ≈ z_ce_double rtol=1e-6 + + # calculate a reference value with hcubature to desired accuracy + #=using HCubature + r(u,v,t) = (1-u)*t[1]+u*((1-v)*t[2]+v*t[3]) + gamma=1.0+1.0im + jac(u,v,t) = u * norm(cross(t[1],t[2])+cross(t[2],t[3])+cross(t[3],t[1])) + function outerf(x) + r2(x2) = r(x2[1],x2[2],t1) - r(x[1],x[2],t2) + innerf(x2) = exp(-gamma*norm(r2(x2)))/(4*pi*norm(r2(x2))) * jac(x2[1],x2[2],t1) + hcubature(innerf, (0.0,0.0), (1.0,1.0), rtol = 1e-8)[1] * jac(x[1],x[2],t2) + end + + @show refval = hcubature(outerf, (0.0,0.0), (1.0,1.0), rtol = 1e-8)[1] =# + refval = 0.00033563892481993545 - 2.22592609372769e-5im # can be calculated with above code + @test z_ce_se[1] ≈ refval rtol = 1e-7 + @test z_ce_double[1] ≈ refval rtol = 1e-6 + + + SE_strategy = BEAST.WiltonSERule( + tqd1[1,1], + BEAST.DoubleQuadRule( + tqd1[1,1], + bqd1[1,2])) + + Double_strategy = BEAST.DoubleQuadRule( #doublequadstrat + tqd1[1,1], + bqd1[1,2] + ) + z_ce_se = zeros(complex(T),3,3) + z_ce_double = zeros(complex(T),3,3) + + BEAST.momintegrals!(op1, lag1, lag1, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op1, lag1, lag1, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_se ≈ z_ce_double rtol=1e-4 + # doublelayer SE_strategy = BEAST.WiltonSERule( tqd0[1,1], @@ -335,13 +695,28 @@ end Double_strategy = BEAST.DoubleQuadRule( tqd0[1,1], bqd1[1,2]) - z_ce_se = zeros(1,3) - z_ce_double = zeros(1,3) + z_ce_se = zeros(complex(T),1,3) + z_ce_double = zeros(complex(T),1,3) BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_ce_se, SE_strategy) BEAST.momintegrals!(op2, lag0, lag1, t1, t2, z_ce_double, Double_strategy) - @test z_ce_se ≈ z_ce_double rtol = 1e-8 - + @test z_ce_se ≈ z_ce_double rtol = 1e-4 + + SE_strategy = BEAST.WiltonSERule( + tqd1[1,1], + BEAST.DoubleQuadRule( + tqd1[1,1], + bqd1[1,2])) + + Double_strategy = BEAST.DoubleQuadRule( + tqd1[1,1], + bqd1[1,2]) + z_ce_se = zeros(complex(T),3,3) + z_ce_double = zeros(complex(T),3,3) + BEAST.momintegrals!(op2, lag1, lag1, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op2, lag1, lag1, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_se ≈ z_ce_double rtol = 1e-4 # doublelayer transposed SE_strategy = BEAST.WiltonSERule( tqd1[1,1], @@ -351,14 +726,29 @@ end Double_strategy = BEAST.DoubleQuadRule( tqd1[1,1], bqd0[1,2]) - z_ce_se = zeros(3,1) - z_ce_double = zeros(3,1) - + z_ce_se = zeros(complex(T),3,1) + z_ce_double = zeros(complex(T),3,1) + BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_ce_se, SE_strategy) BEAST.momintegrals!(op3, lag1, lag0, t1, t2, z_ce_double, Double_strategy) - - @test z_ce_se ≈ z_ce_double rtol=1e-7 - + + @test z_ce_se ≈ z_ce_double rtol=1e-4 + + SE_strategy = BEAST.WiltonSERule( + tqd1[1,1], + BEAST.DoubleQuadRule( + tqd1[1,1], + bqd1[1,2])) + Double_strategy = BEAST.DoubleQuadRule( + tqd1[1,1], + bqd1[1,2]) + z_ce_se = zeros(complex(T),3,3) + z_ce_double = zeros(complex(T),3,3) + + BEAST.momintegrals!(op3, lag1, lag1, t1, t2, z_ce_se, SE_strategy) + BEAST.momintegrals!(op3, lag1, lag1, t1, t2, z_ce_double, Double_strategy) + + @test z_ce_se ≈ z_ce_double rtol=1e-4 # hypersingular SE_strategy = BEAST.WiltonSERule( tqd1[1,1], @@ -368,12 +758,12 @@ end Double_strategy = BEAST.DoubleQuadRule( tqd1[1,1], bqd1[1,2]) - - z_ce_se = zeros(3,3) - z_ce_double = zeros(3,3) - + + z_ce_se = zeros(complex(T),3,3) + z_ce_double = zeros(complex(T),3,3) + BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_ce_se, SE_strategy) BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_ce_double, Double_strategy) - @test z_ce_se ≈ z_ce_double rtol = 1e-7 + @test z_ce_se ≈ z_ce_double rtol = 1e-5 end From db380292ef7b05a034ca30f069555cc2296d0325 Mon Sep 17 00:00:00 2001 From: Paula Respondek Date: Mon, 11 Sep 2023 11:47:20 +0200 Subject: [PATCH 283/528] Adapt subtyping and remove duplicate functions --- src/helmholtz3d/hh3dops.jl | 70 ++++++-------------------------------- 1 file changed, 11 insertions(+), 59 deletions(-) diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index b4e95e89..d9ba5d6d 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -20,7 +20,7 @@ function sign_upon_permutation(op::HH3DHyperSingularFDBIO, I, J) return Combinatorics.levicivita(I) * Combinatorics.levicivita(J) end -struct HH3DHyperSingularReg{T,K} <: Helmholtz3DOpReg +struct HH3DHyperSingularReg{T,K} <: Helmholtz3DOpReg{T,K} "coefficient of the weakly singular term" alpha::T "coefficient of the hyper singular term" @@ -29,7 +29,7 @@ struct HH3DHyperSingularReg{T,K} <: Helmholtz3DOpReg gamma::K end -struct HH3DHyperSingularSng{T,K} <: Helmholtz3DOp +struct HH3DHyperSingularSng{T,K} <: Helmholtz3DOp{T,K} "coefficient of the weakly singular term" alpha::T "coefficient of the hyper singular term" @@ -64,35 +64,35 @@ function sign_upon_permutation(op::HH3DSingleLayerFDBIO, I, J) return 1 end -function sign_upon_permutation(op::HH3DSingleLayerFDBIO, I, J) - return 1 +struct HH3DDoubleLayerFDBIO{T,K} <: Helmholtz3DOp{T,K} + alpha::T + gamma::K end -struct HH3DDoubleLayerFDBIO{T,K} <: Helmholtz3DOp{T,K} +struct HH3DDoubleLayerReg{T,K} <: Helmholtz3DOpReg{T,K} alpha::T gamma::K end -function sign_upon_permutation(op::HH3DDoubleLayerFDBIO, I, J) - return Combinatorics.levicivita(J) +struct HH3DDoubleLayerSng{T,K} <: Helmholtz3DOp{T,K} + alpha::T + gamma::K end function sign_upon_permutation(op::HH3DDoubleLayerFDBIO, I, J) return Combinatorics.levicivita(J) end - struct HH3DDoubleLayerTransposedFDBIO{T,K} <: Helmholtz3DOp{T,K} -struct HH3DDoubleLayerReg{T,K} <: Helmholtz3DOpReg alpha::T gamma::K end -struct HH3DDoubleLayerSng{T,K} <: Helmholtz3DOp +struct HH3DDoubleLayerTransposedReg{T,K} <: Helmholtz3DOpReg{T,K} alpha::T gamma::K end -struct HH3DDoubleLayerTransposedFDBIO{T,K} <: Helmholtz3DOp +struct HH3DDoubleLayerTransposedSng{T,K} <: Helmholtz3DOp{T,K} alpha::T gamma::K end @@ -101,16 +101,6 @@ function sign_upon_permutation(op::HH3DDoubleLayerTransposedFDBIO, I, J) return Combinatorics.levicivita(I) end -struct HH3DDoubleLayerTransposedReg{T,K} <: Helmholtz3DOpReg - alpha::T - gamma::K -end - -struct HH3DDoubleLayerTransposedSng{T,K} <: Helmholtz3DOp - alpha::T - gamma::K -end - defaultquadstrat(::Helmholtz3DOp, ::LagrangeRefSpace, ::LagrangeRefSpace) = DoubleNumWiltonSauterQStrat(2,3,2,3,4,4,4,4) function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, @@ -499,21 +489,6 @@ function (igd::Integrand{<:HH3DSingleLayerFDBIO})(x,y,f,g) end end -function (igd::Integrand{<:HH3DSingleLayerReg})(x,y,f,g) - α = igd.operator.alpha - γ = gamma(igd.operator) - - r = cartesian(x) - cartesian(y) - R = norm(r) - iR = 1 / R - green = (expm1(-γ*R) + γ*R - 0.5*γ^2*R^2) / (4pi*R) - αG = α * green - - _integrands(f,g) do fi, gi - dot(gi.value, αG*fi.value) - end -end - function integrand(op::Union{HH3DSingleLayerFDBIO,HH3DSingleLayerReg}, kernel, test_values, test_element, trial_values, trial_element) @@ -528,29 +503,6 @@ f = trial_values.value end -function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, - test_refspace::LagrangeRefSpace{T,0} where {T}, - trial_refspace::LagrangeRefSpace{T,0} where {T}, - test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) - - γ = gamma(op) - α = op.alpha - - s1, s2, s3 = trial_element.vertices - - x = cartesian(test_neighborhood) - n = normalize((s1-s3)×(s2-s3)) - ρ = x - dot(x - s1, n) * n - - scal, vec = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) - ∫G = (scal[2] - γ*scal[3] + 0.5*γ^2*scal[4]) / (4π) - - zlocal[1,1] += α * ∫G * dx - return nothing -end - - - HH3DDoubleLayerFDBIO(gamma) = HH3DDoubleLayerFDBIO(one(gamma), gamma) regularpart(op::HH3DDoubleLayerFDBIO) = HH3DDoubleLayerReg(op.alpha, op.gamma) From 84aefd91a2c1f14924b97bd015455cd7b79aa8dc Mon Sep 17 00:00:00 2001 From: Paula Respondek Date: Mon, 11 Sep 2023 11:51:08 +0200 Subject: [PATCH 284/528] Increase compat of WiltonInts84 to 0.2.5 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index e765de34..387e9053 100644 --- a/Project.toml +++ b/Project.toml @@ -51,7 +51,7 @@ SauterSchwabQuadrature = "2.2.0" SparseMatrixDicts = "0.2" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" -WiltonInts84 = "0.2.3" +WiltonInts84 = "0.2.5" julia = "1.6" From 3ffa287996da4b2ae5856c43d042ffc88782e8f8 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 22 Sep 2023 12:18:38 +0200 Subject: [PATCH 285/528] use test_broken not broken=true for LTS compat --- test/test_hh3dints.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test_hh3dints.jl b/test/test_hh3dints.jl index a441a647..883085fc 100644 --- a/test/test_hh3dints.jl +++ b/test/test_hh3dints.jl @@ -174,7 +174,7 @@ end @test z_cv_double ≈ z_cv_ss rtol = 1e-4 @test z_cv_se ≈ z_cv_ss rtol=1e-4 - @test norm(z_cv_ss-z_cv_se)/norm(z_cv_ss) < norm(z_cv_ss-z_cv_double)/norm(z_cv_ss) broken = true + @test_broken norm(z_cv_ss-z_cv_se)/norm(z_cv_ss) < norm(z_cv_ss-z_cv_double)/norm(z_cv_ss) #doublelayer z_cv_se = zeros(complex(T),1,1) z_cv_ss = zeros(complex(T),1,1) @@ -222,7 +222,7 @@ end @test z_cv_double ≈ z_cv_ss rtol = 1e-4 @test z_cv_se ≈ z_cv_double rtol=1e-4 - @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) broken = true + @test_broken norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) #doublelayer z_cv_se = zeros(complex(T),3,3) @@ -261,7 +261,7 @@ end @test z_cv_double ≈ z_cv_ss rtol = 1e-4 @test z_cv_se ≈ z_cv_ss rtol=1e-4 - @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) broken = true + @test_broken norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) #pyramid test patch basis SE_strategy = BEAST.WiltonSERule( @@ -287,7 +287,7 @@ end @test z_cv_double ≈ z_cv_ss rtol = 1e-4 @test z_cv_se ≈ z_cv_ss rtol=1e-4 - @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) broken = true + @test_broken norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) #doublelayer z_cv_se = zeros(complex(T),3,1) @@ -339,7 +339,7 @@ end @test z_cv_double ≈ z_cv_ss rtol = 1e-4 @test z_cv_se ≈ z_cv_ss rtol=1e-4 - @test norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) broken = true + @test_broken norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) #double layer z_cv_se = zeros(complex(T),1,3) From a8c94512492c4445aa3645ccd8ce5765861bc48b Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 27 Sep 2023 15:09:55 +0200 Subject: [PATCH 286/528] GMRESSolver allows suppying preconditioner - fixed supplying wavenumber iso gamma to PlaneWaveMW - Fixed wrong eltype deduction for GMRESSolver - FIxed redundant println for MT assembly TD operators --- src/maxwell/maxwell.jl | 2 +- src/maxwell/mwexc.jl | 2 +- src/solvers/itsolver.jl | 44 ++++++++++++++++++++++++++-------- src/timedomain/tdintegralop.jl | 3 ++- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/maxwell/maxwell.jl b/src/maxwell/maxwell.jl index 6e114c74..a155ab8b 100644 --- a/src/maxwell/maxwell.jl +++ b/src/maxwell/maxwell.jl @@ -70,7 +70,7 @@ module Maxwell3D polarization = error("missing arguement `polarization`"), wavenumber = error("missing arguement `wavenumber`"), amplitude = one(real(typeof(wavenumber)))) = - Mod.PlaneWaveMW(direction, polarization, wavenumber, amplitude) + Mod.PlaneWaveMW(direction, polarization, wavenumber*im, amplitude) farfield(; wavenumber = error("missing argument: `wavenumber`")) = diff --git a/src/maxwell/mwexc.jl b/src/maxwell/mwexc.jl index b25f1489..be4a4a80 100644 --- a/src/maxwell/mwexc.jl +++ b/src/maxwell/mwexc.jl @@ -29,7 +29,7 @@ function planewavemw3d(; gamma, wavenumber = gamma_wavenumber_handler(gamma, wavenumber) gamma === nothing && (gamma = zero(eltype(direction))) - return PlaneWaveMW(direction, polarization, wavenumber, amplitude) + return PlaneWaveMW(direction, polarization, gamma, amplitude) end diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index bc6a9fcf..54543331 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -2,19 +2,27 @@ import IterativeSolvers -struct GMRESSolver{L,T} <: LinearMap{T} +struct GMRESSolver{T,L,R,P} <: LinearMap{T} linear_operator::L maxiter::Int restart::Int - tol::T + abstol::R + reltol::R verbose::Bool + left_preconditioner::P end Base.axes(A::GMRESSolver) = reverse(axes(A.linear_operator)) -function GMRESSolver(op; maxiter=0, restart=0, tol=sqrt(eps(real(eltype(op)))), verbose=true) +function GMRESSolver(op::L; + left_preconditioner::P = IterativeSolvers.Identity(), + maxiter=0, + restart=0, + abstol::R = zero(real(eltype(op))), + reltol::R = sqrt(eps(real(eltype(op)))), + verbose=true) where {L,R<:Real,P} m, n = size(op) @assert m == n @@ -22,14 +30,15 @@ function GMRESSolver(op; maxiter=0, restart=0, tol=sqrt(eps(real(eltype(op)))), maxiter == 0 && (maxiter = div(n, 5)) restart == 0 && (restart = n) - GMRESSolver(op, maxiter, restart, tol, verbose) + T = eltype(op) + GMRESSolver{T,L,R,P}(op, maxiter, restart, abstol, reltol, verbose, left_preconditioner) end operator(solver::GMRESSolver) = solver.linear_operator -function solve(solver::GMRESSolver, b; abstol=zero(real(eltype(b))), reltol=solver.tol) +function solve(solver::GMRESSolver, b; abstol=solver.abstol, reltol=solver.reltol) T = promote_type(eltype(solver), eltype(b)) x = similar(Array{T}, axes(solver)[2]) fill!(x,0) @@ -37,10 +46,16 @@ function solve(solver::GMRESSolver, b; abstol=zero(real(eltype(b))), reltol=solv end -function solve!(x, solver::GMRESSolver, b; abstol=zero(real(eltype(b))), reltol=solver.tol) +function solve!(x, solver::GMRESSolver, b; abstol=solver.abstol, reltol=solver.reltol) op = operator(solver) - x, ch = IterativeSolvers.gmres!(x, op, b, log=true, maxiter=solver.maxiter, - restart=solver.restart, reltol=reltol, abstol=abstol, verbose=solver.verbose) + x, ch = IterativeSolvers.gmres!(x, op, b; + log=true, + maxiter=solver.maxiter, + restart=solver.restart, + reltol=reltol, + abstol=abstol, + verbose=solver.verbose, + Pl=solver.left_preconditioner) return x, ch end @@ -63,8 +78,8 @@ function LinearAlgebra.mul!(y::AbstractVecOrMat, solver::GMRESSolver, x::Abstrac return y end -LinearAlgebra.adjoint(A::GMRESSolver) = GMRESSolver(adjoint(A.linear_operator), A.maxiter, A.restart, A.tol, A.verbose) -LinearAlgebra.transpose(A::GMRESSolver) = GMRESSolver(transpose(A.linear_operator), A.maxiter, A.restart, A.tol, A.verbose) +LinearAlgebra.adjoint(A::GMRESSolver) = GMRESSolver(adjoint(A.linear_operator), A.maxiter, A.restart, A.abstol, A.reltol, A.verbose) +LinearAlgebra.transpose(A::GMRESSolver) = GMRESSolver(transpose(A.linear_operator), A.maxiter, A.restart, A.abstol, A.reltol, A.verbose) @@ -156,4 +171,13 @@ Preconditioner(A::L) where {L} = Preconditioner{L,eltype(A)}(A) function LinearAlgebra.ldiv!(y, P::Preconditioner, x) mul!(y, P.A, x) +end + +function LinearAlgebra.ldiv!(P::Preconditioner, b) + c = copy(b) + mul!(b, P.A, c) +end + +function Base.size(p::Preconditioner) + reverse(size(p.A)) end \ No newline at end of file diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index ff74175e..a87c6a3b 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -177,6 +177,7 @@ function assemble!(op::RetardedPotential, testST::Space, trialST::Space, store, store1 = (v,m,n,k) -> store(v,rlo+m-1,clo+n-1,k) assemble_chunk!(op, Y_p ⊗ S, X_q ⊗ R, store1) end + println("") # P = Threads.nthreads() # splits = [round(Int,s) for s in range(0, stop=numfunctions(Y), length=P+1)] @@ -279,7 +280,7 @@ function assemble_chunk!(op::RetardedPotential, testST, trialST, store; end end # next p - println("") + # println("") end From 936fc528f41cc422488b313650ecfcdac8088739 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 29 Sep 2023 12:00:23 +0200 Subject: [PATCH 287/528] symmetric quadstrat adapter for TD op assembly --- src/BEAST.jl | 1 + src/timedomain/td_symmetric_quadstrat.jl | 58 ++++++++++++++++++++++++ src/timedomain/tdintegralop.jl | 2 + 3 files changed, 61 insertions(+) create mode 100644 src/timedomain/td_symmetric_quadstrat.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 31d3886e..d79f78d3 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -201,6 +201,7 @@ include("timedomain/motlu.jl") include("timedomain/tdtimeops.jl") include("timedomain/rkcq.jl") include("timedomain/zdomain.jl") +include("timedomain/td_symmetric_quadstrat.jl") # Support for Maxwell equations diff --git a/src/timedomain/td_symmetric_quadstrat.jl b/src/timedomain/td_symmetric_quadstrat.jl new file mode 100644 index 00000000..9f4d7045 --- /dev/null +++ b/src/timedomain/td_symmetric_quadstrat.jl @@ -0,0 +1,58 @@ +struct SymmetricQuadStrat{S} + quadstrat::S +end + +struct SymmetricQuadRule{R1,R2} + quadrule1::R1 + quadrule2::R2 +end + +function quaddata(op, + test_local_space, trial_local_space, time_local_space, + test_charts, trial_charts, time_charts, + quadstrat::SymmetricQuadStrat) + + qd1 = quaddata(op, + test_local_space, trial_local_space, time_local_space, + test_charts, trial_charts, time_charts, + quadstrat.quadstrat) + qd2 = quaddata(op, + trial_local_space, test_local_space, time_local_space, + trial_charts, test_charts, time_charts, + quadstrat.quadstrat) + + return qd1, qd2 +end + +function quadrule(op, + test_local_space, trial_local_space, time_local_space, + p, test_chart, q, trial_chart, r, time_chart, + qd, quadstrat::SymmetricQuadStrat) + + qd1 = qd[1] + qd2 = qd[2] + + qr1 = quadrule(op, test_local_space, trial_local_space, time_local_space, + p, test_chart, q, trial_chart, r, time_chart, + qd1, quadstrat.quadstrat) + qr2 = quadrule(op, trial_local_space, test_local_space, time_local_space, + q, trial_chart, p, test_chart, r, time_chart, + qd2, quadstrat.quadstrat) + + return SymmetricQuadRule(qr1, qr2) +end + +function momintegrals!(z, op, U, V, W, τ, σ, ι, qr::SymmetricQuadRule) + + qr1 = qr.quadrule1 + qr2 = qr.quadrule2 + + z1 = zero(z) + z2 = zero(z) + + momintegrals!(z1, op, U, V, W, τ, σ, ι, qr1) + momintegrals!(z2, op, V, U, W, σ, τ, ι, qr2) + z2 = permutedims(z2, (2,1,3)) + z .+= (z1+z2)/2 + return nothing +end \ No newline at end of file diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index a87c6a3b..035c89ad 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -148,6 +148,8 @@ end function assemble!(op::RetardedPotential, testST::Space, trialST::Space, store, threading::Type{Threading{:multi}}=Threading{:multi}; quadstrat=defaultquadstrat(op, testST, trialST)) + @show quadstrat + Y, S = spatialbasis(testST), temporalbasis(testST) X, R = spatialbasis(trialST), temporalbasis(trialST) From e8f2ec0b7f16f5d73d2a84b1d31ec849a6883759 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Tue, 3 Oct 2023 00:37:01 +0000 Subject: [PATCH 288/528] CompatHelper: bump compat for FastGaussQuadrature to 1, (keep existing compat) --- Project.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index 387e9053..8276b8d6 100644 --- a/Project.toml +++ b/Project.toml @@ -39,7 +39,7 @@ CompScienceMeshes = "0.5.1" Compat = "2, 3, 4" ConvolutionOperators = "0.4" FFTW = "0.2.3, 1" -FastGaussQuadrature = "0.3, 0.4, 0.5" +FastGaussQuadrature = "0.3, 0.4, 0.5, 1" FillArrays = "0.11, 0.12, 0.13" IterativeSolvers = "0.9" LiftedMaps = "0.5.1" @@ -53,5 +53,3 @@ SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" WiltonInts84 = "0.2.5" julia = "1.6" - - From 307629072f88e9cd15eaeab7aead85320d646459 Mon Sep 17 00:00:00 2001 From: Joshua Date: Fri, 20 Oct 2023 10:09:49 +0200 Subject: [PATCH 289/528] Fixed handling of gamma::Nothing The case that `gamma=nothing` was only handled for the HH3DSingleLayer. I fixed it for the double-layer, double-layer transposed and hyper-singluar. Further improvements could be made if the `gamma=nothing` case is dispatched. I also deleted the redundant functions in the `wiltonints.jl`. --- src/helmholtz3d/hh3dops.jl | 201 +++------------------------------- src/helmholtz3d/wiltonints.jl | 182 +++++++++++++++++------------- 2 files changed, 123 insertions(+), 260 deletions(-) diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index d9ba5d6d..9eccc86c 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -1,4 +1,3 @@ - abstract type Helmholtz3DOp{T,K} <: MaxwellOperator3D{T,K} end abstract type Helmholtz3DOpReg{T,K} <: MaxwellOperator3DReg{T,K} end """ @@ -153,7 +152,8 @@ function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, return (;test_qp, bsis_qp) end -defaultquadstrat(::Helmholtz3DOp, ::subReferenceSpace, ::subReferenceSpace) = DoubleNumWiltonSauterQStrat(4,7,4,7,4,4,4,4) +defaultquadstrat(::Helmholtz3DOp, ::subReferenceSpace, ::subReferenceSpace) = + DoubleNumWiltonSauterQStrat(4,7,4,7,4,4,4,4) function quaddata(op::Helmholtz3DOp, test_refspace::subReferenceSpace, trial_refspace::subReferenceSpace, test_elements, trial_elements, @@ -165,14 +165,11 @@ function quaddata(op::Helmholtz3DOp, test_refspace::subReferenceSpace, return test_qp, bsis_qp end - - function quadrule(op::Helmholtz3DOp, - test_refspace::LagrangeRefSpace, - trial_refspace::LagrangeRefSpace, - i, test_element, j, trial_element, qd, - qs::DoubleNumWiltonSauterQStrat) - + test_refspace::LagrangeRefSpace, + trial_refspace::LagrangeRefSpace, + i, test_element, j, trial_element, qd, + qs::DoubleNumWiltonSauterQStrat) tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 dmin2 = floatmax(eltype(eltype(test_element.vertices))) @@ -204,7 +201,6 @@ function quadrule(op::Helmholtz3DOp, qd.bsis_qp[1,j]) end - function quadrule(op::HH3DSingleLayerFDBIO, test_refspace::LagrangeRefSpace{T,0} where T, trial_refspace::LagrangeRefSpace{T,0} where T, @@ -219,50 +215,17 @@ end regularpart(op::HH3DHyperSingularFDBIO) = HH3DHyperSingularReg(op.alpha, op.beta, op.gamma) singularpart(op::HH3DHyperSingularFDBIO) = HH3DHyperSingularSng(op.alpha, op.beta, op.gamma) -#= function quadrule(op::HH3DHyperSingularFDBIO, - test_refspace::LagrangeRefSpace, - trial_refspace::LagrangeRefSpace, - i, test_element, j, trial_element, qd, - qs::DoubleNumWiltonSauterQStrat) - - tol2, hits = eps(eltype(eltype(test_element.vertices))), 0 - for t in test_element.vertices - for s in trial_element.vertices - hits += (LinearAlgebra.norm_sqr(t-s) < tol2) - end end - - hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) - hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) - hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - - test_quadpoints = qd.test_qp - trial_quadpoints = qd.bsis_qp - h2 = volume(trial_element) - xtol2 = 0.2 * 0.2 - k2 = abs2(op.gamma) - max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( - test_quadpoints[1,i], - DoubleQuadRule( - test_quadpoints[1,i], - trial_quadpoints[1,j])) - - return DoubleQuadRule( - qd.test_qp[1,i], - qd.bsis_qp[1,j]) -end =# - - function quadrule(op::HH3DHyperSingularFDBIO, test_refspace::LagrangeRefSpace{T,1} where T, trial_refspace::LagrangeRefSpace{T,1} where T, i, test_element, j, trial_element, qd, qs::DoubleNumQStrat) - tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 - for t in test_element.vertices - for s in trial_element.vertices - norm(t-s) < tol && (hits +=1) - end end + #tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 + #for t in test_element.vertices + # for s in trial_element.vertices + # norm(t-s) < tol && (hits +=1) + #end end # hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) # hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) @@ -282,7 +245,6 @@ function quadrule(op::HH3DHyperSingularFDBIO, qd.bsis_qp[1,j]) end - function quadrule(op::HH3DSingleLayerFDBIO, test_refspace::subReferenceSpace, trial_refspace::subReferenceSpace, i, test_element, j, trial_element, quadrature_data, qs::DoubleNumWiltonSauterQStrat) @@ -315,7 +277,6 @@ function quadrule(op::HH3DSingleLayerFDBIO, test_refspace::subReferenceSpace, # ) end - function quadrule(op::Helmholtz3DOp, test_refspace::RefSpace, trial_refspace::RefSpace, i, test_element, j, trial_element, quadrature_data, @@ -326,84 +287,6 @@ function quadrule(op::Helmholtz3DOp, quadrature_data[2][1,j]) end -#= function quadrule(op::HH3DDoubleLayerTransposedFDBIO, - test_refspace::LagrangeRefSpace{T,1} where T, - trial_refspace::LagrangeRefSpace{T,0} where T, - i, test_element, j, trial_element, quadrature_data, - qs::DoubleNumWiltonSauterQStrat) - - tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 - dmin2 = floatmax(eltype(eltype(test_element.vertices))) - - for t in test_element.vertices - for s in trial_element.vertices - d2 = LinearAlgebra.norm_sqr(t-s) - dmin2 = min(dmin2, d2) - hits += (d2 < tol) - end end - - hits == 3 && return SauterSchwabQuadrature.CommonFace(quadrature_data.gausslegendre[3]) - hits == 2 && return SauterSchwabQuadrature.CommonEdge(quadrature_data.gausslegendre[2]) - hits == 1 && return SauterSchwabQuadrature.CommonVertex(quadrature_data.gausslegendre[1]) - - test_quadpoints = quadrature_data[1] - trial_quadpoints = quadrature_data[2] - test_quadpoints = quadrature_data.test_qp - trial_quadpoints = quadrature_data.bsis_qp - h2 = volume(trial_element) - xtol2 = 0.2 * 0.2 - k2 = abs2(gamma(op)) - - max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( - test_quadpoints[1,i], - DoubleQuadRule( - test_quadpoints[1,i], - trial_quadpoints[1,j])) - - return DoubleQuadRule( - quadrature_data[1][1,i], - quadrature_data[2][1,j]) -end =# - -#= function quadrule(op::HH3DDoubleLayerFDBIO, - test_refspace::LagrangeRefSpace{T,0} where T, - trial_refspace::LagrangeRefSpace{T,1} where T, - i, test_element, j, trial_element, quadrature_data, - qs::DoubleNumWiltonSauterQStrat) - - tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 - dmin2 = floatmax(eltype(eltype(test_element.vertices))) - - for t in test_element.vertices - for s in trial_element.vertices - d2 = LinearAlgebra.norm_sqr(t-s) - dmin2 = min(dmin2, d2) - hits += (d2 < tol) - end end - - hits == 3 && return SauterSchwabQuadrature.CommonFace(quadrature_data.gausslegendre[3]) - hits == 2 && return SauterSchwabQuadrature.CommonEdge(quadrature_data.gausslegendre[2]) - hits == 1 && return SauterSchwabQuadrature.CommonVertex(quadrature_data.gausslegendre[1]) - test_quadpoints = quadrature_data[1] - trial_quadpoints = quadrature_data[2] - - test_quadpoints = quadrature_data.test_qp - trial_quadpoints = quadrature_data.bsis_qp - h2 = volume(trial_element) - xtol2 = 0.2 * 0.2 - k2 = abs2(gamma(op)) - -#= max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( - test_quadpoints[1,i], - DoubleQuadRule( - test_quadpoints[1,i], - trial_quadpoints[1,j])) =# - return DoubleQuadRule( - quadrature_data[1][1,i], - quadrature_data[2][1,j]) -end =# - - function quadrule(op::Helmholtz3DOp, test_refspace::RefSpace, trial_refspace::RefSpace, i, test_element, j, trial_element, quadrature_data, @@ -427,8 +310,6 @@ function quadrule(op::Helmholtz3DOp, quadrature_data[2][1,j]) end - - function (igd::Integrand{<:HH3DHyperSingularFDBIO})(x,y,f,g) α = igd.operator.alpha β = igd.operator.beta @@ -447,31 +328,10 @@ function (igd::Integrand{<:HH3DHyperSingularFDBIO})(x,y,f,g) end -function integrand(op::HH3DHyperSingularFDBIO, - kernel, test_values, test_element, trial_values, trial_element) - - α = op.alpha - β = op.beta - - G = kernel.green - - g, curlg = test_values - f, curlf = trial_values - - nx = normal(test_element) - ny = normal(trial_element) - - α*dot(nx,ny)*g*f*G + β*dot(curlg,curlf)*G -end - - - - - HH3DSingleLayerFDBIO(gamma) = HH3DSingleLayerFDBIO(one(gamma), gamma) - -regularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerReg(op.alpha, gamma(op)) -singularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerSng(op.alpha, gamma(op)) +# Better to handle this in the wilton ints to allow dispatch +regularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerReg(op.alpha, op.gamma) +singularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerSng(op.alpha, op.gamma) function (igd::Integrand{<:HH3DSingleLayerFDBIO})(x,y,f,g) α = igd.operator.alpha @@ -490,21 +350,7 @@ function (igd::Integrand{<:HH3DSingleLayerFDBIO})(x,y,f,g) end -function integrand(op::Union{HH3DSingleLayerFDBIO,HH3DSingleLayerReg}, - kernel, test_values, test_element, trial_values, trial_element) - -α = op.alpha -G = kernel.green - -g = test_values.value -f = trial_values.value - -α*dot(g, G*f) -end - - HH3DDoubleLayerFDBIO(gamma) = HH3DDoubleLayerFDBIO(one(gamma), gamma) - regularpart(op::HH3DDoubleLayerFDBIO) = HH3DDoubleLayerReg(op.alpha, op.gamma) singularpart(op::HH3DDoubleLayerFDBIO) = HH3DDoubleLayerSng(op.alpha, op.gamma) @@ -526,17 +372,7 @@ function (igd::Integrand{<:HH3DDoubleLayerFDBIO})(x,y,f,g) end -function integrand(biop::HH3DDoubleLayerFDBIO, - kernel, fp, mp, fq, mq) - - nq = normal(mq) - fp[1] * dot(nq, -kernel.gradgreen) * fq[1] -end - - - - HH3DDoubleLayerTransposedFDBIO(gamma) = HH3DDoubleLayerTransposedFDBIO(one(gamma), gamma) - +HH3DDoubleLayerTransposedFDBIO(gamma) = HH3DDoubleLayerTransposedFDBIO(one(gamma), gamma) regularpart(op::HH3DDoubleLayerTransposedFDBIO) = HH3DDoubleLayerTransposedReg(op.alpha, op.gamma) singularpart(op::HH3DDoubleLayerTransposedFDBIO) = HH3DDoubleLayerTransposedSng(op.alpha, op.gamma) @@ -556,10 +392,3 @@ function (igd::Integrand{<:HH3DDoubleLayerTransposedFDBIO})(x,y,f,g) return _krondot(fvalue,gvalue) * dot(n, αgradgreen) end - -function integrand(biop::HH3DDoubleLayerTransposedFDBIO, - kernel, fp, mp, fq, mq) - - np = normal(mp) - fp[1] * dot(np, kernel.gradgreen) * fq[1] -end diff --git a/src/helmholtz3d/wiltonints.jl b/src/helmholtz3d/wiltonints.jl index c1cf7386..0921f00a 100644 --- a/src/helmholtz3d/wiltonints.jl +++ b/src/helmholtz3d/wiltonints.jl @@ -1,8 +1,10 @@ # single layer -function (igd::Integrand{<:HH3DSingleLayerReg})(x,y,f,g) +function (igd::Integrand{<:HH3DSingleLayerReg})(x,y,f,g) + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(igd.operator) α = igd.operator.alpha - γ = igd.operator.gamma - + r = cartesian(x) - cartesian(y) R = norm(r) iR = 1 / R @@ -18,21 +20,23 @@ function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, test_refspace::LagrangeRefSpace{T,0} where {T}, trial_refspace::LagrangeRefSpace{T,0} where {T}, test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(op) + α = op.alpha + + s1, s2, s3 = trial_element.vertices -γ = op.gamma -α = op.alpha - -s1, s2, s3 = trial_element.vertices + x = cartesian(test_neighborhood) + n = normalize((s1-s3)×(s2-s3)) + ρ = x - dot(x - s1, n) * n -x = cartesian(test_neighborhood) -n = normalize((s1-s3)×(s2-s3)) -ρ = x - dot(x - s1, n) * n + scal, vec = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) + ∫G = (scal[2] + 0.5*γ^2*scal[4]) / (4π) -scal, vec = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) -∫G = (scal[2] + 0.5*γ^2*scal[4]) / (4π) + zlocal[1,1] += α * ∫G * dx -zlocal[1,1] += α * ∫G * dx -return nothing + return nothing end # single layer with patch basis and pyramid testing @@ -40,29 +44,31 @@ function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, test_refspace::LagrangeRefSpace{T,1} where {T}, trial_refspace::LagrangeRefSpace{T,0} where {T}, test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(op) + α = op.alpha -γ = op.gamma -α = op.alpha - -s1, s2, s3 = trial_element.vertices -t1, t2, t3 = test_elements.vertices + s1, s2, s3 = trial_element.vertices + t1, t2, t3 = test_elements.vertices -x = cartesian(test_neighborhood) -n = normalize((s1-s3)×(s2-s3)) -ρ = x - dot(x - s1, n) * n + x = cartesian(test_neighborhood) + n = normalize((s1-s3)×(s2-s3)) + ρ = x - dot(x - s1, n) * n -scal, vec = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) -∫G = (scal[2] + 0.5*γ^2*scal[4]) / (4π) -Atot = 1/2*norm(cross(t3-t1,t3-t2)) -for i in 1:numfunctions(test_refspace) - Ai = 1/2*norm(cross(test_elements.vertices[mod1(i-1,3)]-x,test_elements.vertices[mod1(i+1,3)]-x)) - g = Ai/Atot - for j in 1:numfunctions(trial_refspace) - zlocal[i,j] += α * ∫G * g * dx + scal, vec = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) + ∫G = (scal[2] + 0.5*γ^2*scal[4]) / (4π) + Atot = 1/2*norm(cross(t3-t1,t3-t2)) + for i in 1:numfunctions(test_refspace) + Ai = 1/2*norm(cross(test_elements.vertices[mod1(i-1,3)]-x,test_elements.vertices[mod1(i+1,3)]-x)) + g = Ai/Atot + for j in 1:numfunctions(trial_refspace) + zlocal[i,j] += α * ∫G * g * dx + end end -end -return nothing + + return nothing end # single layer with pyramid basis and patch testing @@ -70,23 +76,25 @@ function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, test_refspace::LagrangeRefSpace{T,0} where {T}, trial_refspace::LagrangeRefSpace{T,1} where {T}, test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(op) + α = op.alpha -γ = op.gamma -α = op.alpha + s1, s2, s3 = trial_element.vertices -s1, s2, s3 = trial_element.vertices + x = cartesian(test_neighborhood) + n = normalize((s1-s3)×(s2-s3)) + ρ = x - dot(x - s1, n) * n -x = cartesian(test_neighborhood) -n = normalize((s1-s3)×(s2-s3)) -ρ = x - dot(x - s1, n) * n + ∫Rⁿ, ∫RⁿN = WiltonInts84.higherorder(s1,s2,s3,x,3) + ∫G = (∫RⁿN[2] + 0.5*γ^2*∫RⁿN[3]) / (4π) -∫Rⁿ, ∫RⁿN = WiltonInts84.higherorder(s1,s2,s3,x,3) -∫G = (∫RⁿN[2] + 0.5*γ^2*∫RⁿN[3]) / (4π) + for i in 1:numfunctions(trial_refspace) + zlocal[1,i] += α * ∫G[i] * dx + end -for i in 1:numfunctions(trial_refspace) -zlocal[1,i] += α * ∫G[i] * dx -end -return nothing + return nothing end #single layer with pyramid basis and pyramid testing @@ -94,34 +102,38 @@ function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, test_refspace::LagrangeRefSpace{T,1} where {T}, trial_refspace::LagrangeRefSpace{T,1} where {T}, test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(op) + α = op.alpha -γ = op.gamma -α = op.alpha - -s1, s2, s3 = trial_element.vertices -t1, t2, t3 = test_elements.vertices + s1, s2, s3 = trial_element.vertices + t1, t2, t3 = test_elements.vertices -x = cartesian(test_neighborhood) -n = normalize((s1-s3)×(s2-s3)) -ρ = x - dot(x - s1, n) * n + x = cartesian(test_neighborhood) + n = normalize((s1-s3)×(s2-s3)) + ρ = x - dot(x - s1, n) * n -∫Rⁿ, ∫RⁿN = WiltonInts84.higherorder(s1,s2,s3,x,3) -∫G = (∫RⁿN[2] + 0.5*γ^2*∫RⁿN[3]) / (4π) + ∫Rⁿ, ∫RⁿN = WiltonInts84.higherorder(s1,s2,s3,x,3) + ∫G = (∫RⁿN[2] + 0.5*γ^2*∫RⁿN[3]) / (4π) -Atot = 1/2*norm(cross(t3-t1,t3-t2)) -for i in 1:numfunctions(test_refspace) - Ai = 1/2*norm(cross(test_elements.vertices[mod1(i-1,3)]-x,test_elements.vertices[mod1(i+1,3)]-x)) - g = Ai/Atot - for j in 1:numfunctions(trial_refspace) - zlocal[i,j] += α * ∫G[j] * g * dx + Atot = 1/2*norm(cross(t3-t1,t3-t2)) + for i in 1:numfunctions(test_refspace) + Ai = 1/2*norm(cross(test_elements.vertices[mod1(i-1,3)]-x,test_elements.vertices[mod1(i+1,3)]-x)) + g = Ai/Atot + for j in 1:numfunctions(trial_refspace) + zlocal[i,j] += α * ∫G[j] * g * dx + end end -end -return nothing + + return nothing end # double layer transposed function (igd::Integrand{<:HH3DDoubleLayerTransposedReg})(x,y,f,g) - γ = igd.operator.gamma + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(igd.operator) r = cartesian(x) - cartesian(y) R = norm(r) @@ -143,7 +155,9 @@ function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, test_refspace::LagrangeRefSpace{T,0,3,1} where {T}, trial_refspace::LagrangeRefSpace{T,0,3,1} where {T}, test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) - γ = op.gamma + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(op) α = op.alpha s1, s2, s3 = trial_element.vertices @@ -169,7 +183,9 @@ function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, test_refspace::LagrangeRefSpace{T,1,3,3} where {T}, trial_refspace::LagrangeRefSpace{T,0,3,1} where {T}, test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) - γ = op.gamma + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(op) α = op.alpha s1, s2, s3 = trial_element.vertices @@ -198,7 +214,9 @@ function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, test_refspace::LagrangeRefSpace{T,0,3,1} where {T}, trial_refspace::LagrangeRefSpace{T,1,3,3} where {T}, test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) - γ = op.gamma + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(op) α = op.alpha s1, s2, s3 = trial_element.vertices @@ -224,7 +242,9 @@ function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, test_refspace::LagrangeRefSpace{T,1,3,3} where {T}, trial_refspace::LagrangeRefSpace{T,1,3,3} where {T}, test_elements, trial_element, zlocal, quadrature_rule::WiltonSERule, dx) - γ = op.gamma + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(op) α = op.alpha s1, s2, s3 = trial_element.vertices @@ -251,7 +271,9 @@ function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, end # double layer function (igd::Integrand{<:HH3DDoubleLayerReg})(x,y,f,g) - γ = igd.operator.gamma + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(igd.operator) r = cartesian(x) - cartesian(y) R = norm(r) @@ -273,7 +295,9 @@ function innerintegrals!(op::HH3DDoubleLayerSng, p, g::LagrangeRefSpace{T,1} where {T}, f::LagrangeRefSpace{T,1} where {T}, t, s, z, quadrature_rule::WiltonSERule, dx) - γ = op.gamma + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(op) α = op.alpha s1, s2, s3 = s.vertices @@ -303,7 +327,9 @@ function innerintegrals!(op::HH3DDoubleLayerSng, p, g::LagrangeRefSpace{T,0} where {T}, f::LagrangeRefSpace{T,0} where {T}, t, s, z, quadrature_rule::WiltonSERule, dx) - γ = op.gamma + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(op) α = op.alpha s1, s2, s3 = s.vertices @@ -329,7 +355,9 @@ function innerintegrals!(op::HH3DDoubleLayerSng, p, g::LagrangeRefSpace{T,1} where {T}, f::LagrangeRefSpace{T,0} where {T}, t, s, z, quadrature_rule::WiltonSERule, dx) - γ = op.gamma + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(op) α = op.alpha s1, s2, s3 = s.vertices @@ -359,7 +387,9 @@ function innerintegrals!(op::HH3DDoubleLayerSng, p, g::LagrangeRefSpace{T,0} where {T}, f::LagrangeRefSpace{T,1} where {T}, t, s, z, quadrature_rule::WiltonSERule, dx) - γ = op.gamma + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(op) α = op.alpha s1, s2, s3 = s.vertices @@ -386,7 +416,9 @@ end function (igd::Integrand{<:HH3DHyperSingularReg})(x,y,f,g) α = igd.operator.alpha β = igd.operator.beta - γ = igd.operator.gamma + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(igd.operator) r = cartesian(x) - cartesian(y) R = norm(r) @@ -406,7 +438,9 @@ function innerintegrals!(op::HH3DHyperSingularSng, p, t, s, z, quadrature_rule::WiltonSERule, dx) α = op.alpha β = op.beta - γ = op.gamma + # TODO: It is better to dispatch on γ + # to handle the static case more efficiently + γ = gamma(op) s1, s2, s3 = s.vertices t1, t2, t3 = t.vertices x = cartesian(p) From 6b68dc9b7661ec3c849b1e27e747dd4261b8478e Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 20 Oct 2023 12:19:29 +0200 Subject: [PATCH 290/528] (inverse) Matrix now follows the linearsolver api --- src/solvers/itsolver.jl | 11 +++- src/timedomain/motlu.jl | 120 +++++++--------------------------------- 2 files changed, 28 insertions(+), 103 deletions(-) diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index 54543331..dfff8914 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -174,10 +174,17 @@ function LinearAlgebra.ldiv!(y, P::Preconditioner, x) end function LinearAlgebra.ldiv!(P::Preconditioner, b) - c = copy(b) + c = deepcopy(b) mul!(b, P.A, c) end function Base.size(p::Preconditioner) reverse(size(p.A)) -end \ No newline at end of file +end + + +function solve(iA::AbstractMatrix, b) + ch = nothing + x = iA * b + return x, ch +end \ No newline at end of file diff --git a/src/timedomain/motlu.jl b/src/timedomain/motlu.jl index 61be5840..365c0e8d 100644 --- a/src/timedomain/motlu.jl +++ b/src/timedomain/motlu.jl @@ -1,107 +1,17 @@ -# function timeslice(Z::ConvolutionOperators.ConvOp, k) -# T = eltype(Z.data) -# Zk = zeros(T, size(Z)[1:2]) -# for n in axes(Z,2) -# for m in axes(Z,1) -# Zk[m,n] = Z[m,n,k] -# end end -# return Zk -# end - - - - - - -# function marchonintime(W0,Z,B,I) -# T = eltype(W0) -# M,N = size(Z) -# @assert M == size(B,1) -# x = zeros(T,N,I) -# for i in 1:I -# b = B[:,i] - convolve(Z,x,i,2) -# x[:,i] += W0 * b -# (i % 10 == 0) && print(i, "[", I, "] - ") -# end -# return x -# end - -# function marchonintime(iZ0, Z::ConvolutionOperators.AbstractConvOp, B, Nt) - -# T = eltype(iZ0) -# Ns = size(Z,1) -# x = zeros(T,Ns,Nt) -# csx = zeros(T,Ns,Nt) -# y = zeros(T,Ns) - -# todo, done, pct = Nt, 0, 0 -# for i in 1:Nt -# fill!(y,0) -# ConvolutionOperators.convolve!(y, Z, x, csx, i, 2, Nt) -# y .*= -1 -# y .+= B[:,i] -# # @show norm(B[:,i]) - -# x[:,i] .+= iZ0 * y -# if i > 1 -# csx[:,i] .= csx[:,i-1] .+ x[:,i] -# else -# csx[:,i] .= x[:,i] -# end - -# done += 1 -# new_pct = round(Int, done / todo * 100) -# new_pct > pct+9 && (println("[$new_pct]"); pct=new_pct) -# end -# x -# end - -# function convolve(Z::BlockArray, x, i, j_start) -# # ax1 = axes(Z,1) -# ax2 = axes(Z,2) -# T = eltype(eltype(Z)) -# y = PseudoBlockVector{T}(undef,blocklengths(axes(Z,1))) -# fill!(y,0) -# for I in blockaxes(Z,1) -# for J in blockaxes(Z,2) -# xJ = view(x, ax2[J], :) -# try -# ZIJ = Z[I,J].banded -# y[I] .+= convolve(ZIJ, xJ, i, j_start) -# catch -# @info "Skipping unassigned block." -# continue -# end -# end -# end -# return y -# end - -# function convolve!(y,Z::BlockArray, x, csx, i, j_start, j_stop) -# ax1 = axes(Z,1) -# ax2 = axes(Z,2) -# T = eltype(eltype(Z)) -# fill!(y,0) -# for I in blockaxes(Z,1) -# for J in blockaxes(Z,2) -# xJ = view(x, ax2[J], :) -# csxJ = view(csx, ax2[J], :) -# yI = view(y, ax1[I]) -# ConvolutionOperators.convolve!(yI, Z[I,J], xJ, csxJ, i, j_start, j_stop) -# end -# end -# return y -# end - """ - marchonintime(W0,Z,B,I) + marchonintime(W0,Z,B,I; convhist=false) Solve by marching-on-in-time the causal convolution problem defined by `(W0,Z,B)` up to timestep `I`. Here, `Z` is an array of order 3 that contains a discretisation of a time translation invariant retarded potential operator. `W0` is the inverse of the slice `Z[:,:,1]`. + +Keyword arguments: + - 'convhist': when true, return in addition to the space-time data for the + solution also the vector of convergence histories as returned each time step + by the supplied solver `W0`. """ -function marchonintime(W0,Z,B,I) +function marchonintime(W0,Z,B,I; convhist=false) T = eltype(W0) M,N = size(W0) @@ -111,17 +21,18 @@ function marchonintime(W0,Z,B,I) y = zeros(T,N) csx = zeros(T,N,I) + ch = [] for i in 1:I - # R = [ B[j][i] for j in 1:N ] R = B[:,i] - # @show norm(R) k_start = 2 k_stop = I fill!(y,0) ConvolutionOperators.convolve!(y,Z,x,csx,i,k_start,k_stop) b = R - y - x[:,i] .+= W0 * b + xi, chi = BEAST.solve(W0, b) + x[:,i] .+= xi + push!(ch, chi) if i > 1 csx[:,i] .= csx[:,i-1] .+ x[:,i] else @@ -130,5 +41,12 @@ function marchonintime(W0,Z,B,I) (i % 10 == 0) && print(i, "[", I, "] - ") end - return x + + if convhist + return x, ch + else + return x + end end + + From 46405eac3fb118089bac243052899a222771a95f Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 10 Nov 2023 15:33:23 +0100 Subject: [PATCH 291/528] nxmfie revived --- examples/nxmfie.jl | 33 ++++++++ examples/utils/plotresults.jl | 4 +- examples/utils/postproc.jl | 5 +- src/BEAST.jl | 3 + src/helmholtz3d/hh3dops.jl | 14 ++-- src/maxwell/nxdbllayer.jl | 105 ++---------------------- src/quadrature/doublenumqstrat.jl | 24 ++++++ src/quadrature/doublenumsauterqstrat.jl | 46 +++++++++++ src/quadrature/quaddata.jl | 21 ----- src/quadrature/quadrule.jl | 51 ------------ src/quadrature/sauterschwabints.jl | 25 +++--- 11 files changed, 139 insertions(+), 192 deletions(-) create mode 100644 examples/nxmfie.jl create mode 100644 src/quadrature/doublenumqstrat.jl create mode 100644 src/quadrature/doublenumsauterqstrat.jl diff --git a/examples/nxmfie.jl b/examples/nxmfie.jl new file mode 100644 index 00000000..1599819d --- /dev/null +++ b/examples/nxmfie.jl @@ -0,0 +1,33 @@ +using CompScienceMeshes, BEAST + +# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +Γ = meshsphere(radius=1.0, h=0.15) +X, Y = raviartthomas(Γ), buffachristiansen(Γ) + +ϵ, μ, ω = 1.0, 1.0, 1.0; κ, η = ω * √(ϵ*μ), √(μ/ϵ) +# K, N = Maxwell3D.doublelayer(wavenumber=κ), NCross() +nxK = BEAST.DoubleLayerRotatedMW3D(1.0, κ*im) +Id = BEAST.Identity() +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +# E = -η/(im*κ)*BEAST.CurlCurlGreen(κ, ẑ, point(2,0,0)) +H = -1/(im*μ*ω)*curl(E) + +h = (n × H) × n +nxh = n × H +h = nxh × n + +@hilbertspace j +@hilbertspace m +mfie = @discretise (nxK-0.5Id)[m,j] == nxh[m] j∈X m∈X +u = solve(mfie) + +include("utils/postproc.jl") +include("utils/plotresults.jl") + +# freeze, store = BEAST.allocatestorage(nxK, X, X, Val{:bandedstorage}, BEAST.LongDelays{:compress}) +# @enter BEAST.assemble!(nxK, X, X, store, BEAST.Threading{:single}) +# Z1 = freeze() +# Z2 = assemble(Id, X, X) +# Z = Z1 - 0.5*Z2 +# b = assemble(nxh, X) +# u = Z \ b diff --git a/examples/utils/plotresults.jl b/examples/utils/plotresults.jl index 69c136bf..74c92fe6 100644 --- a/examples/utils/plotresults.jl +++ b/examples/utils/plotresults.jl @@ -8,8 +8,8 @@ if plotresults using LinearAlgebra p1 = Plots.scatter(Θ, real.(norm.(ffd))) - p2 = Plots.heatmap(clamp.(real.(norm.(nfd)), 0.0, 2.0)) - p3 = Plots.contour(clamp.(real.(norm.(nfd)), 0.0, 2.0)) + p2 = Plots.heatmap(clamp.(real.(norm.(nfd)), 0.0, 2.0), clims=(0,2)) + p3 = Plots.contour(clamp.(real.(norm.(nfd)), 0.0, 2.0), clims=(0,2)) Plots.plot(p1,p2,p3,layout=(3,1)) end end diff --git a/examples/utils/postproc.jl b/examples/utils/postproc.jl index 6b1efd56..0b908822 100644 --- a/examples/utils/postproc.jl +++ b/examples/utils/postproc.jl @@ -11,8 +11,9 @@ if postproc fcr, geo = facecurrents(u, X) nx, nz = 50, 100 - xs, zs = range(-2,stop=2,length=nx), range(-4,stop=4,length=nz) - gridpoints = [point(x,0,z) for x in xs, z in zs] + x = 0.0 + ys, zs = range(-2,stop=2,length=nx), range(-4,stop=4,length=nz) + gridpoints = [point(x,y,z) for y in ys, z in zs] nfd = potential(MWSingleLayerField3D(wavenumber = κ), gridpoints, u, X) nfd = reshape(nfd, (nx,nz)) nfd .-= E.(gridpoints) diff --git a/src/BEAST.jl b/src/BEAST.jl index d79f78d3..fb752fe6 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -187,6 +187,9 @@ include("quaddata.jl") include("quadrature/quaddata.jl") include("quadrature/quadrule.jl") +include("quadrature/doublenumqstrat.jl") +include("quadrature/doublenumsauterqstrat.jl") + include("quadrature/double_quadrature.jl") include("quadrature/singularity_extraction.jl") include("quadrature/sauterschwabints.jl") diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index d9ba5d6d..14c404b6 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -258,18 +258,18 @@ function quadrule(op::HH3DHyperSingularFDBIO, i, test_element, j, trial_element, qd, qs::DoubleNumQStrat) - tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 - for t in test_element.vertices - for s in trial_element.vertices - norm(t-s) < tol && (hits +=1) - end end + # tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 + # for t in test_element.vertices + # for s in trial_element.vertices + # norm(t-s) < tol && (hits +=1) + # end end # hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) # hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) # hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - test_quadpoints = qd.test_qp - trial_quadpoints = qd.bsis_qp + # test_quadpoints = qd.test_qp + # trial_quadpoints = qd.bsis_qp # hits != 0 && return WiltonSERule( # test_quadpoints[1,i], diff --git a/src/maxwell/nxdbllayer.jl b/src/maxwell/nxdbllayer.jl index 5343d986..3507acb5 100644 --- a/src/maxwell/nxdbllayer.jl +++ b/src/maxwell/nxdbllayer.jl @@ -6,103 +6,13 @@ mutable struct DoubleLayerRotatedMW3D{T,K} <: MaxwellOperator3D{T,K} gamma::K end -LinearAlgebra.cross(::NormalVector, a::MWDoubleLayer3D) = DoubleLayerRotatedMW3D(a.alpha, a.gamma) - -# defaultquadstrat(::DoubleLayerRotatedMW3D, tfs, bfs) = DoubleNumQStrat(2,3) - -function quaddata(operator::DoubleLayerRotatedMW3D, - local_test_basis::LinearRefSpaceTriangle, local_trial_basis::LinearRefSpaceTriangle, - test_elements, trial_elements, qs::DoubleNumQStrat) - - test_quad_data = quadpoints(local_test_basis, test_elements, (qs.outer_rule,)) - trial_quad_data = quadpoints(local_trial_basis, trial_elements, (qs.inner_rule,)) - - return test_quad_data, trial_quad_data -end - - -function quadrule(operator::DoubleLayerRotatedMW3D, - local_test_basis::LinearRefSpaceTriangle, local_trial_basis::LinearRefSpaceTriangle, - test_id, test_element, trial_id, trial_element, - quad_data, qs::DoubleNumQStrat) - - test_quad_rules = quad_data[1] - trial_quad_rules = quad_data[2] - - DoubleQuadRule( - test_quad_rules[1,test_id], - trial_quad_rules[1,trial_id] - ) -end - -# TODO: this method needs to go and dispatch needs to be dealt with using the defaultquadstrat mechanism -function quadrule(op::DoubleLayerRotatedMW3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd, - qs::DoubleNumWiltonSauterQStrat) - - hits = 0 - dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) - dmin2 = floatmax(eltype(eltype(τ.vertices))) - for t in τ.vertices - for s in σ.vertices - d2 = LinearAlgebra.norm_sqr(t-s) - dmin2 = min(dmin2, d2) - hits += (d2 < dtol) - end - end - - hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) - hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) - hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - - h2 = volume(σ) - xtol2 = 0.2 * 0.2 - k2 = abs2(gamma(op)) - return DoubleQuadRule( - qd.tpoints[1,i], - qd.bpoints[1,j],) -end - -# function integrand(op::DoubleLayerRotatedMW3D, kernel_vals, test_vals, test_nbd, trial_vals, trial_nbd) - -# n = normal(test_nbd) -# g = test_vals[1] -# f = trial_vals[1] -# ∇G = kernel_vals.gradgreen - -# return g ⋅ (n × (∇G × f)) -# end - -# function (igd::Integrand{<:DoubleLayerRotatedMW3D})(u,v) - -# x = neighborhood(igd.test_chart,u) -# y = neighborhood(igd.trial_chart,v) -# j = jacobian(x) * jacobian(y) -# nx = normal(x) - -# r = cartesian(x) - cartesian(y) -# R = norm(r) -# iR = 1/R -# γ = igd.operator.gamma -# G = exp(-γ*R)/(4π*R) -# K = -(γ + iR) * G * (iR * r) - -# f = igd.local_test_space(x) -# g = igd.local_trial_space(y) - -# fvalue = getvalue(f) -# gvalue = getvalue(g) - -# jKg = cross.(Ref(K), j*gvalue) -# jnxKg = cross.(Ref(nx), jKg) -# return _krondot(fvalue, jnxKg) -# end +# defaultquadstrat(op::DoubleLayerRotatedMW3D, tfs::Space, bfs::Space) = DoubleNumSauterQstrat(6,7,5,5,4,3) +defaultquadstrat(op::DoubleLayerRotatedMW3D, tfs::Space, bfs::Space) = DoubleNumQStrat(6,7) +LinearAlgebra.cross(::NormalVector, a::MWDoubleLayer3D) = DoubleLayerRotatedMW3D(a.alpha, a.gamma) function (igd::Integrand{<:DoubleLayerRotatedMW3D})(x,y,f,g) - # x = neighborhood(igd.test_chart,u) - # y = neighborhood(igd.trial_chart,v) - j = jacobian(x) * jacobian(y) nx = normal(x) r = cartesian(x) - cartesian(y) @@ -112,13 +22,10 @@ function (igd::Integrand{<:DoubleLayerRotatedMW3D})(x,y,f,g) G = exp(-γ*R)/(4π*R) K = -(γ + iR) * G * (iR * r) - # f = igd.local_test_space(x) - # g = igd.local_trial_space(y) - fvalue = getvalue(f) gvalue = getvalue(g) - jKg = cross.(Ref(K), j*gvalue) - jnxKg = cross.(Ref(nx), jKg) - return _krondot(fvalue, jnxKg) + Kg = cross.(Ref(K), gvalue) + nxKg = cross.(Ref(nx), Kg) + return _krondot(fvalue, nxKg) end diff --git a/src/quadrature/doublenumqstrat.jl b/src/quadrature/doublenumqstrat.jl new file mode 100644 index 00000000..2c940e31 --- /dev/null +++ b/src/quadrature/doublenumqstrat.jl @@ -0,0 +1,24 @@ +function quaddata(operator::IntegralOperator, + local_test_basis::RefSpace, local_trial_basis::RefSpace, + test_elements, trial_elements, qs::DoubleNumQStrat) + + test_quad_data = quadpoints(local_test_basis, test_elements, (qs.outer_rule,)) + trial_quad_data = quadpoints(local_trial_basis, trial_elements, (qs.inner_rule,)) + + return test_quad_data, trial_quad_data +end + + +function quadrule(operator::IntegralOperator, + local_test_basis::RefSpace, local_trial_basis::RefSpace, + test_id, test_element, trial_id, trial_element, + quad_data, qs::DoubleNumQStrat) + + test_quad_rules = quad_data[1] + trial_quad_rules = quad_data[2] + + DoubleQuadRule( + test_quad_rules[1,test_id], + trial_quad_rules[1,trial_id] + ) +end \ No newline at end of file diff --git a/src/quadrature/doublenumsauterqstrat.jl b/src/quadrature/doublenumsauterqstrat.jl new file mode 100644 index 00000000..825cc5b6 --- /dev/null +++ b/src/quadrature/doublenumsauterqstrat.jl @@ -0,0 +1,46 @@ + + + +function quaddata(op::IntegralOperator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_charts, trial_charts, qs::DoubleNumSauterQstrat) + + T = coordtype(test_charts[1]) + + tqd = quadpoints(test_local_space, test_charts, (qs.outer_rule,)) + bqd = quadpoints(trial_local_space, trial_charts, (qs.inner_rule,)) + + leg = ( + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_vert,0,1)), + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_edge,0,1)), + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_face,0,1)), + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_tetr,0,1)), + ) + + return (tpoints=tqd, bpoints=bqd, gausslegendre=leg) +end + + +function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, + qs::DoubleNumSauterQstrat) + + T = eltype(eltype(τ.vertices)) + hits = 0 + dtol = 1.0e3 * eps(T) + dmin2 = floatmax(T) + for t in τ.vertices + for s in σ.vertices + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < dtol) + end + end + + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) + hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) + hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j],) +end \ No newline at end of file diff --git a/src/quadrature/quaddata.jl b/src/quadrature/quaddata.jl index a18241fa..e69de29b 100644 --- a/src/quadrature/quaddata.jl +++ b/src/quadrature/quaddata.jl @@ -1,21 +0,0 @@ -# This file contains kernel independent implementations of quaddata for -# the various quadrature strategies defined in quadstrats.jl - -function quaddata(op::IntegralOperator, - test_local_space::RefSpace, trial_local_space::RefSpace, - test_charts, trial_charts, qs::DoubleNumSauterQstrat) - - T = coordtype(test_charts[1]) - - tqd = quadpoints(test_local_space, test_charts, (qs.outer_rule,)) - bqd = quadpoints(trial_local_space, trial_charts, (qs.inner_rule,)) - - leg = ( - convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_vert,0,1)), - convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_edge,0,1)), - convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_face,0,1)), - convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_tetr,0,1)), - ) - - return (tpoints=tqd, bpoints=bqd, gausslegendre=leg) -end \ No newline at end of file diff --git a/src/quadrature/quadrule.jl b/src/quadrature/quadrule.jl index 8307a966..e69de29b 100644 --- a/src/quadrature/quadrule.jl +++ b/src/quadrature/quadrule.jl @@ -1,51 +0,0 @@ -# This file contains kernel independent implementations of quadrule for -# the various quadrature strategies defined in quadstrats.jl - -function quadrule(op::IntegralOperator, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd, - qs::DoubleNumSauterQstrat) - - T = eltype(eltype(τ.vertices)) - hits = 0 - dtol = 1.0e3 * eps(T) - dmin2 = floatmax(T) - for t in τ.vertices - for s in σ.vertices - d2 = LinearAlgebra.norm_sqr(t-s) - dmin2 = min(dmin2, d2) - hits += (d2 < dtol) - end - end - - hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) - hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) - hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - - return DoubleQuadRule( - qd.tpoints[1,i], - qd.bpoints[1,j],) -end - - -function quadrule(op::IntegralOperator, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd, - qs::DoubleNumSauterQstrat) - - T = eltype(eltype(τ.vertices)) - hits = 0 - dtol = 1.0e3 * eps(T) - dmin2 = floatmax(T) - for t in τ.vertices - for s in σ.vertices - d2 = LinearAlgebra.norm_sqr(t-s) - dmin2 = min(dmin2, d2) - hits += (d2 < dtol) - end - end - - hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) - hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) - hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - - return DoubleQuadRule( - qd.tpoints[1,i], - qd.bpoints[1,j],) -end \ No newline at end of file diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index 459a36ca..a7c99652 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -95,25 +95,30 @@ function momintegrals!(op::Operator, test_chart.vertices, trial_chart.vertices, rule) - test_chart = simplex( - test_chart.vertices[I[1]], - test_chart.vertices[I[2]], - test_chart.vertices[I[3]]) + # test_chart = simplex( + # test_chart.vertices[I[1]], + # test_chart.vertices[I[2]], + # test_chart.vertices[I[3]]) - trial_chart = simplex( - trial_chart.vertices[J[1]], - trial_chart.vertices[J[2]], - trial_chart.vertices[J[3]]) + # permute_vertices reparametrizes the simplex without affecting the normal + test_chart = CompScienceMeshes.permute_vertices(test_chart, I) + trial_chart = CompScienceMeshes.permute_vertices(trial_chart, J) + + # trial_chart = simplex( + # trial_chart.vertices[J[1]], + # trial_chart.vertices[J[2]], + # trial_chart.vertices[J[3]]) igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, rule) - σ = sign_upon_permutation(op, I, J) + # σ = sign_upon_permutation(op, I, J) K = dof_permutation(test_local_space, I) L = dof_permutation(trial_local_space, J) for i in 1:numfunctions(test_local_space) for j in 1:numfunctions(trial_local_space) - out[i,j] = σ * G[K[i],L[j]] + # out[i,j] = σ * G[K[i],L[j]] + out[i,j] = G[K[i],L[j]] end end nothing From a6256f51c8e8283f0d9726363b630fdc5198a888 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 10 Nov 2023 16:29:22 +0100 Subject: [PATCH 292/528] consolidated quadstrat/data/rule for Maxwell3D --- examples/efie_bdm.jl | 2 +- examples/mfie_bdm.jl | 2 +- src/BEAST.jl | 6 +- src/helmholtz3d/hh3dops.jl | 120 +++++----- src/integralop.jl | 3 +- src/maxwell/mwops.jl | 216 ++++++------------ ...{double_quadrature.jl => doublenumints.jl} | 2 +- .../doublenumwiltonbogaertqstrat.jl | 57 +++++ src/quadrature/doublenumwiltonsauterqstrat.jl | 52 +++++ ...action.jl => singularityextractionints.jl} | 0 10 files changed, 245 insertions(+), 215 deletions(-) rename src/quadrature/{double_quadrature.jl => doublenumints.jl} (91%) create mode 100644 src/quadrature/doublenumwiltonbogaertqstrat.jl create mode 100644 src/quadrature/doublenumwiltonsauterqstrat.jl rename src/quadrature/{singularity_extraction.jl => singularityextractionints.jl} (100%) diff --git a/examples/efie_bdm.jl b/examples/efie_bdm.jl index 226c7aed..b1ae2c41 100644 --- a/examples/efie_bdm.jl +++ b/examples/efie_bdm.jl @@ -2,7 +2,7 @@ using CompScienceMeshes using BEAST # Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) -Γ = meshsphere(radius=1.0, h=0.2) +Γ = meshsphere(radius=1.0, h=0.1) X = brezzidouglasmarini(Γ) κ = 1.0 diff --git a/examples/mfie_bdm.jl b/examples/mfie_bdm.jl index cb7e36ee..d3380659 100644 --- a/examples/mfie_bdm.jl +++ b/examples/mfie_bdm.jl @@ -10,7 +10,7 @@ Y = brezzidouglasmarini(Γ) ϵ, μ, ω = 1.0, 1.0, 1.0; κ = ω * √(ϵ*μ) # κ = 3.0 -NK, Id = BEAST.DoubleLayerRotatedMW3D(im*κ), Identity() +NK, Id = BEAST.DoubleLayerRotatedMW3D(1.0, im*κ), Identity() E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) H = -1/(im*μ*ω)*curl(E) h = n × H diff --git a/src/BEAST.jl b/src/BEAST.jl index fb752fe6..f84bfe10 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -189,9 +189,11 @@ include("quadrature/quadrule.jl") include("quadrature/doublenumqstrat.jl") include("quadrature/doublenumsauterqstrat.jl") +include("quadrature/doublenumwiltonsauterqstrat.jl") +include("quadrature/doublenumwiltonbogaertqstrat.jl") -include("quadrature/double_quadrature.jl") -include("quadrature/singularity_extraction.jl") +include("quadrature/doublenumints.jl") +include("quadrature/singularityextractionints.jl") include("quadrature/sauterschwabints.jl") include("postproc.jl") diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index 14c404b6..c70ef864 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -128,30 +128,30 @@ function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, return (;test_qp, bsis_qp, gausslegendre) end -function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, - trial_refspace::LagrangeRefSpace, test_elements, trial_elements, - qs::DoubleNumQStrat) +# function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, +# trial_refspace::LagrangeRefSpace, test_elements, trial_elements, +# qs::DoubleNumQStrat) - test_eval(x) = test_refspace(x, Val{:withcurl}) - trial_eval(x) = trial_refspace(x, Val{:withcurl}) +# test_eval(x) = test_refspace(x, Val{:withcurl}) +# trial_eval(x) = trial_refspace(x, Val{:withcurl}) - # The combinations of rules (6,7) and (5,7 are) BAAAADDDD - # they result in many near singularity evaluations with any - # resemblence of accuracy going down the drain! Simply don't! - # (same for (5,7) btw...). - # test_qp = quadpoints(test_eval, test_elements, (6,)) - # bssi_qp = quadpoints(trial_eval, trial_elements, (7,)) +# # The combinations of rules (6,7) and (5,7 are) BAAAADDDD +# # they result in many near singularity evaluations with any +# # resemblence of accuracy going down the drain! Simply don't! +# # (same for (5,7) btw...). +# # test_qp = quadpoints(test_eval, test_elements, (6,)) +# # bssi_qp = quadpoints(trial_eval, trial_elements, (7,)) - test_qp = quadpoints(test_eval, test_elements, (qs.outer_rule,)) - bsis_qp = quadpoints(trial_eval, trial_elements, (qs.inner_rule,)) +# test_qp = quadpoints(test_eval, test_elements, (qs.outer_rule,)) +# bsis_qp = quadpoints(trial_eval, trial_elements, (qs.inner_rule,)) - # gausslegendre = ( - # _legendre(qs.sauter_schwab_common_vert,0,1), - # _legendre(qs.sauter_schwab_common_edge,0,1), - # _legendre(qs.sauter_schwab_common_face,0,1),) +# # gausslegendre = ( +# # _legendre(qs.sauter_schwab_common_vert,0,1), +# # _legendre(qs.sauter_schwab_common_edge,0,1), +# # _legendre(qs.sauter_schwab_common_face,0,1),) - return (;test_qp, bsis_qp) -end +# return (;test_qp, bsis_qp) +# end defaultquadstrat(::Helmholtz3DOp, ::subReferenceSpace, ::subReferenceSpace) = DoubleNumWiltonSauterQStrat(4,7,4,7,4,4,4,4) @@ -205,16 +205,16 @@ function quadrule(op::Helmholtz3DOp, end -function quadrule(op::HH3DSingleLayerFDBIO, - test_refspace::LagrangeRefSpace{T,0} where T, - trial_refspace::LagrangeRefSpace{T,0} where T, - i, test_element, j, trial_element, qd, - qs::DoubleNumQStrat) +# function quadrule(op::HH3DSingleLayerFDBIO, +# test_refspace::LagrangeRefSpace{T,0} where T, +# trial_refspace::LagrangeRefSpace{T,0} where T, +# i, test_element, j, trial_element, qd, +# qs::DoubleNumQStrat) - return DoubleQuadRule( - qd.test_qp[1,i], - qd.bsis_qp[1,j]) -end +# return DoubleQuadRule( +# qd.test_qp[1,i], +# qd.bsis_qp[1,j]) +# end regularpart(op::HH3DHyperSingularFDBIO) = HH3DHyperSingularReg(op.alpha, op.beta, op.gamma) singularpart(op::HH3DHyperSingularFDBIO) = HH3DHyperSingularSng(op.alpha, op.beta, op.gamma) @@ -252,35 +252,35 @@ singularpart(op::HH3DHyperSingularFDBIO) = HH3DHyperSingularSng(op.alpha, op.bet end =# -function quadrule(op::HH3DHyperSingularFDBIO, - test_refspace::LagrangeRefSpace{T,1} where T, - trial_refspace::LagrangeRefSpace{T,1} where T, - i, test_element, j, trial_element, qd, - qs::DoubleNumQStrat) +# function quadrule(op::HH3DHyperSingularFDBIO, +# test_refspace::LagrangeRefSpace{T,1} where T, +# trial_refspace::LagrangeRefSpace{T,1} where T, +# i, test_element, j, trial_element, qd, +# qs::DoubleNumQStrat) - # tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 - # for t in test_element.vertices - # for s in trial_element.vertices - # norm(t-s) < tol && (hits +=1) - # end end +# # tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 +# # for t in test_element.vertices +# # for s in trial_element.vertices +# # norm(t-s) < tol && (hits +=1) +# # end end - # hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) - # hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) - # hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) +# # hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) +# # hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) +# # hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - # test_quadpoints = qd.test_qp - # trial_quadpoints = qd.bsis_qp +# # test_quadpoints = qd.test_qp +# # trial_quadpoints = qd.bsis_qp - # hits != 0 && return WiltonSERule( - # test_quadpoints[1,i], - # DoubleQuadRule( - # test_quadpoints[1,i], - # trial_quadpoints[1,j])) +# # hits != 0 && return WiltonSERule( +# # test_quadpoints[1,i], +# # DoubleQuadRule( +# # test_quadpoints[1,i], +# # trial_quadpoints[1,j])) - return DoubleQuadRule( - qd.test_qp[1,i], - qd.bsis_qp[1,j]) -end +# return DoubleQuadRule( +# qd.test_qp[1,i], +# qd.bsis_qp[1,j]) +# end function quadrule(op::HH3DSingleLayerFDBIO, test_refspace::subReferenceSpace, @@ -404,15 +404,15 @@ end =# end =# -function quadrule(op::Helmholtz3DOp, - test_refspace::RefSpace, trial_refspace::RefSpace, - i, test_element, j, trial_element, quadrature_data, - qs::DoubleNumQStrat) +# function quadrule(op::Helmholtz3DOp, +# test_refspace::RefSpace, trial_refspace::RefSpace, +# i, test_element, j, trial_element, quadrature_data, +# qs::DoubleNumQStrat) - return DoubleQuadRule( - quadrature_data[1][1,i], - quadrature_data[2][1,j]) -end +# return DoubleQuadRule( +# quadrature_data[1][1,i], +# quadrature_data[2][1,j]) +# end function quadrule(op::Helmholtz3DOp, test_refspace::RTRefSpace, trial_refspace::RTRefSpace, diff --git a/src/integralop.jl b/src/integralop.jl index f4a61320..b57b7e6a 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -1,6 +1,7 @@ abstract type IntegralOperator <: Operator end - +defaultquadstrat(op::IntegralOperator, tfs::RefSpace, bfs::RefSpace) = + DoubleNumSauterQstrat(2,3,5,5,4,3) """ blockassembler(operator, test_space, trial_space) -> assembler diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 5f034de6..07f30eb3 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -42,7 +42,7 @@ struct MWSingleLayer3D{T,U} <: MaxwellOperator3D{T,U} end scalartype(op::MWSingleLayer3D{T,U}) where {T,U} = promote_type(T,U) -sign_upon_permutation(op::MWSingleLayer3D, I, J) = 1 +# sign_upon_permutation(op::MWSingleLayer3D, I, J) = 1 MWSingleLayer3D(gamma) = MWSingleLayer3D(gamma, -gamma, -1/(gamma)) MWWeaklySingular(gamma) = MWSingleLayer3D(gamma, 1, 0) @@ -75,33 +75,20 @@ function _legendre(n,a,b) collect(zip(x,w)) end -defaultquadstrat(op::MaxwellOperator3D, tfs::Space, bfs::Space) = DoubleNumWiltonSauterQStrat(2,3,6,7,5,5,4,3) -defaultquadstrat(op::MaxwellOperator3D, tfs::RefSpace, bfs::RefSpace) = DoubleNumWiltonSauterQStrat(2,3,6,7,5,5,4,3) +# defaultquadstrat(op::MaxwellOperator3D, tfs::Space, bfs::Space) = DoubleNumWiltonSauterQStrat(2,3,6,7,5,5,4,3) +defaultquadstrat(op::MaxwellOperator3D, tfs::RTRefSpace, bfs::RTRefSpace) = DoubleNumWiltonSauterQStrat(2,3,6,7,5,5,4,3) +# defaultquadstrat(op::MaxwellOperator3D, tfs::RefSpace, bfs::RefSpace) = DoubleNumWiltonSauterQStrat(2,3,6,7,5,5,4,3) -function quaddata(op::MaxwellOperator3D, - test_local_space::RefSpace, trial_local_space::RefSpace, - test_charts, trial_charts, qs::DoubleNumWiltonSauterQStrat) - T = coordtype(test_charts[1]) - tqd = quadpoints(test_local_space, test_charts, (qs.outer_rule_far,qs.outer_rule_near)) - bqd = quadpoints(trial_local_space, trial_charts, (qs.inner_rule_far,qs.inner_rule_near)) - - leg = ( - convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_vert,0,1)), - convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_edge,0,1)), - convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_face,0,1)),) - - return (tpoints=tqd, bpoints=bqd, gausslegendre=leg) -end struct MWDoubleLayer3D{T,K} <: MaxwellOperator3D{T,K} alpha::T gamma::K end -sign_upon_permutation(op::MWDoubleLayer3D, I, J) = 1 +# sign_upon_permutation(op::MWDoubleLayer3D, I, J) = 1 struct MWDoubleLayer3DSng{T,K} <: MaxwellOperator3D{T,K} alpha::T @@ -118,139 +105,70 @@ MWDoubleLayer3D(gamma) = MWDoubleLayer3D(1.0, gamma) # For legacy purposes regularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DReg(op.alpha, op.gamma) singularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DSng(op.alpha, op.gamma) -function quadrule(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd, - qs::DoubleNumWiltonSauterQStrat) - - T = eltype(eltype(τ.vertices)) - hits = 0 - dtol = 1.0e3 * eps(T) - dmin2 = floatmax(T) - for t in τ.vertices - for s in σ.vertices - d2 = LinearAlgebra.norm_sqr(t-s) - dmin2 = min(dmin2, d2) - hits += (d2 < dtol) - end - end - - hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) - hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) - hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - - h2 = volume(σ) - xtol2 = 0.2 * 0.2 - k2 = abs2(gamma(op)) - max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( - qd.tpoints[2,i], - DoubleQuadRule( - qd.tpoints[2,i], - qd.bpoints[2,j],),) - return DoubleQuadRule( - qd.tpoints[1,i], - qd.bpoints[1,j],) -end - -function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::BDMRefSpace, i, τ, j, σ, qd, - qs::DoubleNumWiltonSauterQStrat) - - hits = 0 - dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) - dmin2 = floatmax(eltype(eltype(τ.vertices))) - for t in τ.vertices - for s in σ.vertices - d2 = LinearAlgebra.norm_sqr(t-s) - dmin2 = min(dmin2, d2) - hits += (d2 < dtol) - end - end - - hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) - hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) - hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - - h2 = volume(σ) - xtol2 = 0.2 * 0.2 - k2 = abs2(gamma(op)) - return DoubleQuadRule( - qd.tpoints[1,i], - qd.bpoints[1,j],) -end - -function qrib(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd) - dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) - xtol = 0.2 - - k = norm(gamma(op)) - - hits = 0 - xmin = xtol - for t in τ.vertices - for s in σ.vertices - d = norm(t-s) - xmin = min(xmin, k*d) - if d < dtol - hits +=1 - break - end - end - end - - hits == 3 && return BogaertSelfPatchStrategy(5) - hits == 2 && return BogaertEdgePatchStrategy(8, 4) - hits == 1 && return BogaertPointPatchStrategy(2, 3) - rmin = xmin/k - xmin < xtol && return WiltonSERule( - qd.tpoints[1,i], - DoubleQuadRule( - qd.tpoints[2,i], - qd.bpoints[2,j], - ), - ) - return DoubleQuadRule( - qd.tpoints[1,i], - qd.bpoints[1,j], - ) - -end - - -function qrdf(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd) - # defines coincidence of points - dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) - - # decides on whether to use singularity extraction - xtol = 0.2 - - k = norm(gamma(op)) - - hits = 0 - xmin = xtol - for t in τ.vertices - for s in σ.vertices - d = norm(t-s) - xmin = min(xmin, k*d) - if d < dtol - hits +=1 - break - end - end - end - - xmin < xtol && return WiltonSERule( - qd.tpoints[1,i], - DoubleQuadRule( - qd.tpoints[2,i], - qd.bpoints[2,j], - ), - ) - return DoubleQuadRule( - qd.tpoints[1,i], - qd.bpoints[1,j], - ) - -end +# function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::BDMRefSpace, i, τ, j, σ, qd, +# qs::DoubleNumWiltonSauterQStrat) + +# hits = 0 +# dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) +# dmin2 = floatmax(eltype(eltype(τ.vertices))) +# for t in τ.vertices +# for s in σ.vertices +# d2 = LinearAlgebra.norm_sqr(t-s) +# dmin2 = min(dmin2, d2) +# hits += (d2 < dtol) +# end +# end + +# hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) +# hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) +# hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + +# h2 = volume(σ) +# xtol2 = 0.2 * 0.2 +# k2 = abs2(gamma(op)) +# return DoubleQuadRule( +# qd.tpoints[1,i], +# qd.bpoints[1,j],) +# end + + +# function qrdf(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd) +# # defines coincidence of points +# dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) + +# # decides on whether to use singularity extraction +# xtol = 0.2 + +# k = norm(gamma(op)) + +# hits = 0 +# xmin = xtol +# for t in τ.vertices +# for s in σ.vertices +# d = norm(t-s) +# xmin = min(xmin, k*d) +# if d < dtol +# hits +=1 +# break +# end +# end +# end + +# xmin < xtol && return WiltonSERule( +# qd.tpoints[1,i], +# DoubleQuadRule( +# qd.tpoints[2,i], +# qd.bpoints[2,j], +# ), +# ) +# return DoubleQuadRule( +# qd.tpoints[1,i], +# qd.bpoints[1,j], +# ) + +# end ################################################################################ # diff --git a/src/quadrature/double_quadrature.jl b/src/quadrature/doublenumints.jl similarity index 91% rename from src/quadrature/double_quadrature.jl rename to src/quadrature/doublenumints.jl index 67d207f5..1efdf398 100644 --- a/src/quadrature/double_quadrature.jl +++ b/src/quadrature/doublenumints.jl @@ -5,7 +5,7 @@ end """ - regularcellcellinteractions!(biop, tshs, bshs, tcell, bcell, interactions, strat) +momintegrals!(biop, tshs, bshs, tcell, bcell, interactions, strat) Function for the computation of moment integrals using simple double quadrature. """ diff --git a/src/quadrature/doublenumwiltonbogaertqstrat.jl b/src/quadrature/doublenumwiltonbogaertqstrat.jl new file mode 100644 index 00000000..99c97bee --- /dev/null +++ b/src/quadrature/doublenumwiltonbogaertqstrat.jl @@ -0,0 +1,57 @@ +struct DoubleNumWiltonBogaertQStrat{R} + outer_rule_far::R + inner_rule_far::R + outer_rule_near::R + inner_rule_near::R +end + +function quaddata(op::IntegralOperator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_charts, trial_charts, qs::DoubleNumWiltonBogaertQStrat) + + T = coordtype(test_charts[1]) + + tqd = quadpoints(test_local_space, test_charts, (qs.outer_rule_far,qs.outer_rule_near)) + bqd = quadpoints(trial_local_space, trial_charts, (qs.inner_rule_far,qs.inner_rule_near)) + + return (tpoints=tqd, bpoints=bqd) +end + +function quadrule(op::IntegralOperator, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd, + qs::DoubleNumWiltonBogaertQStrat) + + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) + xtol = 0.2 + + k = norm(gamma(op)) + + hits = 0 + xmin = xtol + for t in τ.vertices + for s in σ.vertices + d = norm(t-s) + xmin = min(xmin, k*d) + if d < dtol + hits +=1 + break + end + end + end + + hits == 3 && return BogaertSelfPatchStrategy(5) + hits == 2 && return BogaertEdgePatchStrategy(8, 4) + hits == 1 && return BogaertPointPatchStrategy(2, 3) + rmin = xmin/k + xmin < xtol && return WiltonSERule( + qd.tpoints[1,i], + DoubleQuadRule( + qd.tpoints[2,i], + qd.bpoints[2,j], + ), + ) + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j], + ) + + end \ No newline at end of file diff --git a/src/quadrature/doublenumwiltonsauterqstrat.jl b/src/quadrature/doublenumwiltonsauterqstrat.jl new file mode 100644 index 00000000..ad9dbb6b --- /dev/null +++ b/src/quadrature/doublenumwiltonsauterqstrat.jl @@ -0,0 +1,52 @@ +function quaddata(op::IntegralOperator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_charts, trial_charts, qs::DoubleNumWiltonSauterQStrat) + + T = coordtype(test_charts[1]) + + tqd = quadpoints(test_local_space, test_charts, (qs.outer_rule_far,qs.outer_rule_near)) + bqd = quadpoints(trial_local_space, trial_charts, (qs.inner_rule_far,qs.inner_rule_near)) + + leg = ( + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_vert,0,1)), + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_edge,0,1)), + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_face,0,1)),) + + return (tpoints=tqd, bpoints=bqd, gausslegendre=leg) +end + + +function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, + qs::DoubleNumWiltonSauterQStrat) + + T = eltype(eltype(τ.vertices)) + hits = 0 + dtol = 1.0e3 * eps(T) + dmin2 = floatmax(T) + for t in τ.vertices + for s in σ.vertices + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < dtol) + end + end + + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) + hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) + hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + + h2 = volume(σ) + xtol2 = 0.2 * 0.2 + k2 = abs2(gamma(op)) + if max(dmin2*k2, dmin2/16h2) < xtol2 + return WiltonSERule( + qd.tpoints[2,i], + DoubleQuadRule( + qd.tpoints[2,i], + qd.bpoints[2,j],),) + end + + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j],) +end \ No newline at end of file diff --git a/src/quadrature/singularity_extraction.jl b/src/quadrature/singularityextractionints.jl similarity index 100% rename from src/quadrature/singularity_extraction.jl rename to src/quadrature/singularityextractionints.jl From 49d27472f5ca052edf7becee65ed127d7e4b326d Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 10 Nov 2023 17:09:11 +0100 Subject: [PATCH 293/528] move kernelvals for MW3D to nitsch.jl at call site --- src/helmholtz3d/nitsche.jl | 28 ++++++++++++++++++++++++++++ src/maxwell/mwops.jl | 26 -------------------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/helmholtz3d/nitsche.jl b/src/helmholtz3d/nitsche.jl index dc10fdef..9b65122a 100644 --- a/src/helmholtz3d/nitsche.jl +++ b/src/helmholtz3d/nitsche.jl @@ -27,6 +27,34 @@ function quadrule(op::NitscheHH3, g::LagrangeRefSpace, f::LagrangeRefSpace, i, ) end + +struct KernelValsMaxwell3D{T,U,P,Q} + "gamma = im * wavenumber" + gamma::U + vect::P + dist::T + green::U + gradgreen::Q +end + +const inv_4pi = 1/(4pi) +function kernelvals(biop::MaxwellOperator3D, p, q) + + γ = gamma(biop) + r = cartesian(p) - cartesian(q) + T = eltype(r) + R = norm(r) + γR = γ*R + + inv_R = 1/R + + expn = exp(-γR) + green = expn * inv_R * T(inv_4pi) + gradgreen = -(γ + inv_R) * green * inv_R * r + + KernelValsMaxwell3D(γ, r, R, green, gradgreen) +end + function integrand(op::NitscheHH3, kernel, test_vals, test_point, trial_vals, trial_point) Gxy = kernel.green @assert length(test_point.patch.tangents) == 1 diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 07f30eb3..7b180211 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -7,32 +7,6 @@ scalartype(op::MaxwellOperator3D{T,K}) where {T, K} = promote_type(T, K) gamma(op::MaxwellOperator3D{T,K}) where {T, K <: Nothing} = T(0) gamma(op::MaxwellOperator3D{T,K}) where {T, K} = op.gamma -struct KernelValsMaxwell3D{T,U,P,Q} - "gamma = im * wavenumber" - gamma::U - vect::P - dist::T - green::U - gradgreen::Q -end - -const inv_4pi = 1/(4pi) -function kernelvals(biop::MaxwellOperator3D, p, q) - - γ = gamma(biop) - r = cartesian(p) - cartesian(q) - T = eltype(r) - R = norm(r) - γR = γ*R - - inv_R = 1/R - - expn = exp(-γR) - green = expn * inv_R * T(inv_4pi) - gradgreen = -(γ + inv_R) * green * inv_R * r - - KernelValsMaxwell3D(γ, r, R, green, gradgreen) -end struct MWSingleLayer3D{T,U} <: MaxwellOperator3D{T,U} From af68d786a5efcdac1a4648e0800ecf121ed89491 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 15 Nov 2023 09:43:12 +0100 Subject: [PATCH 294/528] curl of lags depends on extrinsic normal --- src/BEAST.jl | 4 - src/bases/local/laglocal.jl | 73 ++-- src/helmholtz3d/hh3d_sauterschwabqr.jl | 436 ++++++++++++------------ src/helmholtz3d/hh3dops.jl | 238 ++++++------- src/helmholtz3d/nitsche.jl | 2 +- src/helmholtz3d/timedomain/tdhh3dops.jl | 8 +- src/quaddata.jl | 65 ---- src/quadrature/quaddata.jl | 0 src/quadrature/quadrule.jl | 0 src/quadrature/quadstrats.jl | 36 +- test/test_basis.jl | 3 +- test/test_hh3dexc.jl | 1 + test/test_hh3dints.jl | 4 + test/test_nitschehh3d.jl | 2 +- 14 files changed, 424 insertions(+), 448 deletions(-) delete mode 100644 src/quaddata.jl delete mode 100644 src/quadrature/quaddata.jl delete mode 100644 src/quadrature/quadrule.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index f84bfe10..7788d8e6 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -182,10 +182,6 @@ include("identityop.jl") include("integralop.jl") include("dyadicop.jl") include("interpolation.jl") -include("quaddata.jl") - -include("quadrature/quaddata.jl") -include("quadrature/quadrule.jl") include("quadrature/doublenumqstrat.jl") include("quadrature/doublenumsauterqstrat.jl") diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 29bad742..2421d9ae 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -33,10 +33,11 @@ function (f::LagrangeRefSpace{T,1,3})(t) where T j = jacobian(t) p = t.patch + σ = sign(dot(normal(t), cross(p[1]-p[3],p[2]-p[3]))) SVector( - (value=u, curl=(p[3]-p[2])/j), - (value=v, curl=(p[1]-p[3])/j), - (value=w, curl=(p[2]-p[1])/j)) + (value=u, curl=σ*(p[3]-p[2])/j), + (value=v, curl=σ*(p[1]-p[3])/j), + (value=w, curl=σ*(p[2]-p[1])/j)) end @@ -45,17 +46,18 @@ end Compute the values of the shape functions together with their curl. """ -function (f::LagrangeRefSpace{T,1,3})(t, ::Type{Val{:withcurl}}) where T - # Evaluete linear Lagrange elements on a triange, together with their curl - j = jacobian(t) - u,v,w, = barycentric(t) - p = t.patch - SVector( - (value=u, curl=(p[3]-p[2])/j), - (value=v, curl=(p[1]-p[3])/j), - (value=w, curl=(p[2]-p[1])/j) - ) -end +# function (f::LagrangeRefSpace{T,1,3})(t, ::Type{Val{:withcurl}}) where T +# # Evaluete linear Lagrange elements on a triange, together with their curl +# j = jacobian(t) +# u,v,w, = barycentric(t) +# p = t.patch +# σ = sign(dot(normal(t), cross(p[1]-p[3],p[2]-p[3]))) +# SVector( +# (value=u, curl=σ*(p[3]-p[2])/j), +# (value=v, curl=σ*(p[1]-p[3])/j), +# (value=w, curl=σ*(p[2]-p[1])/j) +# ) +# end # Evaluate constant Lagrange elements on a triangle, with their curls @@ -178,30 +180,33 @@ function (f::LagrangeRefSpace{T,2,3})(t) where T # (value=v, curl=(p[1]-p[3])/j), # (value=w, curl=(p[2]-p[1])/j) + σ = sign(dot(normal(t), cross(p[1]-p[3],p[2]-p[3]))) SVector( - (value=u*(2*u-1), curl=(p[3]-p[2])*(4u-1)/j), - (value=v*(2*v-1), curl=(p[1]-p[3])*(4v-1)/j), - (value=w*(2*w-1), curl=(p[2]-p[1])*(4w-1)/j), - (value=4*v*w, curl=4*(w*(p[1]-p[3])+v*(p[2]-p[1]))/j), - (value=4*w*u, curl=4*(w*(p[3]-p[2])+u*(p[2]-p[1]))/j), - (value=4*u*v, curl=4*(u*(p[1]-p[3])+v*(p[3]-p[2]))/j), + (value=u*(2*u-1), curl=σ*(p[3]-p[2])*(4u-1)/j), + (value=v*(2*v-1), curl=σ*(p[1]-p[3])*(4v-1)/j), + (value=w*(2*w-1), curl=σ*(p[2]-p[1])*(4w-1)/j), + (value=4*v*w, curl=4*σ*(w*(p[1]-p[3])+v*(p[2]-p[1]))/j), + (value=4*w*u, curl=4*σ*(w*(p[3]-p[2])+u*(p[2]-p[1]))/j), + (value=4*u*v, curl=4*σ*(u*(p[1]-p[3])+v*(p[3]-p[2]))/j), ) end -function (f::LagrangeRefSpace{T,2,3})(t, ::Type{Val{:withcurl}}) where T - # Evaluete quadratic Lagrange elements on a triange, together with their curl - j = jacobian(t) - u,v,w, = barycentric(t) - p = t.patch - SVector( - (value=u*(2*u-1), curl=(p[3]-p[2])*(4u-1)/j), - (value=v*(2*v-1), curl=(p[1]-p[3])*(4v-1)/j), - (value=w*(2*w-1), curl=(p[2]-p[1])*(4w-1)/j), - (value=4*v*w, curl=4*(w*(p[1]-p[3])+v*(p[2]-p[1]))/j), - (value=4*w*u, curl=4*(w*(p[3]-p[2])+u*(p[2]-p[1]))/j), - (value=4*u*v, curl=4*(u*(p[1]-p[3])+v*(p[3]-p[2]))/j), - ) -end +# function (f::LagrangeRefSpace{T,2,3})(t, ::Type{Val{:withcurl}}) where T +# # Evaluete quadratic Lagrange elements on a triange, together with their curl +# j = jacobian(t) +# u,v,w, = barycentric(t) +# p = t.patch + +# σ = sign(dot(normal(t), cross(p[1]-p[3],p[2]-p[3]))) +# SVector( +# (value=u*(2*u-1), curl=σ*(p[3]-p[2])*(4u-1)/j), +# (value=v*(2*v-1), curl=σ*(p[1]-p[3])*(4v-1)/j), +# (value=w*(2*w-1), curl=σ*(p[2]-p[1])*(4w-1)/j), +# (value=4*v*w, curl=4*σ*(w*(p[1]-p[3])+v*(p[2]-p[1]))/j), +# (value=4*w*u, curl=4*σ*(w*(p[3]-p[2])+u*(p[2]-p[1]))/j), +# (value=4*u*v, curl=4*σ*(u*(p[1]-p[3])+v*(p[3]-p[2]))/j), +# ) +# end function curl(ref::LagrangeRefSpace{T,2,3} where {T}, sh, el) #curl of lagc0d2 as combination of bdm functions diff --git a/src/helmholtz3d/hh3d_sauterschwabqr.jl b/src/helmholtz3d/hh3d_sauterschwabqr.jl index 1b59a052..19e4ce52 100644 --- a/src/helmholtz3d/hh3d_sauterschwabqr.jl +++ b/src/helmholtz3d/hh3d_sauterschwabqr.jl @@ -1,265 +1,265 @@ -function pulled_back_integrand(op::HH3DSingleLayerFDBIO, - test_local_space::LagrangeRefSpace, - trial_local_space::LagrangeRefSpace, - test_chart, trial_chart) +# function pulled_back_integrand(op::HH3DSingleLayerFDBIO, +# test_local_space::LagrangeRefSpace, +# trial_local_space::LagrangeRefSpace, +# test_chart, trial_chart) - (u,v) -> begin +# (u,v) -> begin - x = neighborhood(test_chart,u) - y = neighborhood(trial_chart,v) +# x = neighborhood(test_chart,u) +# y = neighborhood(trial_chart,v) - f = test_local_space(x) - g = trial_local_space(y) +# f = test_local_space(x) +# g = trial_local_space(y) - j = jacobian(x) * jacobian(y) +# j = jacobian(x) * jacobian(y) - α = op.alpha - γ = gamma(op) - R = norm(cartesian(x)-cartesian(y)) - G = exp(-γ*R)/(4*π*R) +# α = op.alpha +# γ = gamma(op) +# R = norm(cartesian(x)-cartesian(y)) +# G = exp(-γ*R)/(4*π*R) - αjG = α*G*j +# αjG = α*G*j - SMatrix{length(f),length(g)}((f[j].value * αjG * g[i].value for i in 1:length(g) for j in 1:length(f) )...) - end -end +# SMatrix{length(f),length(g)}((f[j].value * αjG * g[i].value for i in 1:length(g) for j in 1:length(f) )...) +# end +# end -function pulled_back_integrand(op::HH3DHyperSingularFDBIO, - test_local_space::LagrangeRefSpace, - trial_local_space::LagrangeRefSpace, - test_chart, trial_chart) +# function pulled_back_integrand(op::HH3DHyperSingularFDBIO, +# test_local_space::LagrangeRefSpace, +# trial_local_space::LagrangeRefSpace, +# test_chart, trial_chart) - (u,v) -> begin +# (u,v) -> begin - x = neighborhood(test_chart,u) - y = neighborhood(trial_chart,v) +# x = neighborhood(test_chart,u) +# y = neighborhood(trial_chart,v) - nx = normal(x) - ny = normal(y) +# nx = normal(x) +# ny = normal(y) - f = test_local_space(x) - g = trial_local_space(y) +# f = test_local_space(x) +# g = trial_local_space(y) - j = jacobian(x) * jacobian(y) +# j = jacobian(x) * jacobian(y) - α = op.alpha - β = op.beta - γ = gamma(op) - R = norm(cartesian(x)-cartesian(y)) - G = exp(-γ*R)/(4*π*R) +# α = op.alpha +# β = op.beta +# γ = gamma(op) +# R = norm(cartesian(x)-cartesian(y)) +# G = exp(-γ*R)/(4*π*R) - αjG = ny*α*G*j - βjG = β*G*j +# αjG = ny*α*G*j +# βjG = β*G*j - A = SA[(αjG*g[i].value for i in 1:length(g))...] - B = SA[(βjG*g[i].curl for i in 1:length(g))...] +# A = SA[(αjG*g[i].value for i in 1:length(g))...] +# B = SA[(βjG*g[i].curl for i in 1:length(g))...] - SMatrix{length(f),length(g)}((((dot(nx*f[j].value,A[i])+dot(f[j].curl,B[i])) for i in 1:length(g) for j in 1:length(f))...)) - end -end +# SMatrix{length(f),length(g)}((((dot(nx*f[j].value,A[i])+dot(f[j].curl,B[i])) for i in 1:length(g) for j in 1:length(f))...)) +# end +# end -function pulled_back_integrand(op::HH3DDoubleLayerFDBIO, - test_local_space::LagrangeRefSpace, - trial_local_space::LagrangeRefSpace, - test_chart, trial_chart) +# function pulled_back_integrand(op::HH3DDoubleLayerFDBIO, +# test_local_space::LagrangeRefSpace, +# trial_local_space::LagrangeRefSpace, +# test_chart, trial_chart) - (u,v) -> begin +# (u,v) -> begin - x = neighborhood(test_chart,u) - y = neighborhood(trial_chart,v) +# x = neighborhood(test_chart,u) +# y = neighborhood(trial_chart,v) - ny = normal(y) - f = test_local_space(x) - g = trial_local_space(y) +# ny = normal(y) +# f = test_local_space(x) +# g = trial_local_space(y) - j = jacobian(x) * jacobian(y) +# j = jacobian(x) * jacobian(y) - α = op.alpha - γ = gamma(op) +# α = op.alpha +# γ = gamma(op) - r = cartesian(x) - cartesian(y) - R = norm(r) - G = exp(-γ*R)/(4*π*R) - inv_R = 1/R - ∇G = -(γ + inv_R) * G * inv_R * r - αnyj∇G = dot(ny,-α*∇G*j) +# r = cartesian(x) - cartesian(y) +# R = norm(r) +# G = exp(-γ*R)/(4*π*R) +# inv_R = 1/R +# ∇G = -(γ + inv_R) * G * inv_R * r +# αnyj∇G = dot(ny,-α*∇G*j) - SMatrix{length(f),length(g)}((f[j].value * αnyj∇G * g[i].value for i in 1:length(g) for j in 1:length(f))...) - end -end +# SMatrix{length(f),length(g)}((f[j].value * αnyj∇G * g[i].value for i in 1:length(g) for j in 1:length(f))...) +# end +# end -function pulled_back_integrand(op::HH3DDoubleLayerTransposedFDBIO, - test_local_space::LagrangeRefSpace, - trial_local_space::LagrangeRefSpace, - test_chart, trial_chart) +# function pulled_back_integrand(op::HH3DDoubleLayerTransposedFDBIO, +# test_local_space::LagrangeRefSpace, +# trial_local_space::LagrangeRefSpace, +# test_chart, trial_chart) - (u,v) -> begin +# (u,v) -> begin - x = neighborhood(test_chart,u) - y = neighborhood(trial_chart,v) +# x = neighborhood(test_chart,u) +# y = neighborhood(trial_chart,v) - nx = normal(x) - f = test_local_space(x) - g = trial_local_space(y) +# nx = normal(x) +# f = test_local_space(x) +# g = trial_local_space(y) - j = jacobian(x) * jacobian(y) +# j = jacobian(x) * jacobian(y) - α = op.alpha - γ = gamma(op) +# α = op.alpha +# γ = gamma(op) - r = cartesian(x) - cartesian(y) - R = norm(r) - G = exp(-γ*R)/(4*π*R) - inv_R = 1/R - ∇G = -(γ + inv_R) * G * inv_R * r - αnxj∇G = dot(nx,α*∇G*j) +# r = cartesian(x) - cartesian(y) +# R = norm(r) +# G = exp(-γ*R)/(4*π*R) +# inv_R = 1/R +# ∇G = -(γ + inv_R) * G * inv_R * r +# αnxj∇G = dot(nx,α*∇G*j) - SMatrix{length(f),length(g)}((f[j].value * αnxj∇G * g[i].value for i in 1:length(g) for j in 1:length(f))...) - end -end +# SMatrix{length(f),length(g)}((f[j].value * αnxj∇G * g[i].value for i in 1:length(g) for j in 1:length(f))...) +# end +# end -function momintegrals!(op::Helmholtz3DOp, - test_local_space::LagrangeRefSpace{<:Any,0}, trial_local_space::LagrangeRefSpace{<:Any,0}, - test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) +# function momintegrals!(op::Helmholtz3DOp, +# test_local_space::LagrangeRefSpace{<:Any,0}, trial_local_space::LagrangeRefSpace{<:Any,0}, +# test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) - I, J, K, L = SauterSchwabQuadrature.reorder( - test_triangular_element.vertices, - trial_triangular_element.vertices, strat) +# I, J, K, L = SauterSchwabQuadrature.reorder( +# test_triangular_element.vertices, +# trial_triangular_element.vertices, strat) - test_triangular_element = simplex( - test_triangular_element.vertices[I[1]], - test_triangular_element.vertices[I[2]], - test_triangular_element.vertices[I[3]]) +# test_triangular_element = simplex( +# test_triangular_element.vertices[I[1]], +# test_triangular_element.vertices[I[2]], +# test_triangular_element.vertices[I[3]]) - trial_triangular_element = simplex( - trial_triangular_element.vertices[J[1]], - trial_triangular_element.vertices[J[2]], - trial_triangular_element.vertices[J[3]]) +# trial_triangular_element = simplex( +# trial_triangular_element.vertices[J[1]], +# trial_triangular_element.vertices[J[2]], +# trial_triangular_element.vertices[J[3]]) - test_sign = Combinatorics.levicivita(I) - trial_sign = Combinatorics.levicivita(J) - σ = momintegrals_sign(op, test_sign, trial_sign) +# test_sign = Combinatorics.levicivita(I) +# trial_sign = Combinatorics.levicivita(J) +# σ = momintegrals_sign(op, test_sign, trial_sign) - igd = pulled_back_integrand(op, test_local_space, trial_local_space, - test_triangular_element, trial_triangular_element) - G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) - out[1,1] += G[1,1] * σ +# igd = pulled_back_integrand(op, test_local_space, trial_local_space, +# test_triangular_element, trial_triangular_element) +# G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) +# out[1,1] += G[1,1] * σ - nothing -end +# nothing +# end -function momintegrals!(op::Helmholtz3DOp, - test_local_space::LagrangeRefSpace, trial_local_space::LagrangeRefSpace, - test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) +# function momintegrals!(op::Helmholtz3DOp, +# test_local_space::LagrangeRefSpace, trial_local_space::LagrangeRefSpace, +# test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) - I, J, K, L = SauterSchwabQuadrature.reorder( - test_triangular_element.vertices, - trial_triangular_element.vertices, strat) +# I, J, K, L = SauterSchwabQuadrature.reorder( +# test_triangular_element.vertices, +# trial_triangular_element.vertices, strat) - test_triangular_element = simplex( - test_triangular_element.vertices[I[1]], - test_triangular_element.vertices[I[2]], - test_triangular_element.vertices[I[3]]) +# test_triangular_element = simplex( +# test_triangular_element.vertices[I[1]], +# test_triangular_element.vertices[I[2]], +# test_triangular_element.vertices[I[3]]) - trial_triangular_element = simplex( - trial_triangular_element.vertices[J[1]], - trial_triangular_element.vertices[J[2]], - trial_triangular_element.vertices[J[3]]) - - test_sign = Combinatorics.levicivita(I) - trial_sign = Combinatorics.levicivita(J) - - σ = momintegrals_sign(op, test_sign, trial_sign) +# trial_triangular_element = simplex( +# trial_triangular_element.vertices[J[1]], +# trial_triangular_element.vertices[J[2]], +# trial_triangular_element.vertices[J[3]]) + +# test_sign = Combinatorics.levicivita(I) +# trial_sign = Combinatorics.levicivita(J) + +# σ = momintegrals_sign(op, test_sign, trial_sign) - igd = pulled_back_integrand(op, test_local_space, trial_local_space, - test_triangular_element, trial_triangular_element) - G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) - for j ∈ 1:3, i ∈ 1:3 - out[i,j] += G[K[i],L[j]] * σ - end - - nothing -end - -function momintegrals!(op::Helmholtz3DOp, - test_local_space::LagrangeRefSpace{<:Any,0}, trial_local_space::LagrangeRefSpace{<:Any,1}, - test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) - - I, J, K, L = SauterSchwabQuadrature.reorder( - test_triangular_element.vertices, - trial_triangular_element.vertices, strat) - - test_triangular_element = simplex( - test_triangular_element.vertices[I[1]], - test_triangular_element.vertices[I[2]], - test_triangular_element.vertices[I[3]]) - - trial_triangular_element = simplex( - trial_triangular_element.vertices[J[1]], - trial_triangular_element.vertices[J[2]], - trial_triangular_element.vertices[J[3]]) - - test_sign = Combinatorics.levicivita(I) - trial_sign = Combinatorics.levicivita(J) - - σ = momintegrals_sign(op, test_sign, trial_sign) - - igd = pulled_back_integrand(op, test_local_space, trial_local_space, - test_triangular_element, trial_triangular_element) - G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) - - for i ∈ 1:3 - out[1,i] += G[L[i]] * σ - end - - nothing -end - -function momintegrals!(op::Helmholtz3DOp, - test_local_space::LagrangeRefSpace{<:Any,1}, trial_local_space::LagrangeRefSpace{<:Any,0}, - test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) - - I, J, K, L = SauterSchwabQuadrature.reorder( - test_triangular_element.vertices, - trial_triangular_element.vertices, strat) - - test_triangular_element = simplex( - test_triangular_element.vertices[I[1]], - test_triangular_element.vertices[I[2]], - test_triangular_element.vertices[I[3]]) - - trial_triangular_element = simplex( - trial_triangular_element.vertices[J[1]], - trial_triangular_element.vertices[J[2]], - trial_triangular_element.vertices[J[3]]) - - test_sign = Combinatorics.levicivita(I) - trial_sign = Combinatorics.levicivita(J) - - σ = momintegrals_sign(op, test_sign, trial_sign) - - igd = pulled_back_integrand(op, test_local_space, trial_local_space, - test_triangular_element, trial_triangular_element) - G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) - - for i ∈ 1:3 - out[i,1] += G[K[i]] * σ - end - - nothing -end - -function momintegrals_sign(op::HH3DSingleLayerFDBIO, test_sign, trial_sign) - return 1 -end -function momintegrals_sign(op::HH3DDoubleLayerFDBIO, test_sign, trial_sign) - return trial_sign -end -function momintegrals_sign(op::HH3DDoubleLayerTransposedFDBIO, test_sign, trial_sign) - return test_sign -end -function momintegrals_sign(op::HH3DHyperSingularFDBIO, test_sign, trial_sign) - return test_sign * trial_sign -end +# igd = pulled_back_integrand(op, test_local_space, trial_local_space, +# test_triangular_element, trial_triangular_element) +# G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) +# for j ∈ 1:3, i ∈ 1:3 +# out[i,j] += G[K[i],L[j]] * σ +# end + +# nothing +# end + +# function momintegrals!(op::Helmholtz3DOp, +# test_local_space::LagrangeRefSpace{<:Any,0}, trial_local_space::LagrangeRefSpace{<:Any,1}, +# test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) + +# I, J, K, L = SauterSchwabQuadrature.reorder( +# test_triangular_element.vertices, +# trial_triangular_element.vertices, strat) + +# test_triangular_element = simplex( +# test_triangular_element.vertices[I[1]], +# test_triangular_element.vertices[I[2]], +# test_triangular_element.vertices[I[3]]) + +# trial_triangular_element = simplex( +# trial_triangular_element.vertices[J[1]], +# trial_triangular_element.vertices[J[2]], +# trial_triangular_element.vertices[J[3]]) + +# test_sign = Combinatorics.levicivita(I) +# trial_sign = Combinatorics.levicivita(J) + +# σ = momintegrals_sign(op, test_sign, trial_sign) + +# igd = pulled_back_integrand(op, test_local_space, trial_local_space, +# test_triangular_element, trial_triangular_element) +# G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) + +# for i ∈ 1:3 +# out[1,i] += G[L[i]] * σ +# end + +# nothing +# end + +# function momintegrals!(op::Helmholtz3DOp, +# test_local_space::LagrangeRefSpace{<:Any,1}, trial_local_space::LagrangeRefSpace{<:Any,0}, +# test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) + +# I, J, K, L = SauterSchwabQuadrature.reorder( +# test_triangular_element.vertices, +# trial_triangular_element.vertices, strat) + +# test_triangular_element = simplex( +# test_triangular_element.vertices[I[1]], +# test_triangular_element.vertices[I[2]], +# test_triangular_element.vertices[I[3]]) + +# trial_triangular_element = simplex( +# trial_triangular_element.vertices[J[1]], +# trial_triangular_element.vertices[J[2]], +# trial_triangular_element.vertices[J[3]]) + +# test_sign = Combinatorics.levicivita(I) +# trial_sign = Combinatorics.levicivita(J) + +# σ = momintegrals_sign(op, test_sign, trial_sign) + +# igd = pulled_back_integrand(op, test_local_space, trial_local_space, +# test_triangular_element, trial_triangular_element) +# G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) + +# for i ∈ 1:3 +# out[i,1] += G[K[i]] * σ +# end + +# nothing +# end + +# function momintegrals_sign(op::HH3DSingleLayerFDBIO, test_sign, trial_sign) +# return 1 +# end +# function momintegrals_sign(op::HH3DDoubleLayerFDBIO, test_sign, trial_sign) +# return trial_sign +# end +# function momintegrals_sign(op::HH3DDoubleLayerTransposedFDBIO, test_sign, trial_sign) +# return test_sign +# end +# function momintegrals_sign(op::HH3DHyperSingularFDBIO, test_sign, trial_sign) +# return test_sign * trial_sign +# end diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index 63ee47c7..4fcf1a45 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -59,9 +59,9 @@ struct HH3DSingleLayerSng{T,K} <: Helmholtz3DOp{T,K} gamma::K end -function sign_upon_permutation(op::HH3DSingleLayerFDBIO, I, J) - return 1 -end +# function sign_upon_permutation(op::HH3DSingleLayerFDBIO, I, J) +# return 1 +# end struct HH3DDoubleLayerFDBIO{T,K} <: Helmholtz3DOp{T,K} alpha::T @@ -78,9 +78,9 @@ struct HH3DDoubleLayerSng{T,K} <: Helmholtz3DOp{T,K} gamma::K end -function sign_upon_permutation(op::HH3DDoubleLayerFDBIO, I, J) - return Combinatorics.levicivita(J) -end +# function sign_upon_permutation(op::HH3DDoubleLayerFDBIO, I, J) +# return Combinatorics.levicivita(J) +# end struct HH3DDoubleLayerTransposedFDBIO{T,K} <: Helmholtz3DOp{T,K} alpha::T gamma::K @@ -96,36 +96,36 @@ struct HH3DDoubleLayerTransposedSng{T,K} <: Helmholtz3DOp{T,K} gamma::K end -function sign_upon_permutation(op::HH3DDoubleLayerTransposedFDBIO, I, J) - return Combinatorics.levicivita(I) -end +# function sign_upon_permutation(op::HH3DDoubleLayerTransposedFDBIO, I, J) +# return Combinatorics.levicivita(I) +# end defaultquadstrat(::Helmholtz3DOp, ::LagrangeRefSpace, ::LagrangeRefSpace) = DoubleNumWiltonSauterQStrat(2,3,2,3,4,4,4,4) -function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, - trial_refspace::LagrangeRefSpace, test_elements, trial_elements, - qs::DoubleNumWiltonSauterQStrat) +# function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, +# trial_refspace::LagrangeRefSpace, test_elements, trial_elements, +# qs::DoubleNumWiltonSauterQStrat) - test_eval(x) = test_refspace(x, Val{:withcurl}) - trial_eval(x) = trial_refspace(x, Val{:withcurl}) +# test_eval(x) = test_refspace(x, Val{:withcurl}) +# trial_eval(x) = trial_refspace(x, Val{:withcurl}) - # The combinations of rules (6,7) and (5,7 are) BAAAADDDD - # they result in many near singularity evaluations with any - # resemblence of accuracy going down the drain! Simply don't! - # (same for (5,7) btw...). - # test_qp = quadpoints(test_eval, test_elements, (6,)) - # bssi_qp = quadpoints(trial_eval, trial_elements, (7,)) - - test_qp = quadpoints(test_eval, test_elements, (qs.outer_rule_far,)) - bsis_qp = quadpoints(trial_eval, trial_elements, (qs.inner_rule_far,)) - - gausslegendre = ( - _legendre(qs.sauter_schwab_common_vert,0,1), - _legendre(qs.sauter_schwab_common_edge,0,1), - _legendre(qs.sauter_schwab_common_face,0,1),) - - return (;test_qp, bsis_qp, gausslegendre) -end +# # The combinations of rules (6,7) and (5,7 are) BAAAADDDD +# # they result in many near singularity evaluations with any +# # resemblence of accuracy going down the drain! Simply don't! +# # (same for (5,7) btw...). +# # test_qp = quadpoints(test_eval, test_elements, (6,)) +# # bssi_qp = quadpoints(trial_eval, trial_elements, (7,)) + +# test_qp = quadpoints(test_eval, test_elements, (qs.outer_rule_far,)) +# bsis_qp = quadpoints(trial_eval, trial_elements, (qs.inner_rule_far,)) + +# gausslegendre = ( +# _legendre(qs.sauter_schwab_common_vert,0,1), +# _legendre(qs.sauter_schwab_common_edge,0,1), +# _legendre(qs.sauter_schwab_common_face,0,1),) + +# return (;test_qp, bsis_qp, gausslegendre) +# end # function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, # trial_refspace::LagrangeRefSpace, test_elements, trial_elements, @@ -155,51 +155,51 @@ end defaultquadstrat(::Helmholtz3DOp, ::subReferenceSpace, ::subReferenceSpace) = DoubleNumWiltonSauterQStrat(4,7,4,7,4,4,4,4) -function quaddata(op::Helmholtz3DOp, test_refspace::subReferenceSpace, - trial_refspace::subReferenceSpace, test_elements, trial_elements, - qs::DoubleNumWiltonSauterQStrat) - - test_qp = quadpoints(test_refspace, test_elements, (qs.outer_rule_far,)) - bsis_qp = quadpoints(trial_refspace, trial_elements, (qs.inner_rule_far,)) - - return test_qp, bsis_qp -end - -function quadrule(op::Helmholtz3DOp, - test_refspace::LagrangeRefSpace, - trial_refspace::LagrangeRefSpace, - i, test_element, j, trial_element, qd, - qs::DoubleNumWiltonSauterQStrat) - - tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 - dmin2 = floatmax(eltype(eltype(test_element.vertices))) +# function quaddata(op::Helmholtz3DOp, test_refspace::subReferenceSpace, +# trial_refspace::subReferenceSpace, test_elements, trial_elements, +# qs::DoubleNumWiltonSauterQStrat) - for t in test_element.vertices - for s in trial_element.vertices - d2 = LinearAlgebra.norm_sqr(t-s) - dmin2 = min(dmin2, d2) - hits += (d2 < tol) - end end +# test_qp = quadpoints(test_refspace, test_elements, (qs.outer_rule_far,)) +# bsis_qp = quadpoints(trial_refspace, trial_elements, (qs.inner_rule_far,)) - hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) - hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) - hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) +# return test_qp, bsis_qp +# end - test_quadpoints = qd.test_qp - trial_quadpoints = qd.bsis_qp - h2 = volume(trial_element) - xtol2 = 0.2 * 0.2 - k2 = abs2(gamma(op)) - max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( - test_quadpoints[1,i], - DoubleQuadRule( - test_quadpoints[1,i], - trial_quadpoints[1,j])) +# function quadrule(op::Helmholtz3DOp, +# test_refspace::LagrangeRefSpace, +# trial_refspace::LagrangeRefSpace, +# i, test_element, j, trial_element, qd, +# qs::DoubleNumWiltonSauterQStrat) + +# tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 +# dmin2 = floatmax(eltype(eltype(test_element.vertices))) + +# for t in test_element.vertices +# for s in trial_element.vertices +# d2 = LinearAlgebra.norm_sqr(t-s) +# dmin2 = min(dmin2, d2) +# hits += (d2 < tol) +# end end + +# hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) +# hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) +# hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + +# test_quadpoints = qd.test_qp +# trial_quadpoints = qd.bsis_qp +# h2 = volume(trial_element) +# xtol2 = 0.2 * 0.2 +# k2 = abs2(gamma(op)) +# max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( +# test_quadpoints[1,i], +# DoubleQuadRule( +# test_quadpoints[1,i], +# trial_quadpoints[1,j])) - return DoubleQuadRule( - qd.test_qp[1,i], - qd.bsis_qp[1,j]) -end +# return DoubleQuadRule( +# qd.test_qp[1,i], +# qd.bsis_qp[1,j]) +# end # function quadrule(op::HH3DSingleLayerFDBIO, @@ -279,47 +279,47 @@ end =# # qd.bsis_qp[1,j]) # end -function quadrule(op::HH3DSingleLayerFDBIO, test_refspace::subReferenceSpace, - trial_refspace::subReferenceSpace, i, test_element, j, trial_element, quadrature_data, - qs::DoubleNumWiltonSauterQStrat) +# function quadrule(op::HH3DSingleLayerFDBIO, test_refspace::subReferenceSpace, +# trial_refspace::subReferenceSpace, i, test_element, j, trial_element, quadrature_data, +# qs::DoubleNumWiltonSauterQStrat) - # tol, hits = 1e-10, 0 - # for t in getelementVertices(test_element) - # for s in getelementVertices(trial_element) - # norm(t-s) < tol && (hits +=1; break) - # end end - # - # test_quadpoints = quadrature_data[1] - # trial_quadpoints = quadrature_data[2] - - # hits != 0 && return WiltonSERule( - # test_quadpoints[1,i], - # DoubleQuadRule( - # test_quadpoints[1,i], - # trial_quadpoints[1,j])) +# # tol, hits = 1e-10, 0 +# # for t in getelementVertices(test_element) +# # for s in getelementVertices(trial_element) +# # norm(t-s) < tol && (hits +=1; break) +# # end end +# # +# # test_quadpoints = quadrature_data[1] +# # trial_quadpoints = quadrature_data[2] - return DoubleQuadRule( - quadrature_data[1][1,i], - quadrature_data[2][1,j]) - # return SauterSchwabStrategy(hits) - # test_qd = qd[1] - # trial_qd = qd[2] - # - # DoubleQuadRule( - # test_qd[1,i], # rule 1 on test element j - # trial_qd[1,j] # rule 1 on trial element i - # ) -end +# # hits != 0 && return WiltonSERule( +# # test_quadpoints[1,i], +# # DoubleQuadRule( +# # test_quadpoints[1,i], +# # trial_quadpoints[1,j])) -function quadrule(op::Helmholtz3DOp, - test_refspace::RefSpace, trial_refspace::RefSpace, - i, test_element, j, trial_element, quadrature_data, - qs::DoubleNumWiltonSauterQStrat) +# return DoubleQuadRule( +# quadrature_data[1][1,i], +# quadrature_data[2][1,j]) +# # return SauterSchwabStrategy(hits) +# # test_qd = qd[1] +# # trial_qd = qd[2] +# # +# # DoubleQuadRule( +# # test_qd[1,i], # rule 1 on test element j +# # trial_qd[1,j] # rule 1 on trial element i +# # ) +# end - return DoubleQuadRule( - quadrature_data[1][1,i], - quadrature_data[2][1,j]) -end +# function quadrule(op::Helmholtz3DOp, +# test_refspace::RefSpace, trial_refspace::RefSpace, +# i, test_element, j, trial_element, quadrature_data, +# qs::DoubleNumWiltonSauterQStrat) + +# return DoubleQuadRule( +# quadrature_data[1][1,i], +# quadrature_data[2][1,j]) +# end #= function quadrule(op::HH3DDoubleLayerTransposedFDBIO, test_refspace::LagrangeRefSpace{T,1} where T, @@ -409,18 +409,18 @@ end =# # quadrature_data[2][1,j]) # end -function quadrule(op::Helmholtz3DOp, - test_refspace::RTRefSpace, trial_refspace::RTRefSpace, - i, test_element, j, trial_element, quadrature_data, - qs::DoubleNumWiltonSauterQStrat) +# function quadrule(op::Helmholtz3DOp, +# test_refspace::RTRefSpace, trial_refspace::RTRefSpace, +# i, test_element, j, trial_element, quadrature_data, +# qs::DoubleNumWiltonSauterQStrat) - test_quadpoints = quadrature_data[1] - trial_quadpoints = quadrature_data[2] +# test_quadpoints = quadrature_data[1] +# trial_quadpoints = quadrature_data[2] - return DoubleQuadRule( - quadrature_data[1][1,i], - quadrature_data[2][1,j]) -end +# return DoubleQuadRule( +# quadrature_data[1][1,i], +# quadrature_data[2][1,j]) +# end function (igd::Integrand{<:HH3DHyperSingularFDBIO})(x,y,f,g) α = igd.operator.alpha diff --git a/src/helmholtz3d/nitsche.jl b/src/helmholtz3d/nitsche.jl index 9b65122a..285e8c89 100644 --- a/src/helmholtz3d/nitsche.jl +++ b/src/helmholtz3d/nitsche.jl @@ -12,7 +12,7 @@ function quaddata(operator::NitscheHH3, testelements, trialelements, qs::DoubleNumWiltonSauterQStrat) tqd = quadpoints(localtestbasis, testelements, (qs.outer_rule_far,)) - bqd = quadpoints(x -> localtrialbasis(x, Val{:withcurl}), trialelements, (qs.inner_rule_far,)) + bqd = quadpoints(x -> localtrialbasis(x), trialelements, (qs.inner_rule_far,)) #return QuadData(tqd, bqd) return (tpoints=tqd, bpoints=bqd) diff --git a/src/helmholtz3d/timedomain/tdhh3dops.jl b/src/helmholtz3d/timedomain/tdhh3dops.jl index 17cf8e0a..afec459a 100644 --- a/src/helmholtz3d/timedomain/tdhh3dops.jl +++ b/src/helmholtz3d/timedomain/tdhh3dops.jl @@ -150,8 +150,8 @@ function innerintegrals!(zlocal, operator::HH3DHyperSingularTDBIO, (isodd(d) ? -1 : 1) * ((1 - h*dot(m,bξ)) * ∫G[d+2] - h*dot(m, ∫Gξy[d+2])) end - test_values = test_local_space(test_point, Val{:withcurl}) - trial_values = trial_local_space(center(trial_element), Val{:withcurl}) + test_values = test_local_space(test_point) + trial_values = trial_local_space(center(trial_element)) # weakly singular term α = dx / (4π) * operator.weight_of_weakly_singular_term @@ -222,8 +222,8 @@ function innerintegrals!(zlocal, operator::HH3DDoubleLayerTDBIO, return σ * ∇G[d+1] end - test_values = test_local_space(test_point, Val{:withcurl}) - trial_values = trial_local_space(center(trial_element), Val{:withcurl}) + test_values = test_local_space(test_point) + trial_values = trial_local_space(center(trial_element)) @assert all(getindex.(trial_values,1) .≈ [1]) # @assert all(getindex.(trial_values,2) .≈ Ref([0,0,0])) diff --git a/src/quaddata.jl b/src/quaddata.jl deleted file mode 100644 index 5432a322..00000000 --- a/src/quaddata.jl +++ /dev/null @@ -1,65 +0,0 @@ - - -""" - quaddata(operator, test_refspace, trial_refspace, test_elements, trial_elements) - -Returns an object cashing data required for the computation of boundary element -interactions. It is up to the client programmer to decide what (if any) data is -cached. For double numberical quadrature, storing the integration points for -example can significantly speed up matrix assembly. - -- `operator` is an integration kernel. -- `test_refspace` and `trial_refspace` are reference space objects. `quadata` -is typically overloaded on the type of these local spaces of shape functions. -(See the implementation in `maxwell.jl` for an example). -- `test_elements` and `trial_elements` are iterable collections of the geometric -elements on which the finite element space are defined. These are provided to -allow computation of the actual integrations points - as opposed to only their -coordinates. -""" -function quaddata end - - -""" - quadrule(operator, test_refspace, trial_refspace, test_index, test_chart, trial_index, trial_chart, quad_data) - -Based on the operator kernel and the test and trial elements, this function builds -an object whose type and data fields specify the quadrature rule that needs to be -used to accurately compute the interaction integrals. The `quad_data` object created -by `quaddata` is passed to allow reuse of any precomputed data such as quadrature -points and weights, geometric quantities, etc. - -The type of the returned quadrature rule will help in deciding which method of -`momintegrals` to dispatch to. -""" -# function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd) -# # defines coincidence of points -# dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) - -# # decides on whether to use singularity extraction -# xtol = 0.2 -# k = norm(op.gamma) - -# hits = 0 -# xmin = xtol -# for t in τ.vertices -# for s in σ.vertices -# d = norm(t-s) -# xmin = min(xmin, k*d) -# if d < dtol -# hits +=1 -# break -# end -# end -# end - -# hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) -# hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) -# hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) -# xmin < xtol && return DoubleQuadRule( -# qd.tpoints[2,i], -# qd.bpoints[2,j],) -# return DoubleQuadRule( -# qd.tpoints[1,i], -# qd.bpoints[1,j],) -# end diff --git a/src/quadrature/quaddata.jl b/src/quadrature/quaddata.jl deleted file mode 100644 index e69de29b..00000000 diff --git a/src/quadrature/quadrule.jl b/src/quadrature/quadrule.jl deleted file mode 100644 index e69de29b..00000000 diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index 40d4bddb..3c801ce5 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -76,4 +76,38 @@ function quadinfo(op, tfs, bfs; quadstrat=defaultquadstrat(op, tfs, bfs)) println(@which quadrule(op,tref,bref,i,τ,j,σ,qd,quadstrat)) nothing -end \ No newline at end of file +end + +""" + quaddata(operator, test_refspace, trial_refspace, test_elements, trial_elements) + +Returns an object cashing data required for the computation of boundary element +interactions. It is up to the client programmer to decide what (if any) data is +cached. For double numberical quadrature, storing the integration points for +example can significantly speed up matrix assembly. + +- `operator` is an integration kernel. +- `test_refspace` and `trial_refspace` are reference space objects. `quadata` +is typically overloaded on the type of these local spaces of shape functions. +(See the implementation in `maxwell.jl` for an example). +- `test_elements` and `trial_elements` are iterable collections of the geometric +elements on which the finite element space are defined. These are provided to +allow computation of the actual integrations points - as opposed to only their +coordinates. +""" +function quaddata end + + +""" + quadrule(operator, test_refspace, trial_refspace, test_index, test_chart, trial_index, trial_chart, quad_data) + +Based on the operator kernel and the test and trial elements, this function builds +an object whose type and data fields specify the quadrature rule that needs to be +used to accurately compute the interaction integrals. The `quad_data` object created +by `quaddata` is passed to allow reuse of any precomputed data such as quadrature +points and weights, geometric quantities, etc. + +The type of the returned quadrature rule will help in deciding which method of +`momintegrals` to dispatch to. +""" +function quadrule end \ No newline at end of file diff --git a/test/test_basis.jl b/test/test_basis.jl index 672ed1ed..1bc38da7 100644 --- a/test/test_basis.jl +++ b/test/test_basis.jl @@ -51,7 +51,8 @@ for T in [Float32, Float64] sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in"),T=T) s = chart(sphere, first(sphere)) t = neighborhood(s, T.([1,1]/3)) - v = f(t, Val{:withcurl}) + # v = f(t, Val{:withcurl}) + v = f(t) A = volume(s) @test v[1][2] == (s[3]-s[2])/2A diff --git a/test/test_hh3dexc.jl b/test/test_hh3dexc.jl index aa9e8c03..cb11981c 100644 --- a/test/test_hh3dexc.jl +++ b/test/test_hh3dexc.jl @@ -43,6 +43,7 @@ for T in [Float32, Float64] numfunctions(X) + BEAST.quadinfo(N, X, X) Nxx = assemble(N, X, X) @test size(Nxx) == (numfunctions(X), numfunctions(X)) diff --git a/test/test_hh3dints.jl b/test/test_hh3dints.jl index 883085fc..008ad7c8 100644 --- a/test/test_hh3dints.jl +++ b/test/test_hh3dints.jl @@ -259,6 +259,10 @@ end BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_cv_ss, SS_strategy) BEAST.momintegrals!(op4, lag1, lag1, t1, t2, z_cv_double, Double_strategy) + # @show z_cv_double + # @show z_cv_se + # @show z_cv_ss + # @show 2 * norm(z_cv_double + z_cv_ss) / norm(z_cv_double - z_cv_ss) @test z_cv_double ≈ z_cv_ss rtol = 1e-4 @test z_cv_se ≈ z_cv_ss rtol=1e-4 @test_broken norm(z_cv_ss-z_cv_se) < norm(z_cv_ss-z_cv_double) diff --git a/test/test_nitschehh3d.jl b/test/test_nitschehh3d.jl index 4f1fd210..498eed1b 100644 --- a/test/test_nitschehh3d.jl +++ b/test/test_nitschehh3d.jl @@ -19,7 +19,7 @@ X = lagrangec0d1(m, boundary(m)) x = refspace(X) s = chart(m, X.fns[1][1].cellid) c = neighborhood(s, [1,1]/3) # get the barycenter of that patch -v = x(c, Val{:withcurl}) # evaluate the Lagrange elements in c, together with their curls +v = x(c) # evaluate the Lagrange elements in c, together with their curls @test (s[3] - s[2]) / (2 * volume(s)) ≈ v[1][2] @test (s[1] - s[3]) / (2 * volume(s)) ≈ v[2][2] From fda2d5bf5bff7982a77d93eeae7e38fc5243f271 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 15 Nov 2023 11:03:16 +0100 Subject: [PATCH 295/528] Tighten compat for CSM and SSQ --- Project.toml | 4 ++-- src/quadrature/singularityextractionints.jl | 15 ++++++--------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/Project.toml b/Project.toml index 387e9053..37945d20 100644 --- a/Project.toml +++ b/Project.toml @@ -35,7 +35,7 @@ AbstractTrees = "0.4.4" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" CollisionDetection = "0.1.5" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.5.1" +CompScienceMeshes = "0.6.0" Compat = "2, 3, 4" ConvolutionOperators = "0.4" FFTW = "0.2.3, 1" @@ -47,7 +47,7 @@ LinearMaps = "3.7 - 3.9" NestedUnitRanges = "0.2" Requires = "1" SauterSchwab3D = "0.1" -SauterSchwabQuadrature = "2.2.0" +SauterSchwabQuadrature = "2.3.0" SparseMatrixDicts = "0.2" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" diff --git a/src/quadrature/singularityextractionints.jl b/src/quadrature/singularityextractionints.jl index 872f0293..10c4685f 100644 --- a/src/quadrature/singularityextractionints.jl +++ b/src/quadrature/singularityextractionints.jl @@ -1,23 +1,20 @@ abstract type SingularityExtractionRule end regularpart_quadrule(qr::SingularityExtractionRule) = qr.regularpart_quadrule -function momintegrals!(op, g, f, t, s, z, strat::SingularityExtractionRule) +function momintegrals!(op, g, f, t, s, z, qrule::SingularityExtractionRule) - - womps = strat.outer_quad_points + womps = qrule.outer_quad_points sop = singularpart(op) rop = regularpart(op) - # compute the regular part - rstrat = regularpart_quadrule(strat) - momintegrals!(rop, g, f, t, s, z, rstrat) + regqrule = regularpart_quadrule(qrule) + momintegrals!(rop, g, f, t, s, z, regqrule) for p in 1 : length(womps) x = womps[p].point dx = womps[p].weight - innerintegrals!(sop, x, g, f, t, s, z, strat, dx) - end # next quadrature point - + innerintegrals!(sop, x, g, f, t, s, z, qrule, dx) + end end From ead29d648506fa6c6fb4f16309f738ed7a939713 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 15 Nov 2023 11:15:00 +0100 Subject: [PATCH 296/528] Bump version to 2.1.0 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c89d9f82..c1c8c34f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "2.0.0" +version = "2.1.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From 9f65b9a8c149d6a4216e6ec61ad9c9fc9c15b1c7 Mon Sep 17 00:00:00 2001 From: azuccott Date: Mon, 20 Nov 2023 14:14:17 +0100 Subject: [PATCH 297/528] integration of acustic singlelayer with all analytic strategy --- Project.toml | 1 + examples/tdacusticsinglelayer.jl | 51 ++++++++++ src/BEAST.jl | 1 + src/maxwell/mwops.jl | 29 +----- src/maxwell/timedomain/acustictdops.jl | 125 +++++++++++++++++++++++++ src/quadrature/quadstrats.jl | 3 + src/timedomain/tdintegralop.jl | 6 ++ 7 files changed, 189 insertions(+), 27 deletions(-) create mode 100644 examples/tdacusticsinglelayer.jl create mode 100644 src/maxwell/timedomain/acustictdops.jl diff --git a/Project.toml b/Project.toml index bbe15231..e2df26f8 100644 --- a/Project.toml +++ b/Project.toml @@ -28,6 +28,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SparseMatrixDicts = "5cb6c4b0-9b79-11e8-24c9-f9621d252589" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +TimeDomainBEMInt = "0303f91b-5811-4998-82cd-e0687f55e8f9" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] diff --git a/examples/tdacusticsinglelayer.jl b/examples/tdacusticsinglelayer.jl new file mode 100644 index 00000000..5d1a3f71 --- /dev/null +++ b/examples/tdacusticsinglelayer.jl @@ -0,0 +1,51 @@ +using CompScienceMeshes, BEAST, LinearAlgebra +Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = meshsphere(radius=1.0, h=0.5) + +X = lagrangecxd0(Γ) +numfunctions(X) +Δt = 0.05 +Nt = 20 +T = timebasisshiftedlagrange(Δt, Nt, 0) +U = timebasisdelta(Δt, Nt) + +V = X ⊗ T +W = X ⊗ U + +duration = 2 * 20 * Δt +delay = 1.5 * duration +amplitude = 1.0 +gaussian = creategaussian(duration, delay, amplitude) +direction, polarisation = ẑ, x̂ +E = planewave(polarisation, direction, derive(gaussian), 1.0) + +@hilbertspace j +@hilbertspace j′ + +SL = TDAcustic3D.acusticsinglelayer(speedofsound=1.0, numdiffs=1) +# BEAST.@defaultquadstrat (SL, W, V) BEAST.OuterNumInnerAnalyticQStrat(7) + +tdacusticsl = @discretise SL[j′,j] == -1.0E[j′] j∈V j′∈W +xacusticsl = solve(tdacusticsl) +#corregere da qui +import Plots +Plots.plot(xefie[1,:]) + +import Plotly +fcr, geo = facecurrents(xefie[:,125], X) +Plotly.plot(patch(geo, norm.(fcr))) + + + + + + +Xefie, Δω, ω0 = fouriertransform(xefie, Δt, 0.0, 2) +ω = collect(ω0 .+ (0:Nt-1)*Δω) +_, i1 = findmin(abs.(ω.-1.0)) + +ω1 = ω[i1] +ue = Xefie[:,i1] / fouriertransform(gaussian)(ω1) + +fcr, geo = facecurrents(ue, X) +Plotly.plot(patch(geo, norm.(fcr))) \ No newline at end of file diff --git a/src/BEAST.jl b/src/BEAST.jl index 17e685d8..66b442fb 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -236,6 +236,7 @@ include("helmholtz3d/timedomain/tdhh3dops.jl") include("helmholtz3d/timedomain/tdhh3dexc.jl") include("helmholtz3d/timedomain/tdhh3dpp.jl") +include("maxwell/timedomain/acustictdops.jl")#support for acusticsinglelayer include("maxwell/timedomain/mwtdops.jl") include("maxwell/timedomain/mwtdexc.jl") include("maxwell/timedomain/tdfarfield.jl") diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index a92d2b1e..866af4f6 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -106,7 +106,7 @@ singularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DSng(op.gamma) const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace, BDMRefSpace, NCrossBDMRefSpace} -function quadrule(op::MaxwellOperator3D, g::LinearRefSpaceTriangle, f::LinearRefSpaceTriangle, i, τ, j, σ, qd, +function quadrule(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd, qs::DoubleNumWiltonSauterQStrat) T = eltype(eltype(τ.vertices)) @@ -138,7 +138,7 @@ function quadrule(op::MaxwellOperator3D, g::LinearRefSpaceTriangle, f::LinearRef qd.bpoints[1,j],) end -function quadrule(op::MaxwellOperator3D, g::NDRefSpace, f::RTRefSpace, i, τ, j, σ, qd, +function quadrule(op::MaxwellOperator3D, g::LinearRefSpaceTriangle, f::LinearRefSpaceTriangle, i, τ, j, σ, qd, qs::DoubleNumWiltonSauterQStrat) T = eltype(eltype(τ.vertices)) @@ -170,31 +170,6 @@ return DoubleQuadRule( qd.bpoints[1,j],) end -function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::BDMRefSpace, i, τ, j, σ, qd, - qs::DoubleNumWiltonSauterQStrat) - - hits = 0 - dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) - dmin2 = floatmax(eltype(eltype(τ.vertices))) - for t in τ.vertices - for s in σ.vertices - d2 = LinearAlgebra.norm_sqr(t-s) - dmin2 = min(dmin2, d2) - hits += (d2 < dtol) - end - end - - hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) - hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) - hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - - h2 = volume(σ) - xtol2 = 0.2 * 0.2 - k2 = abs2(op.gamma) - return DoubleQuadRule( - qd.tpoints[1,i], - qd.bpoints[1,j],) -end diff --git a/src/maxwell/timedomain/acustictdops.jl b/src/maxwell/timedomain/acustictdops.jl new file mode 100644 index 00000000..c1f4100c --- /dev/null +++ b/src/maxwell/timedomain/acustictdops.jl @@ -0,0 +1,125 @@ +mutable struct AcusticSingleLayerTDIO{T} <: RetardedPotential{T} + "speed of sound in medium" + speed_of_light::T #for compatibility with the rest of the package I left light instead of sound + "weight" + weight::T + "number of temporal differentiations" + diffs::Int +end + +function Base.:*(a::Number, op::AcusticSingleLayerTDIO) + @info "scalar product a * op (acusticSL)" + AcusticSingleLayerTDIO( + op.speed_of_light, + a * op.weight, + op.diffs) +end + +AcusticSingleLayerTDIO(;speedofsound) = AcusticSingleLayerTDIO(speedofsound, one(speedofsound), 0) + +module TDAcustic3D +import ...BEAST + +function acusticsinglelayer(;speedofsound, numdiffs) + @assert numdiffs >= 0 + #controllare con Kristof che tipo di schema temporale viene utilizzato e come si con integrali rispetto al tempo extra + #return BEAST.integrate(BEAST.MWSingleLayerTDIO(speedoflight,-1/speedoflight,-speedoflight,2,0)) + return BEAST.AcusticSingleLayerTDIO(speedofsound,one(speedofsound),numdiffs) +end + +end #of the module + +export TDAcustic3D + +defaultquadstrat(::AcusticSingleLayerTDIO, tfs, bfs) = AllAnalyticalQStrat(1) + + + + + +function quaddata(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, + testels, trialels, timeels, quadstrat::AllAnalyticalQStrat) + return 0.0 +end + +quadrule(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, + p, testel, q, trialel, r, timeel, qd, ::AllAnalyticalQStrat) = ZuccottiStrat(1.0) + +function quaddata(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, + testels, trialels, timeels, quadstrat::OuterNumInnerAnalyticQStrat) + + dmax = numfunctions(timerefs)-1 + bn = binomial.((0:dmax),(0:dmax)') + + V = eltype(testels[1].vertices) + ws = WiltonInts84.workspace(V) + # quadpoints(testrefs, testels, (3,)), bn, ws + quadpoints(testrefs, testels, (quadstrat.outer_rule,)), bn, ws +end + + +quadrule(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, + p, testel, q, trialel, r, timeel, qd, ::OuterNumInnerAnalyticQStrat) = WiltonInts84Strat(qd[1][1,p],qd[2],qd[3]) + +function momintegrals!(z, op::AcusticSingleLayerTDIO, g::LagrangeRefSpace{T,0,3}, f::LagrangeRefSpace{T,0,3}, t::MonomialBasis{T,0,1}, τ, σ, ι, qr::ZuccottiStrat) where T + if ι[1]<0 + t1=0.0 + else + t1=ι[1] + end + t2=ι[2] + + + @assert t2 > t1 + + sos = op.speed_of_light + + a1index,a2index,a3index,b1index,b2index,b3index=0,0,0,0,0,0 + Tt = eltype(eltype(τ.vertices)) + hits = 0 + dtol = 1.0e3 * eps(Tt) + dmin2 = floatmax(Tt) + for t in 1:3 + for s in 1:3 + d2 = LinearAlgebra.norm_sqr(τ[t]-σ[s]) + dmin2 = min(dmin2, d2) + if (d2 < dtol) + hits+=1 + if hits==1 + a1index =t + b1index = s + elseif hits==2 + a2index=t + b2index=s + end + end + end + end + #print(τ[1]," ",τ[2]," ",τ[3]," ",σ[1]," ",σ[2]," ",σ[3]," ",t1," ",t2," ",) + if hits==3 + z[1,1,1]+=TimeDomainBEMInt.intcoinctriangles(τ[1],τ[2],τ[3],t1,t2) + elseif hits==2 + + if mod1(a1index +1,3)==a2index + a3index=mod1(a1index-1,3) + else + a3index=mod1(a1index+1,3) + end + + if mod1(b1index+1,3)==b2index + b3index=mod1(a1index-1,3) + else + b3index=mod1(a1index+1,3) + end + + z[1,1,1]+=TimeDomainBEMInt.inttriangletriangleadjacent(τ[a1index],τ[a2index],τ[a3index],σ[b1index],σ[b2index],σ[a3index],t1,t2) + elseif hits==1 + a2index,a3index=mod1(a1index+1,3),mod1(a1index+2,3) + b2index,b3index=mod1(b1index+1,3),mod1(b1index+2,3) + + z[1,1,1]+=TimeDomainBEMInt.intcommonvertex(τ[a1index],τ[a2index],τ[a3index],σ[b2index],σ[b3index],t1,t2) + else + z[1,1,1]+=inttriangletriangle(τ[1],τ[2],τ[3],σ[1],σ[2],σ[3],t1,t2) + end + #for the moment sos=1 but I will correct this +end \ No newline at end of file diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index 40d4bddb..1c583f07 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -38,6 +38,9 @@ struct OuterNumInnerAnalyticQStrat{R} outer_rule::R end +struct AllAnalyticalQStrat{R} + rule::R +end defaultquadstrat(op, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bfs)) macro defaultquadstrat(dop, body) diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 77a9c2e7..316c274f 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -1,4 +1,5 @@ using WiltonInts84 +using TimeDomainBEMInt abstract type RetardedPotential{T} <: Operator end Base.eltype(::RetardedPotential{T}) where {T} = T @@ -289,6 +290,9 @@ struct WiltonInts84Strat{T,V,W} workspace::W end +struct ZuccottiStrat{T} + weight::T #Allanalyticalformula +end function momintegrals!(z, op, g, f, T, τ, σ, ι, qr::WiltonInts84Strat) @@ -300,3 +304,5 @@ function momintegrals!(z, op, g, f, T, τ, σ, ι, qr::WiltonInts84Strat) end end + + From a75d2f918912ac6d0c347b1d9d66296dc9366965 Mon Sep 17 00:00:00 2001 From: azuccott Date: Mon, 20 Nov 2023 14:52:40 +0100 Subject: [PATCH 298/528] update acustisl --- src/maxwell/timedomain/acustictdops.jl | 15 ++++++--------- src/timedomain/tdintegralop.jl | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/maxwell/timedomain/acustictdops.jl b/src/maxwell/timedomain/acustictdops.jl index c1f4100c..9bbbcca0 100644 --- a/src/maxwell/timedomain/acustictdops.jl +++ b/src/maxwell/timedomain/acustictdops.jl @@ -39,11 +39,11 @@ defaultquadstrat(::AcusticSingleLayerTDIO, tfs, bfs) = AllAnalyticalQStrat(1) function quaddata(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, testels, trialels, timeels, quadstrat::AllAnalyticalQStrat) - return 0.0 + return nothing end quadrule(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, - p, testel, q, trialel, r, timeel, qd, ::AllAnalyticalQStrat) = ZuccottiStrat(1.0) + p, testel, q, trialel, r, timeel, qd, ::AllAnalyticalQStrat) = ZuccottiRule(1.0) function quaddata(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, testels, trialels, timeels, quadstrat::OuterNumInnerAnalyticQStrat) @@ -62,12 +62,9 @@ quadrule(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, p, testel, q, trialel, r, timeel, qd, ::OuterNumInnerAnalyticQStrat) = WiltonInts84Strat(qd[1][1,p],qd[2],qd[3]) function momintegrals!(z, op::AcusticSingleLayerTDIO, g::LagrangeRefSpace{T,0,3}, f::LagrangeRefSpace{T,0,3}, t::MonomialBasis{T,0,1}, τ, σ, ι, qr::ZuccottiStrat) where T - if ι[1]<0 - t1=0.0 - else - t1=ι[1] - end - t2=ι[2] + + t1=ι[1] + t2=ι[2] @assert t2 > t1 @@ -99,7 +96,7 @@ function momintegrals!(z, op::AcusticSingleLayerTDIO, g::LagrangeRefSpace{T,0,3} if hits==3 z[1,1,1]+=TimeDomainBEMInt.intcoinctriangles(τ[1],τ[2],τ[3],t1,t2) elseif hits==2 - + #pay attention with double layer and index permutation if mod1(a1index +1,3)==a2index a3index=mod1(a1index-1,3) else diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 316c274f..3adc0d54 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -290,7 +290,7 @@ struct WiltonInts84Strat{T,V,W} workspace::W end -struct ZuccottiStrat{T} +struct ZuccottiRule{T} weight::T #Allanalyticalformula end From 5deb446480a01db5f35c21bc4faf4222f6f403e6 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 29 Nov 2023 13:43:06 +0100 Subject: [PATCH 299/528] offdiagkernel expands correctly --- src/helmholtz3d/hh3dops.jl | 330 +------------------------------------ src/utils/variational.jl | 2 +- 2 files changed, 4 insertions(+), 328 deletions(-) diff --git a/src/helmholtz3d/hh3dops.jl b/src/helmholtz3d/hh3dops.jl index 4fcf1a45..e1eb021b 100644 --- a/src/helmholtz3d/hh3dops.jl +++ b/src/helmholtz3d/hh3dops.jl @@ -59,10 +59,6 @@ struct HH3DSingleLayerSng{T,K} <: Helmholtz3DOp{T,K} gamma::K end -# function sign_upon_permutation(op::HH3DSingleLayerFDBIO, I, J) -# return 1 -# end - struct HH3DDoubleLayerFDBIO{T,K} <: Helmholtz3DOp{T,K} alpha::T gamma::K @@ -78,9 +74,6 @@ struct HH3DDoubleLayerSng{T,K} <: Helmholtz3DOp{T,K} gamma::K end -# function sign_upon_permutation(op::HH3DDoubleLayerFDBIO, I, J) -# return Combinatorics.levicivita(J) -# end struct HH3DDoubleLayerTransposedFDBIO{T,K} <: Helmholtz3DOp{T,K} alpha::T gamma::K @@ -96,332 +89,15 @@ struct HH3DDoubleLayerTransposedSng{T,K} <: Helmholtz3DOp{T,K} gamma::K end -# function sign_upon_permutation(op::HH3DDoubleLayerTransposedFDBIO, I, J) -# return Combinatorics.levicivita(I) -# end - -defaultquadstrat(::Helmholtz3DOp, ::LagrangeRefSpace, ::LagrangeRefSpace) = DoubleNumWiltonSauterQStrat(2,3,2,3,4,4,4,4) - -# function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, -# trial_refspace::LagrangeRefSpace, test_elements, trial_elements, -# qs::DoubleNumWiltonSauterQStrat) - -# test_eval(x) = test_refspace(x, Val{:withcurl}) -# trial_eval(x) = trial_refspace(x, Val{:withcurl}) - -# # The combinations of rules (6,7) and (5,7 are) BAAAADDDD -# # they result in many near singularity evaluations with any -# # resemblence of accuracy going down the drain! Simply don't! -# # (same for (5,7) btw...). -# # test_qp = quadpoints(test_eval, test_elements, (6,)) -# # bssi_qp = quadpoints(trial_eval, trial_elements, (7,)) - -# test_qp = quadpoints(test_eval, test_elements, (qs.outer_rule_far,)) -# bsis_qp = quadpoints(trial_eval, trial_elements, (qs.inner_rule_far,)) - -# gausslegendre = ( -# _legendre(qs.sauter_schwab_common_vert,0,1), -# _legendre(qs.sauter_schwab_common_edge,0,1), -# _legendre(qs.sauter_schwab_common_face,0,1),) - -# return (;test_qp, bsis_qp, gausslegendre) -# end - -# function quaddata(op::Helmholtz3DOp, test_refspace::LagrangeRefSpace, -# trial_refspace::LagrangeRefSpace, test_elements, trial_elements, -# qs::DoubleNumQStrat) - -# test_eval(x) = test_refspace(x, Val{:withcurl}) -# trial_eval(x) = trial_refspace(x, Val{:withcurl}) - -# # The combinations of rules (6,7) and (5,7 are) BAAAADDDD -# # they result in many near singularity evaluations with any -# # resemblence of accuracy going down the drain! Simply don't! -# # (same for (5,7) btw...). -# # test_qp = quadpoints(test_eval, test_elements, (6,)) -# # bssi_qp = quadpoints(trial_eval, trial_elements, (7,)) - -# test_qp = quadpoints(test_eval, test_elements, (qs.outer_rule,)) -# bsis_qp = quadpoints(trial_eval, trial_elements, (qs.inner_rule,)) - -# # gausslegendre = ( -# # _legendre(qs.sauter_schwab_common_vert,0,1), -# # _legendre(qs.sauter_schwab_common_edge,0,1), -# # _legendre(qs.sauter_schwab_common_face,0,1),) - -# return (;test_qp, bsis_qp) -# end +defaultquadstrat(::Helmholtz3DOp, ::LagrangeRefSpace, ::LagrangeRefSpace) = + DoubleNumWiltonSauterQStrat(2,3,2,3,4,4,4,4) defaultquadstrat(::Helmholtz3DOp, ::subReferenceSpace, ::subReferenceSpace) = DoubleNumWiltonSauterQStrat(4,7,4,7,4,4,4,4) -# function quaddata(op::Helmholtz3DOp, test_refspace::subReferenceSpace, -# trial_refspace::subReferenceSpace, test_elements, trial_elements, -# qs::DoubleNumWiltonSauterQStrat) - -# test_qp = quadpoints(test_refspace, test_elements, (qs.outer_rule_far,)) -# bsis_qp = quadpoints(trial_refspace, trial_elements, (qs.inner_rule_far,)) - -# return test_qp, bsis_qp -# end - -# function quadrule(op::Helmholtz3DOp, -# test_refspace::LagrangeRefSpace, -# trial_refspace::LagrangeRefSpace, -# i, test_element, j, trial_element, qd, -# qs::DoubleNumWiltonSauterQStrat) - -# tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 -# dmin2 = floatmax(eltype(eltype(test_element.vertices))) - -# for t in test_element.vertices -# for s in trial_element.vertices -# d2 = LinearAlgebra.norm_sqr(t-s) -# dmin2 = min(dmin2, d2) -# hits += (d2 < tol) -# end end - -# hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) -# hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) -# hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - -# test_quadpoints = qd.test_qp -# trial_quadpoints = qd.bsis_qp -# h2 = volume(trial_element) -# xtol2 = 0.2 * 0.2 -# k2 = abs2(gamma(op)) -# max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( -# test_quadpoints[1,i], -# DoubleQuadRule( -# test_quadpoints[1,i], -# trial_quadpoints[1,j])) - -# return DoubleQuadRule( -# qd.test_qp[1,i], -# qd.bsis_qp[1,j]) -# end - - -# function quadrule(op::HH3DSingleLayerFDBIO, -# test_refspace::LagrangeRefSpace{T,0} where T, -# trial_refspace::LagrangeRefSpace{T,0} where T, -# i, test_element, j, trial_element, qd, -# qs::DoubleNumQStrat) - -# return DoubleQuadRule( -# qd.test_qp[1,i], -# qd.bsis_qp[1,j]) -# end - regularpart(op::HH3DHyperSingularFDBIO) = HH3DHyperSingularReg(op.alpha, op.beta, op.gamma) singularpart(op::HH3DHyperSingularFDBIO) = HH3DHyperSingularSng(op.alpha, op.beta, op.gamma) -#= function quadrule(op::HH3DHyperSingularFDBIO, - test_refspace::LagrangeRefSpace, - trial_refspace::LagrangeRefSpace, - i, test_element, j, trial_element, qd, - qs::DoubleNumWiltonSauterQStrat) - - tol2, hits = eps(eltype(eltype(test_element.vertices))), 0 - for t in test_element.vertices - for s in trial_element.vertices - hits += (LinearAlgebra.norm_sqr(t-s) < tol2) - end end - - hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) - hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) - hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - - test_quadpoints = qd.test_qp - trial_quadpoints = qd.bsis_qp - h2 = volume(trial_element) - xtol2 = 0.2 * 0.2 - k2 = abs2(op.gamma) - max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( - test_quadpoints[1,i], - DoubleQuadRule( - test_quadpoints[1,i], - trial_quadpoints[1,j])) - - return DoubleQuadRule( - qd.test_qp[1,i], - qd.bsis_qp[1,j]) -end =# - - -# function quadrule(op::HH3DHyperSingularFDBIO, -# test_refspace::LagrangeRefSpace{T,1} where T, -# trial_refspace::LagrangeRefSpace{T,1} where T, -# i, test_element, j, trial_element, qd, -# qs::DoubleNumQStrat) - -# # tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 -# # for t in test_element.vertices -# # for s in trial_element.vertices -# # norm(t-s) < tol && (hits +=1) -# # end end - -# # hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) -# # hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) -# # hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - -# # test_quadpoints = qd.test_qp -# # trial_quadpoints = qd.bsis_qp - -# # hits != 0 && return WiltonSERule( -# # test_quadpoints[1,i], -# # DoubleQuadRule( -# # test_quadpoints[1,i], -# # trial_quadpoints[1,j])) - -# return DoubleQuadRule( -# qd.test_qp[1,i], -# qd.bsis_qp[1,j]) -# end - -# function quadrule(op::HH3DSingleLayerFDBIO, test_refspace::subReferenceSpace, -# trial_refspace::subReferenceSpace, i, test_element, j, trial_element, quadrature_data, -# qs::DoubleNumWiltonSauterQStrat) - -# # tol, hits = 1e-10, 0 -# # for t in getelementVertices(test_element) -# # for s in getelementVertices(trial_element) -# # norm(t-s) < tol && (hits +=1; break) -# # end end -# # -# # test_quadpoints = quadrature_data[1] -# # trial_quadpoints = quadrature_data[2] - -# # hits != 0 && return WiltonSERule( -# # test_quadpoints[1,i], -# # DoubleQuadRule( -# # test_quadpoints[1,i], -# # trial_quadpoints[1,j])) - -# return DoubleQuadRule( -# quadrature_data[1][1,i], -# quadrature_data[2][1,j]) -# # return SauterSchwabStrategy(hits) -# # test_qd = qd[1] -# # trial_qd = qd[2] -# # -# # DoubleQuadRule( -# # test_qd[1,i], # rule 1 on test element j -# # trial_qd[1,j] # rule 1 on trial element i -# # ) -# end - -# function quadrule(op::Helmholtz3DOp, -# test_refspace::RefSpace, trial_refspace::RefSpace, -# i, test_element, j, trial_element, quadrature_data, -# qs::DoubleNumWiltonSauterQStrat) - -# return DoubleQuadRule( -# quadrature_data[1][1,i], -# quadrature_data[2][1,j]) -# end - -#= function quadrule(op::HH3DDoubleLayerTransposedFDBIO, - test_refspace::LagrangeRefSpace{T,1} where T, - trial_refspace::LagrangeRefSpace{T,0} where T, - i, test_element, j, trial_element, quadrature_data, - qs::DoubleNumWiltonSauterQStrat) - - tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 - dmin2 = floatmax(eltype(eltype(test_element.vertices))) - - for t in test_element.vertices - for s in trial_element.vertices - d2 = LinearAlgebra.norm_sqr(t-s) - dmin2 = min(dmin2, d2) - hits += (d2 < tol) - end end - - hits == 3 && return SauterSchwabQuadrature.CommonFace(quadrature_data.gausslegendre[3]) - hits == 2 && return SauterSchwabQuadrature.CommonEdge(quadrature_data.gausslegendre[2]) - hits == 1 && return SauterSchwabQuadrature.CommonVertex(quadrature_data.gausslegendre[1]) - - test_quadpoints = quadrature_data[1] - trial_quadpoints = quadrature_data[2] - test_quadpoints = quadrature_data.test_qp - trial_quadpoints = quadrature_data.bsis_qp - h2 = volume(trial_element) - xtol2 = 0.2 * 0.2 - k2 = abs2(gamma(op)) - - max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( - test_quadpoints[1,i], - DoubleQuadRule( - test_quadpoints[1,i], - trial_quadpoints[1,j])) - - return DoubleQuadRule( - quadrature_data[1][1,i], - quadrature_data[2][1,j]) -end =# - -#= function quadrule(op::HH3DDoubleLayerFDBIO, - test_refspace::LagrangeRefSpace{T,0} where T, - trial_refspace::LagrangeRefSpace{T,1} where T, - i, test_element, j, trial_element, quadrature_data, - qs::DoubleNumWiltonSauterQStrat) - - tol, hits = sqrt(eps(eltype(eltype(test_element.vertices)))), 0 - dmin2 = floatmax(eltype(eltype(test_element.vertices))) - - for t in test_element.vertices - for s in trial_element.vertices - d2 = LinearAlgebra.norm_sqr(t-s) - dmin2 = min(dmin2, d2) - hits += (d2 < tol) - end end - - hits == 3 && return SauterSchwabQuadrature.CommonFace(quadrature_data.gausslegendre[3]) - hits == 2 && return SauterSchwabQuadrature.CommonEdge(quadrature_data.gausslegendre[2]) - hits == 1 && return SauterSchwabQuadrature.CommonVertex(quadrature_data.gausslegendre[1]) - test_quadpoints = quadrature_data[1] - trial_quadpoints = quadrature_data[2] - - test_quadpoints = quadrature_data.test_qp - trial_quadpoints = quadrature_data.bsis_qp - h2 = volume(trial_element) - xtol2 = 0.2 * 0.2 - k2 = abs2(gamma(op)) - -#= max(dmin2*k2, dmin2/16h2) < xtol2 && return WiltonSERule( - test_quadpoints[1,i], - DoubleQuadRule( - test_quadpoints[1,i], - trial_quadpoints[1,j])) =# - return DoubleQuadRule( - quadrature_data[1][1,i], - quadrature_data[2][1,j]) -end =# - - -# function quadrule(op::Helmholtz3DOp, -# test_refspace::RefSpace, trial_refspace::RefSpace, -# i, test_element, j, trial_element, quadrature_data, -# qs::DoubleNumQStrat) - -# return DoubleQuadRule( -# quadrature_data[1][1,i], -# quadrature_data[2][1,j]) -# end - -# function quadrule(op::Helmholtz3DOp, -# test_refspace::RTRefSpace, trial_refspace::RTRefSpace, -# i, test_element, j, trial_element, quadrature_data, -# qs::DoubleNumWiltonSauterQStrat) - -# test_quadpoints = quadrature_data[1] -# trial_quadpoints = quadrature_data[2] - -# return DoubleQuadRule( -# quadrature_data[1][1,i], -# quadrature_data[2][1,j]) -# end - function (igd::Integrand{<:HH3DHyperSingularFDBIO})(x,y,f,g) α = igd.operator.alpha β = igd.operator.beta @@ -441,7 +117,7 @@ end HH3DSingleLayerFDBIO(gamma) = HH3DSingleLayerFDBIO(one(gamma), gamma) -# Better to handle this in the wilton ints to allow dispatch + regularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerReg(op.alpha, op.gamma) singularpart(op::HH3DSingleLayerFDBIO) = HH3DSingleLayerSng(op.alpha, op.gamma) diff --git a/src/utils/variational.jl b/src/utils/variational.jl index d14396c2..aa0a5158 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -471,7 +471,7 @@ function getindex(op::OffDiagKernel, V::Vector{HilbertVector}, U::Vector{Hilbert for (i,v) in enumerate(V) for (j,u) in enumerate(U) i == j && continue - term = BilTerm(v.idx, u.idx, v.opstack, u.opstack, 1, op) + term = BilTerm(v.idx, u.idx, v.opstack, u.opstack, 1, op.bilform) push!(terms, term) end end From b3b8bd9ab471e2a3a947b9630f6299c5efe3f553 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 5 Dec 2023 12:48:26 +0100 Subject: [PATCH 300/528] GMRESSolver support Pl/left_preconditioner APIs --- src/solvers/itsolver.jl | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index dfff8914..4ccc1941 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -17,12 +17,24 @@ Base.axes(A::GMRESSolver) = reverse(axes(A.linear_operator)) function GMRESSolver(op::L; - left_preconditioner::P = IterativeSolvers.Identity(), + left_preconditioner = nothing, + Pl = nothing, maxiter=0, restart=0, abstol::R = zero(real(eltype(op))), reltol::R = sqrt(eps(real(eltype(op)))), - verbose=true) where {L,R<:Real,P} + verbose=true) where {L,R<:Real} + + if left_preconditioner == nothing + Pl == nothing && (Pl = IterativeSolvers.Identity()) + else + if Pl == nothing + Pl = BEAST.Preconditioner(left_preconditioner) + else + error("Either supply Pl or left_preconditioner, not both.") + end + end + @assert Pl != nothing m, n = size(op) @assert m == n @@ -30,8 +42,9 @@ function GMRESSolver(op::L; maxiter == 0 && (maxiter = div(n, 5)) restart == 0 && (restart = n) + P = typeof(Pl) T = eltype(op) - GMRESSolver{T,L,R,P}(op, maxiter, restart, abstol, reltol, verbose, left_preconditioner) + GMRESSolver{T,L,R,P}(op, maxiter, restart, abstol, reltol, verbose, Pl) end From aa2ecdadd01cafb2e895563c458c76c6c7193162 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 7 Dec 2023 09:59:58 +0100 Subject: [PATCH 301/528] retired eltype where scalartype is appropriate --- src/timedomain/tdintegralop.jl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 035c89ad..4a0a7ccd 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -1,7 +1,10 @@ using WiltonInts84 +abstract type AbstractTimeOperator end +abstract type TimeDomainOperator end # atomic operator + abstract type RetardedPotential{T} <: Operator end -Base.eltype(::RetardedPotential{T}) where {T} = T +# Base.eltype(::RetardedPotential{T}) where {T} = T scalartype(A::RetardedPotential{T}) where {T} = T mutable struct EmptyRP{T} <: RetardedPotential{T} @@ -37,7 +40,8 @@ function allocatestorage(op::RetardedPotential, testST, basisST, println("\nAllocated memory for convolution operator.") kmax = maximum(K1); - Z = zeros(eltype(op), M, N, kmax) + T = scalartype(op, testST, basisST) + Z = zeros(T, M, N, kmax) store1(v,m,n,k) = (Z[m,n,k] += v) # return ()->MatrixConvolution(Z), store1 return ()->ConvolutionOperators.DenseConvOp(Z), store1 @@ -83,7 +87,7 @@ function allocatestorage(op::RetardedPotential, testST, basisST, @info "Allocating mem for RP op compressing the static tail..." - T = eltype(op) + T = scalartype(op, testST, basisST) tfs = spatialbasis(testST) bfs = spatialbasis(basisST) @@ -232,7 +236,7 @@ function assemble_chunk!(op::RetardedPotential, testST, trialST, store; udim = numfunctions(U) vdim = numfunctions(V) wdim = numfunctions(W) - z = zeros(eltype(op), udim, vdim, wdim) + z = zeros(scalartype(op, testST, trialST), udim, vdim, wdim) # @show length(testels) length(trialels) From 51bb825e81260a2b5c242dc61c93918047f55597 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 7 Dec 2023 12:50:27 +0100 Subject: [PATCH 302/528] split FD and TD type hierarchy --- src/localop.jl | 6 +++--- src/operator.jl | 6 +++--- src/timedomain/tdintegralop.jl | 19 ++++++++++++++++--- src/timedomain/tdtimeops.jl | 8 ++++---- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/localop.jl b/src/localop.jl index df9c337b..7cb5b3b8 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -7,7 +7,7 @@ abstract type LocalOperator <: Operator end function allocatestorage(op::LocalOperator, test_functions, trial_functions, - storage_trait::Type{Val{:bandedstorage}}, longdelays_trait) + storage_trait::Type{Val{:bandedstorage}}) T = scalartype(op, test_functions, trial_functions) @@ -31,7 +31,7 @@ function allocatestorage(op::LocalOperator, test_functions, trial_functions, end function allocatestorage(op::LocalOperator, testfunctions, trialfunctions, - storage_trait::Type{Val{:sparsedicts}}, longdelays_trait) + storage_trait::Type{Val{:sparsedicts}}) T = scalartype(op, testfunctions, trialfunctions) @@ -46,7 +46,7 @@ function allocatestorage(op::LocalOperator, testfunctions, trialfunctions, end function allocatestorage(op::LocalOperator, test_functions, trial_functions, - storage_trait::Type{Val{:densestorage}}, longdelays_trait) + storage_trait::Type{Val{:densestorage}}) T = scalartype(op, test_functions, trial_functions) diff --git a/src/operator.jl b/src/operator.jl index 71c815e2..61d38fa1 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -80,12 +80,12 @@ defaultquadstrat(lc::LinearCombinationOfOperators, tfs, bfs) = function assemble(operator::AbstractOperator, test_functions, trial_functions; storage_policy = Val{:bandedstorage}, - long_delays_policy = LongDelays{:compress}, + # long_delays_policy = LongDelays{:compress}, threading = Threading{:multi}, quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) Z, store = allocatestorage(operator, test_functions, trial_functions, - storage_policy, long_delays_policy) + storage_policy) assemble!(operator, test_functions, trial_functions, store, threading; quadstrat) return Z() end @@ -122,7 +122,7 @@ function assemblecol(operator::AbstractOperator, test_functions, trial_functions end function allocatestorage(operator::AbstractOperator, test_functions, trial_functions, - storage_trait, longdelays_trait) + storage_trait=nothing, longdelays_trait=nothing) T = promote_type( scalartype(operator) , diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 4a0a7ccd..47e68400 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -1,9 +1,22 @@ using WiltonInts84 -abstract type AbstractTimeOperator end -abstract type TimeDomainOperator end # atomic operator +abstract type AbstractSpaceTimeOperator end +abstract type SpaceTimeOperator <: AbstractSpaceTimeOperator end # atomic operator + +function assemble(operator::AbstractSpaceTimeOperator, test_functions, trial_functions; + storage_policy = Val{:bandedstorage}, + long_delays_policy = LongDelays{:compress}, + threading = Threading{:multi}, + quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) + + Z, store = allocatestorage(operator, test_functions, trial_functions, + storage_policy, long_delays_policy) + assemble!(operator, test_functions, trial_functions, store, threading; quadstrat) + return Z() +end + -abstract type RetardedPotential{T} <: Operator end +abstract type RetardedPotential{T} <: SpaceTimeOperator end # Base.eltype(::RetardedPotential{T}) where {T} = T scalartype(A::RetardedPotential{T}) where {T} = T diff --git a/src/timedomain/tdtimeops.jl b/src/timedomain/tdtimeops.jl index 40eed25e..7e871df4 100644 --- a/src/timedomain/tdtimeops.jl +++ b/src/timedomain/tdtimeops.jl @@ -32,7 +32,7 @@ function assemble(op::Identity, return z end -mutable struct TensorOperator <: Operator +mutable struct TensorOperator <: SpaceTimeOperator spatial_factor temporal_factor end @@ -210,13 +210,13 @@ function assemble!(operator::TemporalDifferentiation, testfns, trialfns, store, end -struct TemporalIntegration <: AbstractOperator - operator::AbstractOperator +struct TemporalIntegration <: AbstractSpaceTimeOperator + operator::AbstractSpaceTimeOperator end defaultquadstrat(op::TemporalIntegration, tfs, bfs) = defaultquadstrat(op.operator, tfs, bfs) -integrate(op::AbstractOperator) = TemporalIntegration(op) +integrate(op::SpaceTimeOperator) = TemporalIntegration(op) derive(op::TemporalIntegration) = op.operator scalartype(op::TemporalIntegration) = scalartype(op.operator) Base.:*(a::Number, op::TemporalIntegration) = TemporalIntegration(a * op.operator) From f8784ed98cdb79cabc03a22a5c4452bfff5bcac1 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sat, 9 Dec 2023 16:10:38 +0100 Subject: [PATCH 303/528] consolidate FD and TD assembly --- src/bases/tensorbasis.jl | 6 ++ src/solvers/lusolver.jl | 2 +- src/solvers/solver.jl | 190 +++++++++------------------------------ 3 files changed, 48 insertions(+), 150 deletions(-) diff --git a/src/bases/tensorbasis.jl b/src/bases/tensorbasis.jl index ca9547eb..4c7ced29 100644 --- a/src/bases/tensorbasis.jl +++ b/src/bases/tensorbasis.jl @@ -14,7 +14,13 @@ function SpaceTimeBasis(space, time) return SpaceTimeBasis{S,T,U}(space,time) end +spatialbasis(s) = s spatialbasis(s::SpaceTimeBasis) = s.space +function spatialbasis(s::DirectProductSpace) + r = [spatialbasis(ch) for ch in s.factors] + return DirectProductSpace(r) +end + temporalbasis(s::SpaceTimeBasis) = s.time ⊗(a, b) = SpaceTimeBasis(a,b) diff --git a/src/solvers/lusolver.jl b/src/solvers/lusolver.jl index 7c5ee2e2..0675eae7 100644 --- a/src/solvers/lusolver.jl +++ b/src/solvers/lusolver.jl @@ -67,7 +67,7 @@ function td_solve(eq) # bilform = eq.equation.lhs - A = td_assemble(eq.equation.lhs, eq.test_space_dict, eq.trial_space_dict) + A = assemble(eq.equation.lhs, eq.test_space_dict, eq.trial_space_dict) T = eltype(A) S = zeros(T, size(A)[1:2]) ConvolutionOperators.timeslice!(S, A, 1) diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 0154434e..02cd3020 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -139,12 +139,7 @@ function _spacedict_to_directproductspace(spacedict) end function assemble(lform::LinForm, test_space_dict) - - # xfactors = Vector{AbstractSpace}(undef, length(test_space_dict)) - # for (p,x) in test_space_dict xfactors[p] = x end - # X = DirectProductSpace(xfactors) X = _spacedict_to_directproductspace(test_space_dict) - return assemble(lform, X) end @@ -156,7 +151,6 @@ function assemble(lform::LinForm, X::DirectProductSpace) @assert !isempty(lform.terms) M = length.(X.factors) - # U = BlockArrays.blockedrange(M) U = NestedUnitRanges.nestedrange(X, 1, numfunctions) T = scalartype(lform, X) @@ -177,41 +171,6 @@ function assemble(lform::LinForm, X::DirectProductSpace) return B end -# function assemble(lform::LinForm, test_space_dict) -# T=ComplexF64 -# terms = lform.terms -# blocksizes1 = Int[] -# for p in 1:length(lform.test_space) -# X = test_space_dict[p] -# push!(blocksizes1, numfunctions(X)) -# end - -# Z = zeros(T, sum(blocksizes1)) -# B = PseudoBlockArray{T}(Z, blocksizes1) - -# for t in terms - -# α = t.coeff -# a = t.functional -# m = t.test_id -# X = test_space_dict[m] -# o = t.test_ops - -# # act with the various ops on X -# for op in reverse(o) -# Y = X; -# X = op[end](op[1:end-1]..., Y) -# end - -# b = assemble(a, X) -# # B[I[m] : I[m+1]-1] = α * b -# B[Block(m)] = α * b -# end - -# return B -# end - - struct SpaceTimeData{T} <: AbstractArray{Vector{T},1} data::Array{T,2} end @@ -220,8 +179,6 @@ Base.eltype(x::SpaceTimeData{T}) where {T} = Vector{T} Base.size(x::SpaceTimeData) = (size(x.data)[1],) Base.getindex(x::SpaceTimeData, i::Int) = x.data[i,:] -td_assemble(dlf::DiscreteLinform) = td_assemble(dlf.linform, dlf.test_space_dict) - function td_assemble(lform::LinForm, test_space_dict) terms = lform.terms @@ -263,81 +220,32 @@ function td_assemble(lform::LinForm, test_space_dict) return B end - -# function assemble(bilform::BilForm, test_space_dict, trial_space_dict; -# materialize=BEAST.assemble) - -# @assert !isempty(bilform.terms) - -# M = zeros(Int, length(test_space_dict)) -# N = zeros(Int, length(trial_space_dict)) - -# for (p,x) in test_space_dict M[p]=numfunctions(x) end -# for (p,x) in trial_space_dict N[p]=numfunctions(x) end - -# U = BlockArrays.blockedrange(M) -# V = BlockArrays.blockedrange(N) - -# Z = ZeroMap{Float32}(U, V) - -# for term in bilform.terms - -# x = test_space_dict[term.test_id] -# for op in reverse(term.test_ops) -# x = op[end](op[1:end-1]..., x) -# end - -# y = trial_space_dict[term.trial_id] -# for op in reverse(term.trial_ops) -# y = op[end](op[1:end-1]..., y) -# end - -# z = materialize(term.kernel, x, y) - -# I = Block(term.test_id) -# J = Block(term.trial_id) -# zlifted = LiftedMap(z,I,J,U,V) - -# Z = Z + term.coeff * zlifted -# end - -# return Z -# end - function assemble(bilform::BilForm, test_space_dict, trial_space_dict; materialize=BEAST.assemble) - # xfactors = Vector{AbstractSpace}(undef, length(test_space_dict)) - # yfactors = Vector{AbstractSpace}(undef, length(trial_space_dict)) - - # for (p,x) in test_space_dict xfactors[p] = x end - # for (q,y) in trial_space_dict yfactors[q] = y end - - # X = DirectProductSpace(xfactors) - # Y = DirectProductSpace(yfactors) - X = _spacedict_to_directproductspace(test_space_dict) Y = _spacedict_to_directproductspace(trial_space_dict) return assemble(bilform, X, Y; materialize) end +lift(a,I,J,U,V) = LiftedMaps.LiftedMap(a,I,J,U,V) +lift(a::ConvolutionOperators.AbstractConvOp ,I,J,U,V) = + ConvolutionOperators.LiftedConvOp(a, U, V, I, J) + function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; materialize=BEAST.assemble) @assert !isempty(bf.terms) - M = length.(X.factors) - N = length.(Y.factors) + M = numfunctions.(spatialbasis(X).factors) + N = numfunctions.(spatialbasis(Y).factors) U = BlockArrays.blockedrange(M) V = BlockArrays.blockedrange(N) - lincombv = LinearMap[] - - T = Int32 + sum(bf.terms) do term - for term in bf.terms x = X.factors[term.test_id] for op in reverse(term.test_ops) x = op[end](op[1:end-1]..., x) @@ -348,19 +256,10 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; y = op[end](op[1:end-1]..., y) end - z = materialize(term.kernel, x, y) - - I = Block(term.test_id) - J = Block(term.trial_id) - - Smap = term.coeff * LiftedMap(z, I, J, U, V) - - T = promote_type(T, eltype(Smap)) - - push!(lincombv, Smap) + a = term.coeff * term.kernel + z = materialize(a, x, y) + lift(z, Block(term.test_id), Block(term.trial_id), U, V) end - - return LinearMaps.LinearCombination{T}(lincombv) end @@ -372,53 +271,46 @@ end # z = assemble(bf, test_space_dict, trial_space_dict) # end -td_assemble(dbf::DiscreteBilform; materialize=BEAST.assemble) = - td_assemble(dbf.bilform, dbf.test_space_dict, dbf.trial_space_dict; materialize) - +# td_assemble(dbf::DiscreteBilform; materialize=BEAST.assemble) = +# td_assemble(dbf.bilform, dbf.test_space_dict, dbf.trial_space_dict; materialize) -function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict; - materialize=BEAST.assemble) - - X = _spacedict_to_directproductspace(test_space_dict) - Y = _spacedict_to_directproductspace(trial_space_dict) - return td_assemble(bilform, X, Y) -end +# function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict; +# materialize=BEAST.assemble) -function td_assemble(bilform::BilForm, - test_space_dict::DirectProductSpace, - trial_space_dict::DirectProductSpace) +# X = _spacedict_to_directproductspace(test_space_dict) +# Y = _spacedict_to_directproductspace(trial_space_dict) - lhterms = bilform.terms +# return td_assemble(bilform, X, Y) +# end - M = [length(fct.space) for fct in test_space_dict.factors] - N = [length(fct.space) for fct in trial_space_dict.factors] +# function td_assemble(bilform::BilForm, +# X::DirectProductSpace, +# Y::DirectProductSpace) - row_axis = BlockArrays.blockedrange(M) - col_axis = BlockArrays.blockedrange(N) +# M = [length(fct.space) for fct in X.factors] +# N = [length(fct.space) for fct in Y.factors] - Z = ConvolutionOperators.ZeroConvOp(row_axis, col_axis) +# row_axis = BlockArrays.blockedrange(M) +# col_axis = BlockArrays.blockedrange(N) - for t in lhterms +# sum(bilform.terms) do t - a = t.coeff * t.kernel +# a = t.coeff * t.kernel - m = t.test_id - # x = test_space_dict[m] - x = test_space_dict.factors[m] - for op in reverse(t.test_ops) - x = op[end](op[1:end-1]..., x) - end +# m = t.test_id +# x = X.factors[m] +# for op in reverse(t.test_ops) +# x = op[end](op[1:end-1]..., x) +# end - n = t.trial_id - # y = trial_space_dict[n] - y = trial_space_dict.factors[n] - for op in reverse(t.trial_ops) - y = op[end](op[1:end-1]..., y) - end +# n = t.trial_id +# y = Y.factors[n] +# for op in reverse(t.trial_ops) +# y = op[end](op[1:end-1]..., y) +# end - z = assemble(a, x, y) - Z += ConvolutionOperators.LiftedConvOp(z, row_axis, col_axis, Block(m), Block(n)) - end - return Z -end +# z = assemble(a, x, y) +# lift(z, Block(m), Block(n), row_axis, col_axis) +# end +# end From ed41c9f9ae2693033203e6a09b8e966108233808 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sat, 16 Dec 2023 13:12:16 +0100 Subject: [PATCH 304/528] consolidate linform FD and TD assembly --- examples/efie.jl | 4 +- examples/tdefie.jl | 2 +- examples/tdemfie.jl | 2 +- examples/tdmfie.jl | 2 +- examples/tdpmchwt.jl | 4 +- src/bases/tensorbasis.jl | 12 ++++ src/solvers/lusolver.jl | 67 +-------------------- src/solvers/solver.jl | 125 +++++++++++---------------------------- src/timedomain/motlu.jl | 18 ++++++ 9 files changed, 75 insertions(+), 161 deletions(-) diff --git a/examples/efie.jl b/examples/efie.jl index 5ce77674..ddc6fb86 100644 --- a/examples/efie.jl +++ b/examples/efie.jl @@ -1,9 +1,9 @@ using CompScienceMeshes using BEAST -Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) # Γ = meshsphere(radius=1.0, h=0.1) -# Γ = CompScienceMeshes.meshmobius(h=0.035) +Γ = CompScienceMeshes.meshmobius(h=0.035) X = raviartthomas(Γ) κ, η = 1.0, 1.0 diff --git a/examples/tdefie.jl b/examples/tdefie.jl index 3a410f3e..1ebe9cbf 100644 --- a/examples/tdefie.jl +++ b/examples/tdefie.jl @@ -26,7 +26,7 @@ SL = TDMaxwell3D.singlelayer(speedoflight=1.0, numdiffs=1) # BEAST.@defaultquadstrat (SL, W, V) BEAST.OuterNumInnerAnalyticQStrat(7) tdefie = @discretise SL[j′,j] == -1.0E[j′] j∈V j′∈W -xefie = solve(tdefie) +xefie = motsolve(tdefie) import Plots Plots.plot(xefie[1,:]) diff --git a/examples/tdemfie.jl b/examples/tdemfie.jl index dde00b0f..2064005c 100644 --- a/examples/tdemfie.jl +++ b/examples/tdemfie.jl @@ -33,4 +33,4 @@ N = NCross() emfie = @discretise( (0.5(N⊗I) + 1.0DL)[k,j] + SL[l,m] == -1.0H[k] - E[l], k∈Y⊗δ, l∈X⊗δ, j∈X⊗T1, m∈X⊗T1) -xemfie = solve(emfie) +xemfie = motsolve(emfie) diff --git a/examples/tdmfie.jl b/examples/tdmfie.jl index 49d5d21f..56a47cc3 100644 --- a/examples/tdmfie.jl +++ b/examples/tdmfie.jl @@ -29,7 +29,7 @@ N = NCross() @hilbertspace j mfie = @discretise (0.5(N⊗I) + 1.0DL)[k,j] == -1.0H[k] k∈W j∈V -xmfie = solve(mfie) +xmfie = motsolve(mfie) Xmfie, Δω, ω0 = fouriertransform(xmfie, Δt, 0.0, 2) ω = collect(ω0 .+ (0:Nt-1)*Δω) diff --git a/examples/tdpmchwt.jl b/examples/tdpmchwt.jl index 0cc4945b..f160b240 100644 --- a/examples/tdpmchwt.jl +++ b/examples/tdpmchwt.jl @@ -54,8 +54,10 @@ pmchwt = @discretise( # w = BEAST.ConvolutionOperators.polyvals(Z) # error() -u = solve(pmchwt) +u = BEAST.motsolve(pmchwt) + +Z = BEAST.sysmatrix(pmchwt) using Plots scatter!(u[1,:]) nothing diff --git a/src/bases/tensorbasis.jl b/src/bases/tensorbasis.jl index 4c7ced29..b8bce8ab 100644 --- a/src/bases/tensorbasis.jl +++ b/src/bases/tensorbasis.jl @@ -26,3 +26,15 @@ temporalbasis(s::SpaceTimeBasis) = s.time ⊗(a, b) = SpaceTimeBasis(a,b) numfunctions(S::SpaceTimeBasis) = numfunctions(S.space) * numfunctions(S.time) scalartype(st::SpaceTimeBasis) = promote_type(scalartype(st.space), scalartype(st.time)) + +function tensordim(s::SpaceTimeBasis,i) + i == 1 && return numfunctions(s.space) + i == 2 && return numfunctions(s.time) + return 1 +end + +function tensordim(s, i) + i == 1 && return numfunctions(s) + return 1 +end + diff --git a/src/solvers/lusolver.jl b/src/solvers/lusolver.jl index 0675eae7..745fec07 100644 --- a/src/solvers/lusolver.jl +++ b/src/solvers/lusolver.jl @@ -1,22 +1,6 @@ using LinearAlgebra -# function convert_to_dense(A::LinearMaps.LinearCombination) - -# # @info "convert matrix to dense..." -# # T = eltype(A) -# # # M = zeros(T, size(A)) -# # # M = PseudoBlockMatrix{T}(undef, BlockArrays.blocksizes(A)...) -# # # fill!(M,0) -# # M = zeros(eltype(A), axes(A)) -# # @show typeof(M) -# # for map in A.maps -# # mul!(M, 1, map, 1, 1) -# # end - -# # @info "matrix converted to dense" -# # return M -# return Matrix(A) -# end +lusolve(eq) = solve(eq) """ Solves a variational equation by simply creating the full system matrix @@ -41,10 +25,6 @@ function solve(eq) b = assemble(rhs, X) Z = assemble(lhs, X, Y) - # b = assemble(rhs, test_space_dict) - # Z = assemble(lhs, test_space_dict, trial_space_dict) - # M = convert_to_dense(Z) - # println("Sysmatrix converted to dense.") print("Converting system to Matrix...") M = Matrix(Z) @@ -54,53 +34,8 @@ function solve(eq) u = M \ Vector(b) println("done.") - # return u - # return PseudoBlockVector(u, blocksizes(Z,2)) ax = nestedrange(Y, 1, numfunctions) return PseudoBlockVector(u, (ax,)) end -function td_solve(eq) - - V = eq.trial_space_dict[1] - - # bilform = eq.equation.lhs - - A = assemble(eq.equation.lhs, eq.test_space_dict, eq.trial_space_dict) - T = eltype(A) - S = zeros(T, size(A)[1:2]) - ConvolutionOperators.timeslice!(S, A, 1) - - # S = timeslice(A,1) - # iS = inv(Array(S)) - iS = inv(S) - - b = td_assemble(eq.equation.rhs, eq.test_space_dict) - - nt = numfunctions(temporalbasis(V)) - marchonintime(iS, A, b, nt) -end - -# function timeslice(A::BlockArray, k) - -# I = blocklengths(axes(A,1)) -# J = blocklengths(axes(A,2)) - -# T = eltype(eltype(A)) -# S = PseudoBlockArray{T}(undef, I, J) -# fill!(S,0) - -# for i in 1:blocksize(A,1) -# for j in 1:blocksize(A,2) -# isassigned(A.blocks, i, j) || continue -# A[Block(i,j)] isa Zeros && continue -# A[Block(i,j)] isa Fill && continue -# Aij = A[Block(i,j)] -# Ak = AbstractArray(A[Block(i,j)].convop)[:,:,k] -# S[Block(i,j)] = Ak -# end -# end - -# return S -# end diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 02cd3020..1eb4c743 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -150,12 +150,15 @@ function assemble(lform::LinForm, X::DirectProductSpace) @assert !isempty(lform.terms) - M = length.(X.factors) - U = NestedUnitRanges.nestedrange(X, 1, numfunctions) - T = scalartype(lform, X) - Z = zeros(T, numfunctions(X)) - B = PseudoBlockArray{T}(Z, (U,)) + U = NestedUnitRanges.nestedrange(spatialbasis(X), 1, numfunctions) + + x = first(AbstractTrees.Leaves(X)) + N = Base.OneTo(tensordim(x,2)) + ax = _righthandside_axes(x, U, N) + + B = BlockArray{T}(undef, ax) + fill!(B, 0) for t in lform.terms @@ -165,7 +168,7 @@ function assemble(lform::LinForm, X::DirectProductSpace) for op in reverse(t.test_ops) x = op[end](op[1:end-1]..., x) end b = assemble(t.functional, x) - B[Block(m)] = t.coeff * b + B[Block(m),Block(1)] = t.coeff * b end return B @@ -180,45 +183,42 @@ Base.size(x::SpaceTimeData) = (size(x.data)[1],) Base.getindex(x::SpaceTimeData, i::Int) = x.data[i,:] function td_assemble(lform::LinForm, test_space_dict) + X = _spacedict_to_directproductspace(test_space_dict) + return td_assemble(lform, X) +end - terms = lform.terms +_righthandside_axes(x::SpaceTimeBasis, U, N) = (U,N,) +_righthandside_axes(x, U, N) = (U,) - T = Float32 - for term in lform.terms - T = scalartype(T,term.coeff) - T = scalartype(T,term.functional) - end - for kv in test_space_dict; T = scalartype(T,kv[2]) end +td_assemble(lform::LinForm, X::DirectProductSpace) = assemble(lform, X) - I = [numfunctions(spatialbasis(test_space_dict[i])) for i in 1:length(lform.test_space)] +# function td_assemble(lform::LinForm, X::DirectProductSpace) - M = zeros(Int, length(test_space_dict)) - for (p,x) in test_space_dict M[p]=numfunctions(spatialbasis(x)) end +# @assert !isempty(lform.terms) - N = [numfunctions(temporalbasis(test_space_dict[1]))] - B = BlockArray{T}(undef, M, N) +# T = scalartype(lform, X) +# U = NestedUnitRanges.nestedrange(spatialbasis(X), 1, numfunctions) - for t in terms +# x = first(AbstractTrees.Leaves(X)) +# N = Base.OneTo(tensordim(x,2)) +# ax = _rigthandside_axes(x, U, N) - α = t.coeff - a = t.functional - m = t.test_id - X = test_space_dict[m] - o = t.test_ops - - - # act with the various ops on X - for op in reverse(o) - Y = X; - X = op[end](op[1:end-1]..., Y) - end +# B = BlockArray{T}(undef, ax) +# fill!(B, 0) - b = assemble(a, X) - B[Block(m),Block(1)] = α*b - end +# for t in lform.terms - return B -end +# m = t.test_id +# x = X[m] + +# for op in reverse(t.test_ops) x = op[end](op[1:end-1]..., x) end + +# b = assemble(t.functional, x) +# B[Block(m),Block(1)] = t.coeff * b +# end + +# return B +# end function assemble(bilform::BilForm, test_space_dict, trial_space_dict; materialize=BEAST.assemble) @@ -261,56 +261,3 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; lift(z, Block(term.test_id), Block(term.trial_id), U, V) end end - - -# function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace) - -# test_space_dict = Dict(enumerate(X.factors)) -# trial_space_dict = Dict(enumerate(Y.factors)) - -# z = assemble(bf, test_space_dict, trial_space_dict) -# end - -# td_assemble(dbf::DiscreteBilform; materialize=BEAST.assemble) = -# td_assemble(dbf.bilform, dbf.test_space_dict, dbf.trial_space_dict; materialize) - - -# function td_assemble(bilform::BilForm, test_space_dict, trial_space_dict; -# materialize=BEAST.assemble) - -# X = _spacedict_to_directproductspace(test_space_dict) -# Y = _spacedict_to_directproductspace(trial_space_dict) - -# return td_assemble(bilform, X, Y) -# end - -# function td_assemble(bilform::BilForm, -# X::DirectProductSpace, -# Y::DirectProductSpace) - -# M = [length(fct.space) for fct in X.factors] -# N = [length(fct.space) for fct in Y.factors] - -# row_axis = BlockArrays.blockedrange(M) -# col_axis = BlockArrays.blockedrange(N) - -# sum(bilform.terms) do t - -# a = t.coeff * t.kernel - -# m = t.test_id -# x = X.factors[m] -# for op in reverse(t.test_ops) -# x = op[end](op[1:end-1]..., x) -# end - -# n = t.trial_id -# y = Y.factors[n] -# for op in reverse(t.trial_ops) -# y = op[end](op[1:end-1]..., y) -# end - -# z = assemble(a, x, y) -# lift(z, Block(m), Block(n), row_axis, col_axis) -# end -# end diff --git a/src/timedomain/motlu.jl b/src/timedomain/motlu.jl index 365c0e8d..21f5818b 100644 --- a/src/timedomain/motlu.jl +++ b/src/timedomain/motlu.jl @@ -1,3 +1,21 @@ +motsolve(eq) = td_solve(eq) + +function td_solve(eq) + + V = eq.trial_space_dict[1] + + A = assemble(eq.equation.lhs, eq.test_space_dict, eq.trial_space_dict) + T = eltype(A) + S = zeros(T, size(A)[1:2]) + ConvolutionOperators.timeslice!(S, A, 1) + + iS = inv(S) + b = assemble(eq.equation.rhs, eq.test_space_dict) + + nt = numfunctions(temporalbasis(V)) + marchonintime(iS, A, b, nt) +end + """ marchonintime(W0,Z,B,I; convhist=false) From 19c13ddb611924037d116436ded9f1b38ebe43e5 Mon Sep 17 00:00:00 2001 From: azuccott Date: Thu, 11 Jan 2024 13:56:09 +0100 Subject: [PATCH 305/528] update in acoustic single layer methods --- examples/disabled/tdhh3d_neumann.jl | 20 ++- examples/tdacusticsinglelayer.jl | 60 +++++--- src/helmholtz3d/timedomain/tdhh3dexc.jl | 1 + src/maxwell/timedomain/acustictdops.jl | 195 +++++++++++++++++++++--- src/timedomain/tdintegralop.jl | 6 + test/runtests.jl | 3 +- 6 files changed, 231 insertions(+), 54 deletions(-) diff --git a/examples/disabled/tdhh3d_neumann.jl b/examples/disabled/tdhh3d_neumann.jl index 0c7f6508..5db0c60b 100644 --- a/examples/disabled/tdhh3d_neumann.jl +++ b/examples/disabled/tdhh3d_neumann.jl @@ -2,8 +2,8 @@ using CompScienceMeshes using BEAST using LinearAlgebra -# G = meshsphere(1.0, 0.25) G = meshsphere(1.0, 0.30) +#G = CompScienceMeshes.meshmobius(h=0.1) c = 1.0 S = BEAST.HH3DSingleLayerTDBIO(c) @@ -20,9 +20,9 @@ h = BEAST.gradient(e) # X = lagrangecxd0(G) X = lagrangecxd0(G) -Y = duallagrangec0d1(G) -# Y = lagrangecxd0(G) - +#Y = duallagrangec0d1(G) +Y = lagrangecxd0(G) +numfunctions(X) # X = duallagrangecxd0(G, boundary(G)) # Y = lagrangec0d1(G) @@ -31,7 +31,7 @@ Y = duallagrangec0d1(G) Δt, Nt = 0.16, 300 # T = timebasisc0d1(Δt, Nt) P = timebasiscxd0(Δt, Nt) -H = timebasisc0d1(Δt, Nt) +#H = timebasisc0d1(Δt, Nt) δ = timebasisdelta(Δt, Nt) # assemble the right hand side @@ -43,9 +43,17 @@ Zd = Z0d + (-0.5)*Z1d u = marchonintime(inv(Zd[:,:,1]), Zd, bd, Nt) bs = assemble(e, X ⊗ δ) -Zs = assemble(S, X ⊗ δ, X ⊗ P, Val{:bandedstorage}) +Zs = assemble(S, X ⊗ δ, X ⊗ P) v = marchonintime(inv(Zs[:,:,1]), Zs, -bs, Nt) + +tdacusticsl = @discretise S[j′,j] == -1.0e[j′] j∈ (X ⊗ P) j′∈ (X ⊗ δ) +xacusticsl = solve(tdacusticsl) + + +import Plots +Plots.plot(xacusticsl[1000,2900:3000]) + U, Δω, ω0 = fouriertransform(u, Δt, 0.0, 2) V, Δω, ω0 = fouriertransform(v, Δt, 0.0, 2) diff --git a/examples/tdacusticsinglelayer.jl b/examples/tdacusticsinglelayer.jl index 5d1a3f71..4f049814 100644 --- a/examples/tdacusticsinglelayer.jl +++ b/examples/tdacusticsinglelayer.jl @@ -1,23 +1,20 @@ using CompScienceMeshes, BEAST, LinearAlgebra -Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) -Γ = meshsphere(radius=1.0, h=0.5) +#Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) +Γ = meshsphere(radius=1.0, h=0.30) X = lagrangecxd0(Γ) -numfunctions(X) -Δt = 0.05 -Nt = 20 + +Δt = 0.16 +Nt = 300 T = timebasisshiftedlagrange(Δt, Nt, 0) U = timebasisdelta(Δt, Nt) V = X ⊗ T W = X ⊗ U -duration = 2 * 20 * Δt -delay = 1.5 * duration -amplitude = 1.0 -gaussian = creategaussian(duration, delay, amplitude) -direction, polarisation = ẑ, x̂ -E = planewave(polarisation, direction, derive(gaussian), 1.0) +width, delay, scaling = 16.0, 24.0, 1.0 +gaussian = creategaussian(width, delay, scaling) +e = BEAST.planewave(point(0,0,1), 1.0, gaussian) @hilbertspace j @hilbertspace j′ @@ -25,20 +22,34 @@ E = planewave(polarisation, direction, derive(gaussian), 1.0) SL = TDAcustic3D.acusticsinglelayer(speedofsound=1.0, numdiffs=1) # BEAST.@defaultquadstrat (SL, W, V) BEAST.OuterNumInnerAnalyticQStrat(7) -tdacusticsl = @discretise SL[j′,j] == -1.0E[j′] j∈V j′∈W +tdacusticsl = @discretise SL[j′,j] == -1.0e[j′] j∈V j′∈W xacusticsl = solve(tdacusticsl) -#corregere da qui -import Plots -Plots.plot(xefie[1,:]) - -import Plotly -fcr, geo = facecurrents(xefie[:,125], X) -Plotly.plot(patch(geo, norm.(fcr))) - - - - +Z = assemble(SL,W,V) +#corregere da qui +#import Plots +#Plots.plot(xacusticsl[1,250:300]) + +#import Plotly +#fcr, geo = facecurrents(xefie[:,125], X) +#Plotly.plot(patch(geo, norm.(fcr))) + +import BEAST.ConvolutionOperators + +for a in 0:300 + za=ConvolutionOperators.timeslice(Z,a) + #zawilton=ConvolutionOperators.timeslice(Zs,a) + for i in 1:numfunctions(X) + for j in 1:numfunctions(X) + if (za[i,j])<0.0 + println(a,[i,j]," ",za[i,j]," ",) + end + end + end +end + +name=readline() +parse(Float64, name) Xefie, Δω, ω0 = fouriertransform(xefie, Δt, 0.0, 2) ω = collect(ω0 .+ (0:Nt-1)*Δω) @@ -48,4 +59,5 @@ _, i1 = findmin(abs.(ω.-1.0)) ue = Xefie[:,i1] / fouriertransform(gaussian)(ω1) fcr, geo = facecurrents(ue, X) -Plotly.plot(patch(geo, norm.(fcr))) \ No newline at end of file +Plotly.plot(patch(geo, norm.(fcr))) + diff --git a/src/helmholtz3d/timedomain/tdhh3dexc.jl b/src/helmholtz3d/timedomain/tdhh3dexc.jl index 045ea0b8..9119ca5c 100644 --- a/src/helmholtz3d/timedomain/tdhh3dexc.jl +++ b/src/helmholtz3d/timedomain/tdhh3dexc.jl @@ -10,6 +10,7 @@ function planewave(direction, speedoflight::Number, signature, amplitude=one(spe PlaneWaveHH3DTD(direction, speedoflight, signature, amplitude) end +scalartype(p::PlaneWaveHH3DTD) = scalartype(p.amplitude) *(a, f::PlaneWaveHH3DTD) = PlaneWaveHH3DTD(f.direction, f.speed_of_light, f.signature, a*f.amplitude) diff --git a/src/maxwell/timedomain/acustictdops.jl b/src/maxwell/timedomain/acustictdops.jl index 9bbbcca0..d23edece 100644 --- a/src/maxwell/timedomain/acustictdops.jl +++ b/src/maxwell/timedomain/acustictdops.jl @@ -31,8 +31,8 @@ end #of the module export TDAcustic3D -defaultquadstrat(::AcusticSingleLayerTDIO, tfs, bfs) = AllAnalyticalQStrat(1) - +defaultquadstrat(::AcusticSingleLayerTDIO, tfs, bfs) = #=nothing=# AllAnalyticalQStrat(1) +#nothing goes in hybrid qr, allanalytical goes in zuccottirule @@ -44,24 +44,96 @@ end quadrule(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, p, testel, q, trialel, r, timeel, qd, ::AllAnalyticalQStrat) = ZuccottiRule(1.0) + -function quaddata(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, - testels, trialels, timeels, quadstrat::OuterNumInnerAnalyticQStrat) + +function quaddata(operator::AcusticSingleLayerTDIO, + test_local_space, trial_local_space, time_local_space, + test_element, trial_element, time_element, quadstrat::Nothing) - dmax = numfunctions(timerefs)-1 - bn = binomial.((0:dmax),(0:dmax)') + dmax = numfunctions(time_local_space)-1 + bn = binomial.((0:dmax),(0:dmax)') - V = eltype(testels[1].vertices) - ws = WiltonInts84.workspace(V) - # quadpoints(testrefs, testels, (3,)), bn, ws - quadpoints(testrefs, testels, (quadstrat.outer_rule,)), bn, ws -end + V = eltype(test_element[1].vertices) + ws = WiltonInts84.workspace(V) + order = 4 + @show order + quadpoints(test_local_space, test_element, (order,)), bn, ws + end -quadrule(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, - p, testel, q, trialel, r, timeel, qd, ::OuterNumInnerAnalyticQStrat) = WiltonInts84Strat(qd[1][1,p],qd[2],qd[3]) + + # See: ?BEAST.quadrule for help + function quadrule(operator::AcusticSingleLayerTDIO, + test_local_space, trial_local_space, time_local_space, + p, test_element, q, trial_element, r, time_element, + quad_data, quadstrat::Nothing) + + # WiltonInts84Strat(quad_data[1,p]) + qd = quad_data + HybridZuccottiWiltonStrat(qd[1][1,p],qd[2],qd[3]) + + end + + + function innerintegrals!(zlocal, operator::AcusticSingleLayerTDIO, + test_point, + test_local_space, trial_local_space, time_local_space, + test_element, trial_element, time_element, + quad_rule::HybridZuccottiWiltonStrat, quad_weight) + + # error("Here!!!") + + dx = quad_weight + x = cartesian(test_point) + # n = normal(test_point) + + # a = trial_element[1] + # ξ = x - dot(x -a, n) * n + + r = time_element[1] + R = time_element[2] + @assert r < R + + N = max(degree(time_local_space), 1) + ∫G, ∫vG, ∫∇G = WiltonInts84.wiltonints( + trial_element[1], + trial_element[2], + trial_element[3], + x, r, R, Val{2}, quad_rule.workspace) + + a = dx / (4*pi) + #D = operator.num_diffs + D=0 + @assert D == 0 + @assert numfunctions(test_local_space) == 1 + @assert numfunctions(trial_local_space) == 1 + + @inline function tmRoR_sl(d, iG) + sgn = isodd(d) ? -1 : 1 + r = sgn * iG[d+2] + end + + # bns = quad_rule.binomials + + @assert D == 0 + for k in 1 : numfunctions(time_local_space) + d = k - 1 + d < D && continue + q = reduce(*, d-D+1:d ,init=1) + zlocal[1,1,k] += a * q * tmRoR_sl(d-D, ∫G) + end # k + end -function momintegrals!(z, op::AcusticSingleLayerTDIO, g::LagrangeRefSpace{T,0,3}, f::LagrangeRefSpace{T,0,3}, t::MonomialBasis{T,0,1}, τ, σ, ι, qr::ZuccottiStrat) where T + + +qpclineline=10^8#quasiparallel case controller +qpclinetriang=10^10 +qpctriangtriang=10^13 + +const qpc=[qpclineline, qpclinetriang, qpctriangtriang] + +function momintegrals!(z, op::AcusticSingleLayerTDIO, g::LagrangeRefSpace{T,0,3}, f::LagrangeRefSpace{T,0,3}, t::MonomialBasis{T,0,1}, τ, σ, ι, qr::ZuccottiRule) where T t1=ι[1] t2=ι[2] @@ -92,11 +164,13 @@ function momintegrals!(z, op::AcusticSingleLayerTDIO, g::LagrangeRefSpace{T,0,3} end end end - #print(τ[1]," ",τ[2]," ",τ[3]," ",σ[1]," ",σ[2]," ",σ[3]," ",t1," ",t2," ",) + if hits==3 - z[1,1,1]+=TimeDomainBEMInt.intcoinctriangles(τ[1],τ[2],τ[3],t1,t2) + #print("\np1=",τ[1],"\np2=",τ[2],"\np3=",τ[3],"\nt1=",t1,"\nt2=",t2) + + z[1,1,1]+=(TimeDomainBEMInt.intcoinctriangles(τ[1],τ[2],τ[3],t1,t2))/(4*π) elseif hits==2 - #pay attention with double layer and index permutation + #pay attention with double layer and index permutation or in whatever case has nx if mod1(a1index +1,3)==a2index a3index=mod1(a1index-1,3) else @@ -104,19 +178,94 @@ function momintegrals!(z, op::AcusticSingleLayerTDIO, g::LagrangeRefSpace{T,0,3} end if mod1(b1index+1,3)==b2index - b3index=mod1(a1index-1,3) + b3index=mod1(b1index-1,3) else - b3index=mod1(a1index+1,3) + b3index=mod1(b1index+1,3) end + #print("\np1=",τ[a1index],"\np2=",τ[a2index],"\np3=",τ[a3index],"\nv1=",σ[b1index],"\nv2=",σ[b2index],"\nv3=",σ[b3index],"\nt1=",t1,"\nt2=",t2) + - z[1,1,1]+=TimeDomainBEMInt.inttriangletriangleadjacent(τ[a1index],τ[a2index],τ[a3index],σ[b1index],σ[b2index],σ[a3index],t1,t2) + z[1,1,1]+=(TimeDomainBEMInt.inttriangletriangleadjacent(τ[a1index],τ[a2index],τ[a3index],σ[b1index],σ[b2index],σ[b3index],t1,t2,qpc))/(4*π) elseif hits==1 a2index,a3index=mod1(a1index+1,3),mod1(a1index+2,3) b2index,b3index=mod1(b1index+1,3),mod1(b1index+2,3) - - z[1,1,1]+=TimeDomainBEMInt.intcommonvertex(τ[a1index],τ[a2index],τ[a3index],σ[b2index],σ[b3index],t1,t2) + #print("\np1=",τ[a1index],"\np2=",τ[a2index],"\np3=",τ[a3index],"\nv1=",σ[b1index],"\nv2=",σ[b2index],"\nv3=",σ[b3index],"\nt1=",t1,"\nt2=",t2) + z[1,1,1]+=(TimeDomainBEMInt.intcommonvertex(τ[a1index],τ[a2index],τ[a3index],σ[b2index],σ[b3index],t1,t2,qpc))/(4*π) else - z[1,1,1]+=inttriangletriangle(τ[1],τ[2],τ[3],σ[1],σ[2],σ[3],t1,t2) + #print("\np1=",τ[1],"\np2=",τ[2],"\np3=",τ[3],"\nv1=",σ[1],"\nv2=",σ[2],"\nv3=",σ[3],"\nt1=",t1,"\nt2=",t2) + z[1,1,1]+=(inttriangletriangle(τ[1],τ[2],τ[3],σ[1],σ[2],σ[3],t1,t2,qpc))/(4*π) end #for the moment sos=1 but I will correct this +end + + +function momintegrals!(z, op::AcusticSingleLayerTDIO, g::LagrangeRefSpace{T,0,3}, f::LagrangeRefSpace{T,0,3}, t::MonomialBasis{T,0,1}, τ, σ, ι, qr::HybridZuccottiWiltonStrat) where T + + t1=ι[1] + t2=ι[2] + + + @assert t2 > t1 + + sos = op.speed_of_light + + a1index,a2index,a3index,b1index,b2index,b3index=0,0,0,0,0,0 + Tt = eltype(eltype(τ.vertices)) + hits = 0 + dtol = 1.0e3 * eps(Tt) + dmin2 = floatmax(Tt) + for t in 1:3 + for s in 1:3 + d2 = LinearAlgebra.norm_sqr(τ[t]-σ[s]) + dmin2 = min(dmin2, d2) + if (d2 < dtol) + hits+=1 + if hits==1 + a1index =t + b1index = s + elseif hits==2 + a2index=t + b2index=s + end + end + end + end + + if hits==3 + #print("\np1=",τ[1],"\np2=",τ[2],"\np3=",τ[3],"\nt1=",t1,"\nt2=",t2) + + z[1,1,1]+=(TimeDomainBEMInt.intcoinctriangles(τ[1],τ[2],τ[3],t1,t2))/(4*π) + elseif hits==2 + #pay attention with double layer and index permutation or in whatever case has nx + if mod1(a1index +1,3)==a2index + a3index=mod1(a1index-1,3) + else + a3index=mod1(a1index+1,3) + end + + if mod1(b1index+1,3)==b2index + b3index=mod1(b1index-1,3) + else + b3index=mod1(b1index+1,3) + end + #print("\np1=",τ[a1index],"\np2=",τ[a2index],"\np3=",τ[a3index],"\nv1=",σ[b1index],"\nv2=",σ[b2index],"\nv3=",σ[b3index],"\nt1=",t1,"\nt2=",t2) + + + z[1,1,1]+=(TimeDomainBEMInt.inttriangletriangleadjacent(τ[a1index],τ[a2index],τ[a3index],σ[b1index],σ[b2index],σ[b3index],t1,t2,qpc))/(4*π) + elseif hits==1 + a2index,a3index=mod1(a1index+1,3),mod1(a1index+2,3) + b2index,b3index=mod1(b1index+1,3),mod1(b1index+2,3) + #print("\np1=",τ[a1index],"\np2=",τ[a2index],"\np3=",τ[a3index],"\nv1=",σ[b1index],"\nv2=",σ[b2index],"\nv3=",σ[b3index],"\nt1=",t1,"\nt2=",t2) + z[1,1,1]+=(TimeDomainBEMInt.intcommonvertex(τ[a1index],τ[a2index],τ[a3index],σ[b2index],σ[b3index],t1,t2,qpc))/(4*π) + else + #print("\np1=",τ[1],"\np2=",τ[2],"\np3=",τ[3],"\nv1=",σ[1],"\nv2=",σ[2],"\nv3=",σ[3],"\nt1=",t1,"\nt2=",t2) + XW = qr.outer_quad_points + for p in 1 : length(XW) + x = XW[p].point + w = XW[p].weight + innerintegrals!(z, op, x, g, f, t, τ, σ, ι, qr, w) + end + + end + #for the moment sos=1 but I will correct this end \ No newline at end of file diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 5c6b10c2..1e2637e3 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -293,6 +293,12 @@ struct WiltonInts84Strat{T,V,W} workspace::W end +struct HybridZuccottiWiltonStrat{T,V,W} + outer_quad_points::T + binomials::V + workspace::W +end + struct ZuccottiRule{T} weight::T #Allanalyticalformula end diff --git a/test/runtests.jl b/test/runtests.jl index cf2ca17a..223922c9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -71,7 +71,8 @@ include("test_tdrhs_scaling.jl") include("test_td_tensoroperator.jl") include("test_variational.jl") - +include("test_ncrossbdm.jl") +include("test_curl_lagc0d1_lagc0d2.jl") try Pkg.installed("BogaertInts10") From aa8019f16bcae1de84704eb8d519216a96815e05 Mon Sep 17 00:00:00 2001 From: djukic14 <82029804+djukic14@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:23:40 +0100 Subject: [PATCH 306/528] Fix compat for SparseMatrixDicts for precompilation SparseMatrixDicts disables precompilation in new version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c1c8c34f..8c427f99 100644 --- a/Project.toml +++ b/Project.toml @@ -48,7 +48,7 @@ NestedUnitRanges = "0.2" Requires = "1" SauterSchwab3D = "0.1" SauterSchwabQuadrature = "2.3.0" -SparseMatrixDicts = "0.2" +SparseMatrixDicts = "< 0.2.8" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" WiltonInts84 = "0.2.5" From a1f19cfc3a5ef9bf600b94c6f12c241f4fd58448 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Wed, 17 Jan 2024 00:39:15 +0000 Subject: [PATCH 307/528] CompatHelper: bump compat for SparseMatrixDicts to 0.2, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 8c427f99..1b5c0c44 100644 --- a/Project.toml +++ b/Project.toml @@ -48,7 +48,7 @@ NestedUnitRanges = "0.2" Requires = "1" SauterSchwab3D = "0.1" SauterSchwabQuadrature = "2.3.0" -SparseMatrixDicts = "< 0.2.8" +SparseMatrixDicts = "< 0.2.8, 0.2" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" WiltonInts84 = "0.2.5" From f1093bf0047fd6cbe043e4b074fc3659de7cb261 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 23 Jan 2024 12:42:39 +0100 Subject: [PATCH 308/528] vieops quaddata calls the correct quadpoints implementation --- src/bases/bcspace.jl | 48 +++++++++++++++--------------- src/quadrature/sauterschwabints.jl | 16 ---------- src/solvers/solver.jl | 28 ----------------- 3 files changed, 24 insertions(+), 68 deletions(-) diff --git a/src/bases/bcspace.jl b/src/bases/bcspace.jl index c2fc1395..fdd07481 100644 --- a/src/bases/bcspace.jl +++ b/src/bases/bcspace.jl @@ -534,7 +534,7 @@ function buildhalfbc2(patch, port, dirichlet, prt_fluxes) end - +# Use the algebraic construction also used in dual3d function buffachristiansen2(Faces::CompScienceMeshes.AbstractMesh{U,D1,T}) where {U,D1,T} faces = barycentric_refinement(Faces) @@ -626,7 +626,7 @@ function buffachristiansen2(Faces::CompScienceMeshes.AbstractMesh{U,D1,T}) where RTBasis(faces, bfs, pos) end - +# Extend into both dual faces in a single call to buildhalfbc2 (aka extend_2_form) function buffachristiansen3(Faces::CompScienceMeshes.AbstractMesh{U,D1,T}) where {U,D1,T} faces = barycentric_refinement(Faces) @@ -686,28 +686,28 @@ function buffachristiansen3(Faces::CompScienceMeshes.AbstractMesh{U,D1,T}) where end end - # # Build the minus-patch - # ptch_vert_idx = Edge[2] - # ptch_face_idcs = [i for (i,face) in enumerate(cells(faces)) if ptch_vert_idx in face] - # patch = Mesh(vertices(faces), cells(faces)[ptch_face_idcs]) - # port = Mesh(vertices(faces), filter(c->port_vertex_idx in c, cells(boundary(patch)))) - # @assert numcells(patch) >= 6 - # @assert numcells(port) == 2 - # RT_int, RT_prt, x_int, x_prt = buildhalfbc2(patch, port, nothing) - # - # for (m,bf) in enumerate(RT_int.fns) - # for sh in bf - # cellid = ptch_face_idcs[sh.cellid] - # BEAST.add!(bfs[E], cellid, sh.refid, -1.0 * sh.coeff * x_int[m]) - # end - # end - # - # for (m,bf) in enumerate(RT_prt.fns) - # for sh in bf - # cellid = ptch_face_idcs[sh.cellid] - # BEAST.add!(bfs[E],cellid, sh.refid, -1.0 * sh.coeff * x_prt[m]) - # end - # end + # # Build the minus-patch + # ptch_vert_idx = Edge[2] + # ptch_face_idcs = [i for (i,face) in enumerate(cells(faces)) if ptch_vert_idx in face] + # patch = Mesh(vertices(faces), cells(faces)[ptch_face_idcs]) + # port = Mesh(vertices(faces), filter(c->port_vertex_idx in c, cells(boundary(patch)))) + # @assert numcells(patch) >= 6 + # @assert numcells(port) == 2 + # RT_int, RT_prt, x_int, x_prt = buildhalfbc2(patch, port, nothing) + + # for (m,bf) in enumerate(RT_int.fns) + # for sh in bf + # cellid = ptch_face_idcs[sh.cellid] + # BEAST.add!(bfs[E], cellid, sh.refid, -1.0 * sh.coeff * x_int[m]) + # end + # end + + # for (m,bf) in enumerate(RT_prt.fns) + # for sh in bf + # cellid = ptch_face_idcs[sh.cellid] + # BEAST.add!(bfs[E],cellid, sh.refid, -1.0 * sh.coeff * x_prt[m]) + # end + # end end diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index a7c99652..e9a69d1b 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -83,10 +83,6 @@ function (igd::Integrand)(x,y,f,g) end -# function sign_upon_permutation(op, I, J) -# return 1 -# end - function momintegrals!(op::Operator, test_local_space::RefSpace, trial_local_space::RefSpace, test_chart, trial_chart, out, rule::SauterSchwabStrategy) @@ -95,29 +91,17 @@ function momintegrals!(op::Operator, test_chart.vertices, trial_chart.vertices, rule) - # test_chart = simplex( - # test_chart.vertices[I[1]], - # test_chart.vertices[I[2]], - # test_chart.vertices[I[3]]) - # permute_vertices reparametrizes the simplex without affecting the normal test_chart = CompScienceMeshes.permute_vertices(test_chart, I) trial_chart = CompScienceMeshes.permute_vertices(trial_chart, J) - # trial_chart = simplex( - # trial_chart.vertices[J[1]], - # trial_chart.vertices[J[2]], - # trial_chart.vertices[J[3]]) - igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, rule) - # σ = sign_upon_permutation(op, I, J) K = dof_permutation(test_local_space, I) L = dof_permutation(trial_local_space, J) for i in 1:numfunctions(test_local_space) for j in 1:numfunctions(trial_local_space) - # out[i,j] = σ * G[K[i],L[j]] out[i,j] = G[K[i],L[j]] end end diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 1eb4c743..5cad0369 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -192,34 +192,6 @@ _righthandside_axes(x, U, N) = (U,) td_assemble(lform::LinForm, X::DirectProductSpace) = assemble(lform, X) -# function td_assemble(lform::LinForm, X::DirectProductSpace) - -# @assert !isempty(lform.terms) - -# T = scalartype(lform, X) -# U = NestedUnitRanges.nestedrange(spatialbasis(X), 1, numfunctions) - -# x = first(AbstractTrees.Leaves(X)) -# N = Base.OneTo(tensordim(x,2)) -# ax = _rigthandside_axes(x, U, N) - -# B = BlockArray{T}(undef, ax) -# fill!(B, 0) - -# for t in lform.terms - -# m = t.test_id -# x = X[m] - -# for op in reverse(t.test_ops) x = op[end](op[1:end-1]..., x) end - -# b = assemble(t.functional, x) -# B[Block(m),Block(1)] = t.coeff * b -# end - -# return B -# end - function assemble(bilform::BilForm, test_space_dict, trial_space_dict; materialize=BEAST.assemble) From f77185ad805e508cff1d9d91179084aa1ae126bf Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 23 Jan 2024 12:44:08 +0100 Subject: [PATCH 309/528] quadpoints called with possible multiple rules in vieops --- src/volumeintegral/vieops.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/volumeintegral/vieops.jl b/src/volumeintegral/vieops.jl index 57a0ed5b..fc9efd19 100644 --- a/src/volumeintegral/vieops.jl +++ b/src/volumeintegral/vieops.jl @@ -189,8 +189,8 @@ function quaddata(op::VIEOperator, # they result in many near singularity evaluations with any # resemblence of accuracy going down the drain! Simply don't! # (same for (5,7) btw...). - t_qp = quadpoints(test_local_space, test_charts, (qs.outer_rule)) - b_qp = quadpoints(trial_local_space, trial_charts, (qs.inner_rule)) + t_qp = quadpoints(test_local_space, test_charts, (qs.outer_rule,)) + b_qp = quadpoints(trial_local_space, trial_charts, (qs.inner_rule,)) sing_qp = (SauterSchwab3D._legendre(qs.sauter_schwab_1D,0,1), From 0501fc85ba3b4e32a526cb4caac7d8d94c03dada Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Tue, 30 Jan 2024 14:16:08 +0100 Subject: [PATCH 310/528] Add unitfunction to create function basis of constant function In particular, for electrostatic problems it is useful to define an all constant function (typically, per body). It can be used for the deflection of nullspaces or the formulation of saddlepoint problems (e.g., when we deal with a floating potential boundary condition) --- src/BEAST.jl | 2 +- src/bases/lagrange.jl | 28 ++++++++++++++++++++++++++++ test/test_basis.jl | 13 +++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/BEAST.jl b/src/BEAST.jl index 7788d8e6..f7744286 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -27,7 +27,7 @@ export dot export planewave export RefSpace, numfunctions, coordtype, scalartype, assemblydata, geometry, refspace, valuetype -export lagrangecxd0, lagrangec0d1, duallagrangec0d1 +export lagrangecxd0, lagrangec0d1, duallagrangec0d1, unitfunction export duallagrangecxd0 export lagdimension export restrict diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 790492e3..0e2ece84 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -48,6 +48,34 @@ function lagrangecxd0(mesh) LagrangeBasis{0,-1,NF}(geometry, fns, pos) end +""" + unitfunction(mesh) + +Constructs a constant function with value 1 on `mesh`. +""" +function unitfunction(mesh) + + T = coordtype(mesh) + geometry = mesh + + # create the local shapes + fns = Vector{Vector{Shape{T}}}(undef, 1) + pos = Vector{vertextype(mesh)}(undef, 1) + fns[1] = [Shape(i, 1, T(1.0)) for (i, cell) in enumerate(mesh)] + + # Arguably, the position is fairly meaningless + # in case of a global function. Might be replaced by something + # more useful. + # For now, we fill it with the average position of the shape functions + p = vertextype(mesh)(0.0, 0.0, 0.0) + for cell in mesh + p += cartesian(center(chart(mesh, cell))) + end + pos[1] = p ./ numcells(mesh) + + NF = 1 + LagrangeBasis{0,-1,NF}(geometry, fns, pos) +end """ lagrangec0d1(mesh[, bnd]) diff --git a/test/test_basis.jl b/test/test_basis.jl index 1bc38da7..70e3070e 100644 --- a/test/test_basis.jl +++ b/test/test_basis.jl @@ -79,6 +79,19 @@ for T in [Float32, Float64] @test numfunctions(X) == 1 @test length(X.fns[1]) == 6 end + +## Test unitfunction +using CompScienceMeshes +using BEAST +using Test + +for T in [Float32, Float64] + m = meshrectangle(T(1.0), T(1.0), T(0.5), 3) + X = unitfunction(m) + + @test numfunctions(X) == 1 +end + ## test the scalar trace for Lagrange functions using CompScienceMeshes using BEAST From 7b6c85896015b19ba014a863dac295333701c970 Mon Sep 17 00:00:00 2001 From: djukic14 Date: Wed, 7 Feb 2024 15:17:33 +0100 Subject: [PATCH 311/528] Add farfield to DoubleLayerRotatedMW3D --- src/BEAST.jl | 12 ++++---- src/maxwell/farfield.jl | 65 +++++++++++++++++++-------------------- src/maxwell/mwops.jl | 26 ++++++++-------- src/maxwell/nearfield.jl | 40 +++--------------------- src/maxwell/nxdbllayer.jl | 17 ++++++++-- test/test_dipole.jl | 16 ++++++---- 6 files changed, 79 insertions(+), 97 deletions(-) diff --git a/src/BEAST.jl b/src/BEAST.jl index f7744286..81194910 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -38,7 +38,7 @@ export nedelecd3d export nedelecc3d export portcells, rt_ports, getindex_rtg, subset export StagedTimeStep -export subdsurface,subdBasis,assemblydata,refspace +export subdsurface, subdBasis, assemblydata, refspace export spatialbasis, temporalbasis export ⊗ export timebasisc0d1 @@ -48,7 +48,7 @@ export timebasisshiftedlagrange export TimeBasisDeltaShifted export ntrace export strace -export ttrace +export ttrace export SingleLayer export DoubleLayer export DoubleLayerTransposed @@ -80,7 +80,7 @@ export curl export gradient export MWSingleLayerField3D export SingleLayerTrace -export DoubleLayerRotatedMW3D +export DoubleLayerRotatedMW3D, MWDoubleLayerRotatedFarField3D export MWSingleLayerPotential3D export VIEOperator @@ -259,9 +259,9 @@ include("utils/plotlyglue.jl") -const x̂ = point(1,0,0) -const ŷ = point(0,1,0) -const ẑ = point(0,0,1) +const x̂ = point(1, 0, 0) +const ŷ = point(0, 1, 0) +const ẑ = point(0, 0, 1) export x̂, ŷ, ẑ const n = NormalVector() diff --git a/src/maxwell/farfield.jl b/src/maxwell/farfield.jl index e97f0c26..e00f87a8 100644 --- a/src/maxwell/farfield.jl +++ b/src/maxwell/farfield.jl @@ -18,31 +18,21 @@ struct MWDoubleLayerFarField3D{K, U} <: MWFarField waveimpedance::U end +struct MWDoubleLayerRotatedFarField3D{K,U} <: MWFarField + gamma::K + waveimpedance::U +end + function MWFarField3D(; gamma=nothing, wavenumber=nothing, waveimpedance=nothing ) - if (gamma === nothing) && (wavenumber === nothing) - error("Supply one of (not both) gamma or wavenumber") - end - - if (gamma !== nothing) && (wavenumber !== nothing) - error("Supply one of (not both) gamma or wavenumber") - end - - if gamma === nothing - if iszero(real(wavenumber)) - gamma = -imag(wavenumber) - else - gamma = im*wavenumber - end - end - + gamma, _ = gamma_wavenumber_handler(gamma, wavenumber) @assert gamma !== nothing waveimpedance === nothing && (waveimpedance = 1.0) - + return MWFarField3D(gamma, waveimpedance) end @@ -53,26 +43,12 @@ function MWDoubleLayerFarField3D(; wavenumber=nothing, waveimpedance=nothing ) - if (gamma === nothing) && (wavenumber === nothing) - error("Supply one of (not both) gamma or wavenumber") - end - - if (gamma !== nothing) && (wavenumber !== nothing) - error("Supply one of (not both) gamma or wavenumber") - end - - if gamma === nothing - if iszero(real(wavenumber)) - gamma = -imag(wavenumber) - else - gamma = im*wavenumber - end - end + gamma, _ = gamma_wavenumber_handler(gamma, wavenumber) @assert gamma !== nothing waveimpedance === nothing && (waveimpedance = 1.0) - + return MWDoubleLayerFarField3D(gamma, waveimpedance) end @@ -97,4 +73,25 @@ end kernelvals(op::MWFarField3DDropConstant,y,p) = expm1(op.gamma*dot(y,cartesian(p))) function integrand(op::MWFarField3DDropConstant,krn,y,f,p) op.coeff*(y × (krn * f[1])) × y -end \ No newline at end of file +end + +function MWDoubleLayerRotatedFarField3D(; + gamma=nothing, + wavenumber=nothing, + waveimpedance=nothing +) + gamma, _ = gamma_wavenumber_handler(gamma, wavenumber) + + @assert gamma !== nothing + + waveimpedance === nothing && (waveimpedance = 1.0) + + return MWDoubleLayerRotatedFarField3D(gamma, waveimpedance) +end + +MWDoubleLayerRotatedFarField3D(op::DoubleLayerRotatedMW3D{T,U}) where {T,U} = +MWDoubleLayerRotatedFarField3D(op.gamma, T(1)) + +function integrand(op::MWDoubleLayerRotatedFarField3D, krn, y, f, p) + op.waveimpedance * (y × ((krn * f[1]) × normal(p))) +end diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 7b180211..0d52e537 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -190,7 +190,7 @@ end function (igd::Integrand{<:MWDoubleLayer3D})(x,y,f,g) - + γ = igd.operator.gamma r = cartesian(x) - cartesian(y) @@ -207,7 +207,7 @@ end function (igd::Integrand{<:MWDoubleLayer3DReg})(x,y,f,g) - + γ = igd.operator.gamma r = cartesian(x) - cartesian(y) @@ -231,17 +231,17 @@ end ################################################################################ function gamma_wavenumber_handler(gamma, wavenumber) - if (gamma !== nothing) && (wavenumber !== nothing) - error("Supply one of (not both) gamma or wavenumber") - end + if !((gamma !== nothing) ⊻ (wavenumber !== nothing)) + error("Supply one of (not both) gamma or wavenumber") + end - if gamma === nothing && (wavenumber !== nothing) - if iszero(real(wavenumber)) - gamma = -imag(wavenumber) - else - gamma = im*wavenumber - end - end + if gamma === nothing && (wavenumber !== nothing) + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end return gamma, wavenumber end @@ -260,4 +260,4 @@ function operator_parameter_handler(alpha, gamma, wavenumber) end return alpha, gamma -end \ No newline at end of file +end diff --git a/src/maxwell/nearfield.jl b/src/maxwell/nearfield.jl index 085a05b3..aaeab729 100644 --- a/src/maxwell/nearfield.jl +++ b/src/maxwell/nearfield.jl @@ -21,23 +21,7 @@ function MWSingleLayerField3D(; alpha=nothing, beta=nothing ) - - if (gamma === nothing) && (wavenumber === nothing) - error("Supply one of (not both) gamma or wavenumber") - end - - if (gamma !== nothing) && (wavenumber !== nothing) - error("Supply one of (not both) gamma or wavenumber") - end - - if gamma === nothing - if iszero(real(wavenumber)) - gamma = -imag(wavenumber) - else - gamma = im*wavenumber - end - end - + gamma, _ = gamma_wavenumber_handler(gamma, wavenumber) @assert gamma !== nothing alpha === nothing && (alpha = -gamma) @@ -52,28 +36,12 @@ MWSingleLayerField3D(op::MWSingleLayer3D{T,U}) where {T,U} = MWSingleLayerField3 MWDoubleLayerField3D(; gamma, wavenumber) Create the double layer near field operator, for use with `potential`. -""" +""" function MWDoubleLayerField3D(; gamma=nothing, wavenumber=nothing -) - - if (gamma === nothing) && (wavenumber === nothing) - error("Supply one of (not both) gamma or wavenumber") - end - - if (gamma !== nothing) && (wavenumber !== nothing) - error("Supply one of (not both) gamma or wavenumber") - end - - if gamma === nothing - if iszero(real(wavenumber)) - gamma = -imag(wavenumber) - else - gamma = im*wavenumber - end - end - +) + gamma, _ = gamma_wavenumber_handler(gamma, wavenumber) @assert gamma !== nothing MWDoubleLayerField3D(gamma) diff --git a/src/maxwell/nxdbllayer.jl b/src/maxwell/nxdbllayer.jl index 3507acb5..12358beb 100644 --- a/src/maxwell/nxdbllayer.jl +++ b/src/maxwell/nxdbllayer.jl @@ -1,7 +1,20 @@ +""" + struct DoubleLayerRotatedMW3D{T,K} <: MaxwellOperator3D{T,K} + Bilinear form given by: -mutable struct DoubleLayerRotatedMW3D{T,K} <: MaxwellOperator3D{T,K} - "im times the wavenumber" + ```math + α ∬_{Γ^2} k(x) ⋅ [n̂(x) × (∇G_γ(x-y) × j(y))] + ``` + + with ``G_γ = e^{-γ|x-y|} / 4π|x-y|`` + +# Fields +- `alpha::T`: Factor in front of bilinear form. +- `gamma::K`: imaginary unit times the wavenumber. + +""" +struct DoubleLayerRotatedMW3D{T,K} <: MaxwellOperator3D{T,K} alpha::T gamma::K end diff --git a/test/test_dipole.jl b/test/test_dipole.jl index 3decdff5..336f5d62 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -30,10 +30,10 @@ for U in [Float32,Float64] pts = [point(U,cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] # This is an electric dipole - # The pre-factor (1/ε) is used to resemble + # The pre-factor (1/ε) is used to resemble # (9.18) in Jackson's Classical Electrodynamics - local E = U(1/ε) * dipolemw3d(location=SVector(U(0.4),U(0.2),U(0)), - orientation=U(1e-9).*SVector(U(0.5),U(0.5),U(0)), + local E = U(1/ε) * dipolemw3d(location=SVector(U(0.4),U(0.2),U(0)), + orientation=U(1e-9).*SVector(U(0.5),U(0.5),U(0)), wavenumber=k) local n = BEAST.NormalVector() @@ -57,11 +57,15 @@ for U in [Float32,Float64] nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(𝓚), pts, j_EFIE, X) ff_E_EFIE = potential(MWFarField3D(𝓣), pts, j_EFIE, X) ff_H_EFIE = potential(BEAST.MWDoubleLayerFarField3D(𝓚), pts, j_EFIE, X) + ff_H_EFIE_doublecrossed = potential(BEAST.MWDoubleLayerRotatedFarField3D(n × 𝓚), pts, -j_EFIE, n×X) + @test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 @test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.001 @test norm(ff_H_EFIE - H.(pts, isfarfield=true))/norm(H.(pts, isfarfield=true)) ≈ 0 atol=0.001 + @test norm(ff_H_EFIE_doublecrossed - H.(pts, isfarfield=true))/norm(H.(pts, isfarfield=true)) ≈ 0 atol=0.001 + @test ff_H_EFIE ≈ ff_H_EFIE_doublecrossed rtol=1e-7 K_bc = Matrix(assemble(𝓚,Y,X)) G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) @@ -77,14 +81,14 @@ for U in [Float32,Float64] @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 - H = dipolemw3d(location=SVector(U(0.0),U(0.0),U(0.3)), - orientation=U(1e-9).*SVector(U(0.5),U(0.5),U(0)), + H = dipolemw3d(location=SVector(U(0.0),U(0.0),U(0.3)), + orientation=U(1e-9).*SVector(U(0.5),U(0.5),U(0)), wavenumber=k) # This time, we do not specify alpha and beta # We include η in the magnetic RHS 𝓣 = Maxwell3D.singlelayer(wavenumber=k) - + 𝒉 = (n × H) × n E = (1/(im*ε*ω))*curl(H) 𝒆 = (n × E) × n From 91be4fb717892c08802d5fe4ed0aecc912a503c9 Mon Sep 17 00:00:00 2001 From: djukic14 Date: Thu, 8 Feb 2024 14:15:38 +0100 Subject: [PATCH 312/528] Rename waveimpedance to amplitude --- src/maxwell/farfield.jl | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/maxwell/farfield.jl b/src/maxwell/farfield.jl index e00f87a8..51e0c94d 100644 --- a/src/maxwell/farfield.jl +++ b/src/maxwell/farfield.jl @@ -11,29 +11,29 @@ where ``̂x`` is the unit vector in the direction of observation. Note that the """ struct MWFarField3D{K, U} <: MWFarField gamma::K - waveimpedance::U + amplitude::U end struct MWDoubleLayerFarField3D{K, U} <: MWFarField gamma::K - waveimpedance::U + amplitude::U end struct MWDoubleLayerRotatedFarField3D{K,U} <: MWFarField gamma::K - waveimpedance::U + amplitude::U end function MWFarField3D(; gamma=nothing, wavenumber=nothing, - waveimpedance=nothing + amplitude=nothing ) gamma, _ = gamma_wavenumber_handler(gamma, wavenumber) @assert gamma !== nothing - waveimpedance === nothing && (waveimpedance = 1.0) + amplitude === nothing && (amplitude = 1.0) - return MWFarField3D(gamma, waveimpedance) + return MWFarField3D(gamma, amplitude) end MWFarField3D(op::MWSingleLayer3D{T,U}) where {T,U} = MWFarField3D(op.gamma, sqrt(op.α*op.β)) @@ -41,15 +41,15 @@ MWFarField3D(op::MWSingleLayer3D{T,U}) where {T,U} = MWFarField3D(op.gamma, sqrt function MWDoubleLayerFarField3D(; gamma=nothing, wavenumber=nothing, - waveimpedance=nothing + amplitude=nothing ) gamma, _ = gamma_wavenumber_handler(gamma, wavenumber) @assert gamma !== nothing - waveimpedance === nothing && (waveimpedance = 1.0) + amplitude === nothing && (amplitude = 1.0) - return MWDoubleLayerFarField3D(gamma, waveimpedance) + return MWDoubleLayerFarField3D(gamma, amplitude) end MWDoubleLayerFarField3D(op::MWDoubleLayer3D{T}) where {T} = MWDoubleLayerFarField3D(op.gamma, 1.0) @@ -59,11 +59,11 @@ MWDoubleLayerFarField3D(op::MWDoubleLayer3D{T}) where {T} = MWDoubleLayerFarFiel kernelvals(op::MWFarField,y,p) = exp(op.gamma*dot(y,cartesian(p))) function integrand(op::MWFarField3D,krn,y,f,p) - op.waveimpedance*(y × (krn * f[1])) × y + op.amplitude*(y × (krn * f[1])) × y end function integrand(op::MWDoubleLayerFarField3D,krn,y,f,p) - op.waveimpedance*(y × (krn * f[1])) + op.amplitude*(y × (krn * f[1])) end struct MWFarField3DDropConstant{K, U} <: MWFarField @@ -78,20 +78,20 @@ end function MWDoubleLayerRotatedFarField3D(; gamma=nothing, wavenumber=nothing, - waveimpedance=nothing + amplitude=nothing ) gamma, _ = gamma_wavenumber_handler(gamma, wavenumber) @assert gamma !== nothing - waveimpedance === nothing && (waveimpedance = 1.0) + amplitude === nothing && (amplitude = 1.0) - return MWDoubleLayerRotatedFarField3D(gamma, waveimpedance) + return MWDoubleLayerRotatedFarField3D(gamma, amplitude) end MWDoubleLayerRotatedFarField3D(op::DoubleLayerRotatedMW3D{T,U}) where {T,U} = MWDoubleLayerRotatedFarField3D(op.gamma, T(1)) function integrand(op::MWDoubleLayerRotatedFarField3D, krn, y, f, p) - op.waveimpedance * (y × ((krn * f[1]) × normal(p))) + op.amplitude * (y × ((krn * f[1]) × normal(p))) end From a3ac2383bd9a363fdcef4a12b4280e11d4a83a11 Mon Sep 17 00:00:00 2001 From: djukic14 Date: Thu, 8 Feb 2024 14:19:16 +0100 Subject: [PATCH 313/528] Add cross functionality to double layer farfield --- src/maxwell/farfield.jl | 3 +++ test/test_dipole.jl | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/maxwell/farfield.jl b/src/maxwell/farfield.jl index 51e0c94d..0a41e0b7 100644 --- a/src/maxwell/farfield.jl +++ b/src/maxwell/farfield.jl @@ -95,3 +95,6 @@ MWDoubleLayerRotatedFarField3D(op.gamma, T(1)) function integrand(op::MWDoubleLayerRotatedFarField3D, krn, y, f, p) op.amplitude * (y × ((krn * f[1]) × normal(p))) end + +LinearAlgebra.cross(::NormalVector, a::MWDoubleLayerFarField3D) = MWDoubleLayerRotatedFarField3D(a.gamma, a.amplitude) +LinearAlgebra.cross(::NormalVector, a::MWDoubleLayerRotatedFarField3D) = MWDoubleLayerFarField3D(a.gamma, -a.amplitude) diff --git a/test/test_dipole.jl b/test/test_dipole.jl index 336f5d62..f11e277f 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -57,15 +57,19 @@ for U in [Float32,Float64] nf_H_EFIE = potential(BEAST.MWDoubleLayerField3D(𝓚), pts, j_EFIE, X) ff_E_EFIE = potential(MWFarField3D(𝓣), pts, j_EFIE, X) ff_H_EFIE = potential(BEAST.MWDoubleLayerFarField3D(𝓚), pts, j_EFIE, X) - ff_H_EFIE_doublecrossed = potential(BEAST.MWDoubleLayerRotatedFarField3D(n × 𝓚), pts, -j_EFIE, n×X) + ff_H_EFIE_rotated = potential(n × BEAST.MWDoubleLayerFarField3D(𝓚), pts, -j_EFIE, n × X) + ff_H_EFIE_doublerotated = potential(n × BEAST.MWDoubleLayerRotatedFarField3D(n × 𝓚), pts, -j_EFIE, X) @test norm(nf_E_EFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 @test norm(nf_H_EFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @test norm(ff_E_EFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.001 @test norm(ff_H_EFIE - H.(pts, isfarfield=true))/norm(H.(pts, isfarfield=true)) ≈ 0 atol=0.001 - @test norm(ff_H_EFIE_doublecrossed - H.(pts, isfarfield=true))/norm(H.(pts, isfarfield=true)) ≈ 0 atol=0.001 - @test ff_H_EFIE ≈ ff_H_EFIE_doublecrossed rtol=1e-7 + @test norm(ff_H_EFIE_rotated - H.(pts, isfarfield=true))/norm(H.(pts, isfarfield=true)) ≈ 0 atol=0.001 + @test norm(ff_H_EFIE_doublerotated - H.(pts, isfarfield=true))/norm(H.(pts, isfarfield=true)) ≈ 0 atol=0.001 + @test ff_H_EFIE ≈ ff_H_EFIE_rotated rtol=1e-7 + @test ff_H_EFIE_rotated ≈ ff_H_EFIE_doublerotated rtol=1e-7 + K_bc = Matrix(assemble(𝓚,Y,X)) G_nxbc_rt = Matrix(assemble(𝓝,Y,X)) From 1be83536168b151e2099a6f06b966415afe1b304 Mon Sep 17 00:00:00 2001 From: Paula Respondek Date: Fri, 2 Feb 2024 15:17:01 +0100 Subject: [PATCH 314/528] Add unitfunction for lagrange pyramid functions To enable assembly of hypersingular operator with unitfunction, a Basis consisting of pyramids instead of patches is necessary. I renamed the previous unitfunction to unitfunctioncxd0, and named the pyramid version unitfunctionc0d1. --- src/BEAST.jl | 2 +- src/bases/lagrange.jl | 100 +++++++++++++++++++++++++++++++++++++++++- test/test_basis.jl | 16 ++++++- 3 files changed, 114 insertions(+), 4 deletions(-) diff --git a/src/BEAST.jl b/src/BEAST.jl index 81194910..9617ea2b 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -27,7 +27,7 @@ export dot export planewave export RefSpace, numfunctions, coordtype, scalartype, assemblydata, geometry, refspace, valuetype -export lagrangecxd0, lagrangec0d1, duallagrangec0d1, unitfunction +export lagrangecxd0, lagrangec0d1, duallagrangec0d1, unitfunctioncxd0, unitfunctionc0d1 export duallagrangecxd0 export lagdimension export restrict diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 0e2ece84..5d27f416 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -49,11 +49,11 @@ function lagrangecxd0(mesh) end """ - unitfunction(mesh) + unitfunctioncxd0(mesh) Constructs a constant function with value 1 on `mesh`. """ -function unitfunction(mesh) +function unitfunctioncxd0(mesh) T = coordtype(mesh) geometry = mesh @@ -77,6 +77,102 @@ function unitfunction(mesh) LagrangeBasis{0,-1,NF}(geometry, fns, pos) end +""" + unitfunctionc0d1(mesh) + +Constructs a constant function with value 1 on `mesh` consisting of linear shapes. For dirichlet=true goes to zero on the boundary. +""" +function unitfunctionc0d1(mesh; dirichlet=true) + if dirichlet == false + return unitfunctionc0d1(mesh, skeleton(mesh,0)) + else + return unitfunctionc0d1_dirichlet(mesh) + end +end + +function unitfunctionc0d1_dirichlet(mesh) + + T = coordtype(mesh) + + verts = skeleton(mesh, 0) + detached = trues(numvertices(mesh)) + for v in cells(verts) + detached[v] = false + end + + bnd = boundary(mesh) + bndverts = skeleton(bnd, 0) + notonbnd = trues(numvertices(mesh)) + for v in cells(bndverts) + notonbnd[v] = false + end + + vertexlist = findall(notonbnd .& .!detached) + + cellids, ncells = vertextocellmap(mesh) + + Cells = cells(mesh) + Verts = vertices(mesh) + + # create the local shapes + fns = Vector{Vector{Shape{T}}}(undef, 1) + pos = Vector{vertextype(mesh)}(undef, 1) + + numshapes = sum(ncells[vertexlist]) + shapes = Vector{Shape{T}}(undef,numshapes) + n = 0 + for v in vertexlist + nshapes = ncells[v] + nshapes == 0 && continue + + for s in 1: nshapes + c = cellids[v,s] + + cell = Cells[c] + + localid = something(findfirst(isequal(v), cell),0) + @assert localid != 0 + + shapes[s+n] = Shape(c, localid, T(1.0)) + + end + n += nshapes + end + fns[1] = shapes + p = sum(mesh.vertices[vertexlist])/length(vertexlist) + pos[1] = p + + NF = 3 + LagrangeBasis{1,0,NF}(mesh, fns, pos) +end + +function unitfunctionc0d1(mesh, nodes::CompScienceMeshes.AbstractMesh{U,1} where {U}) + Conn = connectivity(nodes, mesh, abs) + rows = rowvals(Conn) + vals = nonzeros(Conn) + + T = coordtype(mesh) + P = vertextype(mesh) + S = Shape{T} + + fns = Vector{Vector{Shape{T}}}(undef, 1) + pos = Vector{vertextype(mesh)}(undef, 1) + fn = Vector{S}() + for (i,node) in enumerate(nodes) + for k in nzrange(Conn,i) + cellid = rows[k] + refid = vals[k] + push!(fn, Shape(cellid, refid, T(1.0))) + end + end + fns[1] = fn + p = sum(nodes.vertices)/length(nodes.vertices) + pos[1] = p + + NF = dimension(mesh) + 1 + LagrangeBasis{1,0,NF}(mesh, fns, pos) +end + """ lagrangec0d1(mesh[, bnd]) diff --git a/test/test_basis.jl b/test/test_basis.jl index 70e3070e..38e153b7 100644 --- a/test/test_basis.jl +++ b/test/test_basis.jl @@ -87,9 +87,23 @@ using Test for T in [Float32, Float64] m = meshrectangle(T(1.0), T(1.0), T(0.5), 3) - X = unitfunction(m) + X = unitfunctioncxd0(m) @test numfunctions(X) == 1 + @test length(X.fns[1]) == numcells(m) + @test assemble(Identity(), X, X) ≈ [1.0] + + X1 = unitfunctionc0d1(m) + + @test numfunctions(X1) == 1 + @test length(X1.fns[1]) == 6 + @test assemble(Identity(), X1, X1) ≈ [0.125] + + X2 = unitfunctionc0d1(m; dirichlet=false) + + @test numfunctions(X2) == 1 + @test length(X2.fns[1]) == numcells(m) * 3 + @test assemble(Identity(), X2, X2) ≈ [1.0] end ## test the scalar trace for Lagrange functions From 06589747517bcd74fc78ebd9b64af11e8711e332 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 10 Feb 2024 00:35:12 +0000 Subject: [PATCH 315/528] CompatHelper: bump compat for LinearMaps to 3, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 8c427f99..6690ed8a 100644 --- a/Project.toml +++ b/Project.toml @@ -43,7 +43,7 @@ FastGaussQuadrature = "0.3, 0.4, 0.5, 1" FillArrays = "0.11, 0.12, 0.13, 1" IterativeSolvers = "0.9" LiftedMaps = "0.5.1" -LinearMaps = "3.7 - 3.9" +LinearMaps = "3.7 - 3.9, 3" NestedUnitRanges = "0.2" Requires = "1" SauterSchwab3D = "0.1" From a46729c3bd8064bc0462d54acd40909031180135 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 12 Feb 2024 11:42:20 +0100 Subject: [PATCH 316/528] assemble can act on bilform bemspace pair args --- Project.toml | 2 +- src/solvers/solver.jl | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 8c427f99..a0d1a4a2 100644 --- a/Project.toml +++ b/Project.toml @@ -43,7 +43,7 @@ FastGaussQuadrature = "0.3, 0.4, 0.5, 1" FillArrays = "0.11, 0.12, 0.13, 1" IterativeSolvers = "0.9" LiftedMaps = "0.5.1" -LinearMaps = "3.7 - 3.9" +LinearMaps = "3.7 - 3.9, 3.11.2" NestedUnitRanges = "0.2" Requires = "1" SauterSchwab3D = "0.1" diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 5cad0369..bbc0034a 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -233,3 +233,13 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; lift(z, Block(term.test_id), Block(term.trial_id), U, V) end end + +function assemble(bf::BilForm, X::Space, Y::Space) + @assert length(bf.terms) == 1 + assemble(bf, BEAST.DirectProductSpace([X]), BEAST.DirectProductSpace([Y])) +end + +function assemble(bf::BilForm, pairs::Pair...) + dbf = discretise(bf, pairs...) + assemble(dbf) +end \ No newline at end of file From 4b3134354cef043964564f4f4f1db18abaf9f072 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 12 Feb 2024 11:43:04 +0100 Subject: [PATCH 317/528] Set version to 2.2.0 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index a0d1a4a2..c4f68704 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "2.1.0" +version = "2.2.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From 636db30b30644031fce7925bdf0ed64a8746f548 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 12 Feb 2024 14:39:45 +0100 Subject: [PATCH 318/528] test interpolation for RTRefSpace --- examples/efie.jl | 4 +-- src/bases/local/rtlocal.jl | 49 +++++++++++++++++++++++++-- test/runtests.jl | 1 + test/test_interpolate_and_restrict.jl | 29 ++++++++++++++++ 4 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 test/test_interpolate_and_restrict.jl diff --git a/examples/efie.jl b/examples/efie.jl index ddc6fb86..5ce77674 100644 --- a/examples/efie.jl +++ b/examples/efie.jl @@ -1,9 +1,9 @@ using CompScienceMeshes using BEAST -# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) # Γ = meshsphere(radius=1.0, h=0.1) -Γ = CompScienceMeshes.meshmobius(h=0.035) +# Γ = CompScienceMeshes.meshmobius(h=0.035) X = raviartthomas(Γ) κ, η = 1.0, 1.0 diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index f883a3bf..981a3023 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -99,7 +99,52 @@ const _dof_perms_rt = [ ] function dof_permutation(::RTRefSpace, vert_permutation) - i = findfirst(==(tuple(vert_permutation...)), _vert_perms_rt) - @assert i != nothing + i = something(findfirst(==(tuple(vert_permutation...)), _vert_perms_rt),0) + @assert i != 0 return _dof_perms_rt[i] +end + + +""" + interpolate(interpolant::RefSpace, chart1, interpolee::RefSpace, chart2) + +Computes by interpolation approximations of the local shape functions for +`interpolee` on `chart2` in terms of the local shape functions for `interpolant` +on `chart1`. The returned value is a matrix `Q` such that + +```math +\\phi_i \\approx \\sum_j Q_{ij} \\psi_j +``` + +with ``\\phi_i`` the i-th local shape function for `interpolee` and ``\\psi_j`` the +j-th local shape function for `interpolant`. +""" +function interpolate(interpolant::RTRefSpace, chart1, interpolee::RefSpace, chart2) + function fields(p) + x = cartesian(p) + v = carttobary(chart2, x) + r = neighborhood(chart2, v) + fieldvals = [f.value for f in interpolee(r)] + end + + interpolate(fields, interpolant, chart1) +end + +function interpolate(fields, interpolant::RTRefSpace, chart) + Q = map(faces(chart)) do face + p = center(face) + x = cartesian(p) + u = carttobary(chart, x) + q = neighborhood(chart, u) + n = normal(q) + + # minus because in CSM the tangent points towards vertex[1] + t = -tangents(p,1) + m = cross(t,n) + + fieldvals = fields(q) + q = [dot(fv,m) for fv in fieldvals] + end + + return hcat(Q...) end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index cf2ca17a..4fafe1ea 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -32,6 +32,7 @@ include("test_ndjunction.jl") include("test_ndspace.jl") include("test_restrict.jl") include("test_ndlcd_restrict.jl") +include("test_interpolate_and_restrict.jl") include("test_rt3d.jl") include("test_gradient.jl") include("test_mult.jl") diff --git a/test/test_interpolate_and_restrict.jl b/test/test_interpolate_and_restrict.jl new file mode 100644 index 00000000..fd6d4882 --- /dev/null +++ b/test/test_interpolate_and_restrict.jl @@ -0,0 +1,29 @@ +using Test + +using BEAST +using CompScienceMeshes + +chart1 = simplex( + point(1,0,0), + point(0,1,0), + point(0,0,0)) + +chart2 = simplex( + point(1/2,0,0), + point(0,1/2,0), + point(1,1,0)) + +X = BEAST.RTRefSpace{Float64}() +@time Q1 = BEAST.restrict(X, chart1, chart2) +@time Q2 = BEAST.interpolate(X, chart2, X, chart1) +@test Q1 ≈ Q2 + +constant_vector_field = point(1,2,0) +Q3 = BEAST.interpolate(X, chart2) do p + return [constant_vector_field] +end + +ctr = center(chart2) +vals = [f.value for f in X(ctr)] +itpol = sum(w*val for (w,val) in zip(Q3,vals)) +@test itpol ≈ constant_vector_field \ No newline at end of file From 061ab0cca68758c92bcb36bee15eb6fcf88b349d Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 12 Feb 2024 15:12:43 +0100 Subject: [PATCH 319/528] swapped SparseMatrixDicts for ExtendableSparse --- Project.toml | 4 ++-- src/BEAST.jl | 2 +- src/localop.jl | 2 +- test/test_gradient.jl | 2 +- test/test_rt.jl | 2 +- test/test_rt3d.jl | 6 +++--- test/test_rtx.jl | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Project.toml b/Project.toml index c4f68704..2a7abbc8 100644 --- a/Project.toml +++ b/Project.toml @@ -11,6 +11,7 @@ CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" ConvolutionOperators = "15927181-a1bb-497c-b745-8dbf505c019d" Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" +ExtendableSparse = "95c220a8-a1cf-11e9-0c77-dbfce5f500b3" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" @@ -25,7 +26,6 @@ SauterSchwab3D = "0a13313b-1c00-422e-8263-562364ed9544" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -SparseMatrixDicts = "5cb6c4b0-9b79-11e8-24c9-f9621d252589" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" @@ -38,6 +38,7 @@ Combinatorics = "0.7, 1" CompScienceMeshes = "0.6.0" Compat = "2, 3, 4" ConvolutionOperators = "0.4" +ExtendableSparse = "1.4" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4, 0.5, 1" FillArrays = "0.11, 0.12, 0.13, 1" @@ -48,7 +49,6 @@ NestedUnitRanges = "0.2" Requires = "1" SauterSchwab3D = "0.1" SauterSchwabQuadrature = "2.3.0" -SparseMatrixDicts = "< 0.2.8" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" WiltonInts84 = "0.2.5" diff --git a/src/BEAST.jl b/src/BEAST.jl index 81194910..19840639 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -6,7 +6,7 @@ using SharedArrays using SparseArrays using FillArrays using BlockArrays -using SparseMatrixDicts +using ExtendableSparse using ConvolutionOperators using SauterSchwabQuadrature diff --git a/src/localop.jl b/src/localop.jl index 7cb5b3b8..9ce1410b 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -37,7 +37,7 @@ function allocatestorage(op::LocalOperator, testfunctions, trialfunctions, m = numfunctions(testfunctions) n = numfunctions(trialfunctions) - Z = SparseMatrixDict{T,Int}(m,n) + Z = ExtendableSparseMatrix(T,m,n) store(v,m,n) = (Z[m,n] += v) freeze() = SparseArrays.SparseMatrixCSC(Z) diff --git a/test/test_gradient.jl b/test/test_gradient.jl index e3cc0a40..6281f663 100644 --- a/test/test_gradient.jl +++ b/test/test_gradient.jl @@ -5,7 +5,7 @@ using Test for T in [Float32, Float64] local o, x, y, z = euclidianbasis(3,T) tet = simplex(x,y,z,o) - ctr = center(tet) + local ctr = center(tet) iref = BEAST.LagrangeRefSpace{T,1,4,4}() diff --git a/test/test_rt.jl b/test/test_rt.jl index f213df15..56d9b781 100644 --- a/test/test_rt.jl +++ b/test/test_rt.jl @@ -37,7 +37,7 @@ ref = refspace(rwg) ptch = chart(m, first(m)) ctrd = neighborhood(ptch, T.([1,1]/3)) -vals = shapevals(ref, [ctrd]) +local vals = shapevals(ref, [ctrd]) # test edge detection edges = skeleton(m, 1) diff --git a/test/test_rt3d.jl b/test/test_rt3d.jl index 27cdf8c6..386caab9 100644 --- a/test/test_rt3d.jl +++ b/test/test_rt3d.jl @@ -12,8 +12,8 @@ for T in [Float32, Float64] nbd3 = neighborhood(tet, T.([1,1,0]/3)) nbd4 = neighborhood(tet, T.([1,1,1]/3)) - rs = BEAST.NDLCDRefSpace{T}() - fcs = BEAST.faces(tet) + local rs = BEAST.NDLCDRefSpace{T}() + local fcs = BEAST.faces(tet) @test dot(rs(nbd1)[1].value, normal(fcs[1])) > 0 @test dot(rs(nbd2)[2].value, normal(fcs[2])) > 0 @test dot(rs(nbd3)[3].value, normal(fcs[3])) > 0 @@ -41,7 +41,7 @@ for T in [Float32, Float64] @test dot(rs(nbd4)[3].value, normal(fcs[4])) * volume(fcs[4]) ≈ 0 atol=eps(T) - ctr = cartesian(center(tet)) + local ctr = cartesian(center(tet)) @test dot(cartesian(nbd1)-ctr, normal(fcs[1])) > 0 @test dot(cartesian(nbd2)-ctr, normal(fcs[2])) > 0 @test dot(cartesian(nbd3)-ctr, normal(fcs[3])) > 0 diff --git a/test/test_rtx.jl b/test/test_rtx.jl index 29507d28..09425b7f 100644 --- a/test/test_rtx.jl +++ b/test/test_rtx.jl @@ -9,7 +9,7 @@ for T in [Float32, Float64] @test numfunctions(X) == 16*2*3 @test all(length.(X.fns) .== 1) - p = positions(X) + local p = positions(X) i = 12 c = X.fns[i][1].cellid From 92f3b42e6c6ad978428cced3ba8ce447a00fab94 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 12 Feb 2024 16:30:28 +0100 Subject: [PATCH 320/528] set version to 2.2.1 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 2a7abbc8..0ba63493 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "2.2.0" +version = "2.2.1" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From fbc560cfc79a609b871cf53584ed25f3447a69ce Mon Sep 17 00:00:00 2001 From: jay prakash Date: Tue, 20 Feb 2024 14:37:40 +0000 Subject: [PATCH 321/528] general linear mapping of bfs during reordering process in sauterschwabint --- src/BEAST.jl | 1 + src/bases/local/bdmlocal.jl | 5 + src/bases/local/laglocal.jl | 12 +++ src/bases/local/rtlocal.jl | 5 + src/bases/perm_matrices.jl | 142 +++++++++++++++++++++++++++++ src/quadrature/sauterschwabints.jl | 11 +-- 6 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 src/bases/perm_matrices.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 9e8fb82c..4fccaacf 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -163,6 +163,7 @@ include("bases/ndlccspace.jl") include("bases/ndlcdspace.jl") include("bases/dual3d.jl") include("bases/bdm3dspace.jl") +include("bases/perm_matrices.jl") include("bases/subdbasis.jl") diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index fa4b66c9..102e61f8 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -43,5 +43,10 @@ function dof_permutation(::BDMRefSpace, vert_permutation) return _dof_perms_bdm[i] end +function dof_perm_matrix(::BDMRefSpace, vert_permutation) + i = findfirst(==(tuple(vert_permutation...)), _vert_perms_bdm) + return _dof_bdmperm_matrix[i] +end + dimtype(::BDMRefSpace, ::CompScienceMeshes.Simplex{U,2}) where {U} = Val{6} diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 2421d9ae..ea761ea2 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -260,4 +260,16 @@ end function dof_permutation(::LagrangeRefSpace{<:Any,1}, vert_permutation) i = findfirst(==(tuple(vert_permutation...)), _vert_perms_lag) return _dof_perms_lag1[i] +end + +function dof_perm_matrix(::LagrangeRefSpace{<:Any,0}, vert_permutation) + i = findfirst(==(tuple(vert_permutation...)), _vert_perms_rt) + @assert i != nothing + return _dof_lag0perm_matrix[i] +end + +function dof_perm_matrix(::LagrangeRefSpace{<:Any,1}, vert_permutation) + i = findfirst(==(tuple(vert_permutation...)), _vert_perms_rt) + @assert i != nothing + return _dof_rtperm_matrix[i] end \ No newline at end of file diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index 981a3023..2d8d8e93 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -104,6 +104,11 @@ function dof_permutation(::RTRefSpace, vert_permutation) return _dof_perms_rt[i] end +function dof_perm_matrix(::RTRefSpace, vert_permutation) + i = findfirst(==(tuple(vert_permutation...)), _vert_perms_rt) + @assert i != nothing + return _dof_rtperm_matrix[i] +end """ interpolate(interpolant::RefSpace, chart1, interpolee::RefSpace, chart2) diff --git a/src/bases/perm_matrices.jl b/src/bases/perm_matrices.jl new file mode 100644 index 00000000..6749e170 --- /dev/null +++ b/src/bases/perm_matrices.jl @@ -0,0 +1,142 @@ +const _dof_rt2perm_matrix = [ + [1 0 0 0 0 0 0 0; #1. {1,2,3} + 0 1 0 0 0 0 0 0; + 0 0 1 0 0 0 0 0; + 0 0 0 1 0 0 0 0; + 0 0 0 0 1 0 0 0; + 0 0 0 0 0 1 0 0; + 0 0 0 0 0 0 1 0; + 0 0 0 0 0 0 0 1], + + [0 0 0 0 1 0 0 0; #2. {2,3,1} + 0 0 0 0 0 1 0 0; + 1 0 0 0 0 0 0 0; + 0 1 0 0 0 0 0 0; + 0 0 1 0 0 0 0 0; + 0 0 0 1 0 0 0 0; + 0 0 0 0 0 0 -1 1; + 0 0 0 0 0 0 -1 0], + + [0 0 1 0 0 0 0 0; #3. {3,1,2} + 0 0 0 1 0 0 0 0; + 0 0 0 0 1 0 0 0; + 0 0 0 0 0 1 0 0; + 1 0 0 0 0 0 0 0; + 0 1 0 0 0 0 0 0; + 0 0 0 0 0 0 0 -1; + 0 0 0 0 0 0 1 -1], + + [0 0 0 1 0 0 0 0; #4. {2,1,3} + 0 0 1 0 0 0 0 0; + 0 1 0 0 0 0 0 0; + 1 0 0 0 0 0 0 0; + 0 0 0 0 0 1 0 0; + 0 0 0 0 1 0 0 0; + 0 0 0 0 0 0 0 1; + 0 0 0 0 0 0 1 0], + + [0 1 0 0 0 0 0 0; #5. {1,3,2} + 1 0 0 0 0 0 0 0; + 0 0 0 0 0 1 0 0; + 0 0 0 0 1 0 0 0; + 0 0 0 1 0 0 0 0; + 0 0 1 0 0 0 0 0; + 0 0 0 0 0 0 1 -1; + 0 0 0 0 0 0 0 -1], + + [0 0 0 0 0 1 0 0; #6. {3,2,1} + 0 0 0 0 1 0 0 0; + 0 0 0 1 0 0 0 0; + 0 0 1 0 0 0 0 0; + 0 1 0 0 0 0 0 0; + 1 0 0 0 0 0 0 0; + 0 0 0 0 0 0 -1 0; + 0 0 0 0 0 0 -1 1] +] + +const _dof_rtperm_matrix = [ + [1 0 0; # 1. {1,2,3} + 0 1 0; + 0 0 1], + + [0 0 1; # 2. {2,3,1} + 1 0 0; + 0 1 0], + + [0 1 0; # 3. {3,1,2} + 0 0 1; + 1 0 0], + + [0 1 0; # 4. {2,1,3} + 1 0 0; + 0 0 1], + + [1 0 0; # 5. {1,3,2} + 0 0 1; + 0 1 0], + + [0 0 1; # 6. {3,2,1} + 0 1 0; + 1 0 0] +] + +const _dof_bdmperm_matrix = [ + + [1 0 0 0 0 0; #1. {1,2,3} + 0 1 0 0 0 0; + 0 0 1 0 0 0; + 0 0 0 1 0 0; + 0 0 0 0 1 0; + 0 0 0 0 0 1], + + [0 0 0 0 1 0; #2. {2,3,1} + 0 0 0 0 0 1; + 1 0 0 0 0 0; + 0 1 0 0 0 0; + 0 0 1 0 0 0; + 0 0 0 1 0 0], + + [0 0 1 0 0 0; #3. {3,1,2} + 0 0 0 1 0 0; + 0 0 0 0 1 0; + 0 0 0 0 0 1; + 1 0 0 0 0 0; + 0 1 0 0 0 0], + + [0 0 0 1 0 0; #4. {2,1,3} + 0 0 1 0 0 0; + 0 1 0 0 0 0; + 1 0 0 0 0 0; + 0 0 0 0 0 1; + 0 0 0 0 1 0], + + [0 1 0 0 0 0; #5. {1,3,2} + 1 0 0 0 0 0; + 0 0 0 0 0 1; + 0 0 0 0 1 0; + 0 0 0 1 0 0; + 0 0 1 0 0 0], + + [0 0 0 0 0 1; #6. {3,2,1} + 0 0 0 0 1 0; + 0 0 0 1 0 0; + 0 0 1 0 0 0; + 0 1 0 0 0 0; + 1 0 0 0 0 0] + +] + +const _dof_lag0perm_matrix = [ + [1], # 1. {1,2,3} + + [1], # 2. {2,3,1} + + [1], # 3. {3,1,2} + + [1], # 4. {2,1,3} + + [1], # 5. {1,3,2} + + [1], # 6. {3,2,1} + +] \ No newline at end of file diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index e9a69d1b..b14ad0d6 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -98,12 +98,11 @@ function momintegrals!(op::Operator, igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, rule) - K = dof_permutation(test_local_space, I) - L = dof_permutation(trial_local_space, J) - for i in 1:numfunctions(test_local_space) - for j in 1:numfunctions(trial_local_space) - out[i,j] = G[K[i],L[j]] - end end + QTest = dof_perm_matrix(test_local_space, I) + QTrial = dof_perm_matrix(trial_local_space, J) + out_temp = zeros(eltype(out), numfunctions(test_local_space),numfunctions(trial_local_space)) + out_temp = QTest*G*QTrial' + out[1:numfunctions(test_local_space),1:numfunctions(trial_local_space)] = out_temp nothing end From 8aad8d69a3f32275e2114e197a81a29e662cbdee Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 21 Feb 2024 16:05:16 +0100 Subject: [PATCH 322/528] rt2 work --- src/BEAST.jl | 1 + src/bases/local/rt2local.jl | 97 +++++++++++++++++++++++++++++++++++++ src/bases/local/rtlocal.jl | 59 +++++++++++----------- 3 files changed, 130 insertions(+), 27 deletions(-) create mode 100644 src/bases/local/rt2local.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 19840639..7d963095 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -145,6 +145,7 @@ include("bases/divergence.jl") include("bases/local/laglocal.jl") include("bases/local/rtlocal.jl") +include("bases/local/rt2local.jl") include("bases/local/ndlocal.jl") include("bases/local/bdmlocal.jl") include("bases/local/ncrossbdmlocal.jl") diff --git a/src/bases/local/rt2local.jl b/src/bases/local/rt2local.jl new file mode 100644 index 00000000..9cfc9e49 --- /dev/null +++ b/src/bases/local/rt2local.jl @@ -0,0 +1,97 @@ +struct RT2RefSpace{T} <: RefSpace{T,8} end + +function (f::RT2RefSpace)(p) + + u, v = parametric(mp) + j = jacobian(mp) + + tu = tangents(mp,1) + tv = tangents(mp,2) + + inv_j = 1/j + + return SVector( + (value=(4u*(1-2u))*tu + (2v*(1-4u))*tv, divergence=inv_j), + (value=(2u*(1-4v))*tu + (4v*(1-2v))*tv, divergence=inv_j), + (value=(-8*u^2-8*u*v+12*u+6*v-4)*tu + (2*v*(-4*u-4*v+3))*tv, divergence=inv_j), + (value=(8*u*v-2*u-6*v+2)*tu + (4*v*(2*v-1))*tv, divergence=inv_j), + (value=(2*u*(4*u+4*v-3))*tu + (8*u*v-6*u+8*v^2-12*v+4)*tv, divergence=inv_j), + (value=(4*u*(1-2*u))*tu + (-8*u*v+6*u+2*v-2)*tv, divergence=inv_j), + (value=(8*u*(-2*u-v+2))*tu + (8*v*(-2*u-v+1))*tv, divergence=inv_j), + (value=(8*u*(-u-2*v+1))*tu + (8*v*(-u-2*v+2))*tv, divergence=inv_j), + ) +end + + +function interpolate(fields, interpolant::BEAST.RT2RefSpace, chart) + + T = coordtype(chart) + + Q = Any[] + refchart = CompScienceMeshes.domain(chart).simplex + nfields = length(fields(center(chart))) + + for (edge, refedge) in zip(faces(chart), faces(refchart)) + l0 = zeros(T,nfields) + l1 = zeros(T,nfields) + qps = CompScienceMeshes.quadpoints(edge,4) + for (p_edge,w) in qps + s = parametric(p_edge) + x = cartesian(p_edge) + u = carttobary(x, chart) + + p_refchart = neighborhood(refchart, u) + t_refedge = tangents(refedge,1) + m_refedge = point(-t_refedge[2], t_refedge[1]) + m_refedge /= norm(m_refedge) + q0ref = s * m_refedge + q1ref = (1-s) * m_refedge + + nxq0ref = point(-q0ref[2], q0ref[1]) + nxq1ref = point(-q1ref[2], q1ref[1]) + p_chart = neighborhood(u, chart) + n_chart = normal(p_chart) + J_chart = jacobian(p_chart) + t1 = tangents(p_chart,1) + t2 = tangents(p_chart,2) + q0 = -n_chart × (nxq0ref[1]*t1 + xnq0ref[2]*t2) / J_chart^2 + q1 = -n_chart × (nxq1ref[1]*t1 + xnq1ref[2]*t2) / J_chart^2 + + F = fields(p_chart) + p_refedge = neighborhood(s, refedge) + J_edge = jacobian(p_edge) + J_refedge = jacobian(p_refedge) + l0 .+= [w * dot(f,q0) * J_chart / J_edge * J_refedge for f in vals] + l1 .+= [w * dot(f,q1) * J_chart / J_edge * J_refedge for f in vals] + end + push!(Q,l0) + push!(Q,l1) + end + + l6 = zeros(T,nfields) + l7 = zeros(T,nfields) + qps = CompScienceMeshes.quadpoints(chart, 4) + for (p,w) in qps + + q6ref = point(1,0) + q7ref = point(0,1) + + nxq6ref = point(-q6ref[2], q6ref[1]) + nxq7ref = point(-q7ref[2], q7ref[1]) + + J_chart = jacobian(p) + n_chart = normal(p) + + t1 = tangents(p,1) + t2 = tangents(p,2) + + q6 = -n_chart × (nxq6ref[1] * t1 + nxq6ref[2] * t2) / J_chart^2 + q7 = -n_chart × (nxq7ref[1] * t1 + nxq7ref[2] * t2) / J_chart^2 + + vals = fields(p) + l6 .+= [w * dot(f,q6) for f in vals] + l7 .+= [w * dot(f,q6) for f in vals] + end + + return hcat(Q...) +end \ No newline at end of file diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index 981a3023..17170763 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -44,41 +44,41 @@ function ntrace(x::RTRefSpace, el, q, fc) return t end -function restrict(ϕ::RTRefSpace{T}, dom1, dom2) where T +# function restrict(ϕ::RTRefSpace{T}, dom1, dom2) where T - K = numfunctions(ϕ) - D = dimension(dom1) +# K = numfunctions(ϕ) +# D = dimension(dom1) - @assert K == 3 - @assert D == 2 - @assert D == dimension(dom2) +# @assert K == 3 +# @assert D == 2 +# @assert D == dimension(dom2) - Q = zeros(T,K,K) - for i in 1:K +# Q = zeros(T,K,K) +# for i in 1:K - # find the center of edge i of dom2 - a = dom2.vertices[mod1(i+1,D+1)] - b = dom2.vertices[mod1(i+2,D+1)] - c = (a + b) / 2 +# # find the center of edge i of dom2 +# a = dom2.vertices[mod1(i+1,D+1)] +# b = dom2.vertices[mod1(i+2,D+1)] +# c = (a + b) / 2 - # find the outer binormal there - t = b - a - l = norm(t) - n = dom2.normals[1] - m = cross(t, n) / l +# # find the outer binormal there +# t = b - a +# l = norm(t) +# n = dom2.normals[1] +# m = cross(t, n) / l - u = carttobary(dom1, c) - x = neighborhood(dom1, u) +# u = carttobary(dom1, c) +# x = neighborhood(dom1, u) - y = ϕ(x) +# y = ϕ(x) - for j in 1:K - Q[j,i] = dot(y[j][1], m) * l - end - end +# for j in 1:K +# Q[j,i] = dot(y[j][1], m) * l +# end +# end - return Q -end +# return Q +# end const _vert_perms_rt = [ @@ -119,7 +119,7 @@ on `chart1`. The returned value is a matrix `Q` such that with ``\\phi_i`` the i-th local shape function for `interpolee` and ``\\psi_j`` the j-th local shape function for `interpolant`. """ -function interpolate(interpolant::RTRefSpace, chart1, interpolee::RefSpace, chart2) +function interpolate(interpolant::RefSpace, chart1, interpolee::RefSpace, chart2) function fields(p) x = cartesian(p) v = carttobary(chart2, x) @@ -147,4 +147,9 @@ function interpolate(fields, interpolant::RTRefSpace, chart) end return hcat(Q...) +end + + +function restrict(ϕ::RefSpace, dom1, dom2) + interpolate(ϕ, dom2, ϕ, dom1) end \ No newline at end of file From f61b7a4c586d0352d3edb6703241e0ebb28ab741 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 21 Feb 2024 17:06:23 +0100 Subject: [PATCH 323/528] rt2 local shape and interpolators --- src/bases/local/rt2local.jl | 54 ++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/bases/local/rt2local.jl b/src/bases/local/rt2local.jl index 9cfc9e49..5891b166 100644 --- a/src/bases/local/rt2local.jl +++ b/src/bases/local/rt2local.jl @@ -2,21 +2,32 @@ struct RT2RefSpace{T} <: RefSpace{T,8} end function (f::RT2RefSpace)(p) - u, v = parametric(mp) - j = jacobian(mp) + u, v = parametric(p) + j = jacobian(p) - tu = tangents(mp,1) - tv = tangents(mp,2) + tu = tangents(p,1) + tv = tangents(p,2) inv_j = 1/j + # return SVector( + # (value=-(4u*(1-2u))*tu - (2v*(1-4u))*tv, divergence=inv_j), + # (value=-(2u*(1-4v))*tu - (4v*(1-2v))*tv, divergence=inv_j), + # (value=(-8*u^2-8*u*v+12*u+6*v-4)*tu + (2*v*(-4*u-4*v+3))*tv, divergence=inv_j), + # (value=(8*u*v-2*u-6*v+2)*tu + (4*v*(2*v-1))*tv, divergence=inv_j), + # (value=-(2*u*(4*u+4*v-3))*tu - (8*u*v-6*u+8*v^2-12*v+4)*tv, divergence=inv_j), + # (value=-(4*u*(1-2*u))*tu - (-8*u*v+6*u+2*v-2)*tv, divergence=inv_j), + # (value=(8*u*(-2*u-v+2))*tu + (8*v*(-2*u-v+1))*tv, divergence=inv_j), + # (value=(8*u*(-u-2*v+1))*tu + (8*v*(-u-2*v+2))*tv, divergence=inv_j), + # ) + return SVector( - (value=(4u*(1-2u))*tu + (2v*(1-4u))*tv, divergence=inv_j), - (value=(2u*(1-4v))*tu + (4v*(1-2v))*tv, divergence=inv_j), - (value=(-8*u^2-8*u*v+12*u+6*v-4)*tu + (2*v*(-4*u-4*v+3))*tv, divergence=inv_j), (value=(8*u*v-2*u-6*v+2)*tu + (4*v*(2*v-1))*tv, divergence=inv_j), - (value=(2*u*(4*u+4*v-3))*tu + (8*u*v-6*u+8*v^2-12*v+4)*tv, divergence=inv_j), - (value=(4*u*(1-2*u))*tu + (-8*u*v+6*u+2*v-2)*tv, divergence=inv_j), + (value=(-8*u^2-8*u*v+12*u+6*v-4)*tu + (2*v*(-4*u-4*v+3))*tv, divergence=inv_j), + (value=-(2*u*(4*u+4*v-3))*tu - (8*u*v-6*u+8*v^2-12*v+4)*tv, divergence=inv_j), + (value=-(4*u*(1-2*u))*tu - (-8*u*v+6*u+2*v-2)*tv, divergence=inv_j), + (value=-(4u*(1-2u))*tu - (2v*(1-4u))*tv, divergence=inv_j), + (value=-(2u*(1-4v))*tu - (4v*(1-2v))*tv, divergence=inv_j), (value=(8*u*(-2*u-v+2))*tu + (8*v*(-2*u-v+1))*tv, divergence=inv_j), (value=(8*u*(-u-2*v+1))*tu + (8*v*(-u-2*v+2))*tv, divergence=inv_j), ) @@ -38,27 +49,29 @@ function interpolate(fields, interpolant::BEAST.RT2RefSpace, chart) for (p_edge,w) in qps s = parametric(p_edge) x = cartesian(p_edge) - u = carttobary(x, chart) + u = carttobary(chart, x) p_refchart = neighborhood(refchart, u) - t_refedge = tangents(refedge,1) + p_refedge = neighborhood(refedge,s) + t_refedge = tangents(p_refedge,1) m_refedge = point(-t_refedge[2], t_refedge[1]) m_refedge /= norm(m_refedge) - q0ref = s * m_refedge - q1ref = (1-s) * m_refedge + q0ref = s[1] * m_refedge + q1ref = (1-s[1]) * m_refedge nxq0ref = point(-q0ref[2], q0ref[1]) nxq1ref = point(-q1ref[2], q1ref[1]) - p_chart = neighborhood(u, chart) + @show u + p_chart = neighborhood(chart, u) + @show p_chart n_chart = normal(p_chart) J_chart = jacobian(p_chart) t1 = tangents(p_chart,1) t2 = tangents(p_chart,2) - q0 = -n_chart × (nxq0ref[1]*t1 + xnq0ref[2]*t2) / J_chart^2 - q1 = -n_chart × (nxq1ref[1]*t1 + xnq1ref[2]*t2) / J_chart^2 + q0 = -n_chart × (nxq0ref[1]*t1 + nxq0ref[2]*t2) / J_chart^2 + q1 = -n_chart × (nxq1ref[1]*t1 + nxq1ref[2]*t2) / J_chart^2 - F = fields(p_chart) - p_refedge = neighborhood(s, refedge) + vals = fields(p_chart) J_edge = jacobian(p_edge) J_refedge = jacobian(p_refedge) l0 .+= [w * dot(f,q0) * J_chart / J_edge * J_refedge for f in vals] @@ -90,8 +103,11 @@ function interpolate(fields, interpolant::BEAST.RT2RefSpace, chart) vals = fields(p) l6 .+= [w * dot(f,q6) for f in vals] - l7 .+= [w * dot(f,q6) for f in vals] + l7 .+= [w * dot(f,q7) for f in vals] end + push!(Q,l6) + push!(Q,l7) + return hcat(Q...) end \ No newline at end of file From de203f0dafd649e953aa4e74277f7a47aad99ef9 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 23 Feb 2024 12:26:13 +0100 Subject: [PATCH 324/528] Make interpolate less verbose --- src/bases/local/rt2local.jl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/bases/local/rt2local.jl b/src/bases/local/rt2local.jl index 5891b166..16d54928 100644 --- a/src/bases/local/rt2local.jl +++ b/src/bases/local/rt2local.jl @@ -61,9 +61,9 @@ function interpolate(fields, interpolant::BEAST.RT2RefSpace, chart) nxq0ref = point(-q0ref[2], q0ref[1]) nxq1ref = point(-q1ref[2], q1ref[1]) - @show u + # @show u p_chart = neighborhood(chart, u) - @show p_chart + # @show p_chart n_chart = normal(p_chart) J_chart = jacobian(p_chart) t1 = tangents(p_chart,1) @@ -110,4 +110,11 @@ function interpolate(fields, interpolant::BEAST.RT2RefSpace, chart) push!(Q,l7) return hcat(Q...) +end + + +function dof_perm_matrix(::RT2RefSpace, vert_permutation) + i = findfirst(==(tuple(vert_permutation...)), _vert_perms_rt) + @assert i != nothing + return _dof_rt2perm_matrix[i] end \ No newline at end of file From fce8340c8eb82b7d423926c93897058123a66311 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 23 Feb 2024 16:35:40 +0100 Subject: [PATCH 325/528] got rid of carttobary in interpolate --- src/bases/local/rt2local.jl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/bases/local/rt2local.jl b/src/bases/local/rt2local.jl index 16d54928..16666999 100644 --- a/src/bases/local/rt2local.jl +++ b/src/bases/local/rt2local.jl @@ -48,11 +48,12 @@ function interpolate(fields, interpolant::BEAST.RT2RefSpace, chart) qps = CompScienceMeshes.quadpoints(edge,4) for (p_edge,w) in qps s = parametric(p_edge) - x = cartesian(p_edge) - u = carttobary(chart, x) + # x = cartesian(p_edge) + # u = carttobary(chart, x) - p_refchart = neighborhood(refchart, u) p_refedge = neighborhood(refedge,s) + u = cartesian(p_refedge) + p_refchart = neighborhood(refchart, u) t_refedge = tangents(p_refedge,1) m_refedge = point(-t_refedge[2], t_refedge[1]) m_refedge /= norm(m_refedge) @@ -61,9 +62,7 @@ function interpolate(fields, interpolant::BEAST.RT2RefSpace, chart) nxq0ref = point(-q0ref[2], q0ref[1]) nxq1ref = point(-q1ref[2], q1ref[1]) - # @show u p_chart = neighborhood(chart, u) - # @show p_chart n_chart = normal(p_chart) J_chart = jacobian(p_chart) t1 = tangents(p_chart,1) From af418a56abcbbc6e2328d4444d3208d68558d04b Mon Sep 17 00:00:00 2001 From: jay prakash Date: Tue, 27 Feb 2024 17:31:06 +0000 Subject: [PATCH 326/528] 2nd degree div conforming basis fns --- src/BEAST.jl | 2 + src/bases/local/bdmlocal.jl | 4 ++ src/bases/local/rt2local.jl | 121 ++++++++++++++++++++++++++++++++ src/bases/perm_matrices.jl | 8 +-- src/bases/rt2space.jl | 133 ++++++++++++++++++++++++++++++++++++ src/identityop.jl | 2 +- 6 files changed, 265 insertions(+), 5 deletions(-) create mode 100644 src/bases/local/rt2local.jl create mode 100644 src/bases/rt2space.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 4fccaacf..f1587553 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -151,6 +151,7 @@ include("bases/local/ncrossbdmlocal.jl") include("bases/local/ndlcclocal.jl") include("bases/local/ndlcdlocal.jl") include("bases/local/bdm3dlocal.jl") +include("bases/local/rt2local.jl") include("bases/lagrange.jl") include("bases/rtspace.jl") @@ -164,6 +165,7 @@ include("bases/ndlcdspace.jl") include("bases/dual3d.jl") include("bases/bdm3dspace.jl") include("bases/perm_matrices.jl") +include("bases/rt2space.jl") include("bases/subdbasis.jl") diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index 102e61f8..2c37be47 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -1,5 +1,9 @@ struct BDMRefSpace{T} <: RefSpace{T,6} end +function valuetype(ref::BDMRefSpace{T}, charttype::Type) where {T} + SVector{universedimension(charttype),T} +end + function (f::BDMRefSpace)(p) u,v = parametric(p) diff --git a/src/bases/local/rt2local.jl b/src/bases/local/rt2local.jl new file mode 100644 index 00000000..9a10278f --- /dev/null +++ b/src/bases/local/rt2local.jl @@ -0,0 +1,121 @@ +struct RT2RefSpace{T} <: RefSpace{T,8} end + +# valuetype(ref::RTRefSpace{T}, charttype) where {T} = SVector{3,Tuple{SVector{universedimension(charttype),T},T}} +function valuetype(ref::RT2RefSpace{T}, charttype::Type) where {T} + SVector{universedimension(charttype),T} +end + +function (ϕ::RT2RefSpace)(mp) + + u, v = parametric(mp) + j = jacobian(mp) + + tu = tangents(mp,1) + tv = tangents(mp,2) + + inv_j = 1/j + + b1 = tu + b2 = tv + b3 = u*tu + b4 = u*tv + b5 = v*tu + b6 = v*tv + b7 = u*b3+u*b6 + b8 = v*b3+v*b6 + + return SVector(( + (value=(8*b7+8*b8-12*b3-6*b5+4*b1-6*b6)*inv_j, divergence=(24*u+24*v-18)*inv_j), + (value=(-8*b8+2*b3+6*b5-2*b1+4*b6)*inv_j, divergence=(6-24*v)*inv_j), + (value=(4*b3-8*b7+6*b4+2*b6-2*b2)*inv_j, divergence=(6-24*u)*inv_j), + (value=(8*b7+8*b8-6*b3-6*b4-12*b6+4*b2)*inv_j, divergence=(24*u+24*v-18)*inv_j), + (value=(2*b3-8*b8+4*b6)*inv_j, divergence=(6-24*v)*inv_j), + (value=(4*b3-8*b7+2*b6)*inv_j, divergence=(6-24*u)*inv_j), + (value=(-16*b7-8*b8+16*b3+8*b6)*inv_j, divergence=(-48*u-24*v+24)*inv_j), + (value=(-8*b7-16*b8+8*b3+16*b6)*inv_j, divergence=(-24*u-48*v+24)*inv_j) + )) +end + +#divergence(ref::RT2RefSpace, sh, el) = Shape(sh.cellid, 1, sh.coeff/volume(el)) + +""" + ntrace(refspace, element, localindex, face) + +Compute the normal trace of all local shape functions on `elements` belonging to +`refspace` on `face`. This function returns a matrix expressing the traces of local +shape functions in `refspace` as linear combinations of functions in the local +trace space. Cf. `restrict`. `localindex` is the index of `face` in the enumeration +of faces of `elements`. In many special cases knowing this index allows for highly +optimised implementations. +""" +function ntrace(x::RT2RefSpace, el, q, fc) + t = zeros(scalartype(x),1,3) + t[q] = 1 / volume(fc) + return t +end + +#= function restrict(ϕ::RT2RefSpace{T}, dom1, dom2) where T + + K = numfunctions(ϕ) + D = dimension(dom1) + + @assert K == 3 + @assert D == 2 + @assert D == dimension(dom2) + + Q = zeros(T,K,K) + for i in 1:K + + # find the center of edge i of dom2 + a = dom2.vertices[mod1(i+1,D+1)] + b = dom2.vertices[mod1(i+2,D+1)] + c = (a + b) / 2 + + # find the outer binormal there + t = b - a + l = norm(t) + n = dom2.normals[1] + m = cross(t, n) / l + + u = carttobary(dom1, c) + x = neighborhood(dom1, u) + + y = ϕ(x) + + for j in 1:K + Q[j,i] = dot(y[j][1], m) * l + end + end + + return Q +end =# + + +const _vert_perms_rt2 = [ + (1,2,3), + (2,3,1), + (3,1,2), + (2,1,3), + (1,3,2), + (3,2,1), +] +const _dof_perms_rt2 = [ + (1,2,3,4,5,6,7,8), + (5,6,1,2,3,4,7,8), + (3,4,5,6,1,2,7,8), + (4,3,2,1,6,5,7,8), + (2,1,6,5,4,3,7,8), + (6,5,4,3,2,1,7,8), +] + +function dof_permutation(::RT2RefSpace, vert_permutation) + i = findfirst(==(tuple(vert_permutation...)), _vert_perms_rt2) + @assert i != nothing + return _dof_perms_rt2[i] +end + +function dof_perm_matrix(::RT2RefSpace, vert_permutation) + i = findfirst(==(tuple(vert_permutation...)), _vert_perms_rt2) + @assert i != nothing + return _dof_rt2perm_matrix[i] +end \ No newline at end of file diff --git a/src/bases/perm_matrices.jl b/src/bases/perm_matrices.jl index 6749e170..a526c8bc 100644 --- a/src/bases/perm_matrices.jl +++ b/src/bases/perm_matrices.jl @@ -14,8 +14,8 @@ const _dof_rt2perm_matrix = [ 0 1 0 0 0 0 0 0; 0 0 1 0 0 0 0 0; 0 0 0 1 0 0 0 0; - 0 0 0 0 0 0 -1 1; - 0 0 0 0 0 0 -1 0], + 0 0 0 0 0 0 0 -1; + 0 0 0 0 0 0 1 -1], [0 0 1 0 0 0 0 0; #3. {3,1,2} 0 0 0 1 0 0 0 0; @@ -23,8 +23,8 @@ const _dof_rt2perm_matrix = [ 0 0 0 0 0 1 0 0; 1 0 0 0 0 0 0 0; 0 1 0 0 0 0 0 0; - 0 0 0 0 0 0 0 -1; - 0 0 0 0 0 0 1 -1], + 0 0 0 0 0 0 -1 1; + 0 0 0 0 0 0 -1 0], [0 0 0 1 0 0 0 0; #4. {2,1,3} 0 0 1 0 0 0 0 0; diff --git a/src/bases/rt2space.jl b/src/bases/rt2space.jl new file mode 100644 index 00000000..ae95711f --- /dev/null +++ b/src/bases/rt2space.jl @@ -0,0 +1,133 @@ + + +mutable struct RT2Basis{T,M,P} <: Space{T} + geo::M + fns::Vector{Vector{Shape{T}}} + pos::Vector{P} +end + +RT2Basis(geo, fns) = RT2Basis(geo, fns, Vector{vertextype(geo)}(undef,length(fns))) + +#= positions(rt) = rt.pos =# +refspace(space::RT2Basis{T}) where {T} = RT2RefSpace{T}() +subset(rt::RT2Basis,I) = RT2Basis(rt.geo, rt.fns[I], rt.pos[I]) + +#= mutable struct ValDiv end =# + + + +""" + raviartthomas(mesh, cellpairs::Array{Int,2}) + +Constructs the RT basis on the input `mesh`. The i-th RT basis function will + represent a current distribution flowing from cell `cellpairs[1,i]` to + `cellpairs[2,i]` on the mesh. + +Returns an object of type `RTBasis`, which comprises both the mesh and pairs of + Shape objects which corresponds to the cell pairs, containing the necsessary + coefficients and indices to compute the exact basis functions when required + by the solver. +""" +function raviartthomas2(mesh::CompScienceMeshes.AbstractMesh{U,D1,T}, cellpairs::Array{Int,2}) where {U,D1,T} + + # combine now the pairs of monopolar RWGs in div-conforming RWGs + numpairs = size(cellpairs,2) + Cells = cells(mesh) + numcells = size(Cells,1) + functions = Vector{Vector{Shape{T}}}(undef,2*(numpairs+numcells)) + positions = Vector{vertextype(mesh)}(undef,2*(numpairs+numcells)) + chooseref1(x) = if x<0 return 0 else return -1 end + chooseref2(x) = if x<0 return -1 else return 0 end + for i in 1:numpairs + if cellpairs[2,i] > 0 + c1 = cellpairs[1,i]; # cell1 = Cells[c1] #mesh.faces[c1] + c2 = cellpairs[2,i]; # cell2 = Cells[c2] #mesh.faces[c2] + + cell1 = CompScienceMeshes.indices(mesh, c1) + cell2 = CompScienceMeshes.indices(mesh, c2) + + e1, e2 = getcommonedge(cell1, cell2) + functions[2*i-1] = [ + Shape{T}(c1, 2*abs(e1)+chooseref1(sign(e1)), T(+1.0)), + Shape{T}(c2, 2*abs(e2)+chooseref1(sign(e2)), T(-1.0))] + functions[2*i] = [ + Shape{T}(c1, 2*abs(e1)+chooseref2(sign(e1)), T(+1.0)), + Shape{T}(c2, 2*abs(e2)+chooseref2(sign(e2)), T(-1.0))] + isct = intersect(cell1, cell2) + @assert length(isct) == 2 + @assert !(cell1[abs(e1)] in isct) + @assert !(cell2[abs(e2)] in isct) + + ctr1 = cartesian(center(chart(mesh, c1))) + ctr2 = cartesian(center(chart(mesh, c2))) + positions[2*i-1] = (ctr1 + ctr2) / 2 + positions[2*i] = (ctr1 + ctr2) / 2 + else + c1 = cellpairs[1,i] + e1 = cellpairs[2,i] + functions[i] = [ + Shape(c1, abs(e1), T(+1.0))] + positions[i] = cartesian(center(chart(mesh, c1))) + end + end + + for (i,cell) in enumerate(Cells) + functions[2*numpairs+2*i-1] = [ + Shape{T}(i, 7, T(+1.0))] + + functions[2*numpairs+2*i] = [ + Shape{T}(i, 8, T(+1.0))] + + ctr1 = cartesian(center(chart(mesh, i))) + positions[2*numpairs+2*i-1] = ctr1 + positions[2*numpairs+2*i] = ctr1 + end + + geo = mesh + RT2Basis(geo, functions, positions) +end + + +function raviartthomas2(mesh, edges::CompScienceMeshes.AbstractMesh{U,2} where {U}) + cps = CompScienceMeshes.cellpairs(mesh, edges) + # ids = findall(x -> x>0, cps[2,:]) + raviartthomas2(mesh, cps) +end + +function raviartthomas2(mesh::CompScienceMeshes.AbstractMesh{U,3} where {U}) + bnd = boundary(mesh) + edges = submesh(!in(bnd), skeleton(mesh,1)) + return raviartthomas2(mesh, edges) +end + + +""" + raviartthomas(mesh) + +Conducts pre-processing on the input `mesh` by extracting the cell edges, cell pairs + and indices required to construct the RT basis on the `mesh`. + +Calls raviartthomas(mesh::Mesh, cellpairs::Array{Int,2}), which constructs + the RT basis on the `mesh`, using the cell pairs identified. + +Returns the RT basis object. +""" +function raviartthomas2(mesh; sort=:spacefillingcurve) + edges = skeleton(mesh, 1; sort) + cps = cellpairs(mesh, edges, dropjunctionpair=true) + ids = findall(x -> x>0, cps[2,:]) + raviartthomas2(mesh, cps[:,ids]) +end + +divergence(X::RT2Basis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) +ntrace(X::RT2Basis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) + + +function LinearAlgebra.cross(::NormalVector, s::RT2Basis) + @assert CompScienceMeshes.isoriented(s.geo) + fns = similar(s.fns) + for (i,fn) in pairs(s.fns) + fns[i] = [Shape(sh.cellid, sh.refid, -sh.coeff) for sh in fn] + end + NDBasis(s.geo, fns, s.pos) +end diff --git a/src/identityop.jl b/src/identityop.jl index 703566af..7cc6723c 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -20,7 +20,7 @@ function _alloc_workspace(qd, g, f, tels, bels) A = Vector{typeof(a)}(undef,length(qd)) end -const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace, BDMRefSpace, NCrossBDMRefSpace} +const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace, BDMRefSpace, NCrossBDMRefSpace, RT2RefSpace} defaultquadstrat(::LocalOperator, ::LinearRefSpaceTriangle, ::LinearRefSpaceTriangle) = SingleNumQStrat(6) function quaddata(op::LocalOperator, g::LinearRefSpaceTriangle, f::LinearRefSpaceTriangle, tels, bels, qs::SingleNumQStrat) From 4f4d879dc1c23218bd1a2dc5d1fe1eaf41f2f806 Mon Sep 17 00:00:00 2001 From: jay prakash Date: Tue, 27 Feb 2024 18:07:01 +0000 Subject: [PATCH 327/528] cleaning up --- src/BEAST.jl | 2 - src/bases/local/bdmlocal.jl | 4 -- src/bases/local/rt2local.jl | 119 -------------------------------- src/bases/rt2space.jl | 133 ------------------------------------ src/identityop.jl | 2 +- 5 files changed, 1 insertion(+), 259 deletions(-) delete mode 100644 src/bases/rt2space.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index f1c86151..0e74310b 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -152,7 +152,6 @@ include("bases/local/ncrossbdmlocal.jl") include("bases/local/ndlcclocal.jl") include("bases/local/ndlcdlocal.jl") include("bases/local/bdm3dlocal.jl") -include("bases/local/rt2local.jl") include("bases/lagrange.jl") include("bases/rtspace.jl") @@ -166,7 +165,6 @@ include("bases/ndlcdspace.jl") include("bases/dual3d.jl") include("bases/bdm3dspace.jl") include("bases/perm_matrices.jl") -include("bases/rt2space.jl") include("bases/subdbasis.jl") diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index 2c37be47..102e61f8 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -1,9 +1,5 @@ struct BDMRefSpace{T} <: RefSpace{T,6} end -function valuetype(ref::BDMRefSpace{T}, charttype::Type) where {T} - SVector{universedimension(charttype),T} -end - function (f::BDMRefSpace)(p) u,v = parametric(p) diff --git a/src/bases/local/rt2local.jl b/src/bases/local/rt2local.jl index fa0fbad7..16666999 100644 --- a/src/bases/local/rt2local.jl +++ b/src/bases/local/rt2local.jl @@ -1,123 +1,5 @@ struct RT2RefSpace{T} <: RefSpace{T,8} end -<<<<<<< HEAD -# valuetype(ref::RTRefSpace{T}, charttype) where {T} = SVector{3,Tuple{SVector{universedimension(charttype),T},T}} -function valuetype(ref::RT2RefSpace{T}, charttype::Type) where {T} - SVector{universedimension(charttype),T} -end - -function (ϕ::RT2RefSpace)(mp) - - u, v = parametric(mp) - j = jacobian(mp) - - tu = tangents(mp,1) - tv = tangents(mp,2) - - inv_j = 1/j - - b1 = tu - b2 = tv - b3 = u*tu - b4 = u*tv - b5 = v*tu - b6 = v*tv - b7 = u*b3+u*b6 - b8 = v*b3+v*b6 - - return SVector(( - (value=(8*b7+8*b8-12*b3-6*b5+4*b1-6*b6)*inv_j, divergence=(24*u+24*v-18)*inv_j), - (value=(-8*b8+2*b3+6*b5-2*b1+4*b6)*inv_j, divergence=(6-24*v)*inv_j), - (value=(4*b3-8*b7+6*b4+2*b6-2*b2)*inv_j, divergence=(6-24*u)*inv_j), - (value=(8*b7+8*b8-6*b3-6*b4-12*b6+4*b2)*inv_j, divergence=(24*u+24*v-18)*inv_j), - (value=(2*b3-8*b8+4*b6)*inv_j, divergence=(6-24*v)*inv_j), - (value=(4*b3-8*b7+2*b6)*inv_j, divergence=(6-24*u)*inv_j), - (value=(-16*b7-8*b8+16*b3+8*b6)*inv_j, divergence=(-48*u-24*v+24)*inv_j), - (value=(-8*b7-16*b8+8*b3+16*b6)*inv_j, divergence=(-24*u-48*v+24)*inv_j) - )) -end - -#divergence(ref::RT2RefSpace, sh, el) = Shape(sh.cellid, 1, sh.coeff/volume(el)) - -""" - ntrace(refspace, element, localindex, face) - -Compute the normal trace of all local shape functions on `elements` belonging to -`refspace` on `face`. This function returns a matrix expressing the traces of local -shape functions in `refspace` as linear combinations of functions in the local -trace space. Cf. `restrict`. `localindex` is the index of `face` in the enumeration -of faces of `elements`. In many special cases knowing this index allows for highly -optimised implementations. -""" -function ntrace(x::RT2RefSpace, el, q, fc) - t = zeros(scalartype(x),1,3) - t[q] = 1 / volume(fc) - return t -end - -#= function restrict(ϕ::RT2RefSpace{T}, dom1, dom2) where T - - K = numfunctions(ϕ) - D = dimension(dom1) - - @assert K == 3 - @assert D == 2 - @assert D == dimension(dom2) - - Q = zeros(T,K,K) - for i in 1:K - - # find the center of edge i of dom2 - a = dom2.vertices[mod1(i+1,D+1)] - b = dom2.vertices[mod1(i+2,D+1)] - c = (a + b) / 2 - - # find the outer binormal there - t = b - a - l = norm(t) - n = dom2.normals[1] - m = cross(t, n) / l - - u = carttobary(dom1, c) - x = neighborhood(dom1, u) - - y = ϕ(x) - - for j in 1:K - Q[j,i] = dot(y[j][1], m) * l - end - end - - return Q -end =# - - -const _vert_perms_rt2 = [ - (1,2,3), - (2,3,1), - (3,1,2), - (2,1,3), - (1,3,2), - (3,2,1), -] -const _dof_perms_rt2 = [ - (1,2,3,4,5,6,7,8), - (5,6,1,2,3,4,7,8), - (3,4,5,6,1,2,7,8), - (4,3,2,1,6,5,7,8), - (2,1,6,5,4,3,7,8), - (6,5,4,3,2,1,7,8), -] - -function dof_permutation(::RT2RefSpace, vert_permutation) - i = findfirst(==(tuple(vert_permutation...)), _vert_perms_rt2) - @assert i != nothing - return _dof_perms_rt2[i] -end - -function dof_perm_matrix(::RT2RefSpace, vert_permutation) - i = findfirst(==(tuple(vert_permutation...)), _vert_perms_rt2) -======= function (f::RT2RefSpace)(p) u, v = parametric(p) @@ -232,7 +114,6 @@ end function dof_perm_matrix(::RT2RefSpace, vert_permutation) i = findfirst(==(tuple(vert_permutation...)), _vert_perms_rt) ->>>>>>> fce8340c8eb82b7d423926c93897058123a66311 @assert i != nothing return _dof_rt2perm_matrix[i] end \ No newline at end of file diff --git a/src/bases/rt2space.jl b/src/bases/rt2space.jl deleted file mode 100644 index ae95711f..00000000 --- a/src/bases/rt2space.jl +++ /dev/null @@ -1,133 +0,0 @@ - - -mutable struct RT2Basis{T,M,P} <: Space{T} - geo::M - fns::Vector{Vector{Shape{T}}} - pos::Vector{P} -end - -RT2Basis(geo, fns) = RT2Basis(geo, fns, Vector{vertextype(geo)}(undef,length(fns))) - -#= positions(rt) = rt.pos =# -refspace(space::RT2Basis{T}) where {T} = RT2RefSpace{T}() -subset(rt::RT2Basis,I) = RT2Basis(rt.geo, rt.fns[I], rt.pos[I]) - -#= mutable struct ValDiv end =# - - - -""" - raviartthomas(mesh, cellpairs::Array{Int,2}) - -Constructs the RT basis on the input `mesh`. The i-th RT basis function will - represent a current distribution flowing from cell `cellpairs[1,i]` to - `cellpairs[2,i]` on the mesh. - -Returns an object of type `RTBasis`, which comprises both the mesh and pairs of - Shape objects which corresponds to the cell pairs, containing the necsessary - coefficients and indices to compute the exact basis functions when required - by the solver. -""" -function raviartthomas2(mesh::CompScienceMeshes.AbstractMesh{U,D1,T}, cellpairs::Array{Int,2}) where {U,D1,T} - - # combine now the pairs of monopolar RWGs in div-conforming RWGs - numpairs = size(cellpairs,2) - Cells = cells(mesh) - numcells = size(Cells,1) - functions = Vector{Vector{Shape{T}}}(undef,2*(numpairs+numcells)) - positions = Vector{vertextype(mesh)}(undef,2*(numpairs+numcells)) - chooseref1(x) = if x<0 return 0 else return -1 end - chooseref2(x) = if x<0 return -1 else return 0 end - for i in 1:numpairs - if cellpairs[2,i] > 0 - c1 = cellpairs[1,i]; # cell1 = Cells[c1] #mesh.faces[c1] - c2 = cellpairs[2,i]; # cell2 = Cells[c2] #mesh.faces[c2] - - cell1 = CompScienceMeshes.indices(mesh, c1) - cell2 = CompScienceMeshes.indices(mesh, c2) - - e1, e2 = getcommonedge(cell1, cell2) - functions[2*i-1] = [ - Shape{T}(c1, 2*abs(e1)+chooseref1(sign(e1)), T(+1.0)), - Shape{T}(c2, 2*abs(e2)+chooseref1(sign(e2)), T(-1.0))] - functions[2*i] = [ - Shape{T}(c1, 2*abs(e1)+chooseref2(sign(e1)), T(+1.0)), - Shape{T}(c2, 2*abs(e2)+chooseref2(sign(e2)), T(-1.0))] - isct = intersect(cell1, cell2) - @assert length(isct) == 2 - @assert !(cell1[abs(e1)] in isct) - @assert !(cell2[abs(e2)] in isct) - - ctr1 = cartesian(center(chart(mesh, c1))) - ctr2 = cartesian(center(chart(mesh, c2))) - positions[2*i-1] = (ctr1 + ctr2) / 2 - positions[2*i] = (ctr1 + ctr2) / 2 - else - c1 = cellpairs[1,i] - e1 = cellpairs[2,i] - functions[i] = [ - Shape(c1, abs(e1), T(+1.0))] - positions[i] = cartesian(center(chart(mesh, c1))) - end - end - - for (i,cell) in enumerate(Cells) - functions[2*numpairs+2*i-1] = [ - Shape{T}(i, 7, T(+1.0))] - - functions[2*numpairs+2*i] = [ - Shape{T}(i, 8, T(+1.0))] - - ctr1 = cartesian(center(chart(mesh, i))) - positions[2*numpairs+2*i-1] = ctr1 - positions[2*numpairs+2*i] = ctr1 - end - - geo = mesh - RT2Basis(geo, functions, positions) -end - - -function raviartthomas2(mesh, edges::CompScienceMeshes.AbstractMesh{U,2} where {U}) - cps = CompScienceMeshes.cellpairs(mesh, edges) - # ids = findall(x -> x>0, cps[2,:]) - raviartthomas2(mesh, cps) -end - -function raviartthomas2(mesh::CompScienceMeshes.AbstractMesh{U,3} where {U}) - bnd = boundary(mesh) - edges = submesh(!in(bnd), skeleton(mesh,1)) - return raviartthomas2(mesh, edges) -end - - -""" - raviartthomas(mesh) - -Conducts pre-processing on the input `mesh` by extracting the cell edges, cell pairs - and indices required to construct the RT basis on the `mesh`. - -Calls raviartthomas(mesh::Mesh, cellpairs::Array{Int,2}), which constructs - the RT basis on the `mesh`, using the cell pairs identified. - -Returns the RT basis object. -""" -function raviartthomas2(mesh; sort=:spacefillingcurve) - edges = skeleton(mesh, 1; sort) - cps = cellpairs(mesh, edges, dropjunctionpair=true) - ids = findall(x -> x>0, cps[2,:]) - raviartthomas2(mesh, cps[:,ids]) -end - -divergence(X::RT2Basis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) -ntrace(X::RT2Basis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) - - -function LinearAlgebra.cross(::NormalVector, s::RT2Basis) - @assert CompScienceMeshes.isoriented(s.geo) - fns = similar(s.fns) - for (i,fn) in pairs(s.fns) - fns[i] = [Shape(sh.cellid, sh.refid, -sh.coeff) for sh in fn] - end - NDBasis(s.geo, fns, s.pos) -end diff --git a/src/identityop.jl b/src/identityop.jl index 7cc6723c..703566af 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -20,7 +20,7 @@ function _alloc_workspace(qd, g, f, tels, bels) A = Vector{typeof(a)}(undef,length(qd)) end -const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace, BDMRefSpace, NCrossBDMRefSpace, RT2RefSpace} +const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace, BDMRefSpace, NCrossBDMRefSpace} defaultquadstrat(::LocalOperator, ::LinearRefSpaceTriangle, ::LinearRefSpaceTriangle) = SingleNumQStrat(6) function quaddata(op::LocalOperator, g::LinearRefSpaceTriangle, f::LinearRefSpaceTriangle, tels, bels, qs::SingleNumQStrat) From dab0ae5a2d86f84087def74374f3dd4141d18c00 Mon Sep 17 00:00:00 2001 From: jay prakash Date: Tue, 27 Feb 2024 18:30:47 +0000 Subject: [PATCH 328/528] 2nd degree div conforming basis fns --- src/BEAST.jl | 1 + src/bases/local/rt2local.jl | 16 ++--- src/bases/rt2space.jl | 133 ++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 8 deletions(-) create mode 100644 src/bases/rt2space.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 0e74310b..9558d598 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -155,6 +155,7 @@ include("bases/local/bdm3dlocal.jl") include("bases/lagrange.jl") include("bases/rtspace.jl") +include("bases/rt2space.jl") include("bases/rtxspace.jl") include("bases/bcspace.jl") include("bases/ndspace.jl") diff --git a/src/bases/local/rt2local.jl b/src/bases/local/rt2local.jl index 16666999..f2d8cad0 100644 --- a/src/bases/local/rt2local.jl +++ b/src/bases/local/rt2local.jl @@ -22,14 +22,14 @@ function (f::RT2RefSpace)(p) # ) return SVector( - (value=(8*u*v-2*u-6*v+2)*tu + (4*v*(2*v-1))*tv, divergence=inv_j), - (value=(-8*u^2-8*u*v+12*u+6*v-4)*tu + (2*v*(-4*u-4*v+3))*tv, divergence=inv_j), - (value=-(2*u*(4*u+4*v-3))*tu - (8*u*v-6*u+8*v^2-12*v+4)*tv, divergence=inv_j), - (value=-(4*u*(1-2*u))*tu - (-8*u*v+6*u+2*v-2)*tv, divergence=inv_j), - (value=-(4u*(1-2u))*tu - (2v*(1-4u))*tv, divergence=inv_j), - (value=-(2u*(1-4v))*tu - (4v*(1-2v))*tv, divergence=inv_j), - (value=(8*u*(-2*u-v+2))*tu + (8*v*(-2*u-v+1))*tv, divergence=inv_j), - (value=(8*u*(-u-2*v+1))*tu + (8*v*(-u-2*v+2))*tv, divergence=inv_j), + (value=((8*u^2+8*u*v-12*u-6*v+4)*tu + (2*v*(4*u+4*v-3))*tv)*inv_j, divergence=(24*u+24*v-18)*inv_j), + (value=((-8*u*v+2*u+6*v-2)*tu + (4*v*(-2*v+1))*tv)*inv_j, divergence=(6-24*v)*inv_j), + (value=((4*u*(1-2*u))*tu + (-8*u*v+6*u+2*v-2)*tv)*inv_j, divergence=(6-24*u)*inv_j), + (value=((2*u*(4*u+4*v-3))*tu + (8*u*v-6*u+8*v^2-12*v+4)*tv)*inv_j, divergence=(24*u+24*v-18)*inv_j), + (value=((2u*(1-4v))*tu + (4v*(1-2v))*tv)*inv_j, divergence=(6-24*v)*inv_j), + (value=((4u*(1-2u))*tu + (2v*(1-4u))*tv)*inv_j, divergence=(6-24*u)*inv_j), + (value=((8*u*(-2*u-v+2))*tu + (8*v*(-2*u-v+1))*tv)*inv_j, divergence=(-48*u-24*v+24)*inv_j), + (value=((8*u*(-u-2*v+1))*tu + (8*v*(-u-2*v+2))*tv)*inv_j, divergence=(-24*u-48*v+24)*inv_j), ) end diff --git a/src/bases/rt2space.jl b/src/bases/rt2space.jl new file mode 100644 index 00000000..ae95711f --- /dev/null +++ b/src/bases/rt2space.jl @@ -0,0 +1,133 @@ + + +mutable struct RT2Basis{T,M,P} <: Space{T} + geo::M + fns::Vector{Vector{Shape{T}}} + pos::Vector{P} +end + +RT2Basis(geo, fns) = RT2Basis(geo, fns, Vector{vertextype(geo)}(undef,length(fns))) + +#= positions(rt) = rt.pos =# +refspace(space::RT2Basis{T}) where {T} = RT2RefSpace{T}() +subset(rt::RT2Basis,I) = RT2Basis(rt.geo, rt.fns[I], rt.pos[I]) + +#= mutable struct ValDiv end =# + + + +""" + raviartthomas(mesh, cellpairs::Array{Int,2}) + +Constructs the RT basis on the input `mesh`. The i-th RT basis function will + represent a current distribution flowing from cell `cellpairs[1,i]` to + `cellpairs[2,i]` on the mesh. + +Returns an object of type `RTBasis`, which comprises both the mesh and pairs of + Shape objects which corresponds to the cell pairs, containing the necsessary + coefficients and indices to compute the exact basis functions when required + by the solver. +""" +function raviartthomas2(mesh::CompScienceMeshes.AbstractMesh{U,D1,T}, cellpairs::Array{Int,2}) where {U,D1,T} + + # combine now the pairs of monopolar RWGs in div-conforming RWGs + numpairs = size(cellpairs,2) + Cells = cells(mesh) + numcells = size(Cells,1) + functions = Vector{Vector{Shape{T}}}(undef,2*(numpairs+numcells)) + positions = Vector{vertextype(mesh)}(undef,2*(numpairs+numcells)) + chooseref1(x) = if x<0 return 0 else return -1 end + chooseref2(x) = if x<0 return -1 else return 0 end + for i in 1:numpairs + if cellpairs[2,i] > 0 + c1 = cellpairs[1,i]; # cell1 = Cells[c1] #mesh.faces[c1] + c2 = cellpairs[2,i]; # cell2 = Cells[c2] #mesh.faces[c2] + + cell1 = CompScienceMeshes.indices(mesh, c1) + cell2 = CompScienceMeshes.indices(mesh, c2) + + e1, e2 = getcommonedge(cell1, cell2) + functions[2*i-1] = [ + Shape{T}(c1, 2*abs(e1)+chooseref1(sign(e1)), T(+1.0)), + Shape{T}(c2, 2*abs(e2)+chooseref1(sign(e2)), T(-1.0))] + functions[2*i] = [ + Shape{T}(c1, 2*abs(e1)+chooseref2(sign(e1)), T(+1.0)), + Shape{T}(c2, 2*abs(e2)+chooseref2(sign(e2)), T(-1.0))] + isct = intersect(cell1, cell2) + @assert length(isct) == 2 + @assert !(cell1[abs(e1)] in isct) + @assert !(cell2[abs(e2)] in isct) + + ctr1 = cartesian(center(chart(mesh, c1))) + ctr2 = cartesian(center(chart(mesh, c2))) + positions[2*i-1] = (ctr1 + ctr2) / 2 + positions[2*i] = (ctr1 + ctr2) / 2 + else + c1 = cellpairs[1,i] + e1 = cellpairs[2,i] + functions[i] = [ + Shape(c1, abs(e1), T(+1.0))] + positions[i] = cartesian(center(chart(mesh, c1))) + end + end + + for (i,cell) in enumerate(Cells) + functions[2*numpairs+2*i-1] = [ + Shape{T}(i, 7, T(+1.0))] + + functions[2*numpairs+2*i] = [ + Shape{T}(i, 8, T(+1.0))] + + ctr1 = cartesian(center(chart(mesh, i))) + positions[2*numpairs+2*i-1] = ctr1 + positions[2*numpairs+2*i] = ctr1 + end + + geo = mesh + RT2Basis(geo, functions, positions) +end + + +function raviartthomas2(mesh, edges::CompScienceMeshes.AbstractMesh{U,2} where {U}) + cps = CompScienceMeshes.cellpairs(mesh, edges) + # ids = findall(x -> x>0, cps[2,:]) + raviartthomas2(mesh, cps) +end + +function raviartthomas2(mesh::CompScienceMeshes.AbstractMesh{U,3} where {U}) + bnd = boundary(mesh) + edges = submesh(!in(bnd), skeleton(mesh,1)) + return raviartthomas2(mesh, edges) +end + + +""" + raviartthomas(mesh) + +Conducts pre-processing on the input `mesh` by extracting the cell edges, cell pairs + and indices required to construct the RT basis on the `mesh`. + +Calls raviartthomas(mesh::Mesh, cellpairs::Array{Int,2}), which constructs + the RT basis on the `mesh`, using the cell pairs identified. + +Returns the RT basis object. +""" +function raviartthomas2(mesh; sort=:spacefillingcurve) + edges = skeleton(mesh, 1; sort) + cps = cellpairs(mesh, edges, dropjunctionpair=true) + ids = findall(x -> x>0, cps[2,:]) + raviartthomas2(mesh, cps[:,ids]) +end + +divergence(X::RT2Basis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) +ntrace(X::RT2Basis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) + + +function LinearAlgebra.cross(::NormalVector, s::RT2Basis) + @assert CompScienceMeshes.isoriented(s.geo) + fns = similar(s.fns) + for (i,fn) in pairs(s.fns) + fns[i] = [Shape(sh.cellid, sh.refid, -sh.coeff) for sh in fn] + end + NDBasis(s.geo, fns, s.pos) +end From 05f47184c85e60fa9e0b1e63ffd455ebf31b036b Mon Sep 17 00:00:00 2001 From: jay prakash Date: Tue, 27 Feb 2024 18:43:33 +0000 Subject: [PATCH 329/528] example for 2nd degree rt basis --- examples/efie_rt2.jl | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 examples/efie_rt2.jl diff --git a/examples/efie_rt2.jl b/examples/efie_rt2.jl new file mode 100644 index 00000000..8e0a8c61 --- /dev/null +++ b/examples/efie_rt2.jl @@ -0,0 +1,21 @@ +using CompScienceMeshes +using BEAST + +#Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +Γ = meshsphere(radius=1.0, h=0.2) +# Γ = CompScienceMeshes.meshmobius(h=0.035) +X = BEAST.raviartthomas2(Γ) + +κ, η = 1.0, 1.0 +t = Maxwell3D.singlelayer(wavenumber=κ) +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +# E = -η/(im*κ)*BEAST.CurlCurlGreen(κ, ẑ, point(2,0,0)) +e = (n × E) × n + +@hilbertspace j +@hilbertspace k +efie = @discretise t[k,j]==e[k] j∈X k∈X +u, ch = BEAST.gmres_ch(efie; restart=1500) + +include("utils/postproc.jl") +include("utils/plotresults.jl") From 134c64ab4db3ceccc29d7f5d5e4e28e638dc438d Mon Sep 17 00:00:00 2001 From: jay prakash Date: Fri, 1 Mar 2024 15:03:12 +0000 Subject: [PATCH 330/528] fix in future the fns to compute div, ntrace and ncross of RT2 basis --- src/bases/rt2space.jl | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/bases/rt2space.jl b/src/bases/rt2space.jl index ae95711f..b012237b 100644 --- a/src/bases/rt2space.jl +++ b/src/bases/rt2space.jl @@ -17,13 +17,13 @@ subset(rt::RT2Basis,I) = RT2Basis(rt.geo, rt.fns[I], rt.pos[I]) """ - raviartthomas(mesh, cellpairs::Array{Int,2}) + raviartthomas2(mesh, cellpairs::Array{Int,2}) -Constructs the RT basis on the input `mesh`. The i-th RT basis function will +Constructs the RT2 basis on the input `mesh`. The i-th RT2 basis function will represent a current distribution flowing from cell `cellpairs[1,i]` to `cellpairs[2,i]` on the mesh. -Returns an object of type `RTBasis`, which comprises both the mesh and pairs of +Returns an object of type `RT2Basis`, which comprises both the mesh and pairs of Shape objects which corresponds to the cell pairs, containing the necsessary coefficients and indices to compute the exact basis functions when required by the solver. @@ -102,15 +102,15 @@ end """ - raviartthomas(mesh) + raviartthomas2(mesh) Conducts pre-processing on the input `mesh` by extracting the cell edges, cell pairs - and indices required to construct the RT basis on the `mesh`. + and indices required to construct the RT2 basis on the `mesh`. -Calls raviartthomas(mesh::Mesh, cellpairs::Array{Int,2}), which constructs - the RT basis on the `mesh`, using the cell pairs identified. +Calls raviartthomas2(mesh::Mesh, cellpairs::Array{Int,2}), which constructs + the RT2 basis on the `mesh`, using the cell pairs identified. -Returns the RT basis object. +Returns the RT2 basis object. """ function raviartthomas2(mesh; sort=:spacefillingcurve) edges = skeleton(mesh, 1; sort) @@ -119,15 +119,15 @@ function raviartthomas2(mesh; sort=:spacefillingcurve) raviartthomas2(mesh, cps[:,ids]) end -divergence(X::RT2Basis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) -ntrace(X::RT2Basis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) +#= divergence(X::RT2Basis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) +ntrace(X::RT2Basis, geo, fns) = LagrangeBasis{0,-1,1}(geo, fns, deepcopy(positions(X))) =# -function LinearAlgebra.cross(::NormalVector, s::RT2Basis) +#= function LinearAlgebra.cross(::NormalVector, s::RT2Basis) @assert CompScienceMeshes.isoriented(s.geo) fns = similar(s.fns) for (i,fn) in pairs(s.fns) fns[i] = [Shape(sh.cellid, sh.refid, -sh.coeff) for sh in fn] end - NDBasis(s.geo, fns, s.pos) -end + ND2Basis(s.geo, fns, s.pos) +end =# From fb0f1a7847195e37f75fe2e98bfc95c771939590 Mon Sep 17 00:00:00 2001 From: azuccott Date: Mon, 4 Mar 2024 17:02:05 +0100 Subject: [PATCH 331/528] ncrossbdm permutation + minor changes --- examples/disabled/tdhh3d_neumann.jl | 10 ++-- examples/tdacusticsinglelayer.jl | 34 +++++++----- src/bases/local/ncrossbdmlocal.jl | 22 ++++++++ src/maxwell/timedomain/acustictdops.jl | 77 +++++++++++++++++++++----- 4 files changed, 110 insertions(+), 33 deletions(-) diff --git a/examples/disabled/tdhh3d_neumann.jl b/examples/disabled/tdhh3d_neumann.jl index 5db0c60b..adf753ab 100644 --- a/examples/disabled/tdhh3d_neumann.jl +++ b/examples/disabled/tdhh3d_neumann.jl @@ -2,8 +2,8 @@ using CompScienceMeshes using BEAST using LinearAlgebra -G = meshsphere(1.0, 0.30) -#G = CompScienceMeshes.meshmobius(h=0.1) +#G = meshsphere(1.0, 0.30) +G = CompScienceMeshes.meshmobius(h=0.2) c = 1.0 S = BEAST.HH3DSingleLayerTDBIO(c) @@ -37,8 +37,8 @@ P = timebasiscxd0(Δt, Nt) # assemble the right hand side bd = assemble(n⋅h, X ⊗ P) -Z1d = assemble(Id ⊗ Id, X ⊗ P, X ⊗ P, Val{:bandedstorage}) -Z0d = assemble(D, X ⊗ P, X ⊗ P, Val{:bandedstorage}) +Z1d = assemble(Id ⊗ Id, X ⊗ P, X ⊗ P) +Z0d = assemble(D, X ⊗ P, X ⊗ P) Zd = Z0d + (-0.5)*Z1d u = marchonintime(inv(Zd[:,:,1]), Zd, bd, Nt) @@ -47,7 +47,7 @@ Zs = assemble(S, X ⊗ δ, X ⊗ P) v = marchonintime(inv(Zs[:,:,1]), Zs, -bs, Nt) -tdacusticsl = @discretise S[j′,j] == -1.0e[j′] j∈ (X ⊗ P) j′∈ (X ⊗ δ) +tdacusticsl = @discretise D[j′,j] == 1.0(n⋅h)[j′] j∈ (X ⊗ P) j′∈ (X ⊗ δ) xacusticsl = solve(tdacusticsl) diff --git a/examples/tdacusticsinglelayer.jl b/examples/tdacusticsinglelayer.jl index 4f049814..b952de14 100644 --- a/examples/tdacusticsinglelayer.jl +++ b/examples/tdacusticsinglelayer.jl @@ -1,11 +1,10 @@ using CompScienceMeshes, BEAST, LinearAlgebra #Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) -Γ = meshsphere(radius=1.0, h=0.30) +Γ = CompScienceMeshes.meshsphere(1.0,0.3)#CompScienceMeshes.meshcuboid(1.0,1.0,1.0,0.3) X = lagrangecxd0(Γ) -Δt = 0.16 -Nt = 300 +Δt, Nt = 0.08, 600 T = timebasisshiftedlagrange(Δt, Nt, 0) U = timebasisdelta(Δt, Nt) @@ -19,7 +18,7 @@ e = BEAST.planewave(point(0,0,1), 1.0, gaussian) @hilbertspace j @hilbertspace j′ -SL = TDAcustic3D.acusticsinglelayer(speedofsound=1.0, numdiffs=1) +SL = TDAcustic3D.acusticsinglelayer(speedofsound=1.0, numdiffs=0) # BEAST.@defaultquadstrat (SL, W, V) BEAST.OuterNumInnerAnalyticQStrat(7) tdacusticsl = @discretise SL[j′,j] == -1.0e[j′] j∈V j′∈W @@ -27,27 +26,34 @@ xacusticsl = solve(tdacusticsl) Z = assemble(SL,W,V) #corregere da qui -#import Plots -#Plots.plot(xacusticsl[1,250:300]) +import Plots -#import Plotly +Plots.plot(xacusticsl[1,1:300],label="Current") +Plots.xlabel!("t") +Plots.savefig("stablecurrent.png") + +xacusticsl[1,200:300] +#pval=ConvolutionOperators.polyvals(Z) +import Plotly #fcr, geo = facecurrents(xefie[:,125], X) #Plotly.plot(patch(geo, norm.(fcr))) import BEAST.ConvolutionOperators -for a in 0:300 +for a in 1:1 za=ConvolutionOperators.timeslice(Z,a) - #zawilton=ConvolutionOperators.timeslice(Zs,a) - for i in 1:numfunctions(X) - for j in 1:numfunctions(X) - if (za[i,j])<0.0 - println(a,[i,j]," ",za[i,j]," ",) + zawilton=ConvolutionOperators.timeslice(Zs,a) + for i in 1:10 #numfunctions(X) + #for j in 1:numfunctions(X) + if norm(za[i,i]-zawilton[i,i])>=10^(-6) + println(a," ",i," ",za[i,i]," ",zawilton[i,i]) end - end + #end end end + + name=readline() parse(Float64, name) diff --git a/src/bases/local/ncrossbdmlocal.jl b/src/bases/local/ncrossbdmlocal.jl index b7dc7504..fe2a2a85 100644 --- a/src/bases/local/ncrossbdmlocal.jl +++ b/src/bases/local/ncrossbdmlocal.jl @@ -18,3 +18,25 @@ function (f::NCrossBDMRefSpace{T})(p) where T (value= n × (u*tu)/j, curl=d), (value= n × (v*tv)/j, curl=d),] end + +const _vert_perms_ncrossbdm = [ + (1,2,3), + (2,3,1), + (3,1,2), + (2,1,3), + (1,3,2), + (3,2,1), +] +const _dof_perms_ncrossbdm = [ + (1,2,3,4,5,6), + (5,6,1,2,3,4), + (3,4,5,6,1,2), + (4,3,2,1,6,5), + (2,1,6,5,4,3), + (6,5,4,3,2,1), +] + +function dof_permutation(::NCrossBDMRefSpace, vert_permutation) + i = findfirst(==(tuple(vert_permutation...)), _vert_perms_ncrossbdm) + return _dof_perms_ncrossbdm[i] +end \ No newline at end of file diff --git a/src/maxwell/timedomain/acustictdops.jl b/src/maxwell/timedomain/acustictdops.jl index d23edece..4a865d87 100644 --- a/src/maxwell/timedomain/acustictdops.jl +++ b/src/maxwell/timedomain/acustictdops.jl @@ -31,7 +31,7 @@ end #of the module export TDAcustic3D -defaultquadstrat(::AcusticSingleLayerTDIO, tfs, bfs) = #=nothing=# AllAnalyticalQStrat(1) +defaultquadstrat(::AcusticSingleLayerTDIO, tfs, bfs) = nothing # AllAnalyticalQStrat(1) #nothing goes in hybrid qr, allanalytical goes in zuccottirule @@ -233,10 +233,26 @@ function momintegrals!(z, op::AcusticSingleLayerTDIO, g::LagrangeRefSpace{T,0,3} if hits==3 #print("\np1=",τ[1],"\np2=",τ[2],"\np3=",τ[3],"\nt1=",t1,"\nt2=",t2) - - z[1,1,1]+=(TimeDomainBEMInt.intcoinctriangles(τ[1],τ[2],τ[3],t1,t2))/(4*π) + entry=(TimeDomainBEMInt.intcoinctriangles(τ[1],τ[2],τ[3],t1,t2))/(4*π) + if norm(entry)>10.0 || entry<-0.0001 + println("quadrature ",τ[1]," ",τ[2]," ",τ[3]," ", t1," ",t2," ",entry) + XW = qr.outer_quad_points + for p in 1 : length(XW) + x = XW[p].point + w = XW[p].weight + innerintegrals!(z, op, x, g, f, t, τ, σ, ι, qr, w) + end + else + z[1,1,1]+=max(entry,0.0) + end elseif hits==2 - #pay attention with double layer and index permutation or in whatever case has nx + #= XW = qr.outer_quad_points + for p in 1 : length(XW) + x = XW[p].point + w = XW[p].weight + innerintegrals!(z, op, x, g, f, t, τ, σ, ι, qr, w) + end=# + #pay attention with double layer and index permutation or whatever case has n cross product if mod1(a1index +1,3)==a2index a3index=mod1(a1index-1,3) else @@ -250,22 +266,55 @@ function momintegrals!(z, op::AcusticSingleLayerTDIO, g::LagrangeRefSpace{T,0,3} end #print("\np1=",τ[a1index],"\np2=",τ[a2index],"\np3=",τ[a3index],"\nv1=",σ[b1index],"\nv2=",σ[b2index],"\nv3=",σ[b3index],"\nt1=",t1,"\nt2=",t2) - - z[1,1,1]+=(TimeDomainBEMInt.inttriangletriangleadjacent(τ[a1index],τ[a2index],τ[a3index],σ[b1index],σ[b2index],σ[b3index],t1,t2,qpc))/(4*π) + entry=(TimeDomainBEMInt.inttriangletriangleadjacent(τ[a1index],τ[a2index],τ[a3index],σ[b1index],σ[b2index],σ[b3index],t1,t2,qpc))/(4*π) + if norm(entry)>10.0 || entry<-0.0001 + println("quadrature ",τ[a1index]," ",τ[a2index]," ",τ[a3index]," ",σ[b1index]," ",σ[b2index]," ",σ[b3index], t1," ",t2," ",entry) + XW = qr.outer_quad_points + for p in 1 : length(XW) + x = XW[p].point + w = XW[p].weight + innerintegrals!(z, op, x, g, f, t, τ, σ, ι, qr, w) + end + else + z[1,1,1]+=max(0.0,entry) + end elseif hits==1 - a2index,a3index=mod1(a1index+1,3),mod1(a1index+2,3) - b2index,b3index=mod1(b1index+1,3),mod1(b1index+2,3) - #print("\np1=",τ[a1index],"\np2=",τ[a2index],"\np3=",τ[a3index],"\nv1=",σ[b1index],"\nv2=",σ[b2index],"\nv3=",σ[b3index],"\nt1=",t1,"\nt2=",t2) - z[1,1,1]+=(TimeDomainBEMInt.intcommonvertex(τ[a1index],τ[a2index],τ[a3index],σ[b2index],σ[b3index],t1,t2,qpc))/(4*π) - else - #print("\np1=",τ[1],"\np2=",τ[2],"\np3=",τ[3],"\nv1=",σ[1],"\nv2=",σ[2],"\nv3=",σ[3],"\nt1=",t1,"\nt2=",t2) - XW = qr.outer_quad_points + #=XW = qr.outer_quad_points for p in 1 : length(XW) x = XW[p].point w = XW[p].weight innerintegrals!(z, op, x, g, f, t, τ, σ, ι, qr, w) + end=# + a2index,a3index=mod1(a1index+1,3),mod1(a1index+2,3) + b2index,b3index=mod1(b1index+1,3),mod1(b1index+2,3) + #print("\np1=",τ[a1index],"\np2=",τ[a2index],"\np3=",τ[a3index],"\nv1=",σ[b1index],"\nv2=",σ[b2index],"\nv3=",σ[b3index],"\nt1=",t1,"\nt2=",t2) + entry=(TimeDomainBEMInt.intcommonvertex(τ[a1index],τ[a2index],τ[a3index],σ[b2index],σ[b3index],t1,t2,qpc))/(4*π) + if norm(entry)>10.0 || entry<-0.0001 + println("quadrature ",τ[a1index]," ",τ[a2index]," ",τ[a3index]," ",σ[b2index]," ",σ[b3index], t1," ",t2," ",entry) + + XW = qr.outer_quad_points + for p in 1 : length(XW) + x = XW[p].point + w = XW[p].weight + innerintegrals!(z, op, x, g, f, t, τ, σ, ι, qr, w) + end + else + z[1,1,1]+=max(0.0,entry) + end + else + entry=0.1#(inttriangletriangle(τ[1],τ[2],τ[3],σ[1],σ[2],σ[3],t1,t2,qpc))/(4*π) + #print("\np1=",τ[1],"\np2=",τ[2],"\np3=",τ[3],"\nv1=",σ[1],"\nv2=",σ[2],"\nv3=",σ[3],"\nt1=",t1,"\nt2=",t2) + if norm(entry)>0.0 || entry<-0.0001 + XW = qr.outer_quad_points + for p in 1 : length(XW) + x = XW[p].point + w = XW[p].weight + innerintegrals!(z, op, x, g, f, t, τ, σ, ι, qr, w) + end + + else + z[1,1,1]+=max(0.0,entry) end - end #for the moment sos=1 but I will correct this end \ No newline at end of file From e331d182dccf85371cd5270ab2247a7bebce5e00 Mon Sep 17 00:00:00 2001 From: djukic14 Date: Tue, 5 Mar 2024 15:42:33 +0100 Subject: [PATCH 332/528] Add explicit static case --- src/helmholtz3d/helmholtz3d.jl | 25 ++++---- src/maxwell/farfield.jl | 10 ++-- src/maxwell/maxwell.jl | 16 ++--- src/maxwell/mwexc.jl | 4 +- src/maxwell/mwops.jl | 70 +++++++++++++++++----- src/maxwell/nearfield.jl | 5 +- test/runtests.jl | 2 + test/test_handlers.jl | 105 +++++++++++++++++++++++++++++++++ 8 files changed, 192 insertions(+), 45 deletions(-) create mode 100644 test/test_handlers.jl diff --git a/src/helmholtz3d/helmholtz3d.jl b/src/helmholtz3d/helmholtz3d.jl index 831a34c9..df517b46 100644 --- a/src/helmholtz3d/helmholtz3d.jl +++ b/src/helmholtz3d/helmholtz3d.jl @@ -47,18 +47,19 @@ module Helmholtz3D gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) if alpha === nothing - if gamma !== nothing - alpha = gamma^2 + if Mod.isstatic(gamma) #static case + alpha = 0.0 # In the long run, this should probably be rather 'nothing' else - alpha = 0.0 # In the long run, this should probably be rather 'nothing' + alpha = gamma^2 end + end if beta === nothing - if gamma !== nothing - beta = one(gamma) - else + if Mod.isstatic(gamma) #static case beta = one(alpha) + else + beta = one(gamma) end end @@ -75,10 +76,10 @@ module Helmholtz3D # Note: Unlike for the operators, there seems little benefit in # explicitly declaring a Laplace-Type excitation. - - gamma === nothing && (gamma = zero(amplitude)) - return Mod.HH3DPlaneWave(direction, amplitude, gamma) + Mod.isstatic(gamma) && (gamma = zero(amplitude)) + + return Mod.HH3DPlaneWave(direction, gamma, amplitude) end function linearpotential(; direction=SVector(1, 0, 0), amplitude=1.0) @@ -97,7 +98,7 @@ module Helmholtz3D ) gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) - gamma === nothing && (gamma = zero(amplitude)) + Mod.isstatic(gamma) && (gamma = zero(amplitude)) return Mod.HH3DMonopole(position, gamma, amplitude) end @@ -110,11 +111,11 @@ module Helmholtz3D ) gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) - gamma === nothing && (gamma = zero(amplitude)) + Mod.isstatic(gamma) && (gamma = zero(amplitude)) return Mod.gradHH3DMonopole(position, gamma, amplitude) end end -export Helmholtz3D \ No newline at end of file +export Helmholtz3D diff --git a/src/maxwell/farfield.jl b/src/maxwell/farfield.jl index 0a41e0b7..615b3991 100644 --- a/src/maxwell/farfield.jl +++ b/src/maxwell/farfield.jl @@ -28,8 +28,8 @@ function MWFarField3D(; wavenumber=nothing, amplitude=nothing ) - gamma, _ = gamma_wavenumber_handler(gamma, wavenumber) - @assert gamma !== nothing + gamma, _ = gamma_wavenumber_handler(gamma, wavenumber) + @assert !isstatic(gamma) amplitude === nothing && (amplitude = 1.0) @@ -44,8 +44,7 @@ function MWDoubleLayerFarField3D(; amplitude=nothing ) gamma, _ = gamma_wavenumber_handler(gamma, wavenumber) - - @assert gamma !== nothing + @assert !isstatic(gamma) amplitude === nothing && (amplitude = 1.0) @@ -81,8 +80,7 @@ function MWDoubleLayerRotatedFarField3D(; amplitude=nothing ) gamma, _ = gamma_wavenumber_handler(gamma, wavenumber) - - @assert gamma !== nothing + @assert !isstatic(gamma) amplitude === nothing && (amplitude = 1.0) diff --git a/src/maxwell/maxwell.jl b/src/maxwell/maxwell.jl index a155ab8b..ea515750 100644 --- a/src/maxwell/maxwell.jl +++ b/src/maxwell/maxwell.jl @@ -21,10 +21,12 @@ module Maxwell3D alpha=nothing, beta=nothing) - + gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) - @assert gamma !== nothing + if Mod.isstatic(gamma) # static case + @assert !(isnothing(alpha)) && !(isnothing(beta)) + end alpha === nothing && (alpha = -gamma) beta === nothing && (beta = -1/gamma) @@ -54,15 +56,15 @@ module Maxwell3D gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) - if alpha === nothing - if gamma !== nothing - alpha = one(gamma) - else + if isnothing(alpha) + if Mod.isstatic(gamma) # static case alpha = 1.0 # Default to double precision + else + alpha = one(gamma) end end - Mod.MWDoubleLayer3D(gamma) + Mod.MWDoubleLayer3D(alpha, gamma) end planewave(; diff --git a/src/maxwell/mwexc.jl b/src/maxwell/mwexc.jl index be4a4a80..6361c5e5 100644 --- a/src/maxwell/mwexc.jl +++ b/src/maxwell/mwexc.jl @@ -27,7 +27,7 @@ function planewavemw3d(; ) gamma, wavenumber = gamma_wavenumber_handler(gamma, wavenumber) - gamma === nothing && (gamma = zero(eltype(direction))) + isstatic(gamma) && (gamma = zero(eltype(direction))) return PlaneWaveMW(direction, polarization, gamma, amplitude) @@ -98,7 +98,7 @@ function dipolemw3d(; ) gamma, wavenumber = gamma_wavenumber_handler(gamma, wavenumber) - gamma === nothing && (gamma = zero(eltype(orientation))) + isstatic(gamma) && (gamma = zero(eltype(orientation))) return DipoleMW(location, orientation, gamma) diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 0d52e537..eb31154f 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -4,17 +4,18 @@ abstract type MaxwellOperator3DReg{T,K} <: MaxwellOperator3D{T,K} end scalartype(op::MaxwellOperator3D{T,K}) where {T, K <: Nothing} = T scalartype(op::MaxwellOperator3D{T,K}) where {T, K} = promote_type(T, K) -gamma(op::MaxwellOperator3D{T,K}) where {T, K <: Nothing} = T(0) +gamma(op::MaxwellOperator3D{T,Val{0}}) where {T} = zero(T) gamma(op::MaxwellOperator3D{T,K}) where {T, K} = op.gamma - struct MWSingleLayer3D{T,U} <: MaxwellOperator3D{T,U} gamma::T α::U β::U end +gamma(op::MWSingleLayer3D{Val{0}, U}) where {U} = zero(U) + scalartype(op::MWSingleLayer3D{T,U}) where {T,U} = promote_type(T,U) # sign_upon_permutation(op::MWSingleLayer3D, I, J) = 1 @@ -230,12 +231,30 @@ end # ################################################################################ +""" + gamma_wavenumber_handler(gamma, wavenumber) + +This function handles the input of `gamma` and `wavenumber`. It throws an error if both `gamma` and +`wavenumber` are provided. If neither is provided, it assumes a static problem and returns `Val(0)` +for `gamma` and `wavenumber`. + +# Arguments +- `gamma`: `im` * `wavenumber` or `nothing`. +- `wavenumber`: `wavenumber` or `nothing`. + +# Returns +- `gamma` and `wavenumber`: Appropriate pair `gamma` and `wavenumber`. +""" function gamma_wavenumber_handler(gamma, wavenumber) - if !((gamma !== nothing) ⊻ (wavenumber !== nothing)) - error("Supply one of (not both) gamma or wavenumber") + if !isnothing(gamma) && !isnothing(wavenumber) + error("Supplying both gamma and wavenumber is not supported.") + + elseif isnothing(gamma) && isnothing(wavenumber) + # if neither gamma nor wavenumber is supplied, we are assuming a static problem + return Val(0), Val(0) end - if gamma === nothing && (wavenumber !== nothing) + if isnothing(gamma) && !isnothing(wavenumber) if iszero(real(wavenumber)) gamma = -imag(wavenumber) else @@ -243,21 +262,40 @@ function gamma_wavenumber_handler(gamma, wavenumber) end end - return gamma, wavenumber + return gamma, wavenumber +end + +""" + isstatic(gamma) + +This function checks if the provided `gamma` value represents a static problem. +It returns true if `gamma` is of type `Val{0}` indicating a static problem. + +# Arguments +- `gamma`: `gamma` value. + +# Returns +- A boolean indicating whether the problem is static or not. +""" +function isstatic(gamma) + return typeof(gamma) == Val{0} +end + +function isstatic(op::MaxwellOperator3D) + return isstatic(op.gamma) end function operator_parameter_handler(alpha, gamma, wavenumber) - gamma, wavenumber = gamma_wavenumber_handler(gamma, wavenumber) +gamma, wavenumber = gamma_wavenumber_handler(gamma, wavenumber) - if alpha === nothing - if gamma !== nothing - alpha = one(real(typeof(gamma))) - else - # We are dealing with a static problem. Default to double precision. - alpha = 1.0 - end - end + if alpha === nothing + if isstatic(gamma) # static problem + alpha = 1.0 # default to double precision + else + alpha = one(real(typeof(gamma))) + end + end - return alpha, gamma +return alpha, gamma end diff --git a/src/maxwell/nearfield.jl b/src/maxwell/nearfield.jl index aaeab729..ba2492cd 100644 --- a/src/maxwell/nearfield.jl +++ b/src/maxwell/nearfield.jl @@ -22,7 +22,8 @@ function MWSingleLayerField3D(; beta=nothing ) gamma, _ = gamma_wavenumber_handler(gamma, wavenumber) - @assert gamma !== nothing + + @assert !isstatic(gamma) alpha === nothing && (alpha = -gamma) beta === nothing && (beta = -1/gamma) @@ -42,7 +43,7 @@ function MWDoubleLayerField3D(; wavenumber=nothing ) gamma, _ = gamma_wavenumber_handler(gamma, wavenumber) - @assert gamma !== nothing + @assert !isstatic(gamma) MWDoubleLayerField3D(gamma) end diff --git a/test/runtests.jl b/test/runtests.jl index 4fafe1ea..0868c62b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -73,6 +73,8 @@ include("test_td_tensoroperator.jl") include("test_variational.jl") +include("test_handlers.jl") + try Pkg.installed("BogaertInts10") diff --git a/test/test_handlers.jl b/test/test_handlers.jl new file mode 100644 index 00000000..191961d1 --- /dev/null +++ b/test/test_handlers.jl @@ -0,0 +1,105 @@ +using BEAST +using StaticArrays +using Test + +for T in [Float16, Float32, Float64] + @test_throws ErrorException BEAST.gamma_wavenumber_handler(T(1), T(2)) + + gamma, wavenumber = BEAST.gamma_wavenumber_handler(T(1), nothing) + @test gamma === T(1) + + gamma, wavenumber = BEAST.gamma_wavenumber_handler(nothing, T(2)) + @test gamma === T(2) * im + + gamma, wavenumber = BEAST.gamma_wavenumber_handler(nothing, nothing) + @test BEAST.isstatic(gamma) + @test gamma === Val(0) + + gamma, wavenumber = BEAST.gamma_wavenumber_handler(T(0), nothing) + @test gamma === T(0) + @test !BEAST.isstatic(gamma) + + gamma, wavenumber = BEAST.gamma_wavenumber_handler(nothing, T(1)) + @test gamma === T(1) * im + @test !BEAST.isstatic(gamma) + + @test_throws ErrorException BEAST.operator_parameter_handler(T(1), T(1), T(1)) + alpha, gamma = BEAST.operator_parameter_handler(nothing, T(1), nothing) + @test alpha === T(1) + @test gamma === T(1) + + alpha, gamma = BEAST.operator_parameter_handler(nothing, nothing, nothing) + @test alpha === 1.0 + @test BEAST.isstatic(gamma) + + alpha, gamma = BEAST.operator_parameter_handler(nothing, im * T(0), nothing) + @test alpha === T(1) + @test !BEAST.isstatic(gamma) + + # Helmholtz3D + operator = Helmholtz3D.hypersingular(; alpha=nothing, beta=nothing, gamma=nothing, + wavenumber=nothing + ) + @test BEAST.isstatic(operator) + @test operator.alpha === 0.0 + @test operator.beta === 1.0 + @test BEAST.gamma(operator) === 0.0 + + operator = Helmholtz3D.hypersingular(; alpha=T(1), beta=T(2)) + @test BEAST.isstatic(operator) + @test operator.alpha === T(1) + @test operator.beta === T(2) + @test BEAST.gamma(operator) === T(0) + + pwave = Helmholtz3D.planewave(; direction=SVector(T(0), T(0), T(1))) + @test pwave.direction == SVector(T(0), T(0), T(1)) + @test pwave.gamma === T(0) + @test pwave.amplitude === T(1) + + mpol = Helmholtz3D.monopole(; position=SVector(T(0), T(0), T(0)), amplitude=T(1)) + @test mpol.position === SVector(T(0), T(0), T(0)) + @test mpol.gamma === T(0) + @test mpol.amplitude === T(1) + + gradmpol = Helmholtz3D.grad_monopole(; position=SVector(T(0), T(0), T(0)), + amplitude=T(1)) + @test gradmpol.position === SVector(T(0), T(0), T(0)) + @test gradmpol.gamma === T(0) + @test gradmpol.amplitude === T(1) + + # FarFields + @test_throws AssertionError BEAST.MWFarField3D() + @test_throws AssertionError BEAST.MWDoubleLayerFarField3D() + @test_throws AssertionError BEAST.MWDoubleLayerRotatedFarField3D() + + # Maxwell3D + operator = Maxwell3D.singlelayer(; alpha=T(1), beta=T(2)) + @test BEAST.isstatic(operator) + @test operator.α === T(1) + @test operator.β === T(2) + @test BEAST.gamma(operator) === T(0) + + operator = Maxwell3D.doublelayer() + @test BEAST.isstatic(operator) + @test BEAST.gamma(operator) === 0.0 + + operator = Maxwell3D.doublelayer(; alpha=T(1)) + @test BEAST.isstatic(operator) + @test operator.alpha === T(1) + @test BEAST.gamma(operator) === T(0) + + # BEAST + pwave = BEAST.planewavemw3d(; + direction=SVector(T(1), T(0), T(0)), + polarization=SVector(T(0), T(1), T(0)) + ) + @test pwave.direction === SVector(T(1), T(0), T(0)) + @test pwave.polarisation === SVector(T(0), T(1), T(0)) + @test pwave.gamma === T(0) + + dpole = BEAST.dipolemw3d(location=SVector(T(0), T(0), T(0)), + orientation=SVector(T(0), T(0), T(1))) + @test dpole.location === SVector(T(0), T(0), T(0)) + @test dpole.orientation === SVector(T(0), T(0), T(1)) + @test dpole.gamma === T(0) +end From 962947dfae869c2ebf5b30804bc59c961e9d8c6d Mon Sep 17 00:00:00 2001 From: jay prakash Date: Tue, 5 Mar 2024 15:47:11 +0000 Subject: [PATCH 333/528] 2nd degree curl conforming bases and tests for rt2 and nd2 --- src/BEAST.jl | 4 +- src/bases/local/bdmlocal.jl | 45 ++++++++++++ src/bases/local/laglocal.jl | 17 ++++- src/bases/local/nd2local.jl | 28 +++++++ src/bases/local/rt2local.jl | 61 +++++++++++++++- src/bases/local/rtlocal.jl | 28 ++++++- src/bases/nd2space.jl | 90 +++++++++++++++++++++++ src/bases/perm_matrices.jl | 142 ------------------------------------ src/identityop.jl | 2 +- test/runtests.jl | 2 + test/test_nd2.jl | 24 ++++++ test/test_rt2.jl | 23 ++++++ 12 files changed, 318 insertions(+), 148 deletions(-) create mode 100644 src/bases/local/nd2local.jl create mode 100644 src/bases/nd2space.jl delete mode 100644 src/bases/perm_matrices.jl create mode 100644 test/test_nd2.jl create mode 100644 test/test_rt2.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 9558d598..bc1f8443 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -32,6 +32,7 @@ export duallagrangecxd0 export lagdimension export restrict export raviartthomas, raowiltonglisson, positions +export raviartthomas2, nedelec2, nedelec export brezzidouglasmarini export brezzidouglasmarini3d export nedelecd3d @@ -147,6 +148,7 @@ include("bases/local/laglocal.jl") include("bases/local/rtlocal.jl") include("bases/local/rt2local.jl") include("bases/local/ndlocal.jl") +include("bases/local/nd2local.jl") include("bases/local/bdmlocal.jl") include("bases/local/ncrossbdmlocal.jl") include("bases/local/ndlcclocal.jl") @@ -159,13 +161,13 @@ include("bases/rt2space.jl") include("bases/rtxspace.jl") include("bases/bcspace.jl") include("bases/ndspace.jl") +include("bases/nd2space.jl") include("bases/bdmdiv.jl") include("bases/ncrossbdmspace.jl") include("bases/ndlccspace.jl") include("bases/ndlcdspace.jl") include("bases/dual3d.jl") include("bases/bdm3dspace.jl") -include("bases/perm_matrices.jl") include("bases/subdbasis.jl") diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index 102e61f8..a07e42b6 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -50,3 +50,48 @@ end dimtype(::BDMRefSpace, ::CompScienceMeshes.Simplex{U,2}) where {U} = Val{6} +const _dof_bdmperm_matrix = [ + + @SMatrix[1 0 0 0 0 0; #1. {1,2,3} + 0 1 0 0 0 0; + 0 0 1 0 0 0; + 0 0 0 1 0 0; + 0 0 0 0 1 0; + 0 0 0 0 0 1], + + @SMatrix[0 0 0 0 1 0; #2. {2,3,1} + 0 0 0 0 0 1; + 1 0 0 0 0 0; + 0 1 0 0 0 0; + 0 0 1 0 0 0; + 0 0 0 1 0 0], + + @SMatrix[0 0 1 0 0 0; #3. {3,1,2} + 0 0 0 1 0 0; + 0 0 0 0 1 0; + 0 0 0 0 0 1; + 1 0 0 0 0 0; + 0 1 0 0 0 0], + + @SMatrix[0 0 0 1 0 0; #4. {2,1,3} + 0 0 1 0 0 0; + 0 1 0 0 0 0; + 1 0 0 0 0 0; + 0 0 0 0 0 1; + 0 0 0 0 1 0], + + @SMatrix[0 1 0 0 0 0; #5. {1,3,2} + 1 0 0 0 0 0; + 0 0 0 0 0 1; + 0 0 0 0 1 0; + 0 0 0 1 0 0; + 0 0 1 0 0 0], + + @SMatrix[0 0 0 0 0 1; #6. {3,2,1} + 0 0 0 0 1 0; + 0 0 0 1 0 0; + 0 0 1 0 0 0; + 0 1 0 0 0 0; + 1 0 0 0 0 0] + +] \ No newline at end of file diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index ea761ea2..a396add9 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -272,4 +272,19 @@ function dof_perm_matrix(::LagrangeRefSpace{<:Any,1}, vert_permutation) i = findfirst(==(tuple(vert_permutation...)), _vert_perms_rt) @assert i != nothing return _dof_rtperm_matrix[i] -end \ No newline at end of file +end + +const _dof_lag0perm_matrix = [ + @SMatrix[1], # 1. {1,2,3} + + @SMatrix[1], # 2. {2,3,1} + + @SMatrix[1], # 3. {3,1,2} + + @SMatrix[1], # 4. {2,1,3} + + @SMatrix[1], # 5. {1,3,2} + + @SMatrix[1] # 6. {3,2,1} + +] \ No newline at end of file diff --git a/src/bases/local/nd2local.jl b/src/bases/local/nd2local.jl new file mode 100644 index 00000000..2a250df0 --- /dev/null +++ b/src/bases/local/nd2local.jl @@ -0,0 +1,28 @@ + +mutable struct ND2RefSpace{T} <: RefSpace{T,8} end + +function (ϕ::ND2RefSpace)(nbd) + + u, v = parametric(nbd) + n = normal(nbd) + j = jacobian(nbd) + + tu = tangents(nbd,1) + tv = tangents(nbd,2) + + d = 2/j + + inv_j = 1/j + + return SVector(( + (value= n × ((8*u^2+8*u*v-12*u-6*v+4)*tu + (2*v*(4*u+4*v-3))*tv)*inv_j, curl=(24*u+24*v-18)*inv_j), + (value= n × ((-8*u*v+2*u+6*v-2)*tu + (4*v*(-2*v+1))*tv)*inv_j, curl=(6-24*v)*inv_j), + (value= n × ((4*u*(1-2*u))*tu + (-8*u*v+6*u+2*v-2)*tv)*inv_j, curl=(6-24*u)*inv_j), + (value= n × ((2*u*(4*u+4*v-3))*tu + (8*u*v-6*u+8*v^2-12*v+4)*tv)*inv_j, curl=(24*u+24*v-18)*inv_j), + (value= n × ((2u*(1-4v))*tu + (4v*(1-2v))*tv)*inv_j, curl=(6-24*v)*inv_j), + (value= n × ((4u*(1-2u))*tu + (2v*(1-4u))*tv)*inv_j, curl=(6-24*u)*inv_j), + (value= n × ((8*u*(-2*u-v+2))*tu + (8*v*(-2*u-v+1))*tv)*inv_j, curl=(-48*u-24*v+24)*inv_j), + (value= n × ((8*u*(-u-2*v+1))*tu + (8*v*(-u-2*v+2))*tv)*inv_j, curl=(-24*u-48*v+24)*inv_j) + )) + +end diff --git a/src/bases/local/rt2local.jl b/src/bases/local/rt2local.jl index f2d8cad0..c33bb710 100644 --- a/src/bases/local/rt2local.jl +++ b/src/bases/local/rt2local.jl @@ -29,7 +29,7 @@ function (f::RT2RefSpace)(p) (value=((2u*(1-4v))*tu + (4v*(1-2v))*tv)*inv_j, divergence=(6-24*v)*inv_j), (value=((4u*(1-2u))*tu + (2v*(1-4u))*tv)*inv_j, divergence=(6-24*u)*inv_j), (value=((8*u*(-2*u-v+2))*tu + (8*v*(-2*u-v+1))*tv)*inv_j, divergence=(-48*u-24*v+24)*inv_j), - (value=((8*u*(-u-2*v+1))*tu + (8*v*(-u-2*v+2))*tv)*inv_j, divergence=(-24*u-48*v+24)*inv_j), + (value=((8*u*(-u-2*v+1))*tu + (8*v*(-u-2*v+2))*tv)*inv_j, divergence=(-24*u-48*v+24)*inv_j) ) end @@ -116,4 +116,61 @@ function dof_perm_matrix(::RT2RefSpace, vert_permutation) i = findfirst(==(tuple(vert_permutation...)), _vert_perms_rt) @assert i != nothing return _dof_rt2perm_matrix[i] -end \ No newline at end of file +end + +const _dof_rt2perm_matrix = [ + @SMatrix[1 0 0 0 0 0 0 0; #1. {1,2,3} + 0 1 0 0 0 0 0 0; + 0 0 1 0 0 0 0 0; + 0 0 0 1 0 0 0 0; + 0 0 0 0 1 0 0 0; + 0 0 0 0 0 1 0 0; + 0 0 0 0 0 0 1 0; + 0 0 0 0 0 0 0 1], + + @SMatrix[0 0 0 0 1 0 0 0; #2. {2,3,1} + 0 0 0 0 0 1 0 0; + 1 0 0 0 0 0 0 0; + 0 1 0 0 0 0 0 0; + 0 0 1 0 0 0 0 0; + 0 0 0 1 0 0 0 0; + 0 0 0 0 0 0 0 -1; + 0 0 0 0 0 0 1 -1], + + @SMatrix[0 0 1 0 0 0 0 0; #3. {3,1,2} + 0 0 0 1 0 0 0 0; + 0 0 0 0 1 0 0 0; + 0 0 0 0 0 1 0 0; + 1 0 0 0 0 0 0 0; + 0 1 0 0 0 0 0 0; + 0 0 0 0 0 0 -1 1; + 0 0 0 0 0 0 -1 0], + + @SMatrix[0 0 0 1 0 0 0 0; #4. {2,1,3} + 0 0 1 0 0 0 0 0; + 0 1 0 0 0 0 0 0; + 1 0 0 0 0 0 0 0; + 0 0 0 0 0 1 0 0; + 0 0 0 0 1 0 0 0; + 0 0 0 0 0 0 0 1; + 0 0 0 0 0 0 1 0], + + @SMatrix[0 1 0 0 0 0 0 0; #5. {1,3,2} + 1 0 0 0 0 0 0 0; + 0 0 0 0 0 1 0 0; + 0 0 0 0 1 0 0 0; + 0 0 0 1 0 0 0 0; + 0 0 1 0 0 0 0 0; + 0 0 0 0 0 0 1 -1; + 0 0 0 0 0 0 0 -1], + + @SMatrix[0 0 0 0 0 1 0 0; #6. {3,2,1} + 0 0 0 0 1 0 0 0; + 0 0 0 1 0 0 0 0; + 0 0 1 0 0 0 0 0; + 0 1 0 0 0 0 0 0; + 1 0 0 0 0 0 0 0; + 0 0 0 0 0 0 -1 0; + 0 0 0 0 0 0 -1 1] +] + diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index e88e3daf..85c93291 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -157,4 +157,30 @@ end function restrict(ϕ::RefSpace, dom1, dom2) interpolate(ϕ, dom2, ϕ, dom1) -end \ No newline at end of file +end + +const _dof_rtperm_matrix = [ + @SMatrix[1 0 0; # 1. {1,2,3} + 0 1 0; + 0 0 1], + + @SMatrix[0 0 1; # 2. {2,3,1} + 1 0 0; + 0 1 0], + + @SMatrix[0 1 0; # 3. {3,1,2} + 0 0 1; + 1 0 0], + + @SMatrix[0 1 0; # 4. {2,1,3} + 1 0 0; + 0 0 1], + + @SMatrix[1 0 0; # 5. {1,3,2} + 0 0 1; + 0 1 0], + + @SMatrix[0 0 1; # 6. {3,2,1} + 0 1 0; + 1 0 0] +] \ No newline at end of file diff --git a/src/bases/nd2space.jl b/src/bases/nd2space.jl new file mode 100644 index 00000000..c44bc7f0 --- /dev/null +++ b/src/bases/nd2space.jl @@ -0,0 +1,90 @@ +mutable struct ND2Basis{T,M,P} <: Space{T} + geo::M + fns::Vector{Vector{Shape{T}}} + pos::Vector{P} +end + +ND2Basis(geo, fns) = ND2Basis(geo, fns, Vector{vertextype(geo)}(undef,length(fns))) + +refspace(space::ND2Basis{T}) where T = ND2RefSpace{T}() + +""" + nedelec2(mesh, edges) + +Constructs the 2nd degree Nedelec basis of the first kind i.e. H(curl). + +Returns an object of type 'ND2Basis'. + +""" +function nedelec2(surface, edges=skeleton(surface,1)) + + T = coordtype(surface) + P = vertextype(surface) + num_edges = numcells(edges) + + C = connectivity(edges, surface, identity) + rows = rowvals(C) + vals = nonzeros(C) + + Cells = cells(surface) + num_cells = size(Cells,1) + + fns = Vector{Vector{Shape{T}}}(undef,2*(num_edges+num_cells)) + pos = Vector{P}(undef,2*(num_edges+num_cells)) + + chooseref1(x) = if x<0 return 0 else return -1 end + chooseref2(x) = if x<0 return -1 else return 0 end + for (i,edge) in enumerate(edges) + + fns[2*i-1] = Vector{Shape{T}}() + fns[2*i] = Vector{Shape{T}}() + pos[2*i-1] = cartesian(center(chart(edges,edge))) + pos[2*i] = cartesian(center(chart(edges,edge))) + + sgn = 1.0 + + for k in nzrange(C,i) + + j = rows[k] # j is the index of a cell adjacent to edge + s = vals[k] # s contains the oriented (signed) local index of edge[i] in cell[j] + + # i == 3 && @show s + push!(fns[2*i-1], Shape{T}(j, 2*abs(s)+chooseref1(sign(s)), T(sgn))) + sgn *= -1 + end + + sgn = 1.0 + + for k in nzrange(C,i) + + j = rows[k] # j is the index of a cell adjacent to edge + s = vals[k] # s contains the oriented (signed) local index of edge[i] in cell[j] + + # i == 3 && @show s + push!(fns[2*i], Shape{T}(j, 2*abs(s)+chooseref2(sign(s)), T(sgn))) + sgn *= -1 + end + end + + for (i,cell) in enumerate(Cells) + fns[2*num_edges+2*i-1] = Vector{Shape{T}}() + fns[2*num_edges+2*i] = Vector{Shape{T}}() + push!(fns[2*num_edges+2*i-1], Shape{T}(i, 7, T(1.0))) + push!(fns[2*num_edges+2*i], Shape{T}(i, 8, T(1.0))) + ctr = cartesian(center(chart(surface,i))) + pos[2*num_edges+2*i-1] = ctr + pos[2*num_edges+2*i] = ctr + end + + ND2Basis(surface, fns, pos) +end + +function LinearAlgebra.cross(::NormalVector, s::ND2Basis) + # @assert CompScienceMeshes.isoriented(s.geo) + return raviartthomas2(s.geo) +end + +#= +function curl(space::ND2Basis) + divergence(n × space) +end =# diff --git a/src/bases/perm_matrices.jl b/src/bases/perm_matrices.jl deleted file mode 100644 index a526c8bc..00000000 --- a/src/bases/perm_matrices.jl +++ /dev/null @@ -1,142 +0,0 @@ -const _dof_rt2perm_matrix = [ - [1 0 0 0 0 0 0 0; #1. {1,2,3} - 0 1 0 0 0 0 0 0; - 0 0 1 0 0 0 0 0; - 0 0 0 1 0 0 0 0; - 0 0 0 0 1 0 0 0; - 0 0 0 0 0 1 0 0; - 0 0 0 0 0 0 1 0; - 0 0 0 0 0 0 0 1], - - [0 0 0 0 1 0 0 0; #2. {2,3,1} - 0 0 0 0 0 1 0 0; - 1 0 0 0 0 0 0 0; - 0 1 0 0 0 0 0 0; - 0 0 1 0 0 0 0 0; - 0 0 0 1 0 0 0 0; - 0 0 0 0 0 0 0 -1; - 0 0 0 0 0 0 1 -1], - - [0 0 1 0 0 0 0 0; #3. {3,1,2} - 0 0 0 1 0 0 0 0; - 0 0 0 0 1 0 0 0; - 0 0 0 0 0 1 0 0; - 1 0 0 0 0 0 0 0; - 0 1 0 0 0 0 0 0; - 0 0 0 0 0 0 -1 1; - 0 0 0 0 0 0 -1 0], - - [0 0 0 1 0 0 0 0; #4. {2,1,3} - 0 0 1 0 0 0 0 0; - 0 1 0 0 0 0 0 0; - 1 0 0 0 0 0 0 0; - 0 0 0 0 0 1 0 0; - 0 0 0 0 1 0 0 0; - 0 0 0 0 0 0 0 1; - 0 0 0 0 0 0 1 0], - - [0 1 0 0 0 0 0 0; #5. {1,3,2} - 1 0 0 0 0 0 0 0; - 0 0 0 0 0 1 0 0; - 0 0 0 0 1 0 0 0; - 0 0 0 1 0 0 0 0; - 0 0 1 0 0 0 0 0; - 0 0 0 0 0 0 1 -1; - 0 0 0 0 0 0 0 -1], - - [0 0 0 0 0 1 0 0; #6. {3,2,1} - 0 0 0 0 1 0 0 0; - 0 0 0 1 0 0 0 0; - 0 0 1 0 0 0 0 0; - 0 1 0 0 0 0 0 0; - 1 0 0 0 0 0 0 0; - 0 0 0 0 0 0 -1 0; - 0 0 0 0 0 0 -1 1] -] - -const _dof_rtperm_matrix = [ - [1 0 0; # 1. {1,2,3} - 0 1 0; - 0 0 1], - - [0 0 1; # 2. {2,3,1} - 1 0 0; - 0 1 0], - - [0 1 0; # 3. {3,1,2} - 0 0 1; - 1 0 0], - - [0 1 0; # 4. {2,1,3} - 1 0 0; - 0 0 1], - - [1 0 0; # 5. {1,3,2} - 0 0 1; - 0 1 0], - - [0 0 1; # 6. {3,2,1} - 0 1 0; - 1 0 0] -] - -const _dof_bdmperm_matrix = [ - - [1 0 0 0 0 0; #1. {1,2,3} - 0 1 0 0 0 0; - 0 0 1 0 0 0; - 0 0 0 1 0 0; - 0 0 0 0 1 0; - 0 0 0 0 0 1], - - [0 0 0 0 1 0; #2. {2,3,1} - 0 0 0 0 0 1; - 1 0 0 0 0 0; - 0 1 0 0 0 0; - 0 0 1 0 0 0; - 0 0 0 1 0 0], - - [0 0 1 0 0 0; #3. {3,1,2} - 0 0 0 1 0 0; - 0 0 0 0 1 0; - 0 0 0 0 0 1; - 1 0 0 0 0 0; - 0 1 0 0 0 0], - - [0 0 0 1 0 0; #4. {2,1,3} - 0 0 1 0 0 0; - 0 1 0 0 0 0; - 1 0 0 0 0 0; - 0 0 0 0 0 1; - 0 0 0 0 1 0], - - [0 1 0 0 0 0; #5. {1,3,2} - 1 0 0 0 0 0; - 0 0 0 0 0 1; - 0 0 0 0 1 0; - 0 0 0 1 0 0; - 0 0 1 0 0 0], - - [0 0 0 0 0 1; #6. {3,2,1} - 0 0 0 0 1 0; - 0 0 0 1 0 0; - 0 0 1 0 0 0; - 0 1 0 0 0 0; - 1 0 0 0 0 0] - -] - -const _dof_lag0perm_matrix = [ - [1], # 1. {1,2,3} - - [1], # 2. {2,3,1} - - [1], # 3. {3,1,2} - - [1], # 4. {2,1,3} - - [1], # 5. {1,3,2} - - [1], # 6. {3,2,1} - -] \ No newline at end of file diff --git a/src/identityop.jl b/src/identityop.jl index 703566af..8cea7f61 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -20,7 +20,7 @@ function _alloc_workspace(qd, g, f, tels, bels) A = Vector{typeof(a)}(undef,length(qd)) end -const LinearRefSpaceTriangle = Union{RTRefSpace, NDRefSpace, BDMRefSpace, NCrossBDMRefSpace} +const LinearRefSpaceTriangle = Union{RTRefSpace, RT2RefSpace, NDRefSpace, ND2RefSpace, BDMRefSpace, NCrossBDMRefSpace} defaultquadstrat(::LocalOperator, ::LinearRefSpaceTriangle, ::LinearRefSpaceTriangle) = SingleNumQStrat(6) function quaddata(op::LocalOperator, g::LinearRefSpaceTriangle, f::LinearRefSpaceTriangle, tels, bels, qs::SingleNumQStrat) diff --git a/test/runtests.jl b/test/runtests.jl index 4fafe1ea..be3304b8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -21,6 +21,8 @@ include("test_raviartthomas.jl") include("test_rt.jl") include("test_rtx.jl") include("test_subd_basis.jl") +include("test_rt2.jl") +include("test_nd2.jl") include("test_dvg.jl") include("test_bcspace.jl") diff --git a/test/test_nd2.jl b/test/test_nd2.jl new file mode 100644 index 00000000..24acd7e2 --- /dev/null +++ b/test/test_nd2.jl @@ -0,0 +1,24 @@ +using CompScienceMeshes +using BEAST + +using Test + + +""" +This test is achieved by project the coefficients of 1st degree +curl conforming basis functions onto the 2nd degree basis functions +and then projecting back the obtained coefficients of the 2nd degree +basis functions onto the 1st degree basis functions. The resultant +operator should be an Identity operator. +""" + +local m = meshrectangle(1.0,1.0,0.5) +tol = 1e-10 +X2 = nedelec2(m) +X1 = BEAST.nedelec(m) +G11 = assemble(Identity(), X1, X1) +G12 = assemble(Identity(), X1, X2) +G21 = assemble(Identity(), X2, X1) +G22 = assemble(Identity(), X2, X2) +Id = I(numfunctions(X1)) +@test norm(inv(Matrix(G11))*G12*inv(Matrix(G22))*G21-Id) Date: Wed, 6 Mar 2024 12:20:57 +0000 Subject: [PATCH 334/528] removing export of new basis fns --- src/BEAST.jl | 1 - test/test_nd2.jl | 4 ++-- test/test_rt2.jl | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/BEAST.jl b/src/BEAST.jl index bc1f8443..1f71e39c 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -32,7 +32,6 @@ export duallagrangecxd0 export lagdimension export restrict export raviartthomas, raowiltonglisson, positions -export raviartthomas2, nedelec2, nedelec export brezzidouglasmarini export brezzidouglasmarini3d export nedelecd3d diff --git a/test/test_nd2.jl b/test/test_nd2.jl index 24acd7e2..fa6af48f 100644 --- a/test/test_nd2.jl +++ b/test/test_nd2.jl @@ -5,7 +5,7 @@ using Test """ -This test is achieved by project the coefficients of 1st degree +This test is achieved by projecting the coefficients of 1st degree curl conforming basis functions onto the 2nd degree basis functions and then projecting back the obtained coefficients of the 2nd degree basis functions onto the 1st degree basis functions. The resultant @@ -14,7 +14,7 @@ operator should be an Identity operator. local m = meshrectangle(1.0,1.0,0.5) tol = 1e-10 -X2 = nedelec2(m) +X2 = BEAST.nedelec2(m) X1 = BEAST.nedelec(m) G11 = assemble(Identity(), X1, X1) G12 = assemble(Identity(), X1, X2) diff --git a/test/test_rt2.jl b/test/test_rt2.jl index 6e3f6409..eff7daed 100644 --- a/test/test_rt2.jl +++ b/test/test_rt2.jl @@ -4,7 +4,7 @@ using BEAST using Test """ -This test is achieved by project the coefficients of 1st degree +This test is achieved by projecting the coefficients of 1st degree div conforming basis functions onto the 2nd degree basis functions and then projecting back the obtained coefficients of the 2nd degree basis functions onto the 1st degree basis functions. The resultant @@ -12,7 +12,7 @@ operator should be an Identity operator. """ local m = meshrectangle(1.0,1.0,0.5) tol = 1e-10 -X2 = raviartthomas2(m) +X2 = BEAST.raviartthomas2(m) X1 = raviartthomas(m) G11 = assemble(Identity(), X1, X1) G12 = assemble(Identity(), X1, X2) From 3b70b4c44b670114b4f553fa866abc67b5d956f3 Mon Sep 17 00:00:00 2001 From: jay prakash Date: Tue, 12 Mar 2024 18:43:14 +0000 Subject: [PATCH 335/528] bug fix test file --- test/test_nd2.jl | 4 ++-- test/test_rt2.jl | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/test_nd2.jl b/test/test_nd2.jl index fa6af48f..5112773a 100644 --- a/test/test_nd2.jl +++ b/test/test_nd2.jl @@ -4,13 +4,13 @@ using BEAST using Test -""" +#= """ This test is achieved by projecting the coefficients of 1st degree curl conforming basis functions onto the 2nd degree basis functions and then projecting back the obtained coefficients of the 2nd degree basis functions onto the 1st degree basis functions. The resultant operator should be an Identity operator. -""" +""" =# local m = meshrectangle(1.0,1.0,0.5) tol = 1e-10 diff --git a/test/test_rt2.jl b/test/test_rt2.jl index eff7daed..4cc69643 100644 --- a/test/test_rt2.jl +++ b/test/test_rt2.jl @@ -3,13 +3,14 @@ using BEAST using Test -""" +#= """ This test is achieved by projecting the coefficients of 1st degree div conforming basis functions onto the 2nd degree basis functions and then projecting back the obtained coefficients of the 2nd degree basis functions onto the 1st degree basis functions. The resultant operator should be an Identity operator. -""" +""" =# + local m = meshrectangle(1.0,1.0,0.5) tol = 1e-10 X2 = BEAST.raviartthomas2(m) From 2a5ad7c7a89f5cc941461253162835e5bebf6e24 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 13 Mar 2024 16:24:01 +0100 Subject: [PATCH 336/528] removed local from test_rt2 --- test/test_gram.jl | 4 ++-- test/test_nd2.jl | 5 +++-- test/test_rt2.jl | 5 +++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/test/test_gram.jl b/test/test_gram.jl index f421536e..3d50b93c 100644 --- a/test/test_gram.jl +++ b/test/test_gram.jl @@ -53,8 +53,8 @@ BEAST.assemble_local_mixed!(id, rt, rt, (v,m,n)->(G2[m,n]+=v)) # different but overlapping meshes m1 = meshrectangle(1.0,1.0,1.0) m2 = translate(m1, point(0.5,0.5,0.0)) -I = Identity() +Id = Identity() x1 = lagrangecxd0(m1) x2 = lagrangecxd0(m2) -G = assemble(I,x1,x2) +G = assemble(Id,x1,x2) @test sum(G) ≈ 1/4 diff --git a/test/test_nd2.jl b/test/test_nd2.jl index 5112773a..e354973a 100644 --- a/test/test_nd2.jl +++ b/test/test_nd2.jl @@ -1,5 +1,6 @@ using CompScienceMeshes using BEAST +using LinearAlgebra using Test @@ -12,7 +13,7 @@ basis functions onto the 1st degree basis functions. The resultant operator should be an Identity operator. """ =# -local m = meshrectangle(1.0,1.0,0.5) +m = meshrectangle(1.0,1.0,0.5) tol = 1e-10 X2 = BEAST.nedelec2(m) X1 = BEAST.nedelec(m) @@ -20,5 +21,5 @@ G11 = assemble(Identity(), X1, X1) G12 = assemble(Identity(), X1, X2) G21 = assemble(Identity(), X2, X1) G22 = assemble(Identity(), X2, X2) -Id = I(numfunctions(X1)) +Id = Matrix{eltype(G11)}(LinearAlgebra.I, numfunctions(X1), numfunctions(X1)) @test norm(inv(Matrix(G11))*G12*inv(Matrix(G22))*G21-Id) Date: Mon, 18 Mar 2024 12:31:54 +0100 Subject: [PATCH 337/528] correct push forward for curl conformgin fields --- src/bases/local/rt2local.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bases/local/rt2local.jl b/src/bases/local/rt2local.jl index c33bb710..e5ca73bd 100644 --- a/src/bases/local/rt2local.jl +++ b/src/bases/local/rt2local.jl @@ -67,8 +67,8 @@ function interpolate(fields, interpolant::BEAST.RT2RefSpace, chart) J_chart = jacobian(p_chart) t1 = tangents(p_chart,1) t2 = tangents(p_chart,2) - q0 = -n_chart × (nxq0ref[1]*t1 + nxq0ref[2]*t2) / J_chart^2 - q1 = -n_chart × (nxq1ref[1]*t1 + nxq1ref[2]*t2) / J_chart^2 + q0 = -n_chart × (nxq0ref[1]*t1 + nxq0ref[2]*t2) / J_chart + q1 = -n_chart × (nxq1ref[1]*t1 + nxq1ref[2]*t2) / J_chart vals = fields(p_chart) J_edge = jacobian(p_edge) @@ -97,8 +97,8 @@ function interpolate(fields, interpolant::BEAST.RT2RefSpace, chart) t1 = tangents(p,1) t2 = tangents(p,2) - q6 = -n_chart × (nxq6ref[1] * t1 + nxq6ref[2] * t2) / J_chart^2 - q7 = -n_chart × (nxq7ref[1] * t1 + nxq7ref[2] * t2) / J_chart^2 + q6 = -n_chart × (nxq6ref[1] * t1 + nxq6ref[2] * t2) / J_chart + q7 = -n_chart × (nxq7ref[1] * t1 + nxq7ref[2] * t2) / J_chart vals = fields(p) l6 .+= [w * dot(f,q6) for f in vals] From ff98f28f1bae04b217a02004077a064e3321babc Mon Sep 17 00:00:00 2001 From: Simon Adrian Date: Sun, 24 Mar 2024 22:16:53 +0100 Subject: [PATCH 338/528] fixup: return correct scalartype for static operators Due to the change from 'nothing' to Val{0} using to denote a static operator (gamma = 0), this fixup is necessary to correctly compute the scalartype. --- src/maxwell/mwops.jl | 2 +- test/test_handlers.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index eb31154f..d1de4035 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -1,7 +1,7 @@ abstract type MaxwellOperator3D{T,K} <: IntegralOperator end abstract type MaxwellOperator3DReg{T,K} <: MaxwellOperator3D{T,K} end -scalartype(op::MaxwellOperator3D{T,K}) where {T, K <: Nothing} = T +scalartype(op::MaxwellOperator3D{T,K}) where {T, K <: Val{0}} = T scalartype(op::MaxwellOperator3D{T,K}) where {T, K} = promote_type(T, K) gamma(op::MaxwellOperator3D{T,Val{0}}) where {T} = zero(T) diff --git a/test/test_handlers.jl b/test/test_handlers.jl index 191961d1..62ed925b 100644 --- a/test/test_handlers.jl +++ b/test/test_handlers.jl @@ -50,6 +50,7 @@ for T in [Float16, Float32, Float64] @test operator.alpha === T(1) @test operator.beta === T(2) @test BEAST.gamma(operator) === T(0) + @test scalartype(operator) == T pwave = Helmholtz3D.planewave(; direction=SVector(T(0), T(0), T(1))) @test pwave.direction == SVector(T(0), T(0), T(1)) From 43903224367ffd7a5119e9b4997456e642888e61 Mon Sep 17 00:00:00 2001 From: Simon Adrian Date: Sun, 24 Mar 2024 22:19:27 +0100 Subject: [PATCH 339/528] Correct momintegration on mixed dual-primal test and trial combinations Currently, mixed discretizations, at least with the scalar primal and dual functions for Helmholtz-type operators are not working due to wrong hardcoded loop boundaries. This is now fixed by using the number of shape functions. --- src/quadrature/sauterschwabints.jl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index b14ad0d6..88e6972f 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -144,12 +144,13 @@ function momintegrals_test_refines_trial!(out, op, Q = restrict(trial_local_space, trial_chart, chart) zlocal = zero(out) + momintegrals!(op, test_local_space, trial_local_space, test_chart, chart, zlocal, qr) - for j in 1:3 - for i in 1:3 - for k in 1:3 + for j in 1:numfunctions(trial_local_space) + for i in 1:numfunctions(test_local_space) + for k in 1:size(Q, 2) out[i,j] += zlocal[i,k] * Q[j,k] end end end end end @@ -194,9 +195,9 @@ function momintegrals_trial_refines_test!(out, op, momintegrals!(op, test_local_space, trial_local_space, chart, trial_chart, zlocal, qr) - for j in 1:3 - for i in 1:3 - for k in 1:3 + for j in 1:numfunctions(trial_local_space) + for i in 1:numfunctions(test_local_space) + for k in 1:size(Q, 2) out[i,j] += Q[i,k] * zlocal[k,j] end end end end From c1b2feed91e27fc153b51d2155dde93c8d061d0f Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 3 Apr 2024 14:39:15 +0200 Subject: [PATCH 340/528] Fix linear combinations of TD operators --- examples/dot_tdmfie.jl | 34 ++++++++-------------- examples/tdefie.jl | 2 +- examples/tdmfie.jl | 8 ++++-- src/bases/local/rt2local.jl | 4 +-- src/timedomain/tdtimeops.jl | 56 ++++--------------------------------- 5 files changed, 26 insertions(+), 78 deletions(-) diff --git a/examples/dot_tdmfie.jl b/examples/dot_tdmfie.jl index cc77adb9..47a90c84 100644 --- a/examples/dot_tdmfie.jl +++ b/examples/dot_tdmfie.jl @@ -1,43 +1,33 @@ using CompScienceMeshes, BEAST o, x, y, z = euclidianbasis(3) -# D, Δx = 1.0, 0.35 - Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -# Γ = meshsphere(1.0, 0.08) -@show numcells(Γ) X, Y = raviartthomas(Γ), buffachristiansen(Γ) -# Δt, Nt = 0.11, 200 -# Δt, Nt = 0.6, 200 -# Δt, Nt = 0.05, 400 Δt ,Nt = 0.3, 200 T = timebasisshiftedlagrange(Δt, Nt, 2) δ = timebasisdelta(Δt, Nt) V = X ⊗ T W = Y ⊗ δ -# width, delay, scaling = 8.0, 12.0, 1.0 duration = 20 * Δt * 2 delay = 1.5 * duration amplitude = 1.0 gaussian = derive(creategaussian(duration, delay, amplitude)) direction, polarisation = ẑ, x̂ -E = BEAST.planewave(polarisation, direction, gaussian, 1.0) -# E = BEAST.planewave(polarisation, direction, derive(gaussian), 1.0) -H = direction × E - -@hilbertspace j; @hilbertspace m′ -# K, I, N = MWDoubleLayerTDIO(1.0, 1.0, 0), Identity(), NCross() -K = MWDoubleLayerTDIO(1.0, 1.0, 1) -N = BEAST.TemporalDifferentiation(NCross()⊗Identity()) - -M = 0.5*N + 1.0*K -Z_mfie = assemble(M, W, V, storage_policy = Val{:bandedstorage}) -b_mfie = assemble(H, W) -# dot_xmfie = marchonintime(inv(Z_mfie[:,:,1]), Z_mfie, b_mfie, Nt) -dot_xmfie = marchonintime(inv(BEAST.ConvolutionOperators.timeslice(Z_mfie,1)), Z_mfie, b_mfie, Nt) +Ė = BEAST.planewave(polarisation, direction, gaussian, 1.0) +Ḣ = direction × Ė + +@hilbertspace j +@hilbertspace k +K̇ = TDMaxwell3D.doublelayer(speedoflight=1.0, numdiffs=1) +Nİ = BEAST.TemporalDifferentiation(NCross()⊗Identity()) + +@hilbertspace k +@hilbertspace j +mfie_dot = @discretise (0.5*Nİ)[k,j] + K̇[k,j] == -1.0Ḣ[k] k∈W j∈V +xmfie_dot = BEAST.motsolve(mfie_dot) # Xmfie, Δω, ω0 = fouriertransform(xmfie, Δt, 0.0, 2) # ω = collect(ω0 .+ (0:Nt-1)*Δω) diff --git a/examples/tdefie.jl b/examples/tdefie.jl index 1ebe9cbf..87307bf9 100644 --- a/examples/tdefie.jl +++ b/examples/tdefie.jl @@ -26,7 +26,7 @@ SL = TDMaxwell3D.singlelayer(speedoflight=1.0, numdiffs=1) # BEAST.@defaultquadstrat (SL, W, V) BEAST.OuterNumInnerAnalyticQStrat(7) tdefie = @discretise SL[j′,j] == -1.0E[j′] j∈V j′∈W -xefie = motsolve(tdefie) +xefie = BEAST.motsolve(tdefie) import Plots Plots.plot(xefie[1,:]) diff --git a/examples/tdmfie.jl b/examples/tdmfie.jl index 56a47cc3..57910978 100644 --- a/examples/tdmfie.jl +++ b/examples/tdmfie.jl @@ -27,9 +27,9 @@ N = NCross() @hilbertspace k @hilbertspace j -mfie = @discretise (0.5(N⊗I) + 1.0DL)[k,j] == -1.0H[k] k∈W j∈V +mfie = @discretise ((0.5*N)⊗I)[k,j] + 1.0DL[k,j] == -1.0H[k] k∈W j∈V -xmfie = motsolve(mfie) +xmfie = BEAST.motsolve(mfie) Xmfie, Δω, ω0 = fouriertransform(xmfie, Δt, 0.0, 2) ω = collect(ω0 .+ (0:Nt-1)*Δω) @@ -37,3 +37,7 @@ _, i1 = findmin(abs.(ω .- 1.0)) ω1 = ω[i1] um = Xmfie[:,i1] / fouriertransform(gaussian)(ω1) + +import Plotly +fcr, geo = facecurrents(um, X) +Plotly.plot(patch(geo, norm.(fcr))) diff --git a/src/bases/local/rt2local.jl b/src/bases/local/rt2local.jl index e5ca73bd..68f7dbc4 100644 --- a/src/bases/local/rt2local.jl +++ b/src/bases/local/rt2local.jl @@ -57,8 +57,8 @@ function interpolate(fields, interpolant::BEAST.RT2RefSpace, chart) t_refedge = tangents(p_refedge,1) m_refedge = point(-t_refedge[2], t_refedge[1]) m_refedge /= norm(m_refedge) - q0ref = s[1] * m_refedge - q1ref = (1-s[1]) * m_refedge + q0ref = (-1) * (1-s[1]) * m_refedge + q1ref = (-1) * s[1] * m_refedge nxq0ref = point(-q0ref[2], q0ref[1]) nxq1ref = point(-q1ref[2], q1ref[1]) diff --git a/src/timedomain/tdtimeops.jl b/src/timedomain/tdtimeops.jl index 7e871df4..61bf39d7 100644 --- a/src/timedomain/tdtimeops.jl +++ b/src/timedomain/tdtimeops.jl @@ -46,32 +46,13 @@ function scalartype(A::TensorOperator) scalartype(A.temporal_factor)) end - - -# function allocatestorage(op::TensorOperator, test_functions, trial_functions, -# ::Type{Val{:bandedstorage}}, ::Type{LongDelays{:ignore}},) - -# M = numfunctions(spatialbasis(test_functions)) -# N = numfunctions(spatialbasis(trial_functions)) - -# time_basis_function = BEAST.convolve( -# temporalbasis(test_functions), -# temporalbasis(trial_functions)) - -# space_operator = op.spatial_factor -# A = assemble(space_operator, spatialbasis(test_functions), spatialbasis(trial_functions)) - -# K0 = ones(M,N) -# bandwidth = numintervals(time_basis_function) - 1 -# data = zeros(scalartype(op), bandwidth, M, N) -# maxk1 = bandwidth -# Z = SparseND.Banded3D(K0, data, maxk1) -# return ()->Z, (v,m,n,k)->(Z[m,n,k] += v) -# end +function Base.:*(alpha::Number, A::TensorOperator) + return TensorOperator(alpha*A.spatial_factor, A.temporal_factor) +end function allocatestorage(op::TensorOperator, test_functions, trial_functions, - ::Type{Val{:bandedstorage}}, ::Type{LongDelays{:compress}},) + ::Type{Val{:bandedstorage}}, long_delay_traits::Any) M = numfunctions(spatialbasis(test_functions)) N = numfunctions(spatialbasis(trial_functions)) @@ -102,34 +83,7 @@ function allocatestorage(op::TensorOperator, test_functions, trial_functions, return ()->Z, store1 end -# function allocatestorage(op::TensorOperator, test_functions, trial_functions) - -# M = numfunctions(spatialbasis(test_functions)) -# N = numfunctions(spatialbasis(trial_functions)) - -# time_basis_function = BEAST.convolve( -# temporalbasis(test_functions), -# temporalbasis(trial_functions)) - -# tbf = time_basis_function -# has_zero_tail = all(tbf.polys[end].data .== 0) -# @show has_zero_tail - -# if has_zero_tail -# K = numintervals(time_basis_function)-1 -# else -# speedoflight = 1.0 -# @warn "Assuming speed of light to be equal to 1!" -# Δt = timestep(tbf) -# ct, hs = boundingbox(geometry(spatialbasis(trial_functions)).vertices) -# diam = 2 * sqrt(3) * hs -# K = ceil(Int, (numintervals(tbf)-1) + diam/speedoflight/Δt)+1 -# end -# @assert K > 0 - -# Z = zeros(M, N, K) -# return MatrixConvolution(Z), (v,m,n,k)->(Z[m,n,k] += v) -# end + function assemble!(operator::TensorOperator, testfns::SpaceTimeBasis, trialfns::SpaceTimeBasis, store, threading::Type{Threading{:multi}}; From fac1722dc08427b3d3b3ee7bd0a425807a21aba0 Mon Sep 17 00:00:00 2001 From: bmergl Date: Mon, 8 Apr 2024 17:27:07 +0200 Subject: [PATCH 341/528] Added support for LagrangeRefSpace{T, 1, 4, 4} --- src/bases/local/laglocal.jl | 44 ++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index a396add9..aef55c13 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -137,6 +137,23 @@ function strace(x::LagrangeRefSpace, cell, localid, face) Q end +function strace(x::LagrangeRefSpace{T, 1, 4, 4}, cell, localid, face) where {T} + + #T = scalartype(x) + t = zeros(T, 3, 4) + for (k,fvert) in enumerate(face.vertices) + for (l,cvert) in enumerate(cell.vertices) + nrm = norm(fvert - cvert) + if isapprox(nrm, 0, atol=sqrt(eps(T))) + t[k,l] = T(1.0) + break + end + end + end + + return t +end + function restrict(refs::LagrangeRefSpace{T,0}, dom1, dom2) where T #Q = eye(T, numfunctions(refs)) @@ -287,4 +304,29 @@ const _dof_lag0perm_matrix = [ @SMatrix[1] # 6. {3,2,1} -] \ No newline at end of file +] + +function (ϕ::LagrangeRefSpace{T, 1, 4, 4})(lag) where T + + u, v, w = parametric(lag) + + tu = tangents(lag, 1) + tv = tangents(lag, 2) + tw = tangents(lag, 3) + + B = [tu tv tw] + A = inv(transpose(B)) + + # gradient in u,v,w (unit tetrahedron) + gr1=SVector{3, T}(1.0, 0.0, 0.0) + gr2=SVector{3, T}(0.0, 1.0, 0.0) + gr3=SVector{3, T}(0.0, 0.0, 1.0) + gr4=SVector{3, T}(-1.0, -1.0, -1.0) + + return SVector(( + (value = u, gradient = A*gr1), + (value = v, gradient = A*gr2), + (value = w, gradient = A*gr3), + (value = T(1.0)-u-v-w, gradient = A*gr4) + )) +end From 330cad03dcdcba5aeb0d8f5ebe17643191302f19 Mon Sep 17 00:00:00 2001 From: bmergl Date: Mon, 8 Apr 2024 17:30:10 +0200 Subject: [PATCH 342/528] Added 3D strace formulation The new strace formulation supports a 2D mesh as input that comes from parts of the boundary of a 3D body. It is implemented in the same way as the ntrace formulation. --- src/bases/trace.jl | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/bases/trace.jl b/src/bases/trace.jl index ea52a307..89058e01 100644 --- a/src/bases/trace.jl +++ b/src/bases/trace.jl @@ -139,6 +139,60 @@ function strace(X::Space, γ, dim1::Type{Val{2}}) strace(X, Σ, fns) end +function strace(X::Space, γ, dim1::Type{Val{3}}) + + x = refspace(X) + E, ad, P = assemblydata(X) + igeo = geometry(X) + + @assert dimension(γ) == dimension(igeo)-1 + + ogeo = boundary(igeo) + on_target = overlap_gpredicate(γ) + ogeo = submesh(ogeo) do m,f + ch = chart(m,f) + on_target(ch) + end + + D = connectivity(igeo, ogeo, abs) + rows, vals = rowvals(D), nonzeros(D) + + T = scalartype(X) + S = Shape{T} + fns = [Vector{S}() for i in 1:numfunctions(X)] + + for (p,el) in enumerate(E) + + for (q,fc) in enumerate(faces(el)) + on_target(fc) || continue + + r = 0 + for k in nzrange(D,P[p]) + vals[k] == q && (r = rows[k]; break) + end + @assert r != 0 + + fc1 = chart(ogeo, r) + Q = strace(x, el, q, fc1) + + for i in 1:size(Q,1) + for j in 1:size(Q,2) + for (m,a) in ad[p,j] + v = a*Q[i,j] + isapprox(v,0,atol=sqrt(eps(T))) && continue + push!(fns[m], Shape(r, i, v)) + end + end + end + + end + + end + + strace(X, ogeo, fns) + +end + """ currently not working! """ From 15122a3a8d2da68dd4fea956f4390b584f3cbd13 Mon Sep 17 00:00:00 2001 From: bmergl Date: Mon, 8 Apr 2024 18:22:50 +0200 Subject: [PATCH 343/528] Add Lippmann-Schwinger VIE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We add Lippmann-Schwinger VIE in two flavors - with one partial integration (hhvolume & hhboundary functions in vie.jl) - no partial integration (one derivative on the Green's function) (hhvolumegradG) In addition, the function hhvolumek0 is needed for k != 0 in the Helmholtz equation. Note that more flavors exist, which yet need to be implemented. For example, the one described in Bleszynski, E., M. Bleszynski, and T. Jaroszewicz. “Fast Volumetric Integral-Equation Solver for Acoustic Wave Propagation through Inhomogeneous Media.” The Journal of the Acoustical Society of America 124, no. 1 (July 1, 2008): 396–408. https://doi.org/10.1121/1.2924203. where one derivative is moved onto the contrast function. TODO: Add unit test for accoustic version (once it is available in SphericalScattering. So far only the electrostatic version is tested) --- examples/hh_lsvie.jl | 123 ++++++++++++++++ src/volumeintegral/sauterschwab_ints.jl | 29 ++++ src/volumeintegral/vie.jl | 186 ++++++++++++++++++++++++ src/volumeintegral/vieexc.jl | 31 ++++ src/volumeintegral/vieops.jl | 108 +++++++++++++- test/Project.toml | 1 + test/test_hh_lsvie.jl | 101 +++++++++++++ 7 files changed, 575 insertions(+), 4 deletions(-) create mode 100644 examples/hh_lsvie.jl create mode 100644 test/test_hh_lsvie.jl diff --git a/examples/hh_lsvie.jl b/examples/hh_lsvie.jl new file mode 100644 index 00000000..8e32897b --- /dev/null +++ b/examples/hh_lsvie.jl @@ -0,0 +1,123 @@ +using BEAST +using CompScienceMeshes +using StaticArrays +using LinearAlgebra +using Test +using Plots +using SphericalScattering + +# Layered Dielectic Sphere +# MoM (Lippmann Schwinger VIE) vs. Analytical Solution (SphericalScattering) + + + +# Environment +ε1 = 1.0*ε0 +μ1 = μ0 + +# Layered Dielectic Sphere +ε2 = 5.0*ε0 # outer shell +μ2 = μ0 + +ε3 = 20.0*ε0 +μ3 = μ0 + +ε4 = 7.0*ε0 +μ4 = μ0 + +ε5 = 30.0*ε0 # inner core +μ5 = μ0 + +r = 1.0 +radii = SVector(0.2*r, 0.6*r, 0.8*r, r) # inner core stops at first radius +filling = SVector(Medium(ε5, μ5), Medium(ε4, μ4), Medium(ε3, μ3), Medium(ε2, μ2)) + + + + +# Mesh, Basis +h = 0.1 +mesh = CompScienceMeshes.tetmeshsphere(r,h) +X = lagrangec0d1(mesh; dirichlet = false) +@show numfunctions(X) + +bnd = boundary(mesh) +strc = X -> strace(X, bnd) + + +# VIE Operators +function generate_tau(ε_env, radii, filling) + + function tau(x::SVector{U,T}) where {U,T} + for (i, radius) in enumerate(radii) + norm(x) <= radius && (return T(1.0 - filling[i].ε/ε_env)) + end + #return 0.0 + error("Evaluated contrast outside the sphere!") + end + + return tau +end +τ = generate_tau(ε1, radii, filling) #contrast function + +I = Identity() +V = VIE.hhvolume(tau = τ, wavenumber = 0.0) +B = VIE.hhboundary(tau = τ, wavenumber = 0.0) +Y = VIE.hhvolumegradG(tau = τ, wavenumber = 0.0) + + +# Exitation +dirE = SVector(1.0, 0.0, 0.0) +dirgradΦ = - dirE +amp = 1.0 +Φ_inc = VIE.linearpotential(direction = dirgradΦ, amplitude = amp) + + +# Assembly +b = assemble(Φ_inc, X) + +Z_I = assemble(I, X, X) + +Z_V = assemble(V, X, X) +Z_B = assemble(B, strc(X), X) + +Z_Y = assemble(Y, X, X) + + +# System matrix +Z_version1 = Z_I + Z_V + Z_B +Z_version2 = Z_I + Z_Y + + +# Solution of the linear system of equations +u_version1 = Z_version1 \ Vector(b) +u_version2 = Z_version2 \ Vector(b) + + + +## Observe the potential on a x-line in dirgradΦ direction + +# x-line at y0, z0 - Potential only inside the sphere mesh valid! +y0 = 0.0 +z0 = 0.0 +x_range = range(0.0, stop = 1.0*r, length = 200) +points_x = [point(x, y0, z0) for x in x_range] +x = collect(x_range) + +# SphericalScattering solution on x-line +sp = LayeredSphere(radii = radii, filling = filling) +ex = UniformField(direction = dirE, amplitude = amp) +Φ_x = real.(field(sp, ex, ScalarPotential(points_x))) + +# MoM solution on x-line +Φ_MoM_version1_x = BEAST.grideval(points_x, u_version1, X, type = Float64) +Φ_MoM_version2_x = BEAST.grideval(points_x, u_version2, X, type = Float64) + +# Plot +plot(x, Φ_x, label = "SphericalScattering") +plot!(x, Φ_MoM_version1_x, label = "MoM: S=I+B+V, boundary+volume") +plot!(x, Φ_MoM_version2_x, label = "MoM: S=I+Y, gradgreen") +xlims!(0.0, 1.0) # sphere center to radius r +ylims!(-0.3, 0.0) +title!("Potential Φ(x, y0, z0)") +xlabel!("x") \ No newline at end of file diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index b62b3e7f..31164385 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -71,6 +71,35 @@ function reorder_dof(space::LagrangeRefSpace{Float64,0,3,1},I) return SVector{1,Int64}(1),SVector{1,Int64}(1) end +function reorder_dof(space::LagrangeRefSpace{T,1,3,3},I) where T + n = length(I) + K = zeros(MVector{3,Int64}) + for i in 1:n + for j in 1:n + if I[j] == i + K[i] = j + break + end + end + end + + return SVector(K),SVector{3,Int64}(1,1,1) +end + +function reorder_dof(space::LagrangeRefSpace{T,1,4,4},I) where T + n = length(I) + K = zeros(MVector{4,Int64}) + for i in 1:n + for j in 1:n + if I[j] == i + K[i] = j + break + end + end + end + + return SVector(K),SVector{4,Int64}(1,1,1,1) +end function momintegrals!(op::VIEOperator, test_local_space::RefSpace, trial_local_space::RefSpace, diff --git a/src/volumeintegral/vie.jl b/src/volumeintegral/vie.jl index 58f52e06..f1ebebb2 100644 --- a/src/volumeintegral/vie.jl +++ b/src/volumeintegral/vie.jl @@ -212,4 +212,190 @@ module VIE tau=error("missing argument: `tau`")) = Mod.VIEFarField3D(wavenumber=wavenumber, tau= tau) + + + # Operators of the Lippmann Schwinger Volume Integral Equation, + # which is based on the generalized Helmholtz Equation: + """ + hhvolume(;gamma, alpha, tau) + hhvolume(;wavenumber, alpha, tau) + + Bilinear form given by: + + ```math + α ∬_{Ω×Ω} (grad j(x)) ⋅ G_{γ}(x,y)τ(y) (grad k(y)) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast function + """ + function hhvolume(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + tau=nothing) + + if (gamma === nothing) && (wavenumber === nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma !== nothing) && (wavenumber !== nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma === nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma !== nothing + + alpha === nothing && (alpha = -1.0) + tau === nothing && (tau = x->1.0) + + Mod.VIEhhVolume(gamma, alpha, tau) + end + + + """ + hhboundary(;gamma, alpha, tau) + hhboundary(;wavenumber, alpha, tau) + + Bilinear form given by: + + ```math + α ∬_{∂Ω×Ω} n̂(x) ⋅ j(x) G_{γ}(x,y) τ(y) (grad k(y)) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast function + and ``n̂(x)`` normal vector + """ + function hhboundary(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + tau=nothing) + + if (gamma === nothing) && (wavenumber === nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma !== nothing) && (wavenumber !== nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma === nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma !== nothing + + alpha === nothing && (alpha = 1.0) + tau === nothing && (tau = x->1.0) + + Mod.VIEhhBoundary(gamma, alpha, tau) + end + + + """ + hhvolumek0(;gamma, alpha, tau) + hhvolumek0(;wavenumber, alpha, tau) + + Bilinear form given by: + + ```math + α ∬_{Ω×Ω} j(x) G_{γ}(x,y)τ(y) k(y) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` contrast function + """ + function hhvolumek0(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + tau=nothing) + + if (gamma === nothing) && (wavenumber === nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma !== nothing) && (wavenumber !== nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma === nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma !== nothing + + alpha === nothing && (alpha = wavenumber*wavenumber) + tau === nothing && (tau = x->1.0) + + Mod.VIEhhVolumek0(gamma, alpha, tau) + end + + + + """ + hhvolumegradG(;gamma, alpha, tau) + hhvolumegradG(;wavenumber, alpha, tau) + + Bilinear form given by: + + ```math + α ∬_{Ω×Ω} j(x) grad_y(G_{γ}(x,y)) τ(y) ⋅ (grad k(y)) + ``` + + with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|`` + and ``τ(y)`` constant function + """ + function hhvolumegradG(; + gamma=nothing, + wavenumber=nothing, + alpha=nothing, + tau=nothing) + + if (gamma === nothing) && (wavenumber === nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if (gamma !== nothing) && (wavenumber !== nothing) + error("Supply one of (not both) gamma or wavenumber") + end + + if gamma === nothing + if iszero(real(wavenumber)) + gamma = -imag(wavenumber) + else + gamma = im*wavenumber + end + end + + @assert gamma !== nothing + + alpha === nothing && (alpha = -1.0) + tau === nothing && (tau = x->1.0) + + Mod.VIEhhVolumegradG(gamma, alpha, tau) + end + + linearpotential(; + direction = error("missing arguement `direction`"), + amplitude = one(real(typeof(direction[1])))) = + Mod.LinearPotentialVIE(direction, amplitude) + end \ No newline at end of file diff --git a/src/volumeintegral/vieexc.jl b/src/volumeintegral/vieexc.jl index dca5e685..125cb361 100644 --- a/src/volumeintegral/vieexc.jl +++ b/src/volumeintegral/vieexc.jl @@ -42,3 +42,34 @@ function (e::PlaneWaveVIE)(p) *(a::Number, e::PlaneWaveVIE) = PlaneWaveVIE(e.direction, e.polarisation, e.wavenumber, a*e.amplitude) integrand(::PlaneWaveVIE, test_vals, field_val) = test_vals[1] ⋅ field_val + + +# Excitation for Lippmann Schwinger Volume Integral Equation +mutable struct LinearPotentialVIE{T,P} <: Functional + direction::P + amplitude::T +end + +scalartype(x::LinearPotentialVIE{T,P}) where {T,P} = T + +function LinearPotentialVIE_(d,a = 1) + T = promote_type(eltype(d), typeof(a)) + P = similar_type(typeof(d), T) #SVector{3,T} + return LinearPotentialVIE{T,P}(d,a) +end + +linearpotentialvie(; + direction = error("missing argument `direction`"), + amplitude = 1, +) = LinearPotentialVIE_(direction, amplitude) + +function (e::LinearPotentialVIE)(p) + d = e.direction + a = e.amplitude + x = cartesian(p) + return a * dot(d, x) +end + +*(a::Number, e::LinearPotentialVIE) = LinearPotentialVIE_(e.direction, a*e.amplitude) + +integrand(::LinearPotentialVIE, test_vals, field_val) = test_vals[1] ⋅ field_val \ No newline at end of file diff --git a/src/volumeintegral/vieops.jl b/src/volumeintegral/vieops.jl index fc9efd19..87a5fb16 100644 --- a/src/volumeintegral/vieops.jl +++ b/src/volumeintegral/vieops.jl @@ -19,7 +19,7 @@ function kernelvals(viop::VIEOperator, p ,q) expn = exp(-yR) green = expn / (4*pi*R) - gradgreen = - (Y +1/R)*green/R*r + gradgreen = - (Y +1/R)*green/R*r # Derivation after p (test variable) tau = viop.tau(cartesian(q)) @@ -60,6 +60,32 @@ struct VIEDoubleLayer{T,U,P} <: VolumeOperator tau::P end + +struct VIEhhVolume{T,U,P} <: VolumeOperator + gamma::T + α::U + tau::P +end + +struct VIEhhBoundary{T,U,P} <: BoundaryOperator + gamma::T + α::U + tau::P +end + +struct VIEhhVolumegradG{T,U,P} <: VolumeOperator + gamma::T + α::U + tau::P +end + +struct VIEhhVolumek0{T,U,P} <: VolumeOperator + gamma::T + α::U + tau::P +end + + scalartype(op::VIEOperator) = typeof(op.gamma) export VIE @@ -161,7 +187,6 @@ function integrand(viop::VIEBoundary2, kerneldata, tvals, tgeo, bvals, bgeo) α = viop.α @SMatrix[α * dot(gx[i] , cross(gradG, Ty*fy[j])) for i in 1:3, j in 1:6] - end function integrand(viop::VIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) @@ -178,6 +203,81 @@ function integrand(viop::VIEDoubleLayer, kerneldata, tvals, tgeo, bvals, bgeo) end +# Integrands for the operators of the Lippmann Schwinger Volume Integral Equation: + +function integrand(viop::VIEhhVolume, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:4] + fy = @SVector[bvals[i].value for i in 1:4] + + dgx = @SVector[tvals[i].gradient for i in 1:4] + dfy = @SVector[bvals[i].gradient for i in 1:4] + + G = kerneldata.green + gradG = kerneldata.gradgreen + + Ty = kerneldata.tau + + α = viop.α + + return @SMatrix[α * dot(dgx[i], G*Ty*dfy[j]) for i in 1:4, j in 1:4] +end + + +function integrand(viop::VIEhhBoundary, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:3] + dfy = @SVector[bvals[i].gradient for i in 1:4] + + G = kerneldata.green + gradG = kerneldata.gradgreen + + Ty = kerneldata.tau + + α = viop.α + + return @SMatrix[α * dot( tgeo.patch.normals[1]*gx[i],G*Ty*dfy[j]) for i in 1:3, j in 1:4] +end + +function integrand(viop::VIEhhVolumek0, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:4] + fy = @SVector[bvals[i].value for i in 1:4] + + dgx = @SVector[tvals[i].gradient for i in 1:4] + dfy = @SVector[bvals[i].gradient for i in 1:4] + + G = kerneldata.green + gradG = kerneldata.gradgreen + + Ty = kerneldata.tau + + α = viop.α + + return @SMatrix[α * gx[i]*G*Ty*fy[j] for i in 1:4, j in 1:4] +end + +function integrand(viop::VIEhhVolumegradG, kerneldata, tvals, tgeo, bvals, bgeo) + + gx = @SVector[tvals[i].value for i in 1:4] + fy = @SVector[bvals[i].value for i in 1:4] + + dgx = @SVector[tvals[i].gradient for i in 1:4] + dfy = @SVector[bvals[i].gradient for i in 1:4] + + G = kerneldata.green + gradG = -kerneldata.gradgreen # "-" to get nabla'G(r,r') + + Ty = kerneldata.tau + + α = viop.α + + return @SMatrix[α * gx[i] * dot(gradG, Ty*dfy[j]) for i in 1:4, j in 1:4] +end + + + + defaultquadstrat(op::VIEOperator, tfs, bfs) = SauterSchwab3DQStrat(3,3,3,3,3,3) @@ -207,7 +307,7 @@ quadrule(op::VolumeOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs) = q function qr_volume(op::VolumeOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs::SauterSchwab3DQStrat) - + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) hits = 0 @@ -250,7 +350,7 @@ quadrule(op::BoundaryOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs) = function qr_boundary(op::BoundaryOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, qs::SauterSchwab3DQStrat) - + dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) hits = 0 diff --git a/test/Project.toml b/test/Project.toml index 9a7b4b64..d48999af 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -7,6 +7,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +SphericalScattering = "1a9ea918-b599-4f1f-bd9a-d681e8bb5b3e" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" diff --git a/test/test_hh_lsvie.jl b/test/test_hh_lsvie.jl new file mode 100644 index 00000000..899c55b1 --- /dev/null +++ b/test/test_hh_lsvie.jl @@ -0,0 +1,101 @@ +using BEAST +using CompScienceMeshes +using StaticArrays +using LinearAlgebra +using SphericalScattering +using Test + + +# Homogeneous Dielectic Sphere Unit Test + +@testset "Lippmann Schwinger Volume Integral Equation" begin + + # Environment + ε1 = 1.0*ε0 + μ1 = μ0 + + # Dielectic Sphere + ε2 = 5.0*ε0 + μ2 = μ0 + r = 1.0 + sp = DielectricSphere(; radius = r, filling = Medium(ε2, μ2)) + + + # Mesh, Basis + h = 0.35 + mesh = CompScienceMeshes.tetmeshsphere(r,h) + bnd = boundary(mesh) + X = lagrangec0d1(mesh; dirichlet = false) + #@show numfunctions(X) + strc = X -> strace(X,bnd) + + # VIE operators + function generate_tau(ε_ins, ε_env) + contr = 1.0 - ε_ins/ε_env + function tau(x::SVector{U,T}) where {U,T} + return T(contr) + end + return tau + end + τ = generate_tau(ε2, ε1) + + I, V, B = Identity(), VIE.hhvolume(tau = τ, wavenumber = 0.0), VIE.hhboundary(tau = τ, wavenumber = 0.0) + Y = VIE.hhvolumegradG(tau = τ, wavenumber = 0.0) + + + # Exitation + dirE = SVector(1.0, 0.0, 0.0) + dirgradΦ = -dirE + amp = 1.0 + + # SphericalScattering exitation + ex = UniformField(direction = dirE, amplitude = amp, embedding = Medium(ε1, μ1)) + + # VIE exitation + Φ_inc = VIE.linearpotential(direction=dirgradΦ, amplitude=amp) + + + # Assembly + b = assemble(Φ_inc, X) + + Z_I = assemble(I, X, X) + + Z_V = assemble(V, X, X) + Z_B = assemble(B, strc(X), X) + + Z_Y = assemble(Y, X, X) + + Z_version1 = Z_I + Z_V + Z_B + Z_version2 = Z_I + Z_Y + + # MoM solution + u_version1 = Z_version1 \ Vector(b) + u_version2 = Z_version2 \ Vector(b) + + # Observation points + range_ = range(-1.0*r,stop=1.0*r,length=14) + points = [point(x,y,z) for x in range_ for y in range_ for z in range_] + points_sp=[] + for p in points + norm(p)<0.97*r && push!(points_sp,p) + end + + # SphericalScattering solution inside the dielectric sphere + Φ = field(sp, ex, ScalarPotential(points_sp)) + + # MoM solution inside the dielectric sphere + Φ_MoM_version1 = BEAST.grideval(points_sp, u_version1, X, type=Float64) + Φ_MoM_version2 = BEAST.grideval(points_sp, u_version2, X, type=Float64) + + + + err_Φ_version1 = norm(Φ - Φ_MoM_version1) / norm(Φ) + err_Φ_version2 = norm(Φ - Φ_MoM_version2) / norm(Φ) + + @show err_Φ_version1 + @show err_Φ_version2 + + @test err_Φ_version1 < 0.02 + @test err_Φ_version2 < 0.01 + +end \ No newline at end of file From aa4460d065b61d612838e631e56bf5316faf23e1 Mon Sep 17 00:00:00 2001 From: Simon Adrian Date: Mon, 25 Mar 2024 11:20:53 +0100 Subject: [PATCH 344/528] Allow interpolatory dual piecewise constant basis functions These can be useful for mixed discretization of Helmholtz3D type of integral equations. --- src/bases/lagrange.jl | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 5d27f416..271caa1a 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -231,15 +231,24 @@ end """ duallagrangecxd0(mesh, jct) -> basis -Build dual Lagrange piecewise constant elements. Boundary nodes are only considered if they are in the interior of `jct`. +Build dual Lagrange piecewise constant elements. Boundary nodes are only considered if +they are in the interior of `jct`. + +The default dual function (`interpolatory=false`) is similar to the one depicted +in Figure 3 of Buffa et al (doi: 10.1090/S0025-5718-07-01965-5), with the +difference that each individual shape function is normalized with respect to +the area so that overall the integral over the dual function is one. + +When `interpolatory=true` is used, the function value is one on the support, and thus, +it gives rise to a partition of unity. """ -function duallagrangecxd0(mesh, jct=CompScienceMeshes.mesh(coordtype(mesh), dimension(mesh)-1)) +function duallagrangecxd0(mesh, jct=CompScienceMeshes.mesh(coordtype(mesh), dimension(mesh)-1); interpolatory=false) vertexlist = interior_and_junction_vertices(mesh, jct) - duallagrangecxd0(mesh, vertexlist) + duallagrangecxd0(mesh, vertexlist; interpolatory=interpolatory) end -function duallagrangecxd0(mesh, vertexlist::Vector{Int}) +function duallagrangecxd0(mesh, vertexlist::Vector{Int}; interpolatory=false) T = coordtype(mesh) @@ -252,7 +261,7 @@ function duallagrangecxd0(mesh, vertexlist::Vector{Int}) for (k,v) in enumerate(vertexlist) n = vton[v] F = vtoc[v,1:n] - fns[k] = singleduallagd0(fine, F, v) + fns[k] = singleduallagd0(fine, F, v, interpolatory=interpolatory) push!(pos, verts[v]) end @@ -261,26 +270,35 @@ function duallagrangecxd0(mesh, vertexlist::Vector{Int}) end -function duallagrangecxd0(mesh, vertices::CompScienceMeshes.AbstractMesh{U,1}) where {U} +function duallagrangecxd0(mesh, vertices::CompScienceMeshes.AbstractMesh{U,1}; interpolatory=false) where {U} # vertexlist = Int[v[1] for v in vertices] vertexlist =Int[CompScienceMeshes.indices(vertices, v)[1] for v in vertices] - return duallagrangecxd0(mesh, vertexlist) + return duallagrangecxd0(mesh, vertexlist; interpolatory=interpolatory) end """ - singleduallagd0(fine, F, v) + singleduallagd0(fine, F, v; interpolatory=false) + +Build a single dual constant Lagrange element a mesh `fine`. `F` contains the indices +to cells in the support and v is the index in the vertex list of the defining vertex. + +The default dual function (`interpolatory=false`) is similar to the one depicted +in Figure 3 of Buffa et al (doi: 10.1090/S0025-5718-07-01965-5), with the +difference that each individual shape function is normalized with respect to +the area so that overall the integral over the dual function is one. -Build a single dual constant Lagrange element a mesh `fine`. `F` contains the indices to cells in the support and v is the index in the vertex list of the defining vertex. +When `interpolatory=true` is used, the function value is one on the support, and thus, +it gives rise to a partition of unity. """ -function singleduallagd0(fine, F, v) +function singleduallagd0(fine, F, v; interpolatory=false) T = coordtype(fine) fn = Shape{T}[] for cellid in F # cell = cells(fine)[cellid] ptch = chart(fine, cellid) - coeff = 1 / volume(ptch) / length(F) + coeff = interpolatory ? T(1.0) : 1 / volume(ptch) / length(F) refid = 1 push!(fn, Shape(cellid, refid, coeff)) end From b914087b7a7f8946849b17be0f546da11b7db456 Mon Sep 17 00:00:00 2001 From: Simon Adrian Date: Mon, 25 Mar 2024 11:24:03 +0100 Subject: [PATCH 345/528] Add unit test for mixed discretizations Overall, the observed error is comparable to the standard discretizations. Note, however, that it might be useful (TODO) adding unit tests for individual matrix element assemblies. --- test/test_hh3d_nearfield.jl | 149 +++++++++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) diff --git a/test/test_hh3d_nearfield.jl b/test/test_hh3d_nearfield.jl index 7cdb04ea..6081d7c7 100644 --- a/test/test_hh3d_nearfield.jl +++ b/test/test_hh3d_nearfield.jl @@ -155,4 +155,151 @@ using Test @test err_EDPDL_field < 0.03 @test err_ENPSL_field < 0.01 @test err_ENPDL_field < 0.02 -end \ No newline at end of file +end + +## Test here some of the mixed discretizatinos +#@testset "Helmholtz potential operators: mixed discretizations with duallagrangecxd0" begin + r = 10.0 + λ = 20 * r + k = 2 * π / λ + + sphere = meshsphere(r, 0.2 * r) + X0 = lagrangecxd0(sphere) + X1 = lagrangec0d1(sphere) + Y0 = duallagrangecxd0(sphere; interpolatory=true) + + S = Helmholtz3D.singlelayer(; gamma=im * k) + D = Helmholtz3D.doublelayer(; gamma=im * k) + Dt = Helmholtz3D.doublelayer_transposed(; gamma=im * k) + N = Helmholtz3D.hypersingular(; gamma=im * k) + + q = 100.0 + ϵ = 1.0 + + # Interior problem + # Formulations from Sauter and Schwab, Boundary Element Methods(2011), Chapter 3.4.1.1 + pos1 = SVector(r * 1.5, 0.0, 0.0) # positioning of point charges + pos2 = SVector(-r * 1.5, 0.0, 0.0) + + charge1 = Helmholtz3D.monopole(position=pos1, amplitude=q/(4*π*ϵ), wavenumber=k) + charge2 = Helmholtz3D.monopole(position=pos2, amplitude=-q/(4*π*ϵ), wavenumber=k) + + # Potential of point charges + + Φ_inc(x) = charge1(x) + charge2(x) + + gD1 = assemble(DirichletTrace(charge1), Y0) + assemble(DirichletTrace(charge2), Y0) + gN = assemble(∂n(charge1), X1) + assemble(BEAST.n ⋅ grad(charge2), X1) + + G = assemble(Identity(), X1, Y0) + o = ones(numfunctions(X1)) + + # Interior Dirichlet problem - compare Sauter & Schwab eqs. 3.81 + M_IDPDL = (-1 / 2 * assemble(Identity(), Y0, X1) + assemble(D, Y0, X1)) # Double layer (DL) + + # Interior Neumann problem + # Neumann derivative from SL potential with deflected nullspace + M_INPSL = (1 / 2 * assemble(Identity(), X1, Y0) + assemble(Dt, X1, Y0)) + G * o * o' * G + + ρ_IDPDL = M_IDPDL \ (-gD1) + ρ_INPSL = M_INPSL \ (-gN) + + pts = meshsphere(0.8 * r, 0.8 * 0.6 * r).vertices # sphere inside on which the potential and field are evaluated + + pot_IDPDL = potential(HH3DDoubleLayerNear(im * k), pts, ρ_IDPDL, X1; type=ComplexF64) + + pot_INPSL = potential(HH3DSingleLayerNear(im * k), pts, ρ_INPSL, Y0; type=ComplexF64) + + # Total field inside should be zero + err_IDPDL_pot = norm(pot_IDPDL + Φ_inc.(pts)) / norm(Φ_inc.(pts)) + err_INPSL_pot = norm(pot_INPSL + Φ_inc.(pts)) / norm(Φ_inc.(pts)) + + # Efield(x) = - grad Φ_inc(x) + Efield(x) = -grad(charge1)(x) + -grad(charge2)(x) + + field_IDPDL = -potential(HH3DHyperSingularNear(im * k), pts, ρ_IDPDL, X1) + field_INPSL = -potential(HH3DDoubleLayerTransposedNear(im * k), pts, ρ_INPSL, Y0) + + err_IDPDL_field = norm(field_IDPDL + Efield.(pts)) / norm(Efield.(pts)) + err_INPSL_field = norm(field_INPSL + Efield.(pts)) / norm(Efield.(pts)) + + # errors of interior problems + @test err_IDPDL_pot < 0.005 + @test err_INPSL_pot < 0.002 + + @test err_IDPDL_field < 0.008 + @test err_INPSL_field < 0.002 +#end + +## Test here some of the mixed discretizatinos +#@testset "Helmholtz potential operators: mixed discretizations with duallagrangec0d1" begin + r = 10.0 + λ = 20 * r + k = 2 * π / λ + + sphere = meshsphere(r, 0.2 * r) + X0 = lagrangecxd0(sphere) + Y1 = duallagrangec0d1(sphere) + + S = Helmholtz3D.singlelayer(; gamma=im * k) + D = Helmholtz3D.doublelayer(; gamma=im * k) + Dt = Helmholtz3D.doublelayer_transposed(; gamma=im * k) + N = Helmholtz3D.hypersingular(; gamma=im * k) + + q = 100.0 + ϵ = 1.0 + + # Interior problem + # Formulations from Sauter and Schwab, Boundary Element Methods(2011), Chapter 3.4.1.1 + pos1 = SVector(r * 1.5, 0.0, 0.0) # positioning of point charges + pos2 = SVector(-r * 1.5, 0.0, 0.0) + + charge1 = Helmholtz3D.monopole(position=pos1, amplitude=q/(4*π*ϵ), wavenumber=k) + charge2 = Helmholtz3D.monopole(position=pos2, amplitude=-q/(4*π*ϵ), wavenumber=k) + + # Potential of point charges + + Φ_inc(x) = charge1(x) + charge2(x) + + gD1 = assemble(DirichletTrace(charge1), X0) + assemble(DirichletTrace(charge2), X0) + gN = assemble(∂n(charge1), Y1) + assemble(BEAST.n ⋅ grad(charge2), Y1) + + G = assemble(Identity(), Y1, X0) + o = ones(numfunctions(X0)) + + # Interior Dirichlet problem - compare Sauter & Schwab eqs. 3.81 + M_IDPDL = (-1 / 2 * assemble(Identity(), X0, Y1) + assemble(D, X0, Y1)) # Double layer (DL) + + # Interior Neumann problem + # Neumann derivative from SL potential with deflected nullspace + M_INPSL = (1 / 2 * assemble(Identity(), Y1, X0) + assemble(Dt, Y1, X0)) + G * o * o' * G + + ρ_IDPDL = M_IDPDL \ (-gD1) + ρ_INPSL = M_INPSL \ (-gN) + + pts = meshsphere(0.8 * r, 0.8 * 0.6 * r).vertices # sphere inside on which the potential and field are evaluated + + pot_IDPDL = potential(HH3DDoubleLayerNear(im * k), pts, ρ_IDPDL, Y1; type=ComplexF64) + + pot_INPSL = potential(HH3DSingleLayerNear(im * k), pts, ρ_INPSL, X0; type=ComplexF64) + + # Total field inside should be zero + err_IDPDL_pot = norm(pot_IDPDL + Φ_inc.(pts)) / norm(Φ_inc.(pts)) + err_INPSL_pot = norm(pot_INPSL + Φ_inc.(pts)) / norm(Φ_inc.(pts)) + + # Efield(x) = - grad Φ_inc(x) + Efield(x) = -grad(charge1)(x) + -grad(charge2)(x) + + field_IDPDL = -potential(HH3DHyperSingularNear(im * k), pts, ρ_IDPDL, Y1) + field_INPSL = -potential(HH3DDoubleLayerTransposedNear(im * k), pts, ρ_INPSL, X0) + + err_IDPDL_field = norm(field_IDPDL + Efield.(pts)) / norm(Efield.(pts)) + err_INPSL_field = norm(field_INPSL + Efield.(pts)) / norm(Efield.(pts)) + + # errors of interior problems + @test err_IDPDL_pot < 0.02 + @test err_INPSL_pot < 0.025 + + @test err_IDPDL_field < 0.02 + @test err_INPSL_field < 0.025 +#end \ No newline at end of file From 88bf3c650f89e549e3d3b43ce4e58a79820ef483 Mon Sep 17 00:00:00 2001 From: Simon Adrian Date: Thu, 11 Apr 2024 10:56:31 +0200 Subject: [PATCH 346/528] Add interpolation support for Helmholtz RHS This commit enables support of primal piecewise linear and primal piecewise constant RHSs TODO: Probably, either an extension or a rewrite of DofInterpolate is advisable for the LagrangeBasis (for example, also dealing with higher-order functions and dual functions) --- src/helmholtz3d/hh3dexc.jl | 21 ++++++++++++++++ src/interpolation.jl | 50 ++++++++++++++++++++++++++++++++++---- test/test_interpolation.jl | 28 +++++++++++++++++++++ 3 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 test/test_interpolation.jl diff --git a/src/helmholtz3d/hh3dexc.jl b/src/helmholtz3d/hh3dexc.jl index ceac5a0e..59d96e30 100644 --- a/src/helmholtz3d/hh3dexc.jl +++ b/src/helmholtz3d/hh3dexc.jl @@ -11,6 +11,10 @@ function (f::HH3DPlaneWave)(r) return a * exp(-γ*dot(d,r)) end +function (f::HH3DPlaneWave)(r::CompScienceMeshes.MeshPointNM) + return f(cartesian(r)) +end + scalartype(f::HH3DPlaneWave{P,K,T}) where {P,K,T} = promote_type(eltype(P), K, T) """ @@ -32,6 +36,9 @@ function (f::HH3DLinearPotential)(r) return a * dot(d, r) end +function (f::HH3DLinearPotential)(r::CompScienceMeshes.MeshPointNM) + return f(cartesian(r)) +end struct gradHH3DLinearPotential{T,P} direction::P amplitude::T @@ -44,6 +51,11 @@ function (f::gradHH3DLinearPotential)(r) return a * d end +function (f::gradHH3DLinearPotential)(r::CompScienceMeshes.MeshPointNM) + r = cartesian(mp) + return dot(normal(mp), f(r)) +end + function grad(m::HH3DLinearPotential) return gradHH3DLinearPotential(m.direction, m.amplitude) end @@ -66,6 +78,10 @@ end scalartype(x::HH3DMonopole{P,K,T}) where {P,K,T} = promote_type(eltype(P), K, T) +function (f::HH3DMonopole)(r::CompScienceMeshes.MeshPointNM) + return f(cartesian(r)) +end + function (f::HH3DMonopole)(r) γ = f.gamma p = f.position @@ -92,6 +108,11 @@ function (f::gradHH3DMonopole)(r) return -a * vecR * exp(-γ * R) / R^2 * (γ + 1 / R) end +function (f::gradHH3DMonopole)(mp::CompScienceMeshes.MeshPointNM) + r = cartesian(mp) + return dot(normal(mp), f(r)) +end + function grad(m::HH3DMonopole) return gradHH3DMonopole(m.position, m.gamma, m.amplitude) end diff --git a/src/interpolation.jl b/src/interpolation.jl index 8aef944b..a20a69e9 100644 --- a/src/interpolation.jl +++ b/src/interpolation.jl @@ -1,9 +1,11 @@ function DofInterpolate(basis::LagrangeBasis, field) + T = promote_type(scalartype(basis), scalartype(field)) + num_bfs = numfunctions(basis) - res = Vector(undef, num_bfs) + res = Vector{T}(undef, num_bfs) for b in 1 : num_bfs bfs = basis.fns[b] @@ -33,11 +35,43 @@ function DofInterpolate(basis::LagrangeBasis, field) return res end +### Piecewise constant elements require separate treatment +### TODO: Probably a rewrite is advisable to also take into account +### dual elements properly. +function DofInterpolate(basis::LagrangeBasis{0,-1,M,T,NF,P}, field) where {M, T, NF, P} + + TT = promote_type(scalartype(basis), scalartype(field)) + + num_bfs = numfunctions(basis) + + res = Vector{TT}(undef, num_bfs) + + for b in 1 : num_bfs + bfs = basis.fns[b] + + basis.pos[b] + + shape = bfs[1] + + cellid = shape.cellid + + tria = chart(basis.geo, cellid) + + v = neighborhood(tria, [1/3, 1/3]) + + res[b] = field(v) + end + + return res +end + function DofInterpolate(basis::RTBasis, field) + T = promote_type(scalartype(basis), scalartype(field)) + num_bfs = numfunctions(basis) - res = Vector(undef, num_bfs) + res = Vector{T}(undef, num_bfs) for b in 1 : num_bfs @@ -76,9 +110,11 @@ end function DofInterpolate(basis::BDMBasis, field) + T = promote_type(scalartype(basis), scalartype(field)) + num_bfs = numfunctions(basis) - res = Vector(undef, num_bfs) + res = Vector{T}(undef, num_bfs) for b in 1 : num_bfs @@ -129,9 +165,11 @@ end function DofInterpolate(basis::NDLCDBasis, field) + T = promote_type(scalartype(basis), scalartype(field)) + num_bfs = numfunctions(basis) - res = Vector(undef, num_bfs) + res = Vector{T}(undef, num_bfs) for b in 1 : num_bfs @@ -173,9 +211,11 @@ end function DofInterpolate(basis::BEAST.BDM3DBasis, field) + T = promote_type(scalartype(basis), scalartype(field)) + num_bfs = numfunctions(basis) - res = Vector(undef, num_bfs) + res = Vector{T}(undef, num_bfs) for b in 1 : num_bfs diff --git a/test/test_interpolation.jl b/test/test_interpolation.jl new file mode 100644 index 00000000..3836521e --- /dev/null +++ b/test/test_interpolation.jl @@ -0,0 +1,28 @@ +using BEAST +using CompScienceMeshes +using Test + +Γ = meshrectangle(1.0, 1.0, 0.4) + +XN = lagrangecxd0(Γ) # lagrangec0d1(Γₑ₁; dirichlet=true) # Dirichlet=true -> no boundary function +XD = lagrangec0d1(Γ) + +mp = Helmholtz3D.monopole(position=SVector(0.0, 0.0, 3.0)) +gradmp = grad(mp) + +φ = DofInterpolate(XD, mp) + +@test eltype(φ) == Float64 + +for i in eachindex(φ) + @test mp(XD.pos[i]) == φ[i] +end + +σ = DofInterpolate(XN, gradmp) + +@test eltype(σ) == Float64 + +for i in eachindex(σ) + # Orientation of meshrectangle is in -z direction + @test dot(SVector(0.0, 0.0, -1.0), gradmp(XN.pos[i])) == σ[i] +end \ No newline at end of file From 17ca764723559ad5e4ead159c09dda7682d53966 Mon Sep 17 00:00:00 2001 From: Tim Herrigt Date: Thu, 18 Jan 2024 19:23:57 +0100 Subject: [PATCH 347/528] Reactivate RKCQ in Julia 1.9.x - Make latest RKCQ version suitable for Julia 1.9 - Only neccesary changes - No further improvements and adjustments --- examples/disabled/tdefie_irk.jl | 50 ++++++++++++++++++------------- src/solvers/lusolver.jl | 21 ++++++++++++- src/timedomain/motlu.jl | 45 ++++++++++++++++------------ src/timedomain/rkcq.jl | 52 ++++++++++++++++----------------- src/timedomain/zdomain.jl | 14 ++++----- 5 files changed, 109 insertions(+), 73 deletions(-) diff --git a/examples/disabled/tdefie_irk.jl b/examples/disabled/tdefie_irk.jl index 7e31f767..7035434b 100644 --- a/examples/disabled/tdefie_irk.jl +++ b/examples/disabled/tdefie_irk.jl @@ -1,29 +1,37 @@ -using CompScienceMeshes, BEAST +using CompScienceMeshes, BEAST, StaticArrays, LinearAlgebra, Plots -o, x, y, z = euclidianbasis(3) - -sol = 5.0; -Δt, Nt = 100.0/sol,200 - -D, Δx = 1.0, 0.45 -Γ = meshsphere(D, Δx) +Γ = meshsphere(radius=1.0, h=0.35) X = raviartthomas(Γ) +sol = 1.0 +Δt, Nt = 10.0, 200 -(A, b, c) = butcher_tableau_radau_2stages(); -V = StagedTimeStep(X, c, Δt, Nt); - -duration, delay, amplitude = 2000.0/sol, 2000.0/sol, 1.0 -gaussian = creategaussian(duration, delay, duration) +(A, b, c) = butcher_tableau_radau_2stages() +V = StagedTimeStep(X, c, Δt, Nt) +LaplaceEFIO(s::T) where {T}= MWSingleLayer3D(-s/sol, s*s/sol, T(sol)) -direction, polarisation = z, x -E = planewave(polarisation, direction, derive(gaussian), sol) +duration, delay, amplitude = 2 * 20 * Δt, 2 * 30 * Δt, 1.0 +gaussian = creategaussian(duration, delay, amplitude) -LaplaceEFIO(s::T) where {T} = MWSingleLayer3D(-s/sol, s*s/sol, T(sol)); -kmax = 15; -rho = 1.0001; -T = RungeKuttaConvolutionQuadrature(LaplaceEFIO, A, b, Δt, kmax, rho); +direction, polarisation = ẑ , x̂ +E = planewave(polarisation, direction, BEAST.derive(gaussian), sol) +T = RungeKuttaConvolutionQuadrature(LaplaceEFIO, A, b, Δt, 10, 1.001); @hilbertspace j @hilbertspace j′ -tdefie = @discretise T[j′,j] == -1E[j′] j∈V j′∈V -xefie_irk = solve(tdefie) +tdefie_irk = @discretise T[j′,j] == -1E[j′] j∈V j′∈V +xefie_irk = solve(tdefie_irk) + +j = xefie_irk[1:2:end,:] + +import Plots, Plotly +Plots.plot(j[1,:]) +fcr, geo = facecurrents(j[:,1], X) +Plotly.plot(patch(geo, norm.(fcr))) + +Xefie_irk, Δω, ω0 = fouriertransform(j, Δt, 0.0, 2) +ω = collect(ω0 .+ (0:Nt-1)*Δω) +_, i1 = findmin(abs.(ω.-1.0)) +ω1 = ω[i1] +ue = Xefie_irk[:, i1] / fouriertransform(gaussian)(ω1) +fcr, geo = facecurrents(ue, X) +Plotly.plot(patch(geo, norm.(fcr))) diff --git a/src/solvers/lusolver.jl b/src/solvers/lusolver.jl index 745fec07..9e2a4df5 100644 --- a/src/solvers/lusolver.jl +++ b/src/solvers/lusolver.jl @@ -9,11 +9,15 @@ and calling a traditional lu decomposition. function solve(eq) time_domain = isa(first(eq.trial_space_dict).second, BEAST.SpaceTimeBasis) - time_domain |= isa(first(eq.trial_space_dict).second, BEAST.StagedTimeStep) + time_domain_cq = isa(first(eq.trial_space_dict).second, BEAST.StagedTimeStep) if time_domain return td_solve(eq) end + if time_domain_cq + return td_solve_cq(eq) + end + test_space_dict = eq.test_space_dict trial_space_dict = eq.trial_space_dict @@ -38,4 +42,19 @@ function solve(eq) return PseudoBlockVector(u, (ax,)) end +function td_solve_cq(eq) + + @warn("very limited sypport for automated solution of TD equations....") + op = eq.equation.lhs.terms[1].kernel + fn = eq.equation.rhs.terms[1].functional + V = eq.trial_space_dict[1] + W = eq.test_space_dict[1] + + A = assemble(op, W, V) + S = inv(A[:,:,1]) + b = assemble(fn, W) + + nt = numfunctions(temporalbasis(V)) + marchonintime_cq(S, A, b, nt) +end diff --git a/src/timedomain/motlu.jl b/src/timedomain/motlu.jl index 21f5818b..1633fca9 100644 --- a/src/timedomain/motlu.jl +++ b/src/timedomain/motlu.jl @@ -1,21 +1,3 @@ -motsolve(eq) = td_solve(eq) - -function td_solve(eq) - - V = eq.trial_space_dict[1] - - A = assemble(eq.equation.lhs, eq.test_space_dict, eq.trial_space_dict) - T = eltype(A) - S = zeros(T, size(A)[1:2]) - ConvolutionOperators.timeslice!(S, A, 1) - - iS = inv(S) - b = assemble(eq.equation.rhs, eq.test_space_dict) - - nt = numfunctions(temporalbasis(V)) - marchonintime(iS, A, b, nt) -end - """ marchonintime(W0,Z,B,I; convhist=false) @@ -67,4 +49,31 @@ function marchonintime(W0,Z,B,I; convhist=false) end end +function convolve(Z,x,j,k0) + T = promote_type(eltype(Z), eltype(x)) + M,N,K = size(Z) + @assert M == size(x,1) + y = zeros(T,M) + for m in 1:M + for n in 1:N + for k in k0:min(j,K) + i = j - k + 1 + y[m] += Z[m,n,k] * x[n,i] + end + end + end + return y +end +function marchonintime_cq(W0,Z,B,I) + T = eltype(Z) + M,N,K = size(Z) + @assert M == size(B,1) + x = zeros(T,N,I) + for i in 1:I + b = B[:,i] - convolve(Z,x,i,2) + x[:,i] = W0 * b + (i % 10 == 0) && print(i, "[", I, "] - ") + end + return x +end diff --git a/src/timedomain/rkcq.jl b/src/timedomain/rkcq.jl index cda0a1ea..90ca1d8f 100644 --- a/src/timedomain/rkcq.jl +++ b/src/timedomain/rkcq.jl @@ -35,7 +35,7 @@ end # M = H*diagm(D)*invH function diagonalizedmatrix(M :: SArray{Tuple{N,N},Complex{T},2,NN}) where {T,N,NN} - ef = eigfact(Array{Complex{T},2}(M)); + ef = eigen(Array{Complex{T},2}(M)); efValues = SVector{N,Complex{T}}(ef.values) :: SVector{N,Complex{T}}; efVectors = SArray{Tuple{N,N},Complex{T},2,NN}(ef.vectors) :: SArray{Tuple{N,N},Complex{T},2,NN}; @@ -46,55 +46,55 @@ function assemble(rkcq :: RungeKuttaConvolutionQuadrature, testfns :: StagedTimeStep, trialfns :: StagedTimeStep) - laplaceKernel = rkcq.laplaceKernel; - A = rkcq.A; - b = rkcq.b; - Δt = rkcq.Δt; - Q = rkcq.zTransformedTermCount; - rho = rkcq.contourRadius; - p = length(b); # stage count + laplaceKernel = rkcq.laplaceKernel + A = rkcq.A + b = rkcq.b + Δt = rkcq.Δt + Q = rkcq.zTransformedTermCount + rho = rkcq.contourRadius + p = length(b) # stage count - test_spatial_basis = testfns.spatialBasis; - trial_spatial_basis = trialfns.spatialBasis; + test_spatial_basis = testfns.spatialBasis + trial_spatial_basis = trialfns.spatialBasis # Compute the Z transformed sequence. # Assume that the operator applied on the conjugate of s is the same as the # conjugate of the operator applied on s, # so that only half of the values are computed - Qmax = Q>>1+1; - M = numfunctions(test_spatial_basis); - N = numfunctions(trial_spatial_basis); - Tz = promote_type(scalartype(rkcq), scalartype(testfns), scalartype(trialfns)); - Zz = Vector{Array{Tz,2}}(undef,Qmax); - blocksEigenvalues = Vector{Array{Tz,2}}(undef,p); - tmpDiag = Vector{Tz}(undef,p); + Qmax = Q>>1+1 + M = numfunctions(test_spatial_basis) + N = numfunctions(trial_spatial_basis) + Tz = promote_type(scalartype(rkcq), scalartype(testfns), scalartype(trialfns)) + Zz = Vector{Array{Tz,2}}(undef,Qmax) + blocksEigenvalues = Vector{Array{Tz,2}}(undef,p) + tmpDiag = Vector{Tz}(undef,p) for q = 0:Qmax-1 # Build a temporary matrix for each eigenvalue - s = laplace_to_z(rho, q, Q, Δt, A, b); - sFactorized = diagonalizedmatrix(s); + s = laplace_to_z(rho, q, Q, Δt, A, b) + sFactorized = diagonalizedmatrix(s) for (i,sD) in enumerate(sFactorized.D) - blocksEigenvalues[i] = assemble(laplaceKernel(sD), test_spatial_basis, trial_spatial_basis); + blocksEigenvalues[i] = assemble(laplaceKernel(sD), test_spatial_basis, trial_spatial_basis) end # Compute the Z transformed matrix by block - Zz[q+1] = zeros(Tz, M*p, N*p); + Zz[q+1] = zeros(Tz, M*p, N*p) for m = 1:M for n = 1:N for i = 1:p - tmpDiag[i] = blocksEigenvalues[i][m,n]; + tmpDiag[i] = blocksEigenvalues[i][m,n] end D = SVector{p,Tz}(tmpDiag); - Zz[q+1][(m-1)*p+(1:p),(n-1)*p+(1:p)] = sFactorized.H * diagm(D) * sFactorized.invH; + Zz[q+1][(m-1)*p.+(1:p),(n-1)*p.+(1:p)] = sFactorized.H * diagm(D) * sFactorized.invH end end end # return the inverse Z transform - kmax = Q; - T = real(Tz); + kmax = Q + T = real(Tz) Z = zeros(T, M*p, N*p, kmax) for q = 0:kmax-1 - Z[:,:,q+1] = real_inverse_z_transform(q, rho, Q, Zz); + Z[:,:,q+1] = real_inverse_z_transform(q, rho, Q, Zz) end return Z diff --git a/src/timedomain/zdomain.jl b/src/timedomain/zdomain.jl index e0ea789c..a44095c1 100644 --- a/src/timedomain/zdomain.jl +++ b/src/timedomain/zdomain.jl @@ -7,9 +7,9 @@ Returns the complex matrix valued Laplace variable s that correspond to the variable z = rho*exp(2*im*pi*n/N) for a given Butcher tableau (A,b,c) and a time step dt. """ function laplace_to_z(rho, n, N, dt, A, b) - z = rho * exp(2*im*pi*n/N); - s = inv(dt * (A + ones(b) * b' / (z-1))); - return s; + z = rho * exp(2*im*pi*n/N) + s = inv(dt * (A + ones(size(b)) * b' / (z-1))) + return s end """ @@ -19,7 +19,7 @@ Returns the k-th term of the inverse z-transform. X is an array of the z-transfo evaluated in the points z=rho*exp(2*im*pi*n/N) for n in 0:(N-1). """ function inverse_z_transform(k, rho, N, X::AbstractArray{T,1}) where T - return ((rho^k) / N) * sum(n -> X[n+1] * exp(2*im*pi*k*n/N), 0:(N-1)); + return ((rho^k) / N) * sum(n -> X[n+1] * exp(2*im*pi*k*n/N), 0:(N-1)) end """ @@ -32,7 +32,7 @@ X is an array of the z-transform evaluated in the points z=rho*exp(2*im*pi*n/N) for n in 0:(Nmax-1). """ function real_inverse_z_transform(k, rho, N, X::AbstractArray{T,1}) where T - Nmax = (N+1)>>1; - realTerms = (N%2==0) ? real(X[1]) + (-1)^k * real(X[Nmax+1]) : real(X[1]); - return ((rho^k) / N) * (realTerms + 2*sum(n -> real(X[n+1] * exp(2*im*pi*k*n/N)), 1:(Nmax-1))); + Nmax = (N+1)>>1 + realTerms = (N%2==0) ? real(X[1]) + (-1)^k * real(X[Nmax+1]) : real(X[1]) + return ((rho^k) / N) * (realTerms + 2*sum(n -> real(X[n+1] * exp(2*im*pi*k*n/N)), 1:(Nmax-1))) end From aac6fbb4b31b7f1a8040a93fb49ecde1edfd18ac Mon Sep 17 00:00:00 2001 From: Tim Herrigt Date: Thu, 18 Jan 2024 20:19:53 +0100 Subject: [PATCH 348/528] Add 2nd derivative of Gaussian + inverse Fourier transform Second derivative of Gaussian added to enable the usage of the derivative of the Gaussian as a right hand side. This has the advantage that the right hand side does not contain DC information. Added inverse Fourier transform to solve time-domain problem by inverse Fourier transforming the frequency-domain problem. --- examples/disabled/tdefie_irk.jl | 4 +++- src/timedomain/motlu.jl | 18 ++++++++++++++++++ src/utils/specialfns.jl | 14 ++++++++++++-- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/examples/disabled/tdefie_irk.jl b/examples/disabled/tdefie_irk.jl index 7035434b..3de5a532 100644 --- a/examples/disabled/tdefie_irk.jl +++ b/examples/disabled/tdefie_irk.jl @@ -23,8 +23,10 @@ xefie_irk = solve(tdefie_irk) j = xefie_irk[1:2:end,:] -import Plots, Plotly +import Plots Plots.plot(j[1,:]) + +import Plotly fcr, geo = facecurrents(j[:,1], X) Plotly.plot(patch(geo, norm.(fcr))) diff --git a/src/timedomain/motlu.jl b/src/timedomain/motlu.jl index 1633fca9..0bbbcf64 100644 --- a/src/timedomain/motlu.jl +++ b/src/timedomain/motlu.jl @@ -1,3 +1,21 @@ +motsolve(eq) = td_solve(eq) + +function td_solve(eq) + + V = eq.trial_space_dict[1] + + A = assemble(eq.equation.lhs, eq.test_space_dict, eq.trial_space_dict) + T = eltype(A) + S = zeros(T, size(A)[1:2]) + ConvolutionOperators.timeslice!(S, A, 1) + + iS = inv(S) + b = assemble(eq.equation.rhs, eq.test_space_dict) + + nt = numfunctions(temporalbasis(V)) + marchonintime(iS, A, b, nt) +end + """ marchonintime(W0,Z,B,I; convhist=false) diff --git a/src/utils/specialfns.jl b/src/utils/specialfns.jl index 03e8436c..cf6e85b8 100644 --- a/src/utils/specialfns.jl +++ b/src/utils/specialfns.jl @@ -17,11 +17,11 @@ function creategaussian(width,s0,scaling=one(typeof(width))) end -function fouriertransform(g::Gaussian) +function fouriertransform(g::Gaussian; numdiff=0) scaling = g.scaling width = g.width s0 = g.delay - ft(w) = scaling * exp(-im*w*s0 - (width*w/8)^2) / sqrt(2π) + ft(w) = (im*w)^numdiff * scaling * exp(-im*w*s0 - (width*w/8)^2) / sqrt(2π) end @@ -33,11 +33,21 @@ function fouriertransform(a::Array, dt, t0, dim=1) b, dω, ω0 end +function inversefouriertransform(a::Array, dω, ω0, dim=1) + n = size(a,dim) + dt = 2π/ (n*dω) + b = ifft(a,dim) * sqrt(2π) / dt + t0 = -dt * div(n,2) + b, dt, t0 +end + fouriertransform(a::Array; stepsize, offset, dim=1) = fouriertransform(a, stepsize, offset, dim) derive(g::Gaussian) = s -> g(s) * (-8 * (s-g.delay)/g.width) * (4/g.width) +derive2(g::Gaussian) = s -> -(8*4)/g.width^2 * (g(s) + (s-g.delay) * derive(g)(s)) + struct ErrorFunction{T} scaling::T From 3e08f1e358d627ed31bd32f0ec8bc422aadd1ff1 Mon Sep 17 00:00:00 2001 From: TimHRO Date: Fri, 22 Mar 2024 10:38:26 +0100 Subject: [PATCH 349/528] RKCQ leverages modern ConvolutionOperators interface RKCQ uses now the ConvolutionOperators interface that is used by Space-Time-Galerkin MOT. To this end, all information required for RKCQ is contained in the StagedTimeStep type, which is used for building the SpaceTimeBasis. Moreover, the bilinear form can now be build with retarded potentials. --- examples/disabled/tdefie_irk.jl | 36 ++++++++++-------------- examples/tdefie.jl | 2 +- src/BEAST.jl | 4 ++- src/bases/stagedtimestep.jl | 26 +++++++++++------ src/bases/tensorbasis.jl | 4 +++ src/solvers/lusolver.jl | 23 +--------------- src/solvers/solver.jl | 21 +++++++++----- src/timedomain/motlu.jl | 29 ------------------- src/timedomain/rkcq.jl | 49 +++++++++++++++++++-------------- src/timedomain/tdexcitation.jl | 32 ++++++++++++--------- src/timedomain/tdintegralop.jl | 7 ++++- 11 files changed, 107 insertions(+), 126 deletions(-) diff --git a/examples/disabled/tdefie_irk.jl b/examples/disabled/tdefie_irk.jl index 3de5a532..6f1c8704 100644 --- a/examples/disabled/tdefie_irk.jl +++ b/examples/disabled/tdefie_irk.jl @@ -1,39 +1,31 @@ using CompScienceMeshes, BEAST, StaticArrays, LinearAlgebra, Plots -Γ = meshsphere(radius=1.0, h=0.35) +Γ = meshsphere(radius=1.0, h=0.45) X = raviartthomas(Γ) sol = 1.0 Δt, Nt = 10.0, 200 -(A, b, c) = butcher_tableau_radau_2stages() -V = StagedTimeStep(X, c, Δt, Nt) -LaplaceEFIO(s::T) where {T}= MWSingleLayer3D(-s/sol, s*s/sol, T(sol)) +(A, b, c) = butcher_tableau_radau_3stages() +T = StagedTimeStep(Δt, Nt, c, A, b, 10, 1.0001) +V = X ⊗ T -duration, delay, amplitude = 2 * 20 * Δt, 2 * 30 * Δt, 1.0 +duration = 2 * 20 * Δt +delay = 1.5 * duration +amplitude = 1.0 gaussian = creategaussian(duration, delay, amplitude) direction, polarisation = ẑ , x̂ -E = planewave(polarisation, direction, BEAST.derive(gaussian), sol) -T = RungeKuttaConvolutionQuadrature(LaplaceEFIO, A, b, Δt, 10, 1.001); +E = planewave(polarisation, direction, derive(gaussian), sol) + +T = TDMaxwell3D.singlelayer(speedoflight=1.0, numdiffs=1) @hilbertspace j @hilbertspace j′ tdefie_irk = @discretise T[j′,j] == -1E[j′] j∈V j′∈V xefie_irk = solve(tdefie_irk) -j = xefie_irk[1:2:end,:] - import Plots -Plots.plot(j[1,:]) - -import Plotly -fcr, geo = facecurrents(j[:,1], X) -Plotly.plot(patch(geo, norm.(fcr))) - -Xefie_irk, Δω, ω0 = fouriertransform(j, Δt, 0.0, 2) -ω = collect(ω0 .+ (0:Nt-1)*Δω) -_, i1 = findmin(abs.(ω.-1.0)) -ω1 = ω[i1] -ue = Xefie_irk[:, i1] / fouriertransform(gaussian)(ω1) -fcr, geo = facecurrents(ue, X) -Plotly.plot(patch(geo, norm.(fcr))) +Plots.plot(xefie_irk[1,:]) + + + diff --git a/examples/tdefie.jl b/examples/tdefie.jl index 87307bf9..69e9cce1 100644 --- a/examples/tdefie.jl +++ b/examples/tdefie.jl @@ -1,6 +1,6 @@ using CompScienceMeshes, BEAST, LinearAlgebra Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) -Γ = meshsphere(radius=1.0, h=0.1) +Γ = meshsphere(radius=1.0, h=0.25) X = raviartthomas(Γ) diff --git a/src/BEAST.jl b/src/BEAST.jl index 1f71e39c..046d68f4 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -37,8 +37,10 @@ export brezzidouglasmarini3d export nedelecd3d export nedelecc3d export portcells, rt_ports, getindex_rtg, subset -export StagedTimeStep + +export StagedTimeStep, numstages export subdsurface, subdBasis, assemblydata, refspace + export spatialbasis, temporalbasis export ⊗ export timebasisc0d1 diff --git a/src/bases/stagedtimestep.jl b/src/bases/stagedtimestep.jl index 08215e37..5ad66006 100644 --- a/src/bases/stagedtimestep.jl +++ b/src/bases/stagedtimestep.jl @@ -2,19 +2,27 @@ """ - StagedTimeStep{T,N} + StagedTimeStep{T,N,NN} T: the value type of the basis function. N: the number of stages. -It corresponds to a time-space basis function where each time step has -intermediary stages given by the vertor c in a Butcher tableau (A,b,c) +NN: the number of stages squared +Each time step has intermediary stages given by the vertor c in a Butcher tableau (A,b,c) """ -struct StagedTimeStep{SB, T, N} - spatialBasis :: SB - c :: SVector{N,T} +struct StagedTimeStep{T, N, NN, I} Δt :: T - Nt :: Int + Nt :: I + c :: SVector{N,T} + A :: SArray{Tuple{N,N},T,2,NN} + b :: SVector{N,T} + zTransformedTermCount :: I + contourRadius :: T end -scalartype(sts :: StagedTimeStep{SB, T, N}) where {SB, T, N} = T; -temporalbasis(sts :: StagedTimeStep{SB, T, N}) where {SB, T, N} = timebasisdelta(sts.Δt, sts.Nt) +scalartype(sts :: StagedTimeStep{T, N, NN, I}) where {T, N, NN, I} = T +temporalbasis(sts :: StagedTimeStep{T, N, NN, I}) where {T, N, NN, I} = timebasisdelta(sts.Δt, sts.Nt) + +numfunctions(s::StagedTimeStep) = s.Nt + +numstages(s) = 1 +numstages(s::StagedTimeStep) = size(s.c,1) diff --git a/src/bases/tensorbasis.jl b/src/bases/tensorbasis.jl index b8bce8ab..56762da2 100644 --- a/src/bases/tensorbasis.jl +++ b/src/bases/tensorbasis.jl @@ -22,6 +22,10 @@ function spatialbasis(s::DirectProductSpace) end temporalbasis(s::SpaceTimeBasis) = s.time +function numstages(s::DirectProductSpace) + r = [numstages(temporalbasis(ch)) for ch in s.factors] + return r +end ⊗(a, b) = SpaceTimeBasis(a,b) numfunctions(S::SpaceTimeBasis) = numfunctions(S.space) * numfunctions(S.time) diff --git a/src/solvers/lusolver.jl b/src/solvers/lusolver.jl index 9e2a4df5..13197917 100644 --- a/src/solvers/lusolver.jl +++ b/src/solvers/lusolver.jl @@ -9,15 +9,11 @@ and calling a traditional lu decomposition. function solve(eq) time_domain = isa(first(eq.trial_space_dict).second, BEAST.SpaceTimeBasis) - time_domain_cq = isa(first(eq.trial_space_dict).second, BEAST.StagedTimeStep) + if time_domain return td_solve(eq) end - if time_domain_cq - return td_solve_cq(eq) - end - test_space_dict = eq.test_space_dict trial_space_dict = eq.trial_space_dict @@ -41,20 +37,3 @@ function solve(eq) ax = nestedrange(Y, 1, numfunctions) return PseudoBlockVector(u, (ax,)) end - -function td_solve_cq(eq) - - @warn("very limited sypport for automated solution of TD equations....") - op = eq.equation.lhs.terms[1].kernel - fn = eq.equation.rhs.terms[1].functional - - V = eq.trial_space_dict[1] - W = eq.test_space_dict[1] - - A = assemble(op, W, V) - S = inv(A[:,:,1]) - b = assemble(fn, W) - - nt = numfunctions(temporalbasis(V)) - marchonintime_cq(S, A, b, nt) -end diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index bbc0034a..30a6ac22 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -151,9 +151,16 @@ function assemble(lform::LinForm, X::DirectProductSpace) @assert !isempty(lform.terms) T = scalartype(lform, X) - U = NestedUnitRanges.nestedrange(spatialbasis(X), 1, numfunctions) - x = first(AbstractTrees.Leaves(X)) + stagedtimestep = isa(x.time, BEAST.StagedTimeStep) + if stagedtimestep + stages = numstages(x.time) + stagednumfunctions(X) = stages * numfunctions(X) + U = NestedUnitRanges.nestedrange(spatialbasis(X), 1, stagednumfunctions) + else + U = NestedUnitRanges.nestedrange(spatialbasis(X), 1, numfunctions) + end + N = Base.OneTo(tensordim(x,2)) ax = _righthandside_axes(x, U, N) @@ -166,7 +173,6 @@ function assemble(lform::LinForm, X::DirectProductSpace) x = X.factors[m] for op in reverse(t.test_ops) x = op[end](op[1:end-1]..., x) end - b = assemble(t.functional, x) B[Block(m),Block(1)] = t.coeff * b end @@ -210,9 +216,10 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; @assert !isempty(bf.terms) - M = numfunctions.(spatialbasis(X).factors) - N = numfunctions.(spatialbasis(Y).factors) - + M = numfunctions.(spatialbasis(X).factors) .* numstages(X) + N = numfunctions.(spatialbasis(Y).factors) .* numstages(X) + MN = numfunctions(X) + U = BlockArrays.blockedrange(M) V = BlockArrays.blockedrange(N) @@ -227,7 +234,7 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; for op in reverse(term.trial_ops) y = op[end](op[1:end-1]..., y) end - + a = term.coeff * term.kernel z = materialize(a, x, y) lift(z, Block(term.test_id), Block(term.trial_id), U, V) diff --git a/src/timedomain/motlu.jl b/src/timedomain/motlu.jl index 0bbbcf64..fd82452f 100644 --- a/src/timedomain/motlu.jl +++ b/src/timedomain/motlu.jl @@ -66,32 +66,3 @@ function marchonintime(W0,Z,B,I; convhist=false) return x end end - -function convolve(Z,x,j,k0) - T = promote_type(eltype(Z), eltype(x)) - M,N,K = size(Z) - @assert M == size(x,1) - y = zeros(T,M) - for m in 1:M - for n in 1:N - for k in k0:min(j,K) - i = j - k + 1 - y[m] += Z[m,n,k] * x[n,i] - end - end - end - return y -end - -function marchonintime_cq(W0,Z,B,I) - T = eltype(Z) - M,N,K = size(Z) - @assert M == size(B,1) - x = zeros(T,N,I) - for i in 1:I - b = B[:,i] - convolve(Z,x,i,2) - x[:,i] = W0 * b - (i % 10 == 0) && print(i, "[", I, "] - ") - end - return x -end diff --git a/src/timedomain/rkcq.jl b/src/timedomain/rkcq.jl index 90ca1d8f..13ae3b24 100644 --- a/src/timedomain/rkcq.jl +++ b/src/timedomain/rkcq.jl @@ -16,15 +16,11 @@ A, b: Coefficient matrix and vectors from the Butcher tableau. zTransformedTermCount: Number of terms in the inverse Z-transform. contourRadius: radius of circle used as integration contour for the inverse Z-transform. """ -struct RungeKuttaConvolutionQuadrature{LK, T, N, NN} - laplaceKernel :: LK # function of s that returns an IntegralOperator - A :: SArray{Tuple{N,N},T,2,NN} - b :: SVector{N,T} - Δt :: T - zTransformedTermCount :: Int - contourRadius :: T +struct RungeKuttaConvolutionQuadrature{} + timedomainKernel :: AbstractSpaceTimeOperator # function of s that returns an IntegralOperator end -scalartype(rkcq::RungeKuttaConvolutionQuadrature{LK, T, N, NN}) where {LK, T, N, NN} = Complex{T}; +#scalartype(rkcq::RungeKuttaConvolutionQuadrature{LK}) where {LK} = Complex + # M = H*diagm(D)*invH struct DiagonalizedMatrix{T,N,NN} @@ -43,19 +39,27 @@ function diagonalizedmatrix(M :: SArray{Tuple{N,N},Complex{T},2,NN}) where {T,N, end function assemble(rkcq :: RungeKuttaConvolutionQuadrature, - testfns :: StagedTimeStep, - trialfns :: StagedTimeStep) - - laplaceKernel = rkcq.laplaceKernel - A = rkcq.A - b = rkcq.b - Δt = rkcq.Δt - Q = rkcq.zTransformedTermCount - rho = rkcq.contourRadius + testfns :: SpaceTimeBasis, + trialfns :: SpaceTimeBasis) + + @warn "staged assemble of the left-hand side" + sol = rkcq.timedomainKernel.speed_of_light + numdiffweak = rkcq.timedomainKernel.ws_diffs + numdiffhyper = rkcq.timedomainKernel.hs_diffs + + @info "converting time-domain kernel to Laplace-domain kernel" + LaplaceEFIO(s::T) where {T}= MWSingleLayer3D(s/sol, -s^(numdiffweak)/sol, s^(numdiffhyper)*T(sol)) + laplaceKernel = LaplaceEFIO + + A = testfns.time.A + b = testfns.time.b + Δt = testfns.time.Δt + Q = testfns.time.zTransformedTermCount + rho = testfns.time.contourRadius p = length(b) # stage count - test_spatial_basis = testfns.spatialBasis - trial_spatial_basis = trialfns.spatialBasis + test_spatial_basis = testfns.space + trial_spatial_basis = trialfns.space # Compute the Z transformed sequence. # Assume that the operator applied on the conjugate of s is the same as the @@ -64,7 +68,9 @@ function assemble(rkcq :: RungeKuttaConvolutionQuadrature, Qmax = Q>>1+1 M = numfunctions(test_spatial_basis) N = numfunctions(trial_spatial_basis) - Tz = promote_type(scalartype(rkcq), scalartype(testfns), scalartype(trialfns)) + #Tz = promote_type(scalartype(rkcq), scalartype(testfns), scalartype(trialfns)) + #Tz = promote_type(scalartype(testfns), scalartype(trialfns)) + Tz = ComplexF64 Zz = Vector{Array{Tz,2}}(undef,Qmax) blocksEigenvalues = Vector{Array{Tz,2}}(undef,p) tmpDiag = Vector{Tz}(undef,p) @@ -96,6 +102,7 @@ function assemble(rkcq :: RungeKuttaConvolutionQuadrature, for q = 0:kmax-1 Z[:,:,q+1] = real_inverse_z_transform(q, rho, Q, Zz) end - return Z + ZC = ConvolutionOperators.DenseConvOp(Z) + return ZC end diff --git a/src/timedomain/tdexcitation.jl b/src/timedomain/tdexcitation.jl index b6a1f3d5..cf3819af 100644 --- a/src/timedomain/tdexcitation.jl +++ b/src/timedomain/tdexcitation.jl @@ -40,8 +40,13 @@ function quadrule(exc::TDFunctional, testrefs, timerefs::DiracBoundary, p, τ, r end - function assemble(exc::TDFunctional, testST; quaddata=quaddata, quadrule=quadrule) + + stagedtimestep = isa(temporalbasis(testST), BEAST.StagedTimeStep) + if stagedtimestep + return staged_assemble(exc, testST; quaddata=quaddata, quadrule=quadrule) + end + testfns = spatialbasis(testST) timefns = temporalbasis(testST) Z = zeros(eltype(exc), numfunctions(testfns), numfunctions(timefns)) @@ -50,26 +55,27 @@ function assemble(exc::TDFunctional, testST; quaddata=quaddata, quadrule=quadrul return Z end -function assemble(exc::TDFunctional, testST::StagedTimeStep; +function staged_assemble(exc::TDFunctional, testST::SpaceTimeBasis; quaddata=quaddata, quadrule=quadrule) - stageCount = length(testST.c); - spatialBasis = testST.spatialBasis; - Nt = testST.Nt; - Δt = testST.Δt; - Z = zeros(eltype(exc), numfunctions(spatialBasis) * stageCount, Nt); + @warn "staged assemble of the right-hand side" + testfns = spatialbasis(testST) + timefns = temporalbasis(testST) + stageCount = numstages(timefns) + Nt = timefns.Nt + Δt = timefns.Δt + Z = zeros(eltype(exc), numfunctions(testfns) * stageCount, Nt) for i = 1:stageCount - store(v,m,k) = (Z[(m-1)*stageCount+i,k] += v); - tbsd = TimeBasisDeltaShifted(timebasisdelta(Δt, Nt), testST.c[i]); - assemble!(exc, spatialBasis ⊗ tbsd, store, - quaddata=quaddata, quadrule=quadrule); + store(v,m,k) = (Z[(m-1)*stageCount+i,k] += v) + tbsd = TimeBasisDeltaShifted(timebasisdelta(Δt, Nt), timefns.c[i]) + assemble!(exc, testfns ⊗ tbsd, store, + quaddata=quaddata, quadrule=quadrule) end - return Z; + return Z end function assemble!(exc::TDFunctional, testST, store; quaddata=quaddata, quadrule=quadrule) - testfns = spatialbasis(testST) timefns = temporalbasis(testST) diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 47e68400..bd9820f6 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -3,12 +3,17 @@ using WiltonInts84 abstract type AbstractSpaceTimeOperator end abstract type SpaceTimeOperator <: AbstractSpaceTimeOperator end # atomic operator +#TODO RKCQ multithreading + function assemble(operator::AbstractSpaceTimeOperator, test_functions, trial_functions; storage_policy = Val{:bandedstorage}, long_delays_policy = LongDelays{:compress}, threading = Threading{:multi}, quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) - + stagedtimestep = isa(test_functions.time, StagedTimeStep) + if stagedtimestep + return assemble(RungeKuttaConvolutionQuadrature(operator), test_functions, trial_functions) + end Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) assemble!(operator, test_functions, trial_functions, store, threading; quadstrat) From ce8e271b091c5faa9450160ed446a0a4de29a643 Mon Sep 17 00:00:00 2001 From: TimHRO Date: Thu, 11 Apr 2024 18:07:24 +0200 Subject: [PATCH 350/528] Add unit test for Runge-Kutta Convolution Quadrature We compare against Space-Time-Galerkin MOT Alternatively one could compare the individual frequencies against EFIE frequency solutions. However, this results in a considerably longer run time. --- examples/{disabled => }/tdefie_irk.jl | 0 src/bases/tensorbasis.jl | 4 --- src/solvers/solver.jl | 28 +++++++++++---- test/runtests.jl | 1 + test/test_tdefie_irk.jl | 51 +++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 11 deletions(-) rename examples/{disabled => }/tdefie_irk.jl (100%) create mode 100644 test/test_tdefie_irk.jl diff --git a/examples/disabled/tdefie_irk.jl b/examples/tdefie_irk.jl similarity index 100% rename from examples/disabled/tdefie_irk.jl rename to examples/tdefie_irk.jl diff --git a/src/bases/tensorbasis.jl b/src/bases/tensorbasis.jl index 56762da2..b8bce8ab 100644 --- a/src/bases/tensorbasis.jl +++ b/src/bases/tensorbasis.jl @@ -22,10 +22,6 @@ function spatialbasis(s::DirectProductSpace) end temporalbasis(s::SpaceTimeBasis) = s.time -function numstages(s::DirectProductSpace) - r = [numstages(temporalbasis(ch)) for ch in s.factors] - return r -end ⊗(a, b) = SpaceTimeBasis(a,b) numfunctions(S::SpaceTimeBasis) = numfunctions(S.space) * numfunctions(S.time) diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 30a6ac22..aa9bb0e4 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -152,11 +152,16 @@ function assemble(lform::LinForm, X::DirectProductSpace) T = scalartype(lform, X) x = first(AbstractTrees.Leaves(X)) - stagedtimestep = isa(x.time, BEAST.StagedTimeStep) - if stagedtimestep - stages = numstages(x.time) - stagednumfunctions(X) = stages * numfunctions(X) - U = NestedUnitRanges.nestedrange(spatialbasis(X), 1, stagednumfunctions) + spaceTimeBasis = isa(x, BEAST.SpaceTimeBasis) + if spaceTimeBasis + stagedtimestep = isa(x.time, BEAST.StagedTimeStep) + if stagedtimestep + stages = numstages(x.time) + stagednumfunctions(X) = stages * numfunctions(X) + U = NestedUnitRanges.nestedrange(spatialbasis(X), 1, stagednumfunctions) + else + U = NestedUnitRanges.nestedrange(spatialbasis(X), 1, numfunctions) + end else U = NestedUnitRanges.nestedrange(spatialbasis(X), 1, numfunctions) end @@ -216,8 +221,17 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; @assert !isempty(bf.terms) - M = numfunctions.(spatialbasis(X).factors) .* numstages(X) - N = numfunctions.(spatialbasis(Y).factors) .* numstages(X) + spaceTimeBasis = isa(X.factors[1], BEAST.SpaceTimeBasis) + + if spaceTimeBasis + p = [numstages(temporalbasis(ch)) for ch in X.factors] + else + p = 1 + end + + M = numfunctions.(spatialbasis(X).factors) .* p + N = numfunctions.(spatialbasis(Y).factors) .* p + MN = numfunctions(X) U = BlockArrays.blockedrange(M) diff --git a/test/runtests.jl b/test/runtests.jl index 686438c3..b098b208 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -66,6 +66,7 @@ include("test_tdassembly.jl") include("test_tdhhdbl.jl") include("test_tdmwdbl.jl") include("test_compressed_storage.jl") +include("test_tdefie_irk.jl") include("test_dyadicop.jl") # include("test_matrixconv.jl") diff --git a/test/test_tdefie_irk.jl b/test/test_tdefie_irk.jl new file mode 100644 index 00000000..93d935e8 --- /dev/null +++ b/test/test_tdefie_irk.jl @@ -0,0 +1,51 @@ +using CompScienceMeshes +using BEAST +using StaticArrays +using LinearAlgebra +using Test + +Γ = meshsphere(radius=1.0, h=0.45) +X = raviartthomas(Γ) +sol = 1.0 # Speed of light (for sake of simplicity, set to one) + +# Note that certain choices of Δt, Nt, as well as the excitation +# can lead to late-time instabilities +Δt, Nt = 10.0, 200 + +(A, b, c) = butcher_tableau_radau_2stages() +T = StagedTimeStep(Δt, Nt, c, A, b, 5, 1.0001) +V = X ⊗ T + +duration = 2 * 20 * Δt +delay = 1.5 * duration +amplitude = 1.0 +gaussian = creategaussian(duration, delay, amplitude) + +direction, polarisation = ẑ , x̂ +E = planewave(polarisation, direction, derive(gaussian), sol) + +T = TDMaxwell3D.singlelayer(speedoflight=sol, numdiffs=1) + +@hilbertspace j +@hilbertspace j′ +tdefie_irk = @discretise T[j′,j] == -1E[j′] j∈V j′∈V +xefie_irk = solve(tdefie_irk) + +# Set up Space-Time-Galerkin MOT for comparison + +T = timebasisshiftedlagrange(Δt, Nt, 3) +U = timebasisdelta(Δt, Nt) + +V = X ⊗ T +W = X ⊗ U + +SL = TDMaxwell3D.singlelayer(speedoflight=1.0, numdiffs=1) + +tdefie = @discretise SL[j′,j] == -1.0E[j′] j∈V j′∈W +xefie = BEAST.motsolve(tdefie) + +xefie_irk = xefie_irk[1:size(c,1):end,:] + +diff_MOT_max = maximum((norm.(xefie_irk[:,1:end] - xefie[:,1:end])) ./ maximum(xefie[:,1:end])) + +@test diff_MOT_max ≈ 0.137252874891817 atol=1e-8 From a7c2b35d3f392584bb20c8ab965e73debd00a248 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sun, 14 Apr 2024 16:30:43 +0200 Subject: [PATCH 351/528] update doc manifest --- docs/Manifest.toml | 458 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 364 insertions(+), 94 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 450f9c59..9a126a0c 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -7,42 +7,54 @@ version = "0.0.1" [[AbstractFFTs]] deps = ["LinearAlgebra"] -git-tree-sha1 = "485ee0867925449198280d4af84bdb46a2a404d0" +git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" -version = "1.0.1" +version = "1.5.0" + + [AbstractFFTs.extensions] + AbstractFFTsChainRulesCoreExt = "ChainRulesCore" + AbstractFFTsTestExt = "Test" + + [AbstractFFTs.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[AbstractTrees]] +git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" +uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +version = "0.4.5" [[ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.1" [[ArrayLayouts]] -deps = ["FillArrays", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "d68733034d1d5d2cd3ea68e2b4cb11f456f6d015" +deps = ["FillArrays", "LinearAlgebra"] +git-tree-sha1 = "33207a8be6267bc389d0701e97a9bce6a4de68eb" uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "0.6.5" +version = "1.9.2" +weakdeps = ["SparseArrays"] + + [ArrayLayouts.extensions] + ArrayLayoutsSparseArraysExt = "SparseArrays" [[Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" [[BEAST]] -deps = ["BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "Distributed", "FFTW", "FastGaussQuadrature", "IterativeSolvers", "LinearAlgebra", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SparseMatrixDicts", "SpecialFunctions", "StaticArrays", "WiltonInts84"] -git-tree-sha1 = "7754b0c61dac72ec7ea524f2bfa831966edd2d97" +deps = ["AbstractTrees", "BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "ConvolutionOperators", "Distributed", "ExtendableSparse", "FFTW", "FastGaussQuadrature", "FillArrays", "InteractiveUtils", "IterativeSolvers", "LiftedMaps", "LinearAlgebra", "LinearMaps", "NestedUnitRanges", "Requires", "SauterSchwab3D", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SpecialFunctions", "StaticArrays", "WiltonInts84"] +path = ".." uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.3.0" +version = "2.2.1" [[Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" [[BlockArrays]] deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] -git-tree-sha1 = "a048ffafcf6eb52a1f59e32ea7d9e74419736a17" +git-tree-sha1 = "9a9610fbe5779636f75229e423e367124034af41" uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" -version = "0.14.5" - -[[ChainRulesCore]] -deps = ["Compat", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "e8a30e8019a512e4b6c56ccebc065026624660e8" -uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.7.0" +version = "0.16.43" [[ClusterTrees]] deps = ["DelimitedFiles", "LinearAlgebra", "Pkg"] @@ -50,11 +62,17 @@ git-tree-sha1 = "4114d60c95974edf9272d88571d14abeed598942" uuid = "5100927d-02aa-593a-b4f9-7235df19f0db" version = "0.2.1" +[[CodecZlib]] +deps = ["TranscodingStreams", "Zlib_jll"] +git-tree-sha1 = "59939d8a997469ee05c4b4944560a820f9ba0d73" +uuid = "944b1d66-785c-5afd-91f1-9de20f533193" +version = "0.7.4" + [[CollisionDetection]] deps = ["Compat", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "8edb46db045228884ac57fc4a0d45ce3ce1ec399" +git-tree-sha1 = "8d86c864d69f72e23adcd7c2014d205bf32c90a1" uuid = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" -version = "0.1.3" +version = "0.1.5" [[Combinatorics]] git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" @@ -62,26 +80,37 @@ uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" version = "1.0.2" [[CompScienceMeshes]] -deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays"] -git-tree-sha1 = "8e54028f066cc4559e3a6305747d7d7866cbc58a" +deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays"] +git-tree-sha1 = "2c45ee4534fcc94741cd7446067cfd13728fa29b" uuid = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" -version = "0.2.8" +version = "0.6.1" [[Compat]] -deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "31d0151f5716b655421d9d75b7fa74cc4e744df2" +deps = ["TOML", "UUIDs"] +git-tree-sha1 = "c955881e3c981181362ae4088b35995446298b80" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.39.0" +version = "4.14.0" +weakdeps = ["Dates", "LinearAlgebra"] + + [Compat.extensions] + CompatLinearAlgebraExt = "LinearAlgebra" [[CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "1.1.0+0" + +[[ConvolutionOperators]] +deps = ["LinearAlgebra", "LinearMaps"] +git-tree-sha1 = "967b02ba48c8de3546668275b0cf9c46aecde6a5" +uuid = "15927181-a1bb-497c-b745-8dbf505c019d" +version = "0.4.0" [[DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "7d9d316f04214f7efdbb6398d545446e246eff02" +git-tree-sha1 = "0f4b5d62a88d8f59003e43c25a8a90de9eb76317" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.10" +version = "0.18.18" [[Dates]] deps = ["Printf"] @@ -89,7 +118,9 @@ uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" [[DelimitedFiles]] deps = ["Mmap"] +git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" +version = "1.9.1" [[Distributed]] deps = ["Random", "Serialization", "Sockets"] @@ -97,25 +128,50 @@ uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" [[DocStringExtensions]] deps = ["LibGit2"] -git-tree-sha1 = "a32185f5428d3986f47c2ab78b1f216d5e6cc96f" +git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.8.5" +version = "0.9.3" [[Documenter]] -deps = ["ANSIColoredPrinters", "Base64", "Dates", "DocStringExtensions", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "REPL", "Test", "Unicode"] -git-tree-sha1 = "8b43e37cfb4f4edc2b6180409acc0cebce7fede8" +deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] +git-tree-sha1 = "4a40af50e8b24333b9ec6892546d9ca5724228eb" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "0.27.7" +version = "1.3.0" [[Downloads]] -deps = ["ArgTools", "LibCURL", "NetworkOptions"] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.6.0" + +[[Expat_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "4558ab818dcceaab612d1bb8c19cee87eda2b83c" +uuid = "2e619515-83b5-522b-bb60-26c02a35a201" +version = "2.5.0+0" + +[[ExtendableSparse]] +deps = ["DocStringExtensions", "ILUZero", "LinearAlgebra", "Printf", "Requires", "SparseArrays", "Sparspak", "StaticArrays", "SuiteSparse", "Test"] +git-tree-sha1 = "05585b2f21ed4c51878247cb5ce0beb87a71b631" +uuid = "95c220a8-a1cf-11e9-0c77-dbfce5f500b3" +version = "1.4.0" + + [ExtendableSparse.extensions] + ExtendableSparseAMGCLWrapExt = "AMGCLWrap" + ExtendableSparseAlgebraicMultigridExt = "AlgebraicMultigrid" + ExtendableSparseIncompleteLUExt = "IncompleteLU" + ExtendableSparsePardisoExt = "Pardiso" + + [ExtendableSparse.weakdeps] + AMGCLWrap = "4f76b812-4ba5-496d-b042-d70715554288" + AlgebraicMultigrid = "2169fc97-5a83-5252-b627-83903c6c433c" + IncompleteLU = "40713840-3770-5561-ab4c-a76e7d0d7895" + Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" [[FFTW]] deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] -git-tree-sha1 = "463cb335fa22c4ebacfd1faba5fde14edb80d96c" +git-tree-sha1 = "4820348781ae578893311153d69049a93d05f39d" uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" -version = "1.4.5" +version = "1.8.0" [[FFTW_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -125,54 +181,102 @@ version = "3.3.10+0" [[FastGaussQuadrature]] deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "5829b25887e53fb6730a9df2ff89ed24baa6abf6" +git-tree-sha1 = "fd923962364b645f3719855c88f7074413a6ad92" uuid = "442a2c76-b920-505d-bb47-c5924d526838" -version = "0.4.7" +version = "1.0.2" + +[[FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" [[FillArrays]] -deps = ["LinearAlgebra", "Random", "SparseArrays"] -git-tree-sha1 = "693210145367e7685d8604aee33d9bfb85db8b31" +deps = ["LinearAlgebra"] +git-tree-sha1 = "bfe82a708416cf00b73a3198db0859c82f741558" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "0.11.9" +version = "1.10.0" + + [FillArrays.extensions] + FillArraysPDMatsExt = "PDMats" + FillArraysSparseArraysExt = "SparseArrays" + FillArraysStatisticsExt = "Statistics" + + [FillArrays.weakdeps] + PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[Git]] +deps = ["Git_jll"] +git-tree-sha1 = "04eff47b1354d702c3a85e8ab23d539bb7d5957e" +uuid = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2" +version = "1.3.1" + +[[Git_jll]] +deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] +git-tree-sha1 = "d18fb8a1f3609361ebda9bf029b60fd0f120c809" +uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" +version = "2.44.0+2" + +[[GmshTools]] +deps = ["Libdl", "gmsh_jll"] +git-tree-sha1 = "299aa66053646db77f8aa7fafcebe0f9e5c0d1dc" +uuid = "82e2f556-b1bd-5f1a-9576-f93c0da5f0ee" +version = "0.5.2" + +[[GrundmannMoeller]] +deps = ["LinearAlgebra", "StaticArrays", "Test"] +git-tree-sha1 = "e264cf5f081091e4af712a911d3b620567c565e3" +uuid = "36aa67b7-9d79-4e90-bbc0-05abd90a007e" +version = "0.1.2" + +[[ILUZero]] +deps = ["LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "b007cfc7f9bee9a958992d2301e9c5b63f332a90" +uuid = "88f59080-6952-5380-9ea5-54057fb9a43f" +version = "0.2.0" [[IOCapture]] deps = ["Logging", "Random"] -git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a" +git-tree-sha1 = "8b72179abc660bfab5e28472e019392b97d0985c" uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" -version = "0.2.2" +version = "0.2.4" [[IntelOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "d979e54b71da82f3a65b62553da4fc3d18c9004c" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "5fdf2fe6724d8caabf43b557b84ce53f3b7e2f6b" uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2018.0.3+2" +version = "2024.0.2+0" [[InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" [[IrrationalConstants]] -git-tree-sha1 = "f76424439413893a832026ca355fe273e93bce94" +git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.1.0" +version = "0.2.2" [[IterativeSolvers]] deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] -git-tree-sha1 = "1a8c6237e78b714e901e406c096fc8a65528af7d" +git-tree-sha1 = "59545b0a2b27208b0650df0a46b8e3019f85055b" uuid = "42fd0dbc-a981-5370-80f2-aaf504508153" -version = "0.9.1" +version = "0.9.4" [[JLLWrappers]] -deps = ["Preferences"] -git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e" +deps = ["Artifacts", "Preferences"] +git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.3.0" +version = "1.5.0" [[JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "8076680b162ada2a031f707ac7b4953e30667a37" +git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.2" +version = "0.21.4" + +[[LazilyInitializedFields]] +git-tree-sha1 = "8f7f3cabab0fd1800699663533b6d5cb3fc0e612" +uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf" +version = "1.2.2" [[LazyArtifacts]] deps = ["Artifacts", "Pkg"] @@ -181,61 +285,145 @@ uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" [[LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.4" [[LibCURL_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "8.4.0+0" [[LibGit2]] -deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +[[LibGit2_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.6.4+0" + [[LibSSH2_jll]] deps = ["Artifacts", "Libdl", "MbedTLS_jll"] uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.11.0+1" [[Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +[[Libiconv_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" +uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" +version = "1.17.0+0" + +[[LiftedMaps]] +deps = ["LinearAlgebra", "LinearMaps"] +git-tree-sha1 = "68c65fe9d32407ddb13eb26ef96fa803d45369cd" +uuid = "d22a30c1-52ac-4762-a8c9-5838452405e0" +version = "0.5.1" + [[LinearAlgebra]] -deps = ["Libdl"] +deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +[[LinearMaps]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "9948d6f8208acfebc3e8cf4681362b2124339e7e" +uuid = "7a12625a-238d-50fd-b39a-03d52299707e" +version = "3.11.2" + + [LinearMaps.extensions] + LinearMapsChainRulesCoreExt = "ChainRulesCore" + LinearMapsSparseArraysExt = "SparseArrays" + LinearMapsStatisticsExt = "Statistics" + + [LinearMaps.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + [[LogExpFunctions]] -deps = ["ChainRulesCore", "DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "34dc30f868e368f8a17b728a1238f3fcda43931a" +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "18144f3e9cbe9b15b070288eef858f71b291ce37" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.3" +version = "0.3.27" + + [LogExpFunctions.extensions] + LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" + LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" + LogExpFunctionsInverseFunctionsExt = "InverseFunctions" + + [LogExpFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" [[Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" [[MKL_jll]] -deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"] -git-tree-sha1 = "5455aef09b40e5020e1520f551fa3135040d4ed0" +deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl"] +git-tree-sha1 = "72dc3cf284559eb8f53aa593fe62cb33f83ed0c0" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2021.1.1+2" +version = "2024.0.0+0" [[Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" +[[MarkdownAST]] +deps = ["AbstractTrees", "Markdown"] +git-tree-sha1 = "465a70f0fc7d443a00dcdc3267a497397b8a3899" +uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" +version = "0.1.2" + [[MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.2+1" [[Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" [[MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2023.1.10" + +[[NestedUnitRanges]] +deps = ["AbstractTrees", "ArrayLayouts", "BlockArrays"] +git-tree-sha1 = "1cbdce42da2370fee5ef906ef24179f8c070e3b9" +uuid = "032820ab-dc03-4b49-91f4-7d58d4da98b3" +version = "0.2.1" [[NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" + +[[OffsetArrays]] +git-tree-sha1 = "6a731f2b5c03157418a20c12195eb4b74c8f8621" +uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" +version = "1.13.0" + + [OffsetArrays.extensions] + OffsetArraysAdaptExt = "Adapt" + + [OffsetArrays.weakdeps] + Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" + +[[OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.23+4" [[OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" +version = "0.8.1+2" + +[[OpenSSL_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "3da7367955dcc5c54c1ba4d402ccdc09a1a3e046" +uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" +version = "3.0.13+1" [[OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -244,25 +432,37 @@ uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" version = "0.5.5+0" [[OrderedCollections]] -git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.4.1" +version = "1.6.3" + +[[PCRE2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" +version = "10.42.0+1" [[Parsers]] -deps = ["Dates"] -git-tree-sha1 = "9d8c00ef7a8d110787ff6f170579846f776133a9" +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.0.4" +version = "2.8.1" [[Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.10.0" + +[[PrecompileTools]] +deps = ["Preferences"] +git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" +uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +version = "1.2.1" [[Preferences]] deps = ["TOML"] -git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a" +git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6" uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.2.2" +version = "1.4.3" [[Printf]] deps = ["Unicode"] @@ -273,33 +473,47 @@ deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[Random]] -deps = ["Serialization"] +deps = ["SHA"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [[RecipesBase]] -git-tree-sha1 = "44a75aa7a527910ee3d1751d1f0e4148698add9e" +deps = ["PrecompileTools"] +git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "1.1.2" +version = "1.3.4" [[Reexport]] git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" version = "1.2.2" +[[RegistryInstances]] +deps = ["LazilyInitializedFields", "Pkg", "TOML", "Tar"] +git-tree-sha1 = "ffd19052caf598b8653b99404058fce14828be51" +uuid = "2792f1a3-b283-48e8-9a74-f99dce5104f3" +version = "0.1.0" + [[Requires]] deps = ["UUIDs"] -git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621" +git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.1.3" +version = "1.3.0" [[SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" + +[[SauterSchwab3D]] +deps = ["FastGaussQuadrature", "GrundmannMoeller", "LinearAlgebra", "ShunnHamQuadrature", "StaticArrays"] +git-tree-sha1 = "87242fb25711b1f9eaa45506d8b5e6e0b50f086a" +uuid = "0a13313b-1c00-422e-8263-562364ed9544" +version = "0.1.4" [[SauterSchwabQuadrature]] -deps = ["CompScienceMeshes", "FastGaussQuadrature", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "567198b98952f11178854bce14ee1caa663b7652" +deps = ["FastGaussQuadrature", "LinearAlgebra", "StaticArrays"] +git-tree-sha1 = "13e353c9768ef99e21f5f618795c589a9c6aa4e7" uuid = "535c7bfe-2023-5c1d-b712-654ef9d93a38" -version = "2.1.2" +version = "2.3.1" [[Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" @@ -308,47 +522,89 @@ uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" deps = ["Distributed", "Mmap", "Random", "Serialization"] uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" +[[ShunnHamQuadrature]] +deps = ["LinearAlgebra", "StaticArrays"] +git-tree-sha1 = "dfa53166b13cd6f352d54c99a24124321ef95282" +uuid = "164309f2-5039-4884-b6c7-6da8aa5c66ad" +version = "0.1.0" + [[Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" [[SparseArrays]] -deps = ["LinearAlgebra", "Random"] +deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +version = "1.10.0" -[[SparseMatrixDicts]] -deps = ["LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "6ad782435088b00f7abdd4b6ae79fa522cc18758" -uuid = "5cb6c4b0-9b79-11e8-24c9-f9621d252589" -version = "0.2.4" +[[Sparspak]] +deps = ["Libdl", "LinearAlgebra", "Logging", "OffsetArrays", "Printf", "SparseArrays", "Test"] +git-tree-sha1 = "342cf4b449c299d8d1ceaf00b7a49f4fbc7940e7" +uuid = "e56a9233-b9d6-4f03-8d0f-1825330902ac" +version = "0.3.9" [[SpecialFunctions]] -deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "793793f1df98e3d7d554b65a107e9c9a6399a6ed" +deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "e2cfc4012a19088254b3950b85c3c1d8882d864d" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "1.7.0" +version = "2.3.1" + + [SpecialFunctions.extensions] + SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" + + [SpecialFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" [[StaticArrays]] -deps = ["LinearAlgebra", "Random", "Statistics"] -git-tree-sha1 = "3240808c6d463ac46f1c1cd7638375cd22abbccb" +deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] +git-tree-sha1 = "bf074c045d3d5ffd956fa0a461da38a44685d6b2" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.2.12" +version = "1.9.3" -[[Statistics]] -deps = ["LinearAlgebra", "SparseArrays"] -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + [StaticArrays.extensions] + StaticArraysChainRulesCoreExt = "ChainRulesCore" + StaticArraysStatisticsExt = "Statistics" + + [StaticArrays.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[StaticArraysCore]] +git-tree-sha1 = "36b3d696ce6366023a0ea192b4cd442268995a0d" +uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" +version = "1.4.2" + +[[SuiteSparse]] +deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] +uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" + +[[SuiteSparse_jll]] +deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "7.2.1+1" [[TOML]] deps = ["Dates"] uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.3" [[Tar]] deps = ["ArgTools", "SHA"] uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" [[Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +[[TranscodingStreams]] +git-tree-sha1 = "71509f04d045ec714c4748c785a59045c3736349" +uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" +version = "0.10.7" +weakdeps = ["Random", "Test"] + + [TranscodingStreams.extensions] + TestExt = ["Test", "Random"] + [[UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" @@ -358,18 +614,32 @@ uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" [[WiltonInts84]] deps = ["LinearAlgebra"] -git-tree-sha1 = "b7374c784a208b377157146e3bdd94287e41f95a" +git-tree-sha1 = "9d61cac63c100e936194e5db8613e33572fd2bb8" uuid = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" -version = "0.2.2" +version = "0.2.5" [[Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.2.13+1" + +[[gmsh_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "9554bb1cad1926e7d3afb68b0ab117d0b9bb73ee" +uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" +version = "4.9.3+0" + +[[libblastrampoline_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.8.0+1" [[nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.52.0+1" [[p7zip_jll]] deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.4.0+2" From 43f9c3f5020e3df492493f1a86ef5ed33c3a02b2 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 15 Apr 2024 13:51:05 +0200 Subject: [PATCH 352/528] Add windows for CI --- .github/workflows/CI.yml | 2 +- src/integralop.jl | 38 ++++++++++++++++++------------------- test/test_hh3d_nearfield.jl | 1 + 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index fd462381..8d565e59 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -17,9 +17,9 @@ jobs: version: - '1.6' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'. - '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia. - - 'nightly' os: - ubuntu-latest + - windows-lastest arch: - x64 steps: diff --git a/src/integralop.jl b/src/integralop.jl index b57b7e6a..a0287db6 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -29,25 +29,25 @@ for `I` and `J` permutations of `1:numfunctions(test_space)` and function blockassembler end -""" - quadrule(operator,test_refspace,trial_refspace,p,test_element,q_trial_element, qd) - -Returns an object that contains all the dynamic (runtime) information that -defines the integration strategy that will be used by `momintegrals!` to compute -the interactions between the local test/trial functions defined on the specified -geometric elements. The indices `p` and `q` refer to the position of the test -and trial elements as encountered during iteration over the output of -`geometry`. - -The last argument `qd` provides access to all precomputed data required for -quadrature. For example it might be desirable to precompute all the quadrature -points for all possible numerical quadrature schemes that can potentially be -required during matrix assembly. This makes sense, since the number of point is -order N (where N is the number of faces) but these points will appear in N^2 -computations. Precomputation requires some extra memory but can save a lot on -computation time. -""" -function quadrule end +# """ +# quadrule(operator,test_refspace,trial_refspace,p,test_element,q_trial_element, qd) + +# Returns an object that contains all the dynamic (runtime) information that +# defines the integration strategy that will be used by `momintegrals!` to compute +# the interactions between the local test/trial functions defined on the specified +# geometric elements. The indices `p` and `q` refer to the position of the test +# and trial elements as encountered during iteration over the output of +# `geometry`. + +# The last argument `qd` provides access to all precomputed data required for +# quadrature. For example it might be desirable to precompute all the quadrature +# points for all possible numerical quadrature schemes that can potentially be +# required during matrix assembly. This makes sense, since the number of point is +# order N (where N is the number of faces) but these points will appear in N^2 +# computations. Precomputation requires some extra memory but can save a lot on +# computation time. +# """ +# function quadrule end """ diff --git a/test/test_hh3d_nearfield.jl b/test/test_hh3d_nearfield.jl index 6081d7c7..a8b4e74c 100644 --- a/test/test_hh3d_nearfield.jl +++ b/test/test_hh3d_nearfield.jl @@ -205,6 +205,7 @@ end ρ_INPSL = M_INPSL \ (-gN) pts = meshsphere(0.8 * r, 0.8 * 0.6 * r).vertices # sphere inside on which the potential and field are evaluated + @show length(pts) pot_IDPDL = potential(HH3DDoubleLayerNear(im * k), pts, ρ_IDPDL, X1; type=ComplexF64) From 0e27df5f7703104f1b694834da9c3cddd4f7a5e6 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 15 Apr 2024 16:57:17 +0200 Subject: [PATCH 353/528] remove win from CI (too slow), relax error bounds hh3d --- .github/workflows/CI.yml | 1 - test/test_hh3d_nearfield.jl | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8d565e59..0331687a 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -19,7 +19,6 @@ jobs: - '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia. os: - ubuntu-latest - - windows-lastest arch: - x64 steps: diff --git a/test/test_hh3d_nearfield.jl b/test/test_hh3d_nearfield.jl index a8b4e74c..99cc5a53 100644 --- a/test/test_hh3d_nearfield.jl +++ b/test/test_hh3d_nearfield.jl @@ -228,7 +228,7 @@ end @test err_IDPDL_pot < 0.005 @test err_INPSL_pot < 0.002 - @test err_IDPDL_field < 0.008 + @test err_IDPDL_field < 0.0095 @test err_INPSL_field < 0.002 #end From b70ac183315fc1bd9af7b8bf5a2f79926a361c31 Mon Sep 17 00:00:00 2001 From: krcools Date: Mon, 15 Apr 2024 17:20:09 +0200 Subject: [PATCH 354/528] test_tdefie_irk uses a prebuilt mesh --- test/assets/sphere45.in | 323 ++++++++++++++++++++++++++++++++++++++++ test/test_tdefie_irk.jl | 3 +- 2 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 test/assets/sphere45.in diff --git a/test/assets/sphere45.in b/test/assets/sphere45.in new file mode 100644 index 00000000..f1d63191 --- /dev/null +++ b/test/assets/sphere45.in @@ -0,0 +1,323 @@ +1 +109 212 +0.0 0.0 0.0 +1.0 0.0 0.0 +0.0 1.0 0.0 +-1.0 0.0 0.0 +0.0 -1.0 0.0 +0.0 0.0 1.0 +0.0 0.0 -1.0 +0.923879532082714 0.3826834333997559 0.0 +0.7071067795767625 0.7071067827963327 0.0 +0.3826834312295727 0.9238795329816333 0.0 +-0.3826834333997559 0.923879532082714 0.0 +-0.7071067827963327 0.7071067795767625 0.0 +-0.9238795329816333 0.3826834312295727 0.0 +-0.923879532082714 -0.3826834333997559 0.0 +-0.7071067795767625 -0.7071067827963327 0.0 +-0.3826834312295727 -0.9238795329816333 0.0 +0.3826834333997559 -0.923879532082714 0.0 +0.7071067827963327 -0.7071067795767625 0.0 +0.9238795329816333 -0.3826834312295727 0.0 +0.0 0.3826834333997559 0.923879532082714 +0.0 0.7071067827963327 0.7071067795767625 +0.0 0.9238795329816333 0.3826834312295727 +0.0 0.923879532082714 -0.3826834333997559 +0.0 0.7071067795767625 -0.7071067827963327 +0.0 0.3826834312295727 -0.9238795329816333 +0.0 -0.3826834333997559 -0.923879532082714 +0.0 -0.7071067827963327 -0.7071067795767625 +0.0 -0.9238795329816333 -0.3826834312295727 +0.0 -0.923879532082714 0.3826834333997559 +0.0 -0.7071067795767625 0.7071067827963327 +0.0 -0.3826834312295727 0.9238795329816333 +0.7712990324977224 -0.5261836316964337 -0.3580901956251096 +0.7832272477767324 0.5233357175207212 -0.3356706795464225 +0.3356706794722104 -0.183771065508847 -0.923879532419897 +0.3298749710844383 0.7884309806178305 -0.5191908052479812 +0.3364914460521806 -0.5356371469604048 -0.7745103960114611 +0.4943952293272733 -0.7818671890563442 -0.3798118690820019 +0.6320232753776562 -0.3163329681330331 -0.7074461341000068 +0.9179912585278028 0.1751715968730272 -0.3558187191752205 +0.7153911295286901 0.2972003361018707 -0.632366580404594 +0.9247531464560557 -0.2034408415038567 -0.3216262460183055 +0.3182620562185089 0.5161949160681655 -0.7951427998774474 +0.2034339032461793 0.191010725638466 -0.9602757675277923 +0.3318408257257821 -0.7290341394220538 -0.5986575731908655 +0.5143125430732531 0.08759839546161578 -0.8531173009323357 +0.5429324605256425 -0.5927866926067963 -0.5948346664207352 +0.7870222775749804 0.001326899353937282 -0.6169231507560687 +0.5709878661731511 0.6078735380892875 -0.5517813139041982 +0.5481592105556712 0.79368928263904 -0.2637779416609913 +0.2458184845179587 0.9376281485435064 -0.2458184853264101 +0.3356706784848729 -0.1837710644888045 0.9238795329815229 +0.3808964856541316 0.5323383179218478 0.7559985333904224 +0.9238795324198404 0.1837710655050317 0.3356706794744551 +0.7981217874283407 -0.4624241295859055 0.3862195448302793 +0.3239596594514724 -0.7578760046838178 0.5662809378502687 +0.3379819903774152 0.7843488125142359 0.5201587397016689 +0.7746503893874388 0.54404208556427 0.3223894901462653 +0.6762424833857329 0.3096909027609308 0.6684217593790739 +0.7952068118384303 -0.1011561765969377 0.5978407432937709 +0.9361182415423337 -0.1795770587008401 0.3023817419092033 +0.5777467983327468 -0.4173250611831379 0.7014616385268339 +0.2994166271545614 0.2150136179002493 0.9295799199105109 +0.5623828314586349 -0.7723464303375097 0.2953075387210339 +0.2757249103344169 -0.5179234431073846 0.809772240139701 +0.577313184287358 -0.02857822908601 0.8160225315947462 +0.5386170963264684 0.795865112582042 0.2765688813290775 +0.5745678182057902 0.6089374928036108 0.5468701419352591 +0.2640750467215406 0.9279472313776503 0.2630176904270309 +0.26349397886833 -0.9286880418750045 0.2609778610888406 +-0.7782808169164066 -0.5197822937894205 -0.3522858740894094 +-0.7832272498784564 0.5233357150134625 -0.335670678551431 +-0.3356706794739734 -0.1837710655065382 -0.9238795324197158 +-0.5233357175261585 0.7832272477702242 -0.3356706795531307 +-0.3223894903743268 -0.5440420859519188 -0.7746503890202773 +-0.54510520194119 0.5451051986321889 -0.636962040659266 +-0.7050491528145805 0.3255910057135039 -0.629996975479971 +-0.9313698262072214 -0.2067000597440215 -0.2997087455056128 +-0.769142567051783 -0.2521831216418175 -0.5872166420563893 +-0.4625858909497461 -0.7952774519371975 -0.3918520995662882 +-0.5066801882500737 0.3013212963140176 -0.8077627518167138 +-0.5595946567772138 -0.5549240882847868 -0.6155591574720422 +-0.2885513214281583 0.1343905017269157 -0.9479859323574646 +-0.6326688931838378 0.01593457962769268 -0.7742584586361497 +-0.294002442557577 0.5074470400059689 -0.8099753486121409 +-0.2206785158701794 -0.8109710613787934 -0.5418735371278878 +-0.9037335009357622 0.1116491840495477 -0.4132798313340016 +-0.2761954901451649 0.7695834710164464 -0.5757233123313235 +-0.2657595433294145 0.9261685617758262 -0.2675512293140113 +-0.5610283527690639 -0.3131152481702204 -0.7662936961456306 +-0.8986511462892972 -0.2234519520144048 0.3774855525896111 +-0.7832272498729063 0.5233357150155545 0.3356706785611194 +-0.4063938126531793 0.1675901986980527 0.898196857229839 +-0.3862195463776329 -0.4624241289548164 0.7981217870452071 +-0.5163412162948556 0.7883957432990804 0.3344008078684016 +-0.3223894901399288 0.5440420855535103 0.7746503893976325 +-0.5613920645927912 -0.7538101640409378 0.3414808726719812 +-0.6259579262136886 0.3574127511108542 0.6931325991133507 +-0.5654909040789513 -0.2106776780151426 0.7973925967740848 +-0.2733654792854793 -0.2010368837014314 0.9406675747184216 +-0.6948473160149551 -0.4285218225779893 0.5775432927506401 +-0.7787080013966404 -0.546208098467257 0.3086592971702507 +-0.9346316977779511 0.1510686554600859 0.3219345443505349 +-0.2953075384818359 -0.7723464300921336 0.5623828319212242 +-0.2762115444539761 0.7961810039040885 0.5383335320529707 +-0.788420074759952 0.01466700895224715 0.6149623277599258 +-0.5809154623227699 0.5832144663068862 0.5678011200452993 +-0.2626771544471102 0.9281258472912962 0.2637861333000328 +-0.2665923219327522 -0.9241423246462652 0.2736594556815108 +-0.8002939633135487 0.3205923101786786 0.5067051834530513 +72 89 83 +78 86 83 +83 89 78 +81 89 74 +78 89 81 +78 81 70 +77 78 70 +77 86 78 +4 86 77 +4 77 14 +14 77 70 +14 70 15 +70 81 79 +70 79 15 +15 79 16 +16 79 28 +79 85 28 +16 28 5 +26 72 7 +72 82 7 +72 83 82 +74 89 72 +26 74 72 +27 74 26 +27 85 74 +74 85 81 +81 85 79 +28 85 27 +26 36 27 +36 44 27 +44 46 37 +36 46 44 +27 44 28 +28 17 5 +28 44 37 +28 37 17 +37 46 32 +18 37 32 +17 37 18 +18 32 19 +32 46 38 +38 46 36 +34 45 38 +45 47 38 +38 47 41 +41 47 39 +38 41 32 +32 41 19 +19 41 2 +2 41 39 +34 36 26 +34 38 36 +7 34 26 +25 43 7 +7 43 34 +43 45 34 +42 45 43 +42 43 25 +2 39 8 +8 39 33 +39 40 33 +39 47 40 +40 47 45 +40 48 33 +42 48 40 +40 45 42 +8 33 9 +9 49 10 +33 49 9 +48 49 33 +35 49 48 +35 50 49 +49 50 10 +10 50 3 +3 50 23 +23 50 35 +23 35 24 +35 48 42 +35 42 24 +24 42 25 +25 84 24 +84 87 24 +24 87 23 +73 87 75 +75 87 84 +75 80 76 +75 76 71 +83 86 76 +80 83 76 +82 83 80 +7 82 25 +82 84 25 +80 84 82 +75 84 80 +13 86 4 +71 86 13 +76 86 71 +12 73 71 +73 75 71 +12 71 13 +11 73 12 +11 88 73 +73 88 87 +87 88 23 +3 88 11 +23 88 3 +3 107 22 +11 107 3 +22 107 104 +104 107 94 +94 107 11 +12 94 11 +13 91 12 +91 106 94 +91 94 12 +91 109 106 +102 109 91 +13 102 91 +4 102 13 +95 97 92 +20 95 92 +20 92 6 +97 105 92 +97 109 105 +105 109 102 +106 109 97 +95 106 97 +104 106 95 +94 106 104 +22 104 21 +21 104 95 +21 95 20 +20 52 21 +52 56 21 +52 67 56 +21 56 22 +56 68 22 +22 68 3 +3 68 10 +10 68 66 +66 68 56 +56 67 66 +66 67 57 +9 66 57 +10 66 9 +9 57 8 +57 67 58 +58 67 52 +58 65 59 +62 65 58 +58 59 53 +57 58 53 +8 57 53 +8 53 2 +20 62 52 +52 62 58 +51 62 6 +6 62 20 +31 51 6 +51 65 62 +51 64 61 +31 64 51 +53 60 2 +2 60 19 +19 60 54 +54 60 59 +59 60 53 +61 65 51 +59 65 61 +59 61 54 +55 63 61 +19 54 18 +18 63 17 +54 63 18 +61 63 54 +55 69 63 +63 69 17 +17 69 5 +5 69 29 +29 69 55 +29 55 30 +61 64 55 +55 64 30 +30 64 31 +30 103 29 +100 103 93 +93 103 30 +31 93 30 +31 99 93 +93 99 98 +98 99 92 +92 99 6 +6 99 31 +5 108 16 +29 108 5 +103 108 29 +96 108 103 +16 108 96 +96 101 15 +16 96 15 +100 101 96 +15 101 14 +14 101 90 +14 90 4 +90 102 4 +90 105 102 +90 101 100 +98 105 100 +100 105 90 +92 105 98 +98 100 93 +96 103 100 diff --git a/test/test_tdefie_irk.jl b/test/test_tdefie_irk.jl index 93d935e8..053982c7 100644 --- a/test/test_tdefie_irk.jl +++ b/test/test_tdefie_irk.jl @@ -4,7 +4,8 @@ using StaticArrays using LinearAlgebra using Test -Γ = meshsphere(radius=1.0, h=0.45) +# Γ = meshsphere(radius=1.0, h=0.45) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../test/assets/sphere45.in")) X = raviartthomas(Γ) sol = 1.0 # Speed of light (for sake of simplicity, set to one) From ba09d4f065f199ff2efb6361d361989b08ea59af Mon Sep 17 00:00:00 2001 From: krcools Date: Mon, 15 Apr 2024 18:23:16 +0200 Subject: [PATCH 355/528] mesh for test_hh3d_nearfield prebuilt --- test/assets/sphere_rad=10_h=2.in | 1211 ++++++++++++++++++++++++++++++ test/test_hh3d_nearfield.jl | 34 +- 2 files changed, 1232 insertions(+), 13 deletions(-) create mode 100644 test/assets/sphere_rad=10_h=2.in diff --git a/test/assets/sphere_rad=10_h=2.in b/test/assets/sphere_rad=10_h=2.in new file mode 100644 index 00000000..4190f2c8 --- /dev/null +++ b/test/assets/sphere_rad=10_h=2.in @@ -0,0 +1,1211 @@ +1 +405 804 +0.0 0.0 0.0 +10.0 0.0 0.0 +0.0 10.0 0.0 +-10.0 0.0 0.0 +0.0 -10.0 0.0 +0.0 0.0 10.0 +0.0 0.0 -10.0 +9.80785280299685 1.95090322536687 0.0 +9.238795320462739 3.8268343348773 0.0 +8.314696113681087 5.555702344180853 0.0 +7.071067795942936 7.071067827788014 0.0 +5.555702316559705 8.314696132136948 0.0 +3.826834312886675 9.238795329571554 0.0 +1.950903214109354 9.807852805236108 0.0 +-1.95090322536687 9.80785280299685 0.0 +-3.8268343348773 9.238795320462739 0.0 +-5.555702344180853 8.314696113681087 0.0 +-7.071067827788014 7.071067795942936 0.0 +-8.314696132136948 5.555702316559705 0.0 +-9.238795329571554 3.826834312886675 0.0 +-9.807852805236108 1.950903214109354 0.0 +-9.80785280299685 -1.95090322536687 0.0 +-9.238795320462739 -3.8268343348773 0.0 +-8.314696113681087 -5.555702344180853 0.0 +-7.071067795942936 -7.071067827788014 0.0 +-5.555702316559705 -8.314696132136948 0.0 +-3.826834312886675 -9.238795329571554 0.0 +-1.950903214109354 -9.807852805236108 0.0 +1.95090322536687 -9.80785280299685 0.0 +3.8268343348773 -9.238795320462739 0.0 +5.555702344180853 -8.314696113681087 0.0 +7.071067827788014 -7.071067795942936 0.0 +8.314696132136948 -5.555702316559705 0.0 +9.238795329571554 -3.826834312886675 0.0 +9.807852805236108 -1.950903214109354 0.0 +0.0 1.95090322536687 9.80785280299685 +0.0 3.8268343348773 9.238795320462739 +0.0 5.555702344180853 8.314696113681087 +0.0 7.071067827788014 7.071067795942936 +0.0 8.314696132136948 5.555702316559705 +0.0 9.238795329571554 3.826834312886675 +0.0 9.807852805236108 1.950903214109354 +0.0 9.80785280299685 -1.95090322536687 +0.0 9.238795320462739 -3.8268343348773 +0.0 8.314696113681087 -5.555702344180853 +0.0 7.071067795942936 -7.071067827788014 +0.0 5.555702316559705 -8.314696132136948 +0.0 3.826834312886675 -9.238795329571554 +0.0 1.950903214109354 -9.807852805236108 +0.0 -1.95090322536687 -9.80785280299685 +0.0 -3.8268343348773 -9.238795320462739 +0.0 -5.555702344180853 -8.314696113681087 +0.0 -7.071067827788014 -7.071067795942936 +0.0 -8.314696132136948 -5.555702316559705 +0.0 -9.238795329571554 -3.826834312886675 +0.0 -9.807852805236108 -1.950903214109354 +0.0 -9.80785280299685 1.95090322536687 +0.0 -9.238795320462739 3.8268343348773 +0.0 -8.314696113681087 5.555702344180853 +0.0 -7.071067795942936 7.071067827788014 +0.0 -5.555702316559705 8.314696132136948 +0.0 -3.826834312886675 9.238795329571554 +0.0 -1.950903214109354 9.807852805236108 +7.484525235800258 -3.884914731954154 -5.374878558638759 +8.640432252084782 4.616001386420454 -2.008845862105294 +1.694959659960681 2.860845128906918 -9.430942524451918 +7.576281374022892 -6.295881151996815 -1.721000017886417 +1.716699998323463 7.604323764034456 -6.263162237041051 +1.694959664352379 -6.252141737270096 -7.618256718784404 +9.80785280383656 0.9659891891984269 -1.69495966460181 +1.694959659721775 6.252141707960549 -7.618256743868375 +3.386120079414433 6.62080808349219 -6.685737889668639 +1.694959664602064 -0.9659891891983962 -9.80785280383652 +1.678517616006651 -2.888596932216308 -9.425422344698717 +4.628741954402159 -8.69433125572815 -1.727238239278489 +3.340752674331311 -1.866291263567569 -9.238848872477543 +6.458793765802624 7.399888684600718 -1.877666249988903 +3.191006637361629 7.970726703993889 -5.126889159183445 +4.884461856964811 6.810937280894969 -5.454646251002069 +4.874452501062273 5.398025373279759 -6.863019370824746 +6.34842119745199 5.426380392209262 -5.500176737050282 +6.179264675812918 3.958137776738934 -6.793366868247498 +7.491809585589161 3.899344360395977 -5.354241560886327 +7.229760271693815 2.349409388994967 -6.496987143033305 +5.778981370742834 2.350120932879847 -7.81538904453561 +6.714694636539958 0.6681765864766767 -7.380136583241931 +8.025539080989324 0.64983788898619 -5.93029789956213 +1.297918943169261 -8.693914874333723 -4.767730128142872 +9.373403397565864 -2.852202756983408 -2.001061763054043 +2.130400536751212 9.525628225140219 -2.173430576168048 +3.337980475624946 -0.04450701977387266 -9.426341043561786 +3.418835008903087 -3.651041314896926 -8.659183824057228 +5.088323258359434 -2.485065203142772 -8.2421730966152 +4.803831259521475 -4.408945460135892 -7.581847080994936 +2.216340265586997 -9.502447736670753 -2.188909052240096 +7.362819406824999 -1.032535330145154 -6.687507844816572 +8.50992197863405 -0.924511780444419 -5.169768455683545 +7.516214470580177 5.279466217989938 -3.953954537591974 +9.031229940426881 0.6274207606611665 -4.247732212867056 +4.703853507518662 7.942047134518326 -3.846771307589472 +9.295786629399853 -1.178488338118462 -3.492780579651362 +8.534912612782197 3.669564225687408 -3.699941227334102 +4.082949003212194 8.910403900783622 -1.983489289627897 +6.397535389615995 -3.404256264676478 -6.890760496710206 +6.050078815375487 -5.044946925739978 -6.159955912521735 +4.50679199659493 -5.996435354633118 -6.612986385675831 +5.547156310147217 -6.589328324745058 -5.080335530209238 +3.868205087304074 -7.418927582292483 -5.47690632850834 +4.865796494866782 -7.912727679021323 -3.703075093512411 +4.950237856754044 0.7245027537233326 -8.658535726172552 +4.384918808574644 2.55119594948837 -8.617649695219832 +6.581307863960296 -6.714431337460461 -3.406288099134203 +1.895741315324868 8.975632480878645 -3.980475654185891 +7.508645978365221 6.224342766675042 -2.208572546809337 +9.379360363764452 2.77280877972829 -2.083058001522305 +7.063701496654174 -5.452886021206812 -4.513330832754557 +8.276836523799368 -4.285653971100762 -3.623140515945948 +6.275549513143593 6.848453594682692 -3.703533673339683 +1.614017657052355 0.9926510606442275 -9.818838570550241 +1.963853131727953 -7.710740195274058 -6.057042720502084 +9.829594898705071 -0.8938976726943376 -1.606241288879998 +6.167552236300564 -7.6513143719413 -1.848969387044691 +1.474179217245597 4.637668624871222 -8.736064638115163 +4.623729262325734 3.853329286042339 -7.985798715347211 +8.530662103459711 -4.823434248502576 -1.990549202348118 +1.692419959116914 -4.65955796856348 -8.684712673403727 +3.298518568992726 -5.361089261570473 -7.770360170512301 +3.136448370998263 5.116171827811228 -7.999217301984805 +8.374065915881594 2.264035938667739 -4.974852892788996 +5.966980458298531 -0.8328576485417362 -7.98132146624536 +7.717753222218721 -5.518988501858001 -3.158647039048794 +8.582996958897814 -2.604452710607588 -4.421423897542474 +3.373606642630378 -8.944212118799896 -2.935957730403023 +9.511434314250431 0.5222788131548508 -3.042998870691376 +9.18874856174815 1.907123951500339 -3.453951085725598 +4.614340964629106 -0.8886535072375961 -8.827125942582901 +3.152371668898908 3.549517718820688 -8.801333809423518 +3.060499228520063 -6.659110884031874 -6.803645104383116 +7.661250523169691 -2.325725292532114 -5.991347284618756 +3.276073752854909 8.770281569229471 -3.514185817837325 +2.929169316224433 1.809101615716378 -9.38866968536553 +2.904121341587593 -8.462401679424488 -4.466971798591201 +3.213845291127491 -9.371847222130508 -1.356347333739524 +1.3074961364713 -9.327300160576597 -3.360346048790275 +5.301932935228361 8.106864802909042 -2.483596226783921 +1.265689599129985 -9.840279039602926 -1.251774045666988 +1.094723915244614 9.877211828949552 -1.114569888096105 +1.291192931326633 8.493646571743604 -5.117693692318861 +6.543258395339659 -2.004386637289248 -7.291653021097027 +7.589455411460426 -3.679143044266371 5.372529480354709 +8.640432252644828 4.616001390121554 2.008845851191897 +1.694959669238944 2.860845145080079 9.430942517878325 +7.618256743864916 -6.252141707968306 1.694959659708706 +6.450855885595064 7.40977662520076 1.865923017681049 +1.944258897729118 -6.224209280910443 7.581495641760236 +9.807852803836578 0.9659891891984049 1.694959664601724 +4.645760425563159 -8.69160639863923 1.694959668961131 +1.694959659664616 -0.9659891836526907 9.807852805235996 +1.678517608591775 -2.888596919755813 9.425422349837941 +1.694959664299138 6.252141737311508 7.618256718762266 +1.694959659646261 7.618256743894761 6.252141707948871 +3.348410836805147 6.662887710873175 6.662887678796713 +3.340752663294766 -1.866291257542965 9.238848877685339 +3.173818838401166 7.981791228781764 5.120340102097945 +4.884461853472326 6.81093731446191 5.454646212216159 +4.86179525369738 5.430594546821983 6.846282916966912 +6.34666572729495 5.431245799766588 5.497399676983425 +6.178698639502741 3.960147239733798 6.792710575450402 +7.493491623847692 3.885680101472035 5.361816243806642 +7.231225464333012 2.311082762545684 6.509091699203969 +5.778981392942154 2.350120976421075 7.815389015027597 +6.716669873758189 0.6629224560235365 7.37881288719594 +8.025539100298753 0.6498379366665192 5.930297868205684 +1.694959668959868 -8.691606398640305 4.645760425561607 +9.43094252445263 -2.860845128902828 1.694959659963622 +2.158837975923562 9.522543630113772 2.158837975923599 +1.655794917741912 -9.427778990068859 2.894015636584617 +3.372235135751129 0.1662498997649503 9.412778078762598 +3.312630296447432 -3.672115694066237 8.69149278596324 +4.857693151003588 -2.590754852425246 8.34810197262395 +4.688766808881241 -4.620681279858319 7.527600568700528 +7.342489751578588 -1.07042207993445 6.703882518268963 +8.539945127764579 -0.9881245848899494 5.108125587679505 +7.516325072258296 5.279000505085103 3.954366077572048 +9.031229952531442 0.6274208065770261 4.247732180349081 +4.618878064629182 7.997047975029581 3.835777510383348 +9.35401251923927 -1.102512716947698 3.359600496916807 +8.55807778731667 3.504077751378756 3.80535723665064 +3.630526799841694 -8.669484529393333 3.414573641062798 +3.309567566962856 -7.801040017605713 5.309476166574423 +5.050187332916818 -7.506677290556895 4.259742240779825 +4.706747804391623 -6.491576585321064 5.975446304901917 +6.262206837984788 -6.22968886421301 4.687829153605855 +4.08294896806108 8.91040392210775 1.983489266191214 +6.48635910743304 -2.909813141696293 7.032789845419241 +5.138054524935237 0.6512541194018134 8.554312583180176 +4.213519187994706 2.117033438066333 8.818413999949735 +6.501181305435489 -6.907052733859684 3.166585568958708 +1.895741304876226 8.975632489437164 3.980475639863435 +6.304304567705763 -4.832825007115868 6.074499680483962 +7.507276609571816 6.226747532196535 2.20644806819926 +9.379360361691266 2.772808793944486 2.083057991933696 +6.210920431397934 6.865624943702563 3.779902317157534 +1.582420592745625 -7.618157864853675 6.281688930042835 +1.614017652640312 0.9926510633828588 9.818838570998626 +9.80346529711746 -0.7536984109551983 1.823185913048167 +8.745924670883912 -2.920510881845144 3.87032528868676 +8.654891396865057 -4.195609807250448 2.73673404878426 +1.594928387346465 4.764488882519325 8.646146490060538 +4.623729279363438 3.853329325197461 7.985798686589247 +6.376427261889495 -7.538739773316453 1.583849110252942 +3.513915083648801 -5.937217793516878 7.238911911140516 +1.565327941910373 -4.700751607442105 8.686350370518397 +3.268353313412033 5.199765513252965 7.891787201001666 +8.380653272860494 2.203916657326835 4.990731618475391 +5.955560356493085 -1.002749327177809 7.970307059769556 +7.62236586211518 -5.545957317965076 3.337947886257473 +2.908322756154376 -9.423831201848772 1.65319818082894 +3.13597678594112 3.430458311375293 8.854242224603672 +7.243131812391905 -4.967762442408584 4.781043595713291 +9.228192322532836 1.770362317530412 3.421444683611643 +9.558087061835261 0.3010082260922371 2.924442812947828 +4.615138251433526 -0.9418995456048308 8.821186097466967 +5.388427711314114 -7.944601840809216 2.801454691932922 +7.813591740087231 -2.247876327543923 5.821927183957925 +3.258194796900417 8.780235813939173 3.505941487970237 +1.391501843979358 -9.804460476558825 1.391501843979356 +2.738173449283312 1.884002500997701 9.431486666367839 +8.093350344670409 -4.309767073241091 3.990437065388993 +5.301553274031107 8.118345286400222 2.446630886217478 +2.946926312522238 -5.021931343995751 8.129934248489166 +2.908276692001961 -7.000026053627124 6.522389280877033 +1.037480177520195 9.891761045438042 1.037640738024088 +1.316374247500741 8.491715151519735 5.114482635220011 +8.735480837212208 -4.72741157815794 1.158859056743549 +-5.374878556949718 3.884914724699531 -7.484525240778797 +-2.008845851620726 -4.616001387300988 -8.640432254051968 +-9.430942517878332 -2.860845145080152 -1.694959669238789 +-1.709555016031904 6.401141960527851 -7.490180454991041 +-6.252141707968359 -7.618256743864762 -1.694959659709205 +-7.618256743864769 6.252141707968363 -1.694959659709164 +-1.694959664601897 -0.9659891891983583 -9.807852803836553 +-1.694959668943105 8.691606398645341 -4.645760425558302 +-9.807852805236152 0.9659891836528632 -1.694959659663621 +-9.42542234983937 2.888596919751108 -1.678517608591853 +-7.618256718791448 -6.252141737261187 -1.69495966435359 +-6.662887678816417 -6.662887710757489 -3.348410836996143 +-9.23884887768555 1.866291257528204 -3.340752663302424 +-1.909220298133099 -7.648617081923373 -6.15252261981267 +-5.155177515825385 -7.911699573232785 -3.290768093202041 +-5.45464621211022 -6.810937314342715 -4.884461853756839 +-6.846282916917358 -5.430594546609798 -4.86179525400417 +-5.497399667441975 -5.43124579863651 -6.346665736526708 +-6.792710573928432 -3.96014723633121 -6.1786986433568 +-5.361816233406216 -3.885680094806932 -7.493491634745633 +-6.509091686476384 -2.311082751763193 -7.231225479235639 +-7.815389014874922 -2.350120976066626 -5.778981393292771 +-7.378812876720874 -0.6629224511400627 -6.716669885747898 +-5.930297867874392 -0.6498379362895301 -8.025539100574079 +-4.81249152290017 8.626415612974276 -1.557202303571266 +-2.135871451924229 2.782898569782521 -9.364482297017677 +-2.830871992426569 -9.45851564267541 -1.588283853648148 +-9.41277807919409 -0.1662498976392883 -3.372235134651516 +-8.659183837629666 3.651041298807182 -3.418834991709534 +-8.343800929731625 2.590267158576029 -4.865336791241911 +-7.59391435510929 4.365111730742949 -4.82475536637821 +-2.18641178137868 9.502440167744249 -2.218836267210626 +-6.703882501669495 1.07042208854975 -7.342489765478351 +-5.172651312904525 0.9304389119234084 -8.507523836363074 +-3.954366073916201 -5.279000500664388 -7.516325077286485 +-4.247732179956284 -0.6274208062224084 -9.031229952740826 +-3.795257787690934 -7.930211197407921 -4.765266906425845 +-3.338698267487406 1.077152084233651 -9.364445379525032 +-3.805357197840756 -3.50407774708571 -8.558077806331294 +-7.038553059469726 2.890760589124219 -6.488626514555723 +-6.193566453258985 4.92658005685032 -6.112981558332203 +-6.602033182364537 6.031000534397245 -4.476627124639455 +-5.086067527184662 6.574971429234599 -5.558926858096481 +-5.416125392709582 7.424104052501769 -3.943128801861953 +-3.68455843522508 7.911924204552873 -4.881135576771256 +-8.554312583008146 -0.6512541190076759 -5.138054525271608 +-8.818413995692211 -2.117033448829829 -4.213519191497224 +-3.390889515184185 6.734930818427901 -6.56830078230314 +-2.12167074310682 -6.29319756563519 -7.476240877454918 +-2.083057991425826 -2.772808793931354 -9.379360361807938 +-4.513330862451521 5.45288600419915 -7.063701490808601 +-3.60530195997109 4.269328628110605 -8.29304713857481 +-3.706521147702986 -6.796479171732051 -6.330053068485519 +-1.913660425762787 -8.748013728102624 -4.450860544635995 +-3.408096955687423 -8.843674175376385 -3.189718203919417 +-4.597190921082289 -8.7163979381629 -1.700071356945966 +-9.818838571001642 -0.9926510633839429 -1.614017652621297 +-1.89571991462049 0.6803462789504269 -9.795068909713171 +-8.646146483175038 -4.764488898128167 -1.594928378045049 +-7.985798686544285 -3.853329324894388 -4.623729279693669 +-1.930781601493908 7.65396537125016 -6.139128317850673 +-6.280417806791561 7.652711209047571 -1.411511007060566 +-1.995887730336125 4.840996886254995 -8.519458980191356 +-3.000396984770823 5.628306183414012 -7.701934006437716 +-8.684712687221438 4.659557948511561 -1.692419943417625 +-7.753397736237208 5.388924829647754 -3.293070409832998 +-7.891787212241205 -5.199765521593527 -3.268353273003606 +-4.990731574862538 -2.20391663976237 -8.380653303451266 +-7.970307060470303 1.002749316779279 -5.955560357306097 +-1.552135166463686 -9.462757236594953 -2.836741423939901 +-4.137957569339338 2.588283725704439 -8.728006330749356 +-8.854242223403944 -3.430458329637661 -3.135976769351201 +-2.914381321259444 8.946380502785924 -3.386540626312626 +-3.421444644315994 -1.770362310435668 -9.228192338463167 +-6.666706698525027 6.925671604111411 -2.755012636603763 +-2.927967445363524 -0.3123110829432801 -9.55664524958221 +-8.820585902110437 0.9413905837454912 -4.616389077225394 +-5.808140882539739 2.297003518379107 -7.809563004747737 +-1.324292291726 -9.823397907221668 -1.321780421427118 +-9.431486671181649 -1.884002505928987 -2.738173429309438 +-4.366546817555308 8.466696076018525 -3.041106122191419 +-1.307496135174042 9.3273001550238 -3.360346064707906 +-3.321772318766406 9.336591585814478 -1.339360377932518 +-5.572828560608213 7.880564016400061 -2.615395308072933 +-1.269530636930449 9.839706960241353 -1.252381291170076 +-5.374878571566752 -3.884914728589924 7.484525228262488 +-2.008845863581305 4.616001384934471 8.640432252535478 +-9.430942524451918 2.860845128906918 1.69495965996068 +-1.70955498874268 -6.401141969409561 7.490180453629172 +-6.252141737270282 7.618256718784315 1.694959664352093 +-7.618256718784404 -6.252141737270096 1.694959664352379 +-1.69495966460181 0.9659891891984264 9.80785280383656 +-7.618256743868375 6.252141707960551 1.694959659721778 +-6.654981233774063 6.647402589825604 3.394593287404972 +-9.80785280383652 -0.9659891891983954 1.694959664602063 +-9.42542234496626 -2.888596931563689 1.678517615627424 +-1.694959668945985 -8.6916063986437 4.645760425560322 +-9.238848873163763 -1.866291263694047 3.340752672362914 +-1.909220315490329 7.648617077812533 6.152522619536921 +-5.155177552511963 7.911699545659703 3.290768102021899 +-5.454646251271448 6.810937280957861 4.884461856576285 +-6.846282950747401 5.430594510499585 4.86179524670026 +-5.497399707889391 5.431245751808068 6.34666574156576 +-6.792710608820068 3.960147196370969 6.178698630609722 +-5.361816279700077 3.885680052190198 7.493491623719507 +-6.509091726741612 2.311082707663549 7.23122545708557 +-7.815389043480207 2.350120935846277 5.778981370963793 +-7.378812911152689 0.6629224079169684 6.71666985218776 +-5.930297901601751 0.6498378943651693 8.025539079046647 +-4.812491514407762 -8.626415617087982 1.557202307028167 +-2.135871455394768 -2.7828985752925 9.364482294588678 +-2.830872023726072 9.458515632972894 1.588283855641814 +-9.412778078830796 0.1662498889508181 3.372235136093906 +-8.659183828565725 -3.651041318719097 3.418834993402245 +-8.343800927351758 -2.590267176638686 4.865336785706843 +-7.593914345026667 -4.365111744718054 4.82475536960401 +-2.186411775616083 -9.502440171126622 2.218836258403577 +-6.703882533200341 -1.070422129426041 7.342489730730744 +-5.172651353822996 -0.9304389445121214 8.507523807920158 +-3.954366119405836 5.279000466702863 7.51632507720669 +-4.24773221861931 0.6274207654099319 9.031229937391469 +-3.795257830268208 7.930211169315793 4.765266919265583 +-3.338698303533497 -1.077152122803012 9.364445362237078 +-3.80535726344807 3.504077713141948 8.558077791057109 +-7.038553069424285 -2.890760612904486 6.4886264931631 +-6.193566447483134 -4.92658007174417 6.112981552180928 +-6.602033163698644 -6.031000545197974 4.476627137616557 +-5.086067507866664 -6.574971435576019 5.558926868270748 +-5.416125391182664 -7.424104045462543 3.94312881721269 +-3.684558432165985 -7.91192420829659 4.881135573012175 +-8.55431260448588 0.6512540795657944 5.138054494512772 +-8.82247724879782 2.153466988687883 4.186463319208277 +-3.39088948388111 -6.734930828101727 6.568300788544132 +-2.121670775456225 6.293197587071103 7.476240850230643 +-2.083058005690916 2.772808780871042 9.379360362500815 +-4.513330832681101 -5.452886029698087 7.063701490146192 +-3.605301956681974 -4.269328629989363 8.293047139037514 +-3.706521205278666 6.796479128819655 6.330053080846827 +-1.913660453382623 8.748013718462884 4.450860551707344 +-3.408096993934809 8.843674154769358 3.189718220187639 +-4.597190959661578 8.71639791701253 1.700071361062665 +-9.818838570529465 0.9926510585536407 1.614017658464505 +-1.895719920900001 -0.6803462995499572 9.795068907067042 +-6.280417771910004 -7.652711227604057 1.411511061656903 +-1.930781594809315 -7.653965376259936 6.13912831370706 +-8.780920392686157 4.516933059837277 1.578845397832683 +-7.978123647028463 3.849559062994188 4.64009031089316 +-1.995887711159481 -4.840996887387293 8.519458984040542 +-3.000396954110874 -5.628306185706372 7.701934016706556 +-8.684712675864203 -4.659557964904648 1.692419956564349 +-7.753397726555823 -5.388924836096233 3.293070422074845 +-7.902114708597709 5.124329387745007 3.361224696157135 +-4.990731637058082 2.203916605369499 8.380653275457954 +-7.970307083189042 -1.002749332266295 5.955560324294104 +-8.863829617063514 3.378892033896668 3.16474535198921 +-1.552135189646684 9.462757231740394 2.836741427448994 +-4.13795760386107 -2.588283712108062 8.728006318414558 +-2.914381323573151 -8.946380508409229 3.386540609466159 +-3.421444705361096 1.770362287897678 9.228192320153836 +-6.666706677133172 -6.925671598523495 2.755012702415913 +-2.927967481508825 0.3123110603476493 9.556645239246427 +-8.82058591347917 -0.9413906087727384 4.616389050399381 +-5.808140907850898 -2.297003546073666 7.809562977777562 +-1.324292307962778 9.823397904854742 1.321780422750342 +-9.432270542487016 1.879936637208693 2.738267819883979 +-4.366546831208668 -8.466696073387492 3.041106109912366 +-1.307496137571986 -9.327300155131484 3.360346063475979 +-3.321772290255145 -9.336591598485487 1.33936036031507 +-5.572828542137912 -7.880564021938099 2.615395330742148 +-1.269530632042144 -9.83970696226377 1.252381280235589 +252 253 251 +252 254 253 +256 303 255 +259 303 256 +269 271 259 +271 303 259 +257 282 281 +281 304 258 +257 281 258 +258 268 259 +258 259 256 +257 258 256 +254 257 256 +254 256 255 +254 255 253 +252 295 254 +254 295 257 +307 315 282 +238 315 307 +294 307 302 +302 307 295 +295 307 282 +257 295 282 +252 302 295 +281 282 263 +282 315 263 +263 315 292 +263 292 244 +292 315 238 +22 292 238 +4 292 22 +244 292 4 +238 307 294 +22 238 23 +238 294 23 +23 294 24 +294 302 246 +24 294 246 +24 246 25 +247 252 251 +247 302 252 +246 302 247 +247 250 240 +247 251 250 +250 291 240 +25 240 26 +240 291 26 +246 247 240 +25 246 240 +27 262 28 +26 291 27 +27 291 262 +262 291 290 +290 291 250 +290 305 262 +289 290 272 +272 290 250 +251 272 250 +55 289 54 +55 305 289 +56 305 55 +289 305 290 +305 314 262 +56 314 305 +5 314 56 +28 314 5 +262 314 28 +255 270 253 +285 309 242 +50 285 242 +50 242 7 +242 293 7 +242 311 293 +309 311 242 +271 311 309 +271 309 303 +274 285 237 +274 309 285 +255 303 274 +303 309 274 +255 274 270 +270 274 237 +237 285 51 +51 285 50 +52 237 51 +53 284 52 +52 284 237 +249 284 53 +284 288 270 +249 288 284 +251 288 272 +253 288 251 +270 288 253 +237 284 270 +249 289 272 +272 288 249 +54 289 249 +54 249 53 +52 69 53 +52 126 69 +126 127 69 +69 120 53 +69 138 120 +127 138 69 +106 138 127 +106 127 94 +107 108 106 +108 138 106 +120 138 108 +108 142 120 +120 142 88 +53 120 54 +54 120 88 +54 88 55 +88 144 55 +55 144 56 +95 144 133 +142 144 88 +5 146 29 +56 146 5 +144 146 56 +95 146 144 +143 146 95 +29 146 143 +29 143 30 +133 143 95 +75 143 133 +30 143 75 +30 75 31 +107 109 108 +109 142 108 +75 133 109 +133 142 109 +133 144 142 +112 122 109 +107 112 109 +107 116 112 +116 131 112 +112 131 67 +67 122 112 +32 122 67 +75 122 31 +109 122 75 +31 122 32 +32 67 33 +67 131 125 +67 125 33 +117 131 116 +105 116 107 +105 107 106 +105 106 94 +64 139 132 +93 149 104 +93 104 94 +104 105 94 +64 105 104 +64 116 105 +104 149 139 +104 139 64 +96 149 130 +139 149 96 +96 130 86 +130 136 110 +130 149 93 +93 136 130 +132 139 97 +97 139 96 +87 97 96 +99 101 97 +99 134 101 +101 134 121 +89 132 101 +101 132 97 +64 132 117 +64 117 116 +89 125 117 +125 131 117 +117 132 89 +34 125 89 +33 125 34 +34 89 35 +89 121 35 +35 121 2 +101 121 89 +50 74 51 +74 126 51 +51 126 52 +92 126 74 +92 127 126 +94 127 92 +76 93 92 +93 94 92 +76 92 74 +76 136 93 +91 136 76 +110 136 91 +73 91 76 +73 119 91 +73 76 74 +73 74 50 +7 73 50 +7 119 73 +49 119 7 +66 119 49 +66 141 119 +119 141 91 +91 141 110 +110 141 111 +111 141 137 +137 141 66 +111 137 124 +111 124 85 +124 128 80 +124 137 128 +128 137 123 +47 123 48 +48 123 66 +48 66 49 +123 137 66 +81 98 83 +70 115 8 +70 135 115 +121 134 70 +2 121 70 +2 70 8 +8 115 9 +9 65 10 +9 115 65 +65 115 102 +115 135 102 +65 102 98 +98 102 83 +102 129 83 +102 135 129 +129 135 99 +99 135 134 +134 135 70 +87 99 97 +87 129 99 +84 129 87 +110 111 85 +86 110 85 +86 130 110 +87 96 86 +84 87 86 +84 86 85 +84 85 82 +83 84 82 +81 83 82 +81 82 80 +82 124 80 +85 124 82 +83 129 84 +79 81 80 +98 114 65 +65 114 10 +10 114 11 +11 77 12 +77 145 12 +12 145 103 +11 114 77 +114 118 77 +98 118 114 +81 118 98 +79 118 81 +100 118 79 +100 145 118 +118 145 77 +103 140 90 +113 140 78 +100 140 103 +103 145 100 +78 140 100 +78 100 79 +12 103 13 +13 103 90 +13 90 14 +90 147 14 +43 147 90 +3 147 43 +14 147 3 +78 148 113 +90 140 113 +90 113 44 +43 90 44 +113 148 44 +44 148 45 +45 148 68 +45 68 46 +68 148 78 +68 78 72 +79 80 72 +78 79 72 +80 128 72 +68 72 71 +68 71 46 +71 128 123 +71 123 47 +46 71 47 +72 128 71 +298 299 239 +47 298 239 +47 239 46 +239 296 46 +283 296 239 +45 296 243 +46 296 45 +243 296 280 +280 296 283 +280 283 278 +283 299 286 +239 299 283 +283 286 278 +286 299 287 +266 277 276 +277 278 276 +278 286 276 +306 313 236 +276 286 236 +236 313 275 +266 276 275 +275 276 236 +266 275 265 +281 312 304 +304 312 265 +275 304 265 +258 304 268 +275 313 268 +268 304 275 +268 269 259 +268 313 269 +269 313 306 +269 273 271 +269 306 273 +273 306 261 +273 311 271 +293 311 273 +7 293 49 +261 293 273 +49 293 261 +49 261 48 +48 298 47 +261 298 48 +287 298 261 +261 306 287 +287 306 236 +286 287 236 +287 299 298 +263 312 281 +248 312 263 +265 312 248 +248 263 244 +21 244 4 +21 245 244 +245 248 244 +20 245 21 +20 300 245 +19 300 20 +245 300 264 +245 264 248 +264 265 248 +264 301 266 +264 266 265 +300 301 264 +18 297 241 +297 310 241 +310 319 279 +277 279 278 +266 301 277 +277 310 279 +241 310 301 +301 310 277 +241 300 19 +241 301 300 +18 241 19 +279 319 316 +297 319 310 +260 319 297 +260 297 17 +17 297 18 +16 260 17 +16 318 260 +260 318 316 +308 318 267 +15 318 16 +316 319 260 +279 280 278 +279 316 280 +316 318 308 +280 308 243 +280 316 308 +308 317 243 +267 317 308 +44 317 43 +243 317 44 +45 243 44 +15 320 318 +318 320 267 +267 320 317 +317 320 43 +43 320 3 +3 320 15 +15 399 3 +3 399 42 +42 399 391 +391 399 347 +347 399 15 +41 374 40 +41 391 374 +42 391 41 +374 391 375 +374 375 357 +375 391 347 +375 376 335 +357 375 335 +336 357 335 +16 347 15 +347 376 375 +16 376 347 +17 376 16 +18 325 17 +325 376 17 +335 376 325 +19 328 18 +381 387 328 +19 381 328 +328 387 329 +329 337 336 +329 387 337 +329 336 335 +329 335 325 +328 329 325 +18 328 325 +381 390 387 +382 390 367 +342 382 367 +337 387 382 +387 390 382 +323 400 390 +20 381 19 +323 381 20 +21 323 20 +323 390 381 +377 400 323 +21 377 323 +4 377 21 +330 377 4 +348 377 330 +348 400 377 +390 400 367 +367 400 348 +366 367 348 +340 355 338 +355 359 322 +340 359 355 +340 388 359 +359 394 370 +359 370 322 +388 394 359 +322 370 37 +38 322 37 +37 370 36 +36 370 327 +370 394 327 +36 327 6 +327 378 6 +327 396 378 +356 396 394 +394 396 327 +356 394 388 +344 388 341 +354 356 344 +356 388 344 +342 343 341 +343 344 341 +343 353 344 +342 367 366 +366 389 343 +342 366 343 +339 382 342 +339 341 340 +339 342 341 +337 382 339 +337 339 338 +339 340 338 +341 388 340 +337 338 336 +322 369 355 +334 373 369 +369 373 355 +355 373 338 +336 373 357 +338 373 336 +334 374 357 +357 373 334 +40 374 334 +40 334 39 +334 369 39 +39 369 38 +38 369 322 +38 160 39 +38 209 160 +209 214 160 +160 161 39 +160 162 161 +160 214 162 +162 214 166 +162 165 164 +162 166 165 +162 164 161 +164 234 161 +39 161 40 +161 234 40 +40 234 41 +41 234 199 +41 176 42 +41 199 176 +199 226 176 +199 234 164 +3 233 14 +42 233 3 +176 233 42 +14 233 176 +14 176 13 +176 194 13 +13 194 12 +165 186 164 +186 230 194 +194 226 186 +186 226 164 +164 226 199 +176 226 194 +154 230 203 +203 230 186 +165 203 186 +167 203 165 +184 203 167 +201 203 184 +154 203 201 +154 201 11 +194 230 12 +12 230 154 +12 154 11 +11 201 10 +10 201 151 +151 201 184 +166 167 165 +170 215 169 +168 210 171 +166 210 168 +166 168 167 +168 169 167 +168 170 169 +168 171 170 +171 172 170 +172 173 170 +172 182 173 +196 216 172 +171 196 172 +171 197 196 +173 215 170 +185 215 173 +183 185 173 +221 222 156 +185 222 221 +185 221 215 +215 221 188 +169 215 188 +169 188 184 +184 188 151 +188 202 151 +188 221 202 +151 202 9 +10 151 9 +9 202 8 +8 156 2 +156 206 2 +156 222 206 +202 221 156 +8 202 156 +169 184 167 +152 219 209 +36 152 37 +152 209 37 +37 209 38 +209 219 214 +214 219 210 +166 214 210 +171 210 197 +210 219 197 +152 228 219 +197 228 178 +219 228 197 +196 197 178 +178 228 205 +178 205 158 +205 228 152 +36 205 152 +6 205 36 +158 205 6 +63 158 6 +63 159 158 +159 163 158 +163 178 158 +178 223 196 +163 223 178 +180 223 163 +159 179 163 +179 181 180 +179 180 163 +179 231 181 +213 231 179 +159 213 179 +61 213 62 +62 213 159 +62 159 63 +175 206 187 +2 206 35 +35 206 175 +35 175 34 +34 235 33 +175 235 34 +208 235 175 +217 235 208 +207 208 175 +207 229 208 +208 229 217 +220 229 150 +150 229 207 +183 207 187 +187 207 175 +183 225 207 +206 222 187 +187 222 185 +183 187 185 +182 183 173 +182 225 183 +216 223 180 +195 216 180 +196 223 216 +172 216 182 +182 216 195 +195 225 182 +150 225 195 +200 220 150 +195 200 150 +181 200 195 +181 195 180 +207 225 150 +192 200 181 +193 200 192 +193 220 200 +191 193 192 +217 229 220 +153 235 217 +33 235 153 +33 153 32 +32 211 31 +211 224 157 +31 211 157 +153 211 32 +198 211 153 +153 217 198 +217 220 193 +198 217 193 +191 198 193 +198 224 211 +191 224 198 +177 218 189 +189 190 174 +157 224 189 +189 224 191 +189 191 190 +31 157 30 +157 218 30 +189 218 157 +30 218 29 +218 227 29 +177 227 218 +57 227 177 +5 227 57 +29 227 5 +177 189 174 +57 177 58 +58 177 174 +58 174 59 +174 204 59 +59 204 60 +190 204 174 +190 232 204 +192 232 190 +155 232 212 +212 232 192 +191 192 190 +181 212 192 +204 232 155 +60 204 155 +155 231 213 +155 213 61 +60 155 61 +212 231 155 +181 231 212 +60 380 59 +59 380 332 +332 380 365 +365 380 368 +371 384 372 +368 371 363 +365 368 363 +324 384 368 +368 384 371 +368 380 324 +324 380 60 +383 384 324 +61 383 324 +61 324 60 +62 383 61 +63 346 62 +346 383 62 +372 383 346 +372 384 383 +371 372 321 +372 392 321 +346 392 372 +358 392 346 +354 392 358 +354 358 356 +358 396 356 +378 396 358 +6 378 63 +346 378 358 +63 378 346 +28 405 403 +5 405 28 +57 405 5 +402 405 57 +352 405 402 +403 405 352 +352 402 393 +58 402 57 +332 402 58 +59 332 58 +393 402 332 +364 365 363 +364 401 365 +365 393 332 +365 401 393 +401 403 393 +401 404 345 +393 403 352 +345 403 401 +27 403 345 +27 345 26 +28 403 27 +25 379 326 +379 395 326 +345 379 26 +26 379 25 +379 404 395 +345 404 379 +364 404 401 +395 404 364 +362 364 363 +326 395 386 +386 395 362 +351 386 362 +362 395 364 +25 326 24 +326 385 24 +326 386 385 +24 385 23 +23 385 331 +23 331 22 +331 385 349 +22 330 4 +22 331 330 +331 333 330 +333 348 330 +350 397 333 +333 397 348 +348 397 366 +349 386 351 +349 351 350 +385 386 349 +331 349 333 +349 350 333 +351 360 350 +351 361 360 +360 361 321 +361 371 321 +321 398 360 +353 389 360 +360 398 353 +343 389 353 +366 397 389 +389 397 350 +360 389 350 +353 354 344 +354 398 392 +353 398 354 +392 398 321 +351 362 361 +362 363 361 +363 371 361 diff --git a/test/test_hh3d_nearfield.jl b/test/test_hh3d_nearfield.jl index 99cc5a53..ff55747e 100644 --- a/test/test_hh3d_nearfield.jl +++ b/test/test_hh3d_nearfield.jl @@ -5,11 +5,13 @@ using LinearAlgebra using Test @testset "Helmholtz potential operators" begin - r = 10.0 - λ = 20 * r - k = 2 * π / λ + # r = 10.0 + # λ = 20 * r + # k = 2 * π / λ + # sphere = meshsphere(r, 0.2 * r) + k = 0.031415926535897934 + sphere = readmesh(joinpath(dirname(pathof(BEAST)),"../test/assets/sphere_rad=10_h=2.in")) - sphere = meshsphere(r, 0.2 * r) X0 = lagrangecxd0(sphere) X1 = lagrangec0d1(sphere) @@ -23,6 +25,7 @@ using Test # Interior problem # Formulations from Sauter and Schwab, Boundary Element Methods(2011), Chapter 3.4.1.1 + r = 10.0 pos1 = SVector(r * 1.5, 0.0, 0.0) # positioning of point charges pos2 = SVector(-r * 1.5, 0.0, 0.0) @@ -159,11 +162,12 @@ end ## Test here some of the mixed discretizatinos #@testset "Helmholtz potential operators: mixed discretizations with duallagrangecxd0" begin - r = 10.0 - λ = 20 * r - k = 2 * π / λ - - sphere = meshsphere(r, 0.2 * r) + # r = 10.0 + # sphere = meshsphere(r, 0.2 * r) + # λ = 20 * r + # k = 2 * π / λ + k = 0.031415926535897934 + sphere = readmesh(joinpath(dirname(pathof(BEAST)),"../test/assets/sphere_rad=10_h=2.in")) X0 = lagrangecxd0(sphere) X1 = lagrangec0d1(sphere) Y0 = duallagrangecxd0(sphere; interpolatory=true) @@ -178,6 +182,7 @@ end # Interior problem # Formulations from Sauter and Schwab, Boundary Element Methods(2011), Chapter 3.4.1.1 + r = 10.0 pos1 = SVector(r * 1.5, 0.0, 0.0) # positioning of point charges pos2 = SVector(-r * 1.5, 0.0, 0.0) @@ -234,11 +239,13 @@ end ## Test here some of the mixed discretizatinos #@testset "Helmholtz potential operators: mixed discretizations with duallagrangec0d1" begin - r = 10.0 - λ = 20 * r - k = 2 * π / λ + # r = 10.0 + # λ = 20 * r + # k = 2 * π / λ + # sphere = meshsphere(r, 0.2 * r) + k = 0.031415926535897934 + sphere = readmesh(joinpath(dirname(pathof(BEAST)),"../test/assets/sphere_rad=10_h=2.in")) - sphere = meshsphere(r, 0.2 * r) X0 = lagrangecxd0(sphere) Y1 = duallagrangec0d1(sphere) @@ -252,6 +259,7 @@ end # Interior problem # Formulations from Sauter and Schwab, Boundary Element Methods(2011), Chapter 3.4.1.1 + r = 10.0 pos1 = SVector(r * 1.5, 0.0, 0.0) # positioning of point charges pos2 = SVector(-r * 1.5, 0.0, 0.0) From 281ec5f994d6f65791b5bbad9daa991497aa0609 Mon Sep 17 00:00:00 2001 From: krcools Date: Tue, 16 Apr 2024 12:33:22 +0200 Subject: [PATCH 356/528] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 37ad8863..ad22f8ab 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Boundary Element Analysis and Simulation Toolkit -[![Build status](https://github.com/krcools/BEAST.jl/workflows/CI/badge.svg)](https://github.com/krcools/BEAST.jl/actions) +[![CI](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml) [![codecov.io](http://codecov.io/github/krcools/BEAST.jl/coverage.svg?branch=master)](http://codecov.io/github/krcools/BEAST.jl?branch=master) [![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://krcools.github.io/BEAST.jl/latest/) [![DOI](https://zenodo.org/badge/87720391.svg)](https://zenodo.org/badge/latestdoi/87720391) From 2c20358709e896866720d01bd795b58eb5ea5ef8 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 16 Apr 2024 12:48:04 +0200 Subject: [PATCH 357/528] Set version to 2.3.0 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 0ba63493..25fa7456 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "2.2.1" +version = "2.3.0 [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From 809ff69f1d676b0c929ad58d3fe817d8580b1fef Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 16 Apr 2024 12:50:14 +0200 Subject: [PATCH 358/528] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 25fa7456..6f052b4e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "2.3.0 +version = "2.3.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From 5b645d94c9a2927ef29d84f5b017de784b2202e0 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Mon, 22 Apr 2024 12:05:31 +0200 Subject: [PATCH 359/528] scalartype for scalar plane wave --- src/helmholtz3d/timedomain/tdhh3dexc.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helmholtz3d/timedomain/tdhh3dexc.jl b/src/helmholtz3d/timedomain/tdhh3dexc.jl index 045ea0b8..4fc28ffa 100644 --- a/src/helmholtz3d/timedomain/tdhh3dexc.jl +++ b/src/helmholtz3d/timedomain/tdhh3dexc.jl @@ -10,6 +10,7 @@ function planewave(direction, speedoflight::Number, signature, amplitude=one(spe PlaneWaveHH3DTD(direction, speedoflight, signature, amplitude) end +scalartype(p::PlaneWaveHH3DTD) = eltype(p.amplitude) *(a, f::PlaneWaveHH3DTD) = PlaneWaveHH3DTD(f.direction, f.speed_of_light, f.signature, a*f.amplitude) From ac693d5eee5d1f66a1a27510e4d48938cc975733 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Mon, 22 Apr 2024 12:21:35 +0200 Subject: [PATCH 360/528] Merge with upstream v2 --- src/volumeintegral/sauterschwab_ints.jl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index bee0b007..b2d96b69 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -71,9 +71,6 @@ function reorder_dof(space::LagrangeRefSpace{Float64,0,3,1},I) return SVector{1,Int64}(1),SVector{1,Int64}(1) end -<<<<<<< HEAD -#Method of moments volume integrals operators -======= function reorder_dof(space::LagrangeRefSpace{T,1,3,3},I) where T n = length(I) K = zeros(MVector{3,Int64}) @@ -104,7 +101,6 @@ function reorder_dof(space::LagrangeRefSpace{T,1,4,4},I) where T return SVector(K),SVector{4,Int64}(1,1,1,1) end ->>>>>>> upstream/master function momintegrals!(op::VIEOperator, test_local_space::RefSpace, trial_local_space::RefSpace, test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) From 85ee3a69e89ef62b2d6b98b76fd3f4ee598cffd0 Mon Sep 17 00:00:00 2001 From: bmergl Date: Tue, 30 Apr 2024 14:26:58 +0200 Subject: [PATCH 361/528] bug fix VIEIntegrand --- src/volumeintegral/vieops.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/volumeintegral/vieops.jl b/src/volumeintegral/vieops.jl index 87a5fb16..26f19c61 100644 --- a/src/volumeintegral/vieops.jl +++ b/src/volumeintegral/vieops.jl @@ -115,7 +115,7 @@ function (igd::VIEIntegrand)(u,v) #jacobian j = jacobian(tgeo) * jacobian(bgeo) - integrand(igd.op, kerneldata,tval,tgeo,bval,tgeo) * j + integrand(igd.op, kerneldata,tval,tgeo,bval,bgeo) * j end From 7d6707e7743af615f24a92307c65e6fb1fbb9245 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Fri, 10 May 2024 00:38:56 +0000 Subject: [PATCH 362/528] CompatHelper: bump compat for CompScienceMeshes to 0.7, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 6f052b4e..3108e7eb 100644 --- a/Project.toml +++ b/Project.toml @@ -35,7 +35,7 @@ AbstractTrees = "0.4.4" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" CollisionDetection = "0.1.5" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.6.0" +CompScienceMeshes = "0.6.0, 0.7" Compat = "2, 3, 4" ConvolutionOperators = "0.4" ExtendableSparse = "1.4" From f095821d95af5c5a141c2e56c922d0e122c949c1 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 10 May 2024 13:11:56 +0200 Subject: [PATCH 363/528] integralop quadstrat for non conforming meshes --- Project.toml | 2 +- examples/efie.jl | 11 ++ examples/nonconfmeshes.jl | 184 ++++++++++++++++++ src/BEAST.jl | 8 + src/bases/rtxspace.jl | 5 +- src/helmholtz2d/helmholtzop.jl | 2 +- src/integralop.jl | 96 ++++----- src/maxwell/wiltonints.jl | 2 +- src/operator.jl | 26 +-- .../commonfaceoverlappingedgeqstrat.jl | 26 +++ src/quadrature/doublenumsauterqstrat.jl | 10 +- .../nonconformingintegralopqstrat.jl | 25 +++ src/quadrature/nonconformingoverlapqrule.jl | 43 ++++ src/quadrature/nonconformingtouchqrule.jl | 157 +++++++++++++++ src/quadrature/quadstrats.jl | 8 - src/quadrature/sauterschwabints.jl | 8 +- .../selfsauterdnumotherwiseqstrat.jl | 44 +++++ .../cfcvsautercewiltonpdnumqstrat.jl | 65 +++++++ src/utils/specialfns.jl | 2 +- 19 files changed, 642 insertions(+), 82 deletions(-) create mode 100644 examples/nonconfmeshes.jl create mode 100644 src/quadrature/commonfaceoverlappingedgeqstrat.jl create mode 100644 src/quadrature/nonconformingintegralopqstrat.jl create mode 100644 src/quadrature/nonconformingoverlapqrule.jl create mode 100644 src/quadrature/nonconformingtouchqrule.jl create mode 100644 src/quadrature/selfsauterdnumotherwiseqstrat.jl create mode 100644 src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl diff --git a/Project.toml b/Project.toml index 6f052b4e..22df9faa 100644 --- a/Project.toml +++ b/Project.toml @@ -35,7 +35,7 @@ AbstractTrees = "0.4.4" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" CollisionDetection = "0.1.5" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.6.0" +CompScienceMeshes = "0.7" Compat = "2, 3, 4" ConvolutionOperators = "0.4" ExtendableSparse = "1.4" diff --git a/examples/efie.jl b/examples/efie.jl index 5ce77674..76d42cb1 100644 --- a/examples/efie.jl +++ b/examples/efie.jl @@ -19,3 +19,14 @@ u, ch = BEAST.gmres_ch(efie; restart=1500) include("utils/postproc.jl") include("utils/plotresults.jl") + +qsg(g) = BEAST.DoubleNumWiltonSauterQStrat(6, 7, 6, 7, g, g, g, g) +A1 = assemble(t,X,X;quadstrat=qsg(5)) +A2 = assemble(t,X,X;quadstrat=qsg(10)) +A2 = assemble(t,X,X;quadstrat=qsg(20)) +val, pos = findmax(abs.(A2-A3)) + +import Plots +Plots.plotly() +Plots.plot(abs.(A2[537,:])) +Plots.scatter!(abs.(A3[537,:])) \ No newline at end of file diff --git a/examples/nonconfmeshes.jl b/examples/nonconfmeshes.jl new file mode 100644 index 00000000..fa38d391 --- /dev/null +++ b/examples/nonconfmeshes.jl @@ -0,0 +1,184 @@ +using CompScienceMeshes +using BEAST + +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +Γref = CompScienceMeshes.barycentric_refinement(Γ) +X = raviartthomas(Γ, BEAST.Continuity{:none}) +Y = raviartthomas(Γref, BEAST.Continuity{:none}) + +# X = subset(X,1690:1692) +# Y = subset(Y,10141:10143) + +# X = subset(X,[1692]) +# Y = subset(Y,[10147]) + +# Y = buffachristiansen(Γ) + +κ, η = 1.0, 1.0 +t = Maxwell3D.singlelayer(wavenumber=κ) +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +e = (n × E) × n + +g = 6 +# ssstrat(g) = BEAST.DoubleNumSauterQstrat(7, 6, g, g, g, g) +ssstrat(g) = BEAST.CommonFaceVertexSauterCommonEdgeWiltonPostitiveDistanceNumQStrat( + 7, 6, 10, 8, g, g, g+3, g) + +qs1 = ssstrat(g) +qs2 = BEAST.NonConformingIntegralOpQStrat(ssstrat(g)) + +A1 = assemble(t,Y,X, quadstrat=qs1, threading=BEAST.Threading{:single}) +A2 = assemble(t,Y,X, quadstrat=qs2, threading=BEAST.Threading{:single}) + +# @enter assemble(t,Y,X, quadstrat=qs2, threading=BEAST.Threading{:single}) + +import Plots +Plots.plotly() +step = 1 +rowidx = 10894 +Plots.plot(imag.(A1[rowidx,1:step:end])) +Plots.scatter!(imag.(A2[rowidx,1:step:end])) +val, pos = findmax(abs.(A1-A2)) + +rowidx = Y.fns[pos[1]][1].cellid +colidx = X.fns[pos[2]][1].cellid + +τ = chart(Γref, rowidx) +σ = chart(Γ, colidx) + +# τ = [ +# [0.18517788982868066, 0.05998671122432393, 0.9735312677690339], +# [0.1647116144395066, 0.0050896055947335095, 0.9807011064756881], +# [0.245326672427806, -0.06121735585211891, 0.9675056894602609]] + +# σ = [ +# [0.1133762981558132, -0.1176791567283826, 0.9865583769286949], +# [0.1647116144395066, 0.0050896055947335025, 0.9807011064756881], +# [0.08409655645120719, 0.07139656704158594, 0.9938965234911153]] + +# eτ = simplex( +# point(0.1647116144395066, 0.0050896055947335095, 0.9807011064756881), +# point(0.245326672427806, -0.06121735585211891, 0.9675056894602609)) + +# eσ = simplex( +# point(0.1647116144395066, 0.0050896055947335025, 0.9807011064756881), +# point(0.08409655645120719, 0.07139656704158594, 0.9938965234911153)) + +# CompScienceMeshes.overlap(eτ, eσ) + +# τ = τ.vertices +# σ = σ.vertices + +import Plotly +function Plotly.mesh3d(a::Vector{<:CompScienceMeshes.Simplex}; opacity=0.5, kwargs...) + T = coordtype(a[1]) + n = length(a) + X = Vector{T}(undef, 3*n) + Y = Vector{T}(undef, 3*n) + Z = Vector{T}(undef, 3*n) + for i in 1:n + X[3*(i-1)+1:3*i] = getindex.(a[i].vertices, 1) + Y[3*(i-1)+1:3*i] = getindex.(a[i].vertices, 2) + Z[3*(i-1)+1:3*i] = getindex.(a[i].vertices, 3) + end + + I = collect(0:3:3*(n-1)) + J = I .+ 1 + K = I .+ 2 + + return Plotly.mesh3d(x=X,y=Y,z=Z,i=I,j=J,k=K; opacity, kwargs...) +end + +m1 = Plotly.mesh3d([τ], opacity=0.5) +m2 = Plotly.mesh3d([σ], opacity=0.5) + + +# m1 = Plotly.mesh3d( +# x=getindex.(τ,1), +# y=getindex.(τ,2), +# z=getindex.(τ,3), +# i=[0], j=[1], k=[2]) +# m2 = Plotly.mesh3d( +# x=getindex.(σ,1), +# y=getindex.(σ,2), +# z=getindex.(σ,3), +# i=[0], j=[1], k=[2]) +Plotly.plot([m1,m2]) + + + + +isct1, clps1 = CompScienceMeshes.intersection_keep_clippings(τ,σ) +isct2, clps2 = CompScienceMeshes.intersection_keep_clippings(σ,τ) + + +m3 = Plotly.mesh3d(isct1, opacity=0.5, color="blue") +m4 = Plotly.mesh3d(isct2, opacity=0.5, color="red") +Plotly.plot([m3,m4]) + +m5 = Plotly.mesh3d(clps2[1], opacity=0.5, color="green") +m6 = Plotly.mesh3d(clps2[1][[1]], opacity=0.5, color="yellow") +Plotly.plot([m3,m6]) + +p = isct1[1] +q = clps2[1][1] +for (i,λ) in pairs(faces(p)) + for (j,μ) in pairs(faces(q)) + if CompScienceMeshes.overlap(λ, μ) + global gi = i + global gj = j + global qr = BEAST.NonConformingTouchQRule(qs1, i, j) +end end end + +𝒳 = refspace(X) +𝒴 = refspace(Y) +out10 = zeros(ComplexF64, 3, 3) +BEAST.momintegrals!(t, 𝒴, 𝒳, p, q, out10, BEAST.NonConformingTouchQRule(ssstrat(10), gi, gj)) +out20 = zeros(ComplexF64, 3, 3) +BEAST.momintegrals!(t, 𝒴, 𝒳, p, q, out20, BEAST.NonConformingTouchQRule(ssstrat(20), gi, gj)) + +@show out10[1,1] out20[1,1] + +τs, σs = BEAST._conforming_refinement_touching_triangles(p,q,2,3) +@assert length(τs) == 1 +@assert length(σs) == 2 +@assert volume(p) ≈ sum(volume.(τs)) +@assert volume(q) ≈ sum(volume.(σs)) +@assert all(volume.(τs) .> eps(Float64)*1e3) +@assert all(volume.(σs) .> eps(Float64)*1e3) +m7 = Plotly.mesh3d(τs[[1]], color="blue", opacity=0.5) +m8 = Plotly.mesh3d(σs[[1]], color="red", opacity=0.5) +Plotly.plot([m7,m8]) + +out1_10 = zeros(ComplexF64, 3, 3) +qs = ssstrat(10) +qd = quaddata(t, 𝒴, 𝒳, τs, σs, qs) +qr = quadrule(t, 𝒴, 𝒳, 1, τs[1], 1, σs[1], qd, qs) +BEAST.momintegrals!(t, 𝒴, 𝒳, τs[1], σs[1], out1_10, qr) +out1_20 = zeros(ComplexF64, 3, 3) +qs = ssstrat(20) +qd = quaddata(t, 𝒴, 𝒳, τs, σs, qs) +qr = quadrule(t, 𝒴, 𝒳, 1, τs[1], 1, σs[1], qd, qs) +BEAST.momintegrals!(t, 𝒴, 𝒳, τs[1], σs[1], out1_20, qr) +@show out1_10[1,1] out1_20[1,1] + +out2_10 = zeros(ComplexF64, 3, 3) +qs = ssstrat(10) +qd = quaddata(t, 𝒴, 𝒳, τs, σs, qs) +qr = quadrule(t, 𝒴, 𝒳, 1, τs[1], 1, σs[2], qd, qs) +BEAST.momintegrals!(t, 𝒴, 𝒳, τs[1], σs[2], out2_10, qr) +out2_20 = zeros(ComplexF64, 3, 3) +qs = ssstrat(20) +qd = quaddata(t, 𝒴, 𝒳, τs, σs, qs) +qr = quadrule(t, 𝒴, 𝒳, 1, τs[1], 2, σs[2], qd, qs) +BEAST.momintegrals!(t, 𝒴, 𝒳, τs[1], σs[2], out2_20, qr) +@show out2_10[1,1] out2_20[1,1] + + +out_ref = zeros(ComplexF64, 3, 3) +qrss = BEAST.SauterSchwabQuadrature.CommonVertex(g) +BEAST.momintegrals_test_refines_trial!(out_ref, t, + Y, rowidx, p, + X, colidx, q, + qrss, qs1) +@show out_ref \ No newline at end of file diff --git a/src/BEAST.jl b/src/BEAST.jl index 046d68f4..9788ad5f 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -18,6 +18,8 @@ using LiftedMaps using AbstractTrees using NestedUnitRanges +# using Infiltrator + import LinearAlgebra: cross, dot import LinearAlgebra: ×, ⋅ @@ -193,10 +195,16 @@ include("quadrature/doublenumqstrat.jl") include("quadrature/doublenumsauterqstrat.jl") include("quadrature/doublenumwiltonsauterqstrat.jl") include("quadrature/doublenumwiltonbogaertqstrat.jl") +include("quadrature/selfsauterdnumotherwiseqstrat.jl") +include("quadrature/nonconformingintegralopqstrat.jl") +include("quadrature/commonfaceoverlappingedgeqstrat.jl") +include("quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl") include("quadrature/doublenumints.jl") include("quadrature/singularityextractionints.jl") include("quadrature/sauterschwabints.jl") +include("quadrature/nonconformingoverlapqrule.jl") +include("quadrature/nonconformingtouchqrule.jl") include("postproc.jl") include("postproc/segcurrents.jl") diff --git a/src/bases/rtxspace.jl b/src/bases/rtxspace.jl index 4440ffcb..77f728b1 100644 --- a/src/bases/rtxspace.jl +++ b/src/bases/rtxspace.jl @@ -17,7 +17,10 @@ function raviartthomas(mesh, ::Type{Continuity{:none}}) functions[3*(i-1)+2] = [S(i,2,+1.0)] functions[3*(i-1)+3] = [S(i,3,+1.0)] - ctr = sum(mesh.vertices[mesh.faces[i]])/3 + I = CompScienceMeshes.indices(mesh,i) + V = CompScienceMeshes.vertices(mesh,I) + ctr = sum(V)/3 + # ctr = sum(mesh.vertices[mesh.faces[i]])/3 positions[3*(i-1)+1] = ctr positions[3*(i-1)+2] = ctr positions[3*(i-1)+3] = ctr diff --git a/src/helmholtz2d/helmholtzop.jl b/src/helmholtz2d/helmholtzop.jl index f5213d06..3e10ef0e 100644 --- a/src/helmholtz2d/helmholtzop.jl +++ b/src/helmholtz2d/helmholtzop.jl @@ -1,4 +1,4 @@ -using SpecialFunctions +import SpecialFunctions: hankelh2 abstract type HelmholtzOperator2D <: IntegralOperator end scalartype(::HelmholtzOperator2D) = ComplexF64 diff --git a/src/integralop.jl b/src/integralop.jl index a0287db6..289d02b3 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -119,6 +119,7 @@ function assemblechunk_body!(biop, fill!(zlocal, 0) qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, qd, quadstrat) + # @show "integralop" qrule momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule) I = length(test_assembly_data[p]) J = length(trial_assembly_data[q]) @@ -156,6 +157,7 @@ function assemblechunk_body_test_refines_trial!(biop, fill!(zlocal, 0) qrule = quadrule(biop, test_shapes, trial_shapes, p, tchart, q, bchart, qd, quadstrat) + # @show ("1", qrule) momintegrals_test_refines_trial!(zlocal, biop, test_functions, tcell, tchart, trial_functions, bcell, bchart, @@ -458,53 +460,53 @@ function assembleblock_body_test_refines_trial!(biop::IntegralOperator, end end end end end end end -function assembleblock_body_nested!(biop::IntegralOperator, - tfs, test_ids, test_elements, test_assembly_data, - bfs, trial_ids, bsis_elements, trial_assembly_data, - quadrature_data, zlocals, store; quadstrat) - - test_shapes = refspace(tfs) - trial_shapes = refspace(bfs) - - # Enumerate all the active test elements - active_test_el_ids = Vector{Int}() - active_trial_el_ids = Vector{Int}() - - test_id_in_blk = Dict{Int,Int}() - trial_id_in_blk = Dict{Int,Int}() - - for (i,m) in enumerate(test_ids); test_id_in_blk[m] = i; end - for (i,m) in enumerate(trial_ids); trial_id_in_blk[m] = i; end - - for m in test_ids, sh in tfs.fns[m]; push!(active_test_el_ids, sh.cellid); end - for m in trial_ids, sh in bfs.fns[m]; push!(active_trial_el_ids, sh.cellid); end - - active_test_el_ids = unique(sort(active_test_el_ids)) - active_trial_el_ids = unique(sort(active_trial_el_ids)) - - for p in active_test_el_ids - tcell = test_elements[p] - for q in active_trial_el_ids - bcell = bsis_elements[q] - - fill!(zlocals[Threads.threadid()], 0) - qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) - momintegrals_test_refines_trial!(zlocals[Threads.threadid()], biop, - tfs, p, tcell, - bfs, q, bcell, - qrule, quadstrat) - # momintegrals_test_refines_trial!(biop, test_shapes, trial_shapes, tcell, bcell, zlocals[Threads.threadid()], qrule, quadstrat) - - for j in 1 : size(zlocals[Threads.threadid()],2) - for i in 1 : size(zlocals[Threads.threadid()],1) - for (n,b) in trial_assembly_data[q,j] - n′ = get(trial_id_in_blk, n, 0) - n′ == 0 && continue - for (m,a) in test_assembly_data[p,i] - m′ = get(test_id_in_blk, m, 0) - m′ == 0 && continue - store(a*zlocals[Threads.threadid()][i,j]*b, m′, n′) -end end end end end end end +# function assembleblock_body_nested!(biop::IntegralOperator, +# tfs, test_ids, test_elements, test_assembly_data, +# bfs, trial_ids, bsis_elements, trial_assembly_data, +# quadrature_data, zlocals, store; quadstrat) + +# test_shapes = refspace(tfs) +# trial_shapes = refspace(bfs) + +# # Enumerate all the active test elements +# active_test_el_ids = Vector{Int}() +# active_trial_el_ids = Vector{Int}() + +# test_id_in_blk = Dict{Int,Int}() +# trial_id_in_blk = Dict{Int,Int}() + +# for (i,m) in enumerate(test_ids); test_id_in_blk[m] = i; end +# for (i,m) in enumerate(trial_ids); trial_id_in_blk[m] = i; end + +# for m in test_ids, sh in tfs.fns[m]; push!(active_test_el_ids, sh.cellid); end +# for m in trial_ids, sh in bfs.fns[m]; push!(active_trial_el_ids, sh.cellid); end + +# active_test_el_ids = unique(sort(active_test_el_ids)) +# active_trial_el_ids = unique(sort(active_trial_el_ids)) + +# for p in active_test_el_ids +# tcell = test_elements[p] +# for q in active_trial_el_ids +# bcell = bsis_elements[q] + +# fill!(zlocals[Threads.threadid()], 0) +# qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) +# momintegrals_test_refines_trial!(zlocals[Threads.threadid()], biop, +# tfs, p, tcell, +# bfs, q, bcell, +# qrule, quadstrat) +# # momintegrals_test_refines_trial!(biop, test_shapes, trial_shapes, tcell, bcell, zlocals[Threads.threadid()], qrule, quadstrat) + +# for j in 1 : size(zlocals[Threads.threadid()],2) +# for i in 1 : size(zlocals[Threads.threadid()],1) +# for (n,b) in trial_assembly_data[q,j] +# n′ = get(trial_id_in_blk, n, 0) +# n′ == 0 && continue +# for (m,a) in test_assembly_data[p,i] +# m′ = get(test_id_in_blk, m, 0) +# m′ == 0 && continue +# store(a*zlocals[Threads.threadid()][i,j]*b, m′, n′) +# end end end end end end end function assemblerow!(biop::IntegralOperator, test_functions::Space, trial_functions::Space, store; diff --git a/src/maxwell/wiltonints.jl b/src/maxwell/wiltonints.jl index ccb6fb0f..4ffe2d80 100644 --- a/src/maxwell/wiltonints.jl +++ b/src/maxwell/wiltonints.jl @@ -2,7 +2,7 @@ import WiltonInts84 mutable struct WiltonSERule{P,Q} <: SingularityExtractionRule outer_quad_points::P - regularpart_quadrule::DoubleQuadRule{P,Q} + regularpart_quadrule::Q end function innerintegrals!(op::MWSingleLayer3DSng, p, g, f, t, s, z, diff --git a/src/operator.jl b/src/operator.jl index 61d38fa1..e1958954 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -144,7 +144,7 @@ function allocatestorage(operator::LinearCombinationOfOperators, storage_policy::Type{Val{:bandedstorage}}, long_delays_policy::Type{LongDelays{:ignore}}) - # TODO: remove this ugly, ugly patch + # This works when are terms in the LC can share storage return allocatestorage(operator.ops[end], test_functions, trial_functions, storage_policy, long_delays_policy) end @@ -155,7 +155,7 @@ function allocatestorage(operator::LinearCombinationOfOperators, storage_policy::Type{Val{S}}, long_delays_policy::Type{LongDelays{L}}) where {L,S} - # TODO: remove this ugly, ugly patch + # This works when are terms in the LC can share storage return allocatestorage(operator.ops[end], test_functions, trial_functions, storage_policy, long_delays_policy) end @@ -172,31 +172,23 @@ function assemble!(operator::Operator, test_functions::Space, trial_functions::S store, threading::Type{Threading{:multi}}; quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) - # @info "Multi-threaded assembly:" - P = Threads.nthreads() numchunks = P @assert numchunks >= 1 splits = [round(Int,s) for s in range(0, stop=numfunctions(test_functions), length=numchunks+1)] Threads.@threads for i in 1:P - # @batch per=thread for i in 1:P lo, hi = splits[i]+1, splits[i+1] lo <= hi || continue test_functions_p = subset(test_functions, lo:hi) - # store1 = (v,m,n) -> store(v,lo+m-1,n) store1 = _OffsetStore(store, lo-1, 0) assemblechunk!(operator, test_functions_p, trial_functions, store1, quadstrat=quadstrat) - end - -end +end end function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, threading::Type{Threading{:single}}; quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) - # @info "Single-threaded assembly" - assemblechunk!(operator, test_functions, trial_functions, store; quadstrat) end @@ -260,6 +252,9 @@ function assemble!(op::AbstractOperator, tfs::DirectProductSpace, bfs::DirectPro end end +# TODO: Remove BlockDiagonalOperator in favour of manipulations +# on the level of bilinear forms. + # Discretisation and assembly of these operators # will respect the direct product structure of the # HilbertSpace/FiniteElmeentSpace @@ -288,6 +283,7 @@ function assemble!(op::BlockDiagonalOperator, U::DirectProductSpace, V::DirectPr end end +# BlockFull is default so not sure when this exists -> remove struct BlockFullOperators <: AbstractOperator op::AbstractOperator @@ -303,17 +299,9 @@ function assemble!(op::BlockFullOperators, U::DirectProductSpace, V::DirectProdu store, threading; quadstrat = defaultquadstrat(op, U, V)) - # @assert length(U.factors) == length(V.factors) I = Int[0]; for u in U.factors push!(I, last(I) + numfunctions(u)) end J = Int[0]; for v in V.factors push!(J, last(J) + numfunctions(v)) end - # k = 1 - # for (u,v) in zip(U.factors, V.factors) - # store1(v,m,n) = store(v, I[k] + m, J[k] + n) - # assemble!(op.op, u, v, store1; quadstrat) - # k += 1 - # end - for (k,u) in enumerate(U.factors) for (l,v) in enumerate(V.factors) store1(x,m,n) = store(x, I[k]+m, J[l]+n) diff --git a/src/quadrature/commonfaceoverlappingedgeqstrat.jl b/src/quadrature/commonfaceoverlappingedgeqstrat.jl new file mode 100644 index 00000000..f7c98f6e --- /dev/null +++ b/src/quadrature/commonfaceoverlappingedgeqstrat.jl @@ -0,0 +1,26 @@ +struct CommonFaceOverlappingEdgeQStrat{S} + conforming_qstrat::S +end + +function quaddata(a, X, Y, tels, bels, qs::CommonFaceOverlappingEdgeQStrat) + return quaddata(a, X, Y, tels, bels, qs.conforming_qstrat) +end + + +function quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, + qs::CommonFaceOverlappingEdgeQStrat) + + if CompScienceMeshes.overlap(τ, σ) + return quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, qs.conforming_qstrat) + end + + for (i,λ) in pairs(faces(τ)) + for (j,μ) in pairs(faces(σ)) + if CompScienceMeshes.overlap(λ, μ) + return NonConformingTouchQRule(qs.conforming_qstrat, i, j) + end end end + + # Either positive distance, common face, or common vertex, which can + # be handled directly by the parent quadrature strategy + return quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, qs.conforming_qstrat) +end \ No newline at end of file diff --git a/src/quadrature/doublenumsauterqstrat.jl b/src/quadrature/doublenumsauterqstrat.jl index 825cc5b6..3cdbbef4 100644 --- a/src/quadrature/doublenumsauterqstrat.jl +++ b/src/quadrature/doublenumsauterqstrat.jl @@ -1,5 +1,11 @@ - - +struct DoubleNumSauterQstrat{R,S} + outer_rule::R + inner_rule::R + sauter_schwab_common_tetr::S + sauter_schwab_common_face::S + sauter_schwab_common_edge::S + sauter_schwab_common_vert::S +end function quaddata(op::IntegralOperator, test_local_space::RefSpace, trial_local_space::RefSpace, diff --git a/src/quadrature/nonconformingintegralopqstrat.jl b/src/quadrature/nonconformingintegralopqstrat.jl new file mode 100644 index 00000000..7d0bd004 --- /dev/null +++ b/src/quadrature/nonconformingintegralopqstrat.jl @@ -0,0 +1,25 @@ +struct NonConformingIntegralOpQStrat{S} + conforming_qstrat::S +end + +function quaddata(a, X, Y, tels, bels, qs::NonConformingIntegralOpQStrat) + return quaddata(a, X, Y, tels, bels, qs.conforming_qstrat) +end + +function quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, + qs::NonConformingIntegralOpQStrat) + + if CompScienceMeshes.overlap(τ, σ) + return NonConformingOverlapQRule(qs.conforming_qstrat) + end + + for (i,λ) in pairs(faces(τ)) + for (j,μ) in pairs(faces(σ)) + if CompScienceMeshes.overlap(λ, μ) + return NonConformingTouchQRule(qs.conforming_qstrat, i, j) + end end end + + # Either positive distance or common vertex, both can + # be handled directly by the parent quadrature strategy + return quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, qs.conforming_qstrat) +end \ No newline at end of file diff --git a/src/quadrature/nonconformingoverlapqrule.jl b/src/quadrature/nonconformingoverlapqrule.jl new file mode 100644 index 00000000..19164948 --- /dev/null +++ b/src/quadrature/nonconformingoverlapqrule.jl @@ -0,0 +1,43 @@ +struct NonConformingOverlapQRule{S} + conforming_qstrat::S +end + + +function momintegrals!(op, test_local_space, basis_local_space, + test_chart::CompScienceMeshes.Simplex, basis_chart::CompScienceMeshes.Simplex, + out, qrule::NonConformingOverlapQRule) + + test_charts, tclps = CompScienceMeshes.intersection_keep_clippings(test_chart, basis_chart) + _, bclps = CompScienceMeshes.intersection_keep_clippings(basis_chart, test_chart) + bsis_charts = copy(test_charts) + + for tclp in tclps append!(test_charts, tclp) end + for bclp in bclps append!(bsis_charts, bclp) end + + @assert volume(test_chart) ≈ sum(volume.(test_charts)) + @assert volume(basis_chart) ≈ sum(volume.(bsis_charts)) + + qstrat = CommonFaceOverlappingEdgeQStrat(qrule.conforming_qstrat) + qdata = quaddata(op, test_local_space, basis_local_space, + test_charts, bsis_charts, qstrat) + + for (p,tchart) in enumerate(test_charts) + for (q,bchart) in enumerate(bsis_charts) + qrule = quadrule(op, test_local_space, basis_local_space, + p, tchart, q, bchart, qdata, qstrat) + # @show qrule + + P = restrict(test_local_space, test_chart, tchart) + Q = restrict(basis_local_space, basis_chart, bchart) + zlocal = zero(out) + + momintegrals!(op, test_local_space, basis_local_space, + tchart, bchart, zlocal, qrule) + + for i in axes(P,1) + for j in axes(Q,1) + for k in axes(P,2) + for l in axes(Q,2) + out[i,j] += P[i,k] * zlocal[k,l] * Q[j,l] + # out .+= P * zlocal * Q' +end end end end end end end \ No newline at end of file diff --git a/src/quadrature/nonconformingtouchqrule.jl b/src/quadrature/nonconformingtouchqrule.jl new file mode 100644 index 00000000..09f049af --- /dev/null +++ b/src/quadrature/nonconformingtouchqrule.jl @@ -0,0 +1,157 @@ +struct NonConformingTouchQRule{S} + conforming_qstrat::S + test_overlapping_edge_index::Int + bsis_overlapping_edge_index::Int +end + +function momintegrals!(op, test_locspace, bsis_locspace, + τ::CompScienceMeshes.Simplex, σ::CompScienceMeshes.Simplex, + out, qrule::NonConformingTouchQRule) + + T = coordtype(τ) + P = eltype(τ.vertices) + + i = qrule.test_overlapping_edge_index + j = qrule.bsis_overlapping_edge_index + + τs, σs = _conforming_refinement_touching_triangles(τ,σ,i,j) + + # test conformity + for a in τs + for b in σs + @assert _test_conformity(a, b) + end + end + + @assert volume(τ) ≈ sum(volume.(τs)) + @assert volume(σ) ≈ sum(volume.(σs)) + + qstrat = qrule.conforming_qstrat + qdata = quaddata(op, test_locspace, bsis_locspace, τs, σs, qstrat) + + for (p,tchart) in enumerate(τs) + for (q,bchart) in enumerate(σs) + qrule = quadrule(op, test_locspace, bsis_locspace, + p, tchart, q, bchart, qdata, qstrat) + # @show qrule + + P = restrict(test_locspace, τ, tchart) + Q = restrict(bsis_locspace, σ, bchart) + zlocal = zero(out) + + momintegrals!(op, test_locspace, bsis_locspace, + tchart, bchart, zlocal, qrule) + + # out .+= P * zlocal * Q' + for i in axes(P,1) + for j in axes(Q,1) + for k in axes(P,2) + for l in axes(Q,2) + out[i,j] += P[i,k] * zlocal[k,l] * Q[j,l] +end end end end end end end + + +function _conforming_refinement_touching_triangles(τ,σ,i,j) + λ = faces(τ)[i] + μ = faces(σ)[j] + ρ = CompScienceMeshes.intersection(λ,μ)[1] + + τ_verts = [τ[mod1(i+2,3)], τ[mod1(i,3)], τ[mod1(i+1,3)]] + σ_verts = [σ[mod1(j+2,3)], σ[mod1(j,3)], σ[mod1(j+1,3)]] + + T = coordtype(τ) + P = eltype(τ.vertices) + + U = T[] + V = P[] + for v in ρ.vertices + if CompScienceMeshes.isinside(λ,v) + push!(V,v) + push!(U, carttobary(λ,v)[1]) + end end + if length(U) == 2 + if U[1] < U[2] + temp = V[1] + V[1] = V[2] + V[2] = temp + end end + append!(τ_verts, V) + + U = T[] + V = P[] + for v in ρ.vertices + if CompScienceMeshes.isinside(μ,v) + push!(V,v) + push!(U, carttobary(μ,v)[1]) + end end + if length(U) == 2 + if U[1] < U[2] + temp = V[1] + V[1] = V[2] + V[2] = temp + end end + append!(σ_verts, V) + + τ_verts = push!(τ_verts[2:end], τ_verts[1]) + σ_verts = push!(σ_verts[2:end], σ_verts[1]) + + # @show τ_verts + + τ_charts = [ simplex(τ_verts[1], τ_verts[i], τ_verts[i+1]) for i in 2:length(τ_verts)-1 ] + σ_charts = [ simplex(σ_verts[1], σ_verts[i], σ_verts[i+1]) for i in 2:length(σ_verts)-1 ] + + signs = Int.(sign.(dot.(normal.(τ_charts),Ref(normal(τ))))) + τ_charts = flip_normal.(τ_charts,signs) + signs = Int.(sign.(dot.(normal.(σ_charts),Ref(normal(σ))))) + σ_charts = flip_normal.(σ_charts,signs) + + return τ_charts, σ_charts +end + + +function _num_common_vertices(τ, σ) + hits = 0 + T = coordtype(σ) + tol = eps(T) * 10^3 + for v in τ.vertices + for w in σ.vertices + if norm(v - w) < tol + hits += 1 + break + end end end + return hits +end + + +function _test_conformity(τ, σ) + if CompScienceMeshes.overlap(τ, σ) + # if _num_common_vertices(τ, σ) != 3 + # @infiltrate + # end + return _num_common_vertices(τ, σ) == 3 + end + + for eτ in faces(τ) + for eσ in faces(σ) + if CompScienceMeshes.overlap(eτ, eσ) + # if _num_common_vertices(τ, σ) != 2 + # @infiltrate + # end + return _num_common_vertices(τ, σ) == 2 + end + end end + + for u in τ.vertices + for v in σ.vertices + su = simplex([u], Val{0}) + sv = simplex([v], Val{0}) + if CompScienceMeshes.overlap(su, sv) + # if _num_common_vertices(τ, σ) != 1 + # @infiltrate + # end + return _num_common_vertices(τ, σ) == 1 + end end end + + @assert _num_common_vertices(τ, σ) == 0 + return _num_common_vertices(τ, σ) == 0 +end \ No newline at end of file diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index 3c801ce5..534f9a46 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -11,14 +11,6 @@ struct DoubleNumWiltonSauterQStrat{R,S} sauter_schwab_common_vert::S end -struct DoubleNumSauterQstrat{R,S} - outer_rule::R - inner_rule::R - sauter_schwab_common_tetr::S - sauter_schwab_common_face::S - sauter_schwab_common_edge::S - sauter_schwab_common_vert::S -end struct DoubleNumQStrat{R} outer_rule::R diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index 88e6972f..ee92a1f8 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -95,6 +95,11 @@ function momintegrals!(op::Operator, test_chart = CompScienceMeshes.permute_vertices(test_chart, I) trial_chart = CompScienceMeshes.permute_vertices(trial_chart, J) + if rule isa SauterSchwabQuadrature.CommonEdge + @assert test_chart.vertices[1] ≈ trial_chart.vertices[1] + @assert test_chart.vertices[3] ≈ trial_chart.vertices[3] + end + igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, rule) @@ -102,7 +107,7 @@ function momintegrals!(op::Operator, QTrial = dof_perm_matrix(trial_local_space, J) out_temp = zeros(eltype(out), numfunctions(test_local_space),numfunctions(trial_local_space)) out_temp = QTest*G*QTrial' - out[1:numfunctions(test_local_space),1:numfunctions(trial_local_space)] = out_temp + out[1:numfunctions(test_local_space),1:numfunctions(trial_local_space)] .+= out_temp nothing end @@ -141,6 +146,7 @@ function momintegrals_test_refines_trial!(out, op, for (q,chart) in enumerate(trial_charts) qr = quadrule(op, test_local_space, trial_local_space, 1, test_chart, q ,chart, qd, quadstrat) + # @show qr Q = restrict(trial_local_space, trial_chart, chart) zlocal = zero(out) diff --git a/src/quadrature/selfsauterdnumotherwiseqstrat.jl b/src/quadrature/selfsauterdnumotherwiseqstrat.jl new file mode 100644 index 00000000..e96f303f --- /dev/null +++ b/src/quadrature/selfsauterdnumotherwiseqstrat.jl @@ -0,0 +1,44 @@ +struct SelfSauterOtherwiseDNumQStrat{R,S} + outer_rule::R + inner_rule::R + sauter_schwab_common::S +end + +function quaddata(op::IntegralOperator, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_charts, trial_charts, qs::SelfSauterOtherwiseDNumQStrat) + + T = coordtype(test_charts[1]) + + tqd = quadpoints(test_local_space, test_charts, (qs.outer_rule,)) + bqd = quadpoints(trial_local_space, trial_charts, (qs.inner_rule,)) + + leg = ( + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common,0,1)), + ) + + return (tpoints=tqd, bpoints=bqd, gausslegendre=leg) +end + + +function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, + qs::SelfSauterOtherwiseDNumQStrat) + + T = eltype(eltype(τ.vertices)) + hits = 0 + dtol = 1.0e3 * eps(T) + dmin2 = floatmax(T) + for t in τ.vertices + for s in σ.vertices + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < dtol) + end + end + + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[1]) + + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j],) +end \ No newline at end of file diff --git a/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl b/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl new file mode 100644 index 00000000..0c9ed1c5 --- /dev/null +++ b/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl @@ -0,0 +1,65 @@ +struct CommonFaceVertexSauterCommonEdgeWiltonPostitiveDistanceNumQStrat{R,S} + outer_rule_far::R + inner_rule_far::R + outer_rule_near::R + inner_rule_near::R + sauter_schwab_common_tetr::S + sauter_schwab_common_face::S + sauter_schwab_common_edge::S + sauter_schwab_common_vert::S +end + +function quaddata(op::IntegralOperator, test_local_space, bsis_local_space, + test_charts, bsis_charts, qs::CommonFaceVertexSauterCommonEdgeWiltonPostitiveDistanceNumQStrat) + + return quaddata(op, test_local_space, bsis_local_space, + test_charts, bsis_charts, DoubleNumWiltonSauterQStrat( + qs.outer_rule_far, + qs.inner_rule_far, + qs.outer_rule_near, + qs.inner_rule_near, + qs.sauter_schwab_common_tetr, + qs.sauter_schwab_common_face, + qs.sauter_schwab_common_edge, + qs.sauter_schwab_common_vert,)) +end + +function quadrule(op::IntegralOperator, g, f, i, τ, j, σ, + qd, qs::CommonFaceVertexSauterCommonEdgeWiltonPostitiveDistanceNumQStrat) + + T = eltype(eltype(τ.vertices)) + hits = 0 + dtol = 1.0e3 * eps(T) + dmin2 = floatmax(T) + for t in τ.vertices + for s in σ.vertices + d2 = LinearAlgebra.norm_sqr(t-s) + dmin2 = min(dmin2, d2) + hits += (d2 < dtol) + end + end + + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) + # hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) + if hits == 2 + return WiltonSERule( + qd.tpoints[2,i], + SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]),) + end + hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) + + h2 = volume(σ) + xtol2 = 0.2 * 0.2 + k2 = abs2(gamma(op)) + if max(dmin2*k2, dmin2/16h2) < xtol2 + return WiltonSERule( + qd.tpoints[2,i], + DoubleQuadRule( + qd.tpoints[2,i], + qd.bpoints[2,j],),) + end + + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j],) +end \ No newline at end of file diff --git a/src/utils/specialfns.jl b/src/utils/specialfns.jl index cf6e85b8..18cda970 100644 --- a/src/utils/specialfns.jl +++ b/src/utils/specialfns.jl @@ -1,4 +1,4 @@ - +import SpecialFunctions: erf struct Gaussian{T} scaling::T From e7164c483a23bc291d6889154c427b8e935a8eb0 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 10 May 2024 13:38:02 +0200 Subject: [PATCH 364/528] set version to 2.4.0 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 22df9faa..0b76ee60 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "2.3.0" +version = "2.4.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From e939dd8bf9fa798fd77ab8e873637c4f4f3acb55 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 13 May 2024 13:27:05 +0200 Subject: [PATCH 365/528] bring point coincidence checking in line with SSQ.jl - put in Infiltrator bulkheads to capture inconsistencies --- Project.toml | 2 + examples/nonconfmeshes_general.jl | 237 ++++++++++++++++++ src/BEAST.jl | 2 +- src/quadrature/doublenumsauterqstrat.jl | 6 +- src/quadrature/doublenumwiltonsauterqstrat.jl | 6 +- src/quadrature/nonconformingoverlapqrule.jl | 8 + src/quadrature/nonconformingtouchqrule.jl | 95 ++++++- src/quadrature/sauterschwabints.jl | 8 + .../selfsauterdnumotherwiseqstrat.jl | 6 +- .../cfcvsautercewiltonpdnumqstrat.jl | 6 +- src/volumeintegral/vieops.jl | 11 +- 11 files changed, 377 insertions(+), 10 deletions(-) create mode 100644 examples/nonconfmeshes_general.jl diff --git a/Project.toml b/Project.toml index 0b76ee60..8a7484b3 100644 --- a/Project.toml +++ b/Project.toml @@ -15,6 +15,7 @@ ExtendableSparse = "95c220a8-a1cf-11e9-0c77-dbfce5f500b3" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" +Infiltrator = "5903a43b-9cc3-4c30-8d17-598619ec4e9b" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" LiftedMaps = "d22a30c1-52ac-4762-a8c9-5838452405e0" @@ -43,6 +44,7 @@ FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4, 0.5, 1" FillArrays = "0.11, 0.12, 0.13, 1" IterativeSolvers = "0.9" +Infiltrator = "1.8.2" LiftedMaps = "0.5.1" LinearMaps = "3.7 - 3.9, 3.11.2" NestedUnitRanges = "0.2" diff --git a/examples/nonconfmeshes_general.jl b/examples/nonconfmeshes_general.jl new file mode 100644 index 00000000..23de52d7 --- /dev/null +++ b/examples/nonconfmeshes_general.jl @@ -0,0 +1,237 @@ +using CompScienceMeshes +using BEAST + +# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +# Γref = CompScienceMeshes.barycentric_refinement(Γ) + +h = 0.2*0.7 +M1 = meshcuboid(0.5, 1.0, 1.0, h) +M2 = meshcuboid(0.5, 1.0, 1.0, 1.2*h) +M2 = CompScienceMeshes.translate(M2, point(-0.5, 0, 0)) + +import Plotly +Plotly.plot([ + patch(M1,color="red", opacity=0.5), patch(M2,color="blue", opacity=0.5), + wireframe(M1), wireframe(M2)]) + + +X = raviartthomas(M1, BEAST.Continuity{:none}) +Y = raviartthomas(M2, BEAST.Continuity{:none}) + +# X = subset(X,1690:1692) +# Y = subset(Y,10141:10143) + +# X = subset(X,[1692]) +# Y = subset(Y,[10147]) + +# Y = buffachristiansen(Γ) + +κ, η = 1.0, 1.0 +t = Maxwell3D.singlelayer(wavenumber=κ) +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +e = (n × E) × n + +g = 6 +# ssstrat(g) = BEAST.DoubleNumSauterQstrat(7, 6, g, g, g, g) +ssstrat(g) = BEAST.DoubleNumWiltonSauterQStrat( + 7, 6, 10, 8, g, g, g+3, g) + +qs1 = BEAST.DoubleNumWiltonSauterQStrat( + 2, 3, 6, 7, 5, 5, 4, 3) +qs2 = BEAST.NonConformingIntegralOpQStrat(qs1) + +# A1 = assemble(t,Y,X, quadstrat=qs1, threading=BEAST.Threading{:single}) +A2 = assemble(t,Y,X, quadstrat=qs2, threading=BEAST.Threading{:single}) + +error() + +# @enter assemble(t,Y,X, quadstrat=qs2, threading=BEAST.Threading{:single}) + +import Plots +Plots.plotly() +step = 1 +rowidx = 10894 +Plots.plot(imag.(A1[rowidx,1:step:end])) +Plots.scatter!(imag.(A2[rowidx,1:step:end])) +val, pos = findmax(abs.(A1-A2)) + +rowidx = Y.fns[pos[1]][1].cellid +colidx = X.fns[pos[2]][1].cellid + +τ = chart(Γref, rowidx) +σ = chart(Γ, colidx) + +# τ = [ +# [0.18517788982868066, 0.05998671122432393, 0.9735312677690339], +# [0.1647116144395066, 0.0050896055947335095, 0.9807011064756881], +# [0.245326672427806, -0.06121735585211891, 0.9675056894602609]] + +# σ = [ +# [0.1133762981558132, -0.1176791567283826, 0.9865583769286949], +# [0.1647116144395066, 0.0050896055947335025, 0.9807011064756881], +# [0.08409655645120719, 0.07139656704158594, 0.9938965234911153]] + +# eτ = simplex( +# point(0.1647116144395066, 0.0050896055947335095, 0.9807011064756881), +# point(0.245326672427806, -0.06121735585211891, 0.9675056894602609)) + +# eσ = simplex( +# point(0.1647116144395066, 0.0050896055947335025, 0.9807011064756881), +# point(0.08409655645120719, 0.07139656704158594, 0.9938965234911153)) + +# CompScienceMeshes.overlap(eτ, eσ) + +# τ = τ.vertices +# σ = σ.vertices + +import Plotly +function Plotly.mesh3d(a::Vector{<:CompScienceMeshes.Simplex}; opacity=0.5, kwargs...) + T = coordtype(a[1]) + n = length(a) + X = Vector{T}(undef, 3*n) + Y = Vector{T}(undef, 3*n) + Z = Vector{T}(undef, 3*n) + for i in 1:n + X[3*(i-1)+1:3*i] = getindex.(a[i].vertices, 1) + Y[3*(i-1)+1:3*i] = getindex.(a[i].vertices, 2) + Z[3*(i-1)+1:3*i] = getindex.(a[i].vertices, 3) + end + + I = collect(0:3:3*(n-1)) + J = I .+ 1 + K = I .+ 2 + + return Plotly.mesh3d(x=X,y=Y,z=Z,i=I,j=J,k=K; opacity, kwargs...) +end + +m1 = Plotly.mesh3d([τ], opacity=0.5) +m2 = Plotly.mesh3d([σ], opacity=0.5) + + +# m1 = Plotly.mesh3d( +# x=getindex.(τ,1), +# y=getindex.(τ,2), +# z=getindex.(τ,3), +# i=[0], j=[1], k=[2]) +# m2 = Plotly.mesh3d( +# x=getindex.(σ,1), +# y=getindex.(σ,2), +# z=getindex.(σ,3), +# i=[0], j=[1], k=[2]) +Plotly.plot([m1,m2]) + + + + +isct1, clps1 = CompScienceMeshes.intersection_keep_clippings(τ,σ) +isct2, clps2 = CompScienceMeshes.intersection_keep_clippings(σ,τ) + + +m3 = Plotly.mesh3d(isct1, opacity=0.5, color="blue") +m4 = Plotly.mesh3d(isct2, opacity=0.5, color="red") +Plotly.plot([m3,m4]) + +m5 = Plotly.mesh3d(clps2[1], opacity=0.5, color="green") +m6 = Plotly.mesh3d(clps2[1][[1]], opacity=0.5, color="yellow") +Plotly.plot([m3,m6]) + +p = isct1[1] +q = clps2[1][1] +for (i,λ) in pairs(faces(p)) + for (j,μ) in pairs(faces(q)) + if CompScienceMeshes.overlap(λ, μ) + global gi = i + global gj = j + global qr = BEAST.NonConformingTouchQRule(qs1, i, j) +end end end + +𝒳 = refspace(X) +𝒴 = refspace(Y) +out10 = zeros(ComplexF64, 3, 3) +BEAST.momintegrals!(t, 𝒴, 𝒳, p, q, out10, BEAST.NonConformingTouchQRule(ssstrat(10), gi, gj)) +out20 = zeros(ComplexF64, 3, 3) +BEAST.momintegrals!(t, 𝒴, 𝒳, p, q, out20, BEAST.NonConformingTouchQRule(ssstrat(20), gi, gj)) + +@show out10[1,1] out20[1,1] + +τs, σs = BEAST._conforming_refinement_touching_triangles(p,q,2,3) +@assert length(τs) == 1 +@assert length(σs) == 2 +@assert volume(p) ≈ sum(volume.(τs)) +@assert volume(q) ≈ sum(volume.(σs)) +@assert all(volume.(τs) .> eps(Float64)*1e3) +@assert all(volume.(σs) .> eps(Float64)*1e3) +m7 = Plotly.mesh3d(τs[[1]], color="blue", opacity=0.5) +m8 = Plotly.mesh3d(σs[[1]], color="red", opacity=0.5) +Plotly.plot([m7,m8]) + +out1_10 = zeros(ComplexF64, 3, 3) +qs = ssstrat(10) +qd = quaddata(t, 𝒴, 𝒳, τs, σs, qs) +qr = quadrule(t, 𝒴, 𝒳, 1, τs[1], 1, σs[1], qd, qs) +BEAST.momintegrals!(t, 𝒴, 𝒳, τs[1], σs[1], out1_10, qr) +out1_20 = zeros(ComplexF64, 3, 3) +qs = ssstrat(20) +qd = quaddata(t, 𝒴, 𝒳, τs, σs, qs) +qr = quadrule(t, 𝒴, 𝒳, 1, τs[1], 1, σs[1], qd, qs) +BEAST.momintegrals!(t, 𝒴, 𝒳, τs[1], σs[1], out1_20, qr) +@show out1_10[1,1] out1_20[1,1] + +out2_10 = zeros(ComplexF64, 3, 3) +qs = ssstrat(10) +qd = quaddata(t, 𝒴, 𝒳, τs, σs, qs) +qr = quadrule(t, 𝒴, 𝒳, 1, τs[1], 1, σs[2], qd, qs) +BEAST.momintegrals!(t, 𝒴, 𝒳, τs[1], σs[2], out2_10, qr) +out2_20 = zeros(ComplexF64, 3, 3) +qs = ssstrat(20) +qd = quaddata(t, 𝒴, 𝒳, τs, σs, qs) +qr = quadrule(t, 𝒴, 𝒳, 1, τs[1], 2, σs[2], qd, qs) +BEAST.momintegrals!(t, 𝒴, 𝒳, τs[1], σs[2], out2_20, qr) +@show out2_10[1,1] out2_20[1,1] + + +out_ref = zeros(ComplexF64, 3, 3) +qrss = BEAST.SauterSchwabQuadrature.CommonVertex(g) +BEAST.momintegrals_test_refines_trial!(out_ref, t, + Y, rowidx, p, + X, colidx, q, + qrss, qs1) +@show out_ref + + +τ = simplex( + point(0.0, 0.0, 0.1666666666673599), + point(-0.12200846792768633, 0.0, 0.1220084679279961), + point(0.0, 0.0, 0.0)) + +σ = simplex( + point(0.0, 0.0, 0.1249999999997732), + point(0.0, 0.0, 0.2499999999994112), + point(0.0, 0.1038676364273151, 0.1864502994601513)) + +for (i,λ) in pairs(faces(τ)) + for (j,μ) in pairs(faces(σ)) + if CompScienceMeshes.overlap(λ, μ) + global gi = i + global gj = j + # global qr = BEAST.NonConformingTouchQRule(qs1, i, j) +end end end + + +τs, σs = BEAST._conforming_refinement_touching_triangles(τ,σ,gi,gj); +@assert volume(τ) ≈ sum(volume.(τs)) +@assert volume(σ) ≈ sum(volume.(σs)) + +Plotly.plot([Plotly.mesh3d(τs, color="red"), Plotly.mesh3d(σs, color="blue")]) +Plotly.plot([Plotly.mesh3d([τ], color="red"), Plotly.mesh3d([σ], color="blue")]) + + +τ = simplex( + point(0.0, 0.893151804804907, 0.4384102834058539), + point(0.0, 0.8931541172685571, 0.4383125008447698), + point(0.0, 0.8932364620235818, 0.43836004261125),) +σ = simplex( + point(0.0, 0.893151804804907, 0.4384102834058539), + point(0.0, 0.8931541172685571, 0.4383125008447698), + point(0.0, 0.8932364620235818, 0.43836004261125),) +CompScienceMeshes.overlap(τ, σ) \ No newline at end of file diff --git a/src/BEAST.jl b/src/BEAST.jl index 9788ad5f..26abf040 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -18,7 +18,7 @@ using LiftedMaps using AbstractTrees using NestedUnitRanges -# using Infiltrator +using Infiltrator import LinearAlgebra: cross, dot import LinearAlgebra: ×, ⋅ diff --git a/src/quadrature/doublenumsauterqstrat.jl b/src/quadrature/doublenumsauterqstrat.jl index 3cdbbef4..8e1bc94d 100644 --- a/src/quadrature/doublenumsauterqstrat.jl +++ b/src/quadrature/doublenumsauterqstrat.jl @@ -37,11 +37,15 @@ function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, for t in τ.vertices for s in σ.vertices d2 = LinearAlgebra.norm_sqr(t-s) + d = norm(t-s) dmin2 = min(dmin2, d2) - hits += (d2 < dtol) + # hits += (d2 < dtol) + hits += (d < dtol) end end + @assert hits <= 3 + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) diff --git a/src/quadrature/doublenumwiltonsauterqstrat.jl b/src/quadrature/doublenumwiltonsauterqstrat.jl index ad9dbb6b..2cdde450 100644 --- a/src/quadrature/doublenumwiltonsauterqstrat.jl +++ b/src/quadrature/doublenumwiltonsauterqstrat.jl @@ -26,11 +26,15 @@ function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, for t in τ.vertices for s in σ.vertices d2 = LinearAlgebra.norm_sqr(t-s) + d = norm(t-s) dmin2 = min(dmin2, d2) - hits += (d2 < dtol) + # hits += (d2 < dtol) + hits += (d < dtol) end end + @assert hits <= 3 + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) diff --git a/src/quadrature/nonconformingoverlapqrule.jl b/src/quadrature/nonconformingoverlapqrule.jl index 19164948..88c2b958 100644 --- a/src/quadrature/nonconformingoverlapqrule.jl +++ b/src/quadrature/nonconformingoverlapqrule.jl @@ -14,6 +14,14 @@ function momintegrals!(op, test_local_space, basis_local_space, for tclp in tclps append!(test_charts, tclp) end for bclp in bclps append!(bsis_charts, bclp) end + T = coordtype(test_chart) + h = max(volume(test_chart), volume(test_chart)) + test_charts = [ch for ch in test_charts if volume(ch) .> 1e6 * eps(T) * h] + bsis_charts = [ch for ch in bsis_charts if volume(ch) .> 1e6 * eps(T) * h] + + test_charts = [ch for ch in test_charts if volume(ch) .> 1e6 * eps(T)] + bsis_charts = [ch for ch in bsis_charts if volume(ch) .> 1e6 * eps(T)] + @assert volume(test_chart) ≈ sum(volume.(test_charts)) @assert volume(basis_chart) ≈ sum(volume.(bsis_charts)) diff --git a/src/quadrature/nonconformingtouchqrule.jl b/src/quadrature/nonconformingtouchqrule.jl index 09f049af..e930d4c8 100644 --- a/src/quadrature/nonconformingtouchqrule.jl +++ b/src/quadrature/nonconformingtouchqrule.jl @@ -14,21 +14,33 @@ function momintegrals!(op, test_locspace, bsis_locspace, i = qrule.test_overlapping_edge_index j = qrule.bsis_overlapping_edge_index + @assert volume(τ) > eps(T) * 1e3 + @assert volume(σ) > eps(T) * 1e3 τs, σs = _conforming_refinement_touching_triangles(τ,σ,i,j) # test conformity for a in τs for b in σs - @assert _test_conformity(a, b) + if !_test_conformity(a, b) + @infiltrate + end end end + volume(σ) ≈ sum(volume.(σs)) || @infiltrate + @assert volume(τ) ≈ sum(volume.(τs)) @assert volume(σ) ≈ sum(volume.(σs)) + + @assert all(volume.(τs) .> 1e3 * eps(T) * (volume(τ))) + @assert all(volume.(σs) .> 1e3 * eps(T) * (volume(σ))) qstrat = qrule.conforming_qstrat qdata = quaddata(op, test_locspace, bsis_locspace, τs, σs, qstrat) + any(volume.(τs) .< 1e-13) && @infiltrate + any(volume.(σs) .< 1e-13) && @infiltrate + for (p,tchart) in enumerate(τs) for (q,bchart) in enumerate(σs) qrule = quadrule(op, test_locspace, bsis_locspace, @@ -51,7 +63,7 @@ function momintegrals!(op, test_locspace, bsis_locspace, end end end end end end end -function _conforming_refinement_touching_triangles(τ,σ,i,j) +function _conforming_refinement_touching_triangles_bak(τ,σ,i,j) λ = faces(τ)[i] μ = faces(σ)[j] ρ = CompScienceMeshes.intersection(λ,μ)[1] @@ -105,6 +117,85 @@ function _conforming_refinement_touching_triangles(τ,σ,i,j) signs = Int.(sign.(dot.(normal.(σ_charts),Ref(normal(σ))))) σ_charts = flip_normal.(σ_charts,signs) + h = sqrt(volume(τ)) + τ_charts = τ_charts[volume.(τ_charts) .> 1e3 * eps(T) * h] + σ_charts = τ_charts[volume.(σ_charts) .> 1e3 * eps(T) * h] + + return τ_charts, σ_charts +end + + +function _conforming_refinement_touching_triangles(τ,σ,i,j) + λ = faces(τ)[i] + μ = faces(σ)[j] + ρ = CompScienceMeshes.intersection(λ,μ)[1] + + # τ_verts = [τ[mod1(i+2,3)], τ[mod1(i,3)], τ[mod1(i+1,3)]] + # σ_verts = [σ[mod1(j+2,3)], σ[mod1(j,3)], σ[mod1(j+1,3)]] + # τ_verts = [v for v in τ.vertices] + # σ_verts = [v for v in σ.vertices] + τ_verts = Array(τ.vertices) + σ_verts = Array(σ.vertices) + # @show typeof(τ_verts) + + T = coordtype(τ) + P = eltype(τ.vertices) + + U = T[] + V = P[] + for v in ρ.vertices + push!(V,v) + push!(U, carttobary(λ,v)[1]) + end + if U[1] < U[2] + temp = V[1] + V[1] = V[2] + V[2] = temp + end + p = mod1(i+2,3) + new_i = p <= i ? i+2 : i + insert!(τ_verts, p, V[2]) + insert!(τ_verts, p, V[1]) + + U = T[] + V = P[] + for v in ρ.vertices + push!(V,v) + push!(U, carttobary(μ,v)[1]) + end + if U[1] < U[2] + temp = V[1] + V[1] = V[2] + V[2] = temp + end + p = mod1(j+2,3) + new_j = p <= j ? j+2 : j + insert!(σ_verts, p, V[2]) + insert!(σ_verts, p, V[1]) + + # println(τ_verts) + # println(σ_verts) + + τ_charts = [ simplex(τ_verts[mod1(new_i,5)], τ_verts[mod1(new_i+s,5)], τ_verts[mod1(new_i+s+1,5)]) for s in 1:3 ] + σ_charts = [ simplex(σ_verts[mod1(new_j,5)], σ_verts[mod1(new_j+s,5)], σ_verts[mod1(new_j+s+1,5)]) for s in 1:3 ] + # σ_charts = [ simplex(σ_verts[1], σ_verts[i], σ_verts[i+1]) for i in 2:length(σ_verts)-1 ] + + h = volume(τ) + τ_charts = [ch for ch in τ_charts if volume(ch) .> 1e6 * eps(T) * h] + σ_charts = [ch for ch in σ_charts if volume(ch) .> 1e6 * eps(T) * h] + + τ_charts = [ch for ch in τ_charts if volume(ch) .> 1e6 * eps(T)] + σ_charts = [ch for ch in σ_charts if volume(ch) .> 1e6 * eps(T)] + + signs = Int.(sign.(dot.(normal.(τ_charts),Ref(normal(τ))))) + τ_charts = flip_normal.(τ_charts,signs) + signs = Int.(sign.(dot.(normal.(σ_charts),Ref(normal(σ))))) + σ_charts = flip_normal.(σ_charts,signs) + + + # τ_charts = τ_charts[volume.(τ_charts) .> 1e3 * eps(T) * h] + # σ_charts = τ_charts[volume.(σ_charts) .> 1e3 * eps(T) * h] + return τ_charts, σ_charts end diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index ee92a1f8..2de5f2de 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -92,6 +92,14 @@ function momintegrals!(op::Operator, trial_chart.vertices, rule) # permute_vertices reparametrizes the simplex without affecting the normal + if 0 in I || 0 in J + @show typeof(rule) I J + @show test_chart.vertices + @show trial_chart.vertices + @infiltrate + # error("on purpose") + end + test_chart = CompScienceMeshes.permute_vertices(test_chart, I) trial_chart = CompScienceMeshes.permute_vertices(trial_chart, J) diff --git a/src/quadrature/selfsauterdnumotherwiseqstrat.jl b/src/quadrature/selfsauterdnumotherwiseqstrat.jl index e96f303f..857d1b46 100644 --- a/src/quadrature/selfsauterdnumotherwiseqstrat.jl +++ b/src/quadrature/selfsauterdnumotherwiseqstrat.jl @@ -31,11 +31,15 @@ function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, for t in τ.vertices for s in σ.vertices d2 = LinearAlgebra.norm_sqr(t-s) + d = norm(t-s) dmin2 = min(dmin2, d2) - hits += (d2 < dtol) + # hits += (d2 < dtol) + hits += (d < dtol) end end + @assert hits <= 3 + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[1]) return DoubleQuadRule( diff --git a/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl b/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl index 0c9ed1c5..5726e610 100644 --- a/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl +++ b/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl @@ -34,11 +34,15 @@ function quadrule(op::IntegralOperator, g, f, i, τ, j, σ, for t in τ.vertices for s in σ.vertices d2 = LinearAlgebra.norm_sqr(t-s) + d = norm(t-s) dmin2 = min(dmin2, d2) - hits += (d2 < dtol) + # hits += (d2 < dtol) + hits += (d < dtol) end end + @assert hits <= 3 + hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) # hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) if hits == 2 diff --git a/src/volumeintegral/vieops.jl b/src/volumeintegral/vieops.jl index 26f19c61..90e2bd6e 100644 --- a/src/volumeintegral/vieops.jl +++ b/src/volumeintegral/vieops.jl @@ -320,8 +320,10 @@ function qr_volume(op::VolumeOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, q for (i,t) in enumerate(τ.vertices) for (j,s) in enumerate(σ.vertices) d2 = LinearAlgebra.norm_sqr(t-s) + d = norm(t-s) dmin2 = min(dmin2, d2) - if d2 < dtol + # if d2 < dtol + if d < dtol push!(idx_t,i) push!(idx_s,j) hits +=1 @@ -331,7 +333,7 @@ function qr_volume(op::VolumeOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, q end #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) - + @assert hits <= 4 hits == 4 && return SauterSchwab3D.CommonVolume6D_S(SauterSchwab3D.Singularity6DVolume(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[4])) hits == 3 && return SauterSchwab3D.CommonFace6D_S(SauterSchwab3D.Singularity6DFace(idx_t,idx_s),(qd.sing_qp[1],qd.sing_qp[2],qd.sing_qp[3])) @@ -363,8 +365,10 @@ function qr_boundary(op::BoundaryOperator, g::RefSpace, f::RefSpace, i, τ, j, for (i,t) in enumerate(τ.vertices) for (j,s) in enumerate(σ.vertices) d2 = LinearAlgebra.norm_sqr(t-s) + d = norm(t-s) dmin2 = min(dmin2, d2) - if d2 < dtol + # if d2 < dtol + if d < dtol push!(idx_t,i) push!(idx_s,j) hits +=1 @@ -373,6 +377,7 @@ function qr_boundary(op::BoundaryOperator, g::RefSpace, f::RefSpace, i, τ, j, end end + @assert hits <= 3 #singData = SauterSchwab3D.Singularity{D,hits}(idx_t, idx_s ) From 8398e18fd721156cc600c4c737af4ea59a14f431 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 13 May 2024 14:19:55 +0200 Subject: [PATCH 366/528] add examples meshes --- examples/assets/M1.in | 1009 +++++++++++++++++++++++++++++++++++++++++ examples/assets/M2.in | 574 +++++++++++++++++++++++ 2 files changed, 1583 insertions(+) create mode 100644 examples/assets/M1.in create mode 100644 examples/assets/M2.in diff --git a/examples/assets/M1.in b/examples/assets/M1.in new file mode 100644 index 00000000..909e09df --- /dev/null +++ b/examples/assets/M1.in @@ -0,0 +1,1009 @@ +1 +337 670 +0.0 0.0 0.0 +0.0 0.0 1.0 +0.5 0.0 1.0 +0.5 0.0 0.0 +0.0 1.0 0.0 +0.0 1.0 1.0 +0.5 1.0 1.0 +0.5 1.0 0.0 +0.0 0.0 0.1249999999997732 +0.0 0.0 0.2499999999994112 +0.0 0.0 0.3749999999990465 +0.0 0.0 0.4999999999986927 +0.0 0.0 0.624999999999011 +0.0 0.0 0.7499999999993407 +0.0 0.0 0.8749999999996704 +0.1249999999997056 0.0 1.0 +0.2499999999993463 0.0 1.0 +0.3749999999996704 0.0 1.0 +0.5 0.0 0.8749999999995015 +0.5 0.0 0.7500000000003476 +0.5 0.0 0.6250000000012154 +0.5 0.0 0.5000000000020615 +0.5 0.0 0.3750000000015626 +0.5 0.0 0.2500000000010417 +0.5 0.0 0.1250000000005209 +0.3750000000001738 0.0 0.0 +0.2500000000010307 0.0 0.0 +0.1250000000005209 0.0 0.0 +0.0 1.0 0.1249999999997732 +0.0 1.0 0.2499999999994112 +0.0 1.0 0.3749999999990465 +0.0 1.0 0.4999999999986927 +0.0 1.0 0.624999999999011 +0.0 1.0 0.7499999999993407 +0.0 1.0 0.8749999999996704 +0.1249999999997056 1.0 1.0 +0.2499999999993463 1.0 1.0 +0.3749999999996704 1.0 1.0 +0.5 1.0 0.8749999999995015 +0.5 1.0 0.7500000000003476 +0.5 1.0 0.6250000000012154 +0.5 1.0 0.5000000000020615 +0.5 1.0 0.3750000000015626 +0.5 1.0 0.2500000000010417 +0.5 1.0 0.1250000000005209 +0.3750000000001738 1.0 0.0 +0.2500000000010307 1.0 0.0 +0.1250000000005209 1.0 0.0 +0.0 0.1249999999997732 0.0 +0.0 0.2499999999994112 0.0 +0.0 0.3749999999990465 0.0 +0.0 0.4999999999986927 0.0 +0.0 0.624999999999011 0.0 +0.0 0.7499999999993407 0.0 +0.0 0.8749999999996704 0.0 +0.0 0.1249999999997732 1.0 +0.0 0.2499999999994112 1.0 +0.0 0.3749999999990465 1.0 +0.0 0.4999999999986927 1.0 +0.0 0.624999999999011 1.0 +0.0 0.7499999999993407 1.0 +0.0 0.8749999999996704 1.0 +0.5 0.1249999999997732 1.0 +0.5 0.2499999999994112 1.0 +0.5 0.3749999999990465 1.0 +0.5 0.4999999999986927 1.0 +0.5 0.624999999999011 1.0 +0.5 0.7499999999993407 1.0 +0.5 0.8749999999996704 1.0 +0.5 0.1249999999997732 0.0 +0.5 0.2499999999994112 0.0 +0.5 0.3749999999990465 0.0 +0.5 0.4999999999986927 0.0 +0.5 0.624999999999011 0.0 +0.5 0.7499999999993407 0.0 +0.5 0.8749999999996704 0.0 +0.3874424230244282 0.0 0.6879859346131602 +0.3917468245269385 0.0 0.4375000000016365 +0.108253175472739 0.0 0.3124999999992288 +0.1104767907802638 0.0 0.5495034007864623 +0.3941534943676811 0.0 0.1880296711609176 +0.1058465056321822 0.0 0.8119703288394107 +0.3204049132914262 0.0 0.9011617204021374 +0.1795950867088213 0.0 0.098838279597838 +0.3921479361669236 0.0 0.3125882785281645 +0.2888568906084722 0.0 0.3750147130896808 +0.2855166394278844 0.0 0.497289094523428 +0.1752404735809824 0.0 0.4375000000021429 +0.2980878577804649 0.0 0.2493377043688435 +0.1082531754732787 0.0 0.6874999999992824 +0.2028728677521018 0.0 0.7506794609007599 +0.2024306704562214 0.0 0.6333638102810373 +0.3946405315505427 0.0 0.556458537272301 +0.4027268679816424 0.0 0.8199103159451206 +0.09655573176801091 0.0 0.1801706731567673 +0.3035071324072291 0.0 0.1118491644533768 +0.1965628913197734 0.0 0.8881520866595964 +0.2791806972154096 0.0 0.6919651983778352 +0.07647483778489925 0.0 0.4374999999988696 +0.2120651139228778 0.0 0.3146474036946574 +0.408493649053709 0.0 0.9084936490536635 +0.4084936490535773 0.0 0.09150635094660889 +0.0915063509465559 0.0 0.09150635094635556 +0.09150635094628985 0.0 0.9084936490535431 +0.1999010530413667 0.0 0.2052734479857873 +0.300020222520291 0.0 0.7968205916019144 +0.2969510646885307 0.0 0.5956725561948821 +0.2031529418856968 0.0 0.5371732103368083 +0.0 0.5670536380046087 0.8943758686552945 +0.0 0.5624999999988518 0.1082531754730394 +0.0 0.1124243810245421 0.5714876988465326 +0.0 0.8808310019958672 0.5741214795607947 +0.0 0.112037015619272 0.3016592931610405 +0.0 0.8959570073500618 0.3197922505577883 +0.0 0.3128108659840297 0.8955068139137721 +0.0 0.3124999999992842 0.1082531754728282 +0.0 0.8076656081751146 0.8945379619478856 +0.0 0.120769536763528 0.813090642376082 +0.0 0.8119703288394229 0.1058465056321634 +0.0 0.6874117214724684 0.1078520638329271 +0.0 0.6236899155261182 0.2156916159880773 +0.0 0.4997816525869273 0.2163705617862025 +0.0 0.5609498899663387 0.3238532227145602 +0.0 0.4372052570918011 0.3245858442746743 +0.0 0.49969252450903 0.4328327042500744 +0.0 0.6162104309147753 0.4337567545930024 +0.0 0.5593815006335847 0.5422849894666124 +0.0 0.4369290041899559 0.5414057297743196 +0.0 0.4993850841365262 0.6426639661559268 +0.0 0.6276311351227919 0.6463285523418856 +0.0 0.3748023480540149 0.6483998471250553 +0.0 0.3157952145740176 0.5391261956275301 +0.0 0.4374999999990491 0.7577722283096371 +0.0 0.2332538498070924 0.6405275871648053 +0.0 0.7505502180075261 0.2051526528524255 +0.0 0.1103830098873939 0.6904888009980186 +0.0 0.3749563698701903 0.2164800353587819 +0.0 0.3160693983617056 0.3268195465292577 +0.0 0.6874999999992156 0.8917468245266825 +0.0 0.7537479060341656 0.7865878827521369 +0.0 0.8855247200895949 0.7967657096188994 +0.0 0.8170240472073322 0.6845448607410298 +0.0 0.6343680478893469 0.7749720748802604 +0.0 0.1273937278648768 0.4354123541366469 +0.0 0.4398459425664173 0.8866050300249827 +0.0 0.8906292179090657 0.4406522883524269 +0.0 0.7834936490542321 0.4999999999989449 +0.0 0.7952549537499449 0.3843067512267823 +0.0 0.2310045288704008 0.2274878048597269 +0.0 0.1866262784732894 0.1058176347971227 +0.0 0.4374563370754987 0.108226158015142 +0.0 0.3749999999993984 0.4330127018913694 +0.0 0.2585921732017576 0.4335162638741182 +0.0 0.3124999999994057 0.7577722283095741 +0.0 0.1918537211810209 0.9070743513264233 +0.0 0.8856109484396917 0.1966413099095797 +0.0 0.1038676364273152 0.1864502994601513 +0.0 0.695482497809001 0.326840676787839 +0.0 0.6775056216160694 0.5255800299582942 +0.0 0.2055883939701014 0.7454468522927225 +0.0 0.2090770309821333 0.5255549114488995 +0.0 0.9166759538585588 0.6860864099838151 +0.0 0.2090866243337802 0.3427906503125625 +0.0 0.7189163598558483 0.6972086508131028 +0.0 0.5279873263605452 0.8039633961756577 +0.0 0.09150635094629671 0.9084936490535541 +0.0 0.9084936490535542 0.09150635094629654 +0.0 0.09150635094593877 0.09150635094593877 +0.0 0.9084936490536488 0.9084936490536488 +0.0 0.7540175483468565 0.6014818206493495 +0.0 0.7165063509454275 0.4374999999991191 +0.0 0.3686845263394476 0.8247185888446038 +0.0 0.5502673705008129 0.7223808150408089 +0.0 0.2510355733930868 0.8253550026151322 +0.0 0.8052157255528772 0.2876237401409069 +0.391746824527261 0.3124999999992288 1.0 +0.3917468245269606 0.5624999999988518 1.0 +0.1082531754728408 0.4374999999988965 1.0 +0.102316632530284 0.6804156785873245 1.0 +0.3076656081752543 0.894537961948085 1.0 +0.1058465056316792 0.1880296711597031 1.0 +0.3204049132913607 0.0988382795978682 1.0 +0.1877360370596645 0.8943230240947262 1.0 +0.2469258655412699 0.7945981967009401 1.0 +0.3845501428764332 0.7979100401831004 1.0 +0.3067607882128057 0.6775841068209039 1.0 +0.105678205107362 0.5613192797635527 1.0 +0.2088374798569323 0.5008063548263575 1.0 +0.2099318161495282 0.3751343924702438 1.0 +0.3247595264185403 0.4374999999986193 1.0 +0.1067563080332205 0.3126106772711187 1.0 +0.2015276340457712 0.2493613840553483 1.0 +0.4034442682320421 0.1801706731567887 1.0 +0.1964648422157708 0.1118508903778972 1.0 +0.117489182722353 0.802350626167754 1.0 +0.2013945080314591 0.6170446839893258 1.0 +0.4166115511232398 0.6825988294002414 1.0 +0.2878811751500963 0.5617818348618663 1.0 +0.2877069144461185 0.314667883559932 1.0 +0.4235251622148318 0.4374999999988697 1.0 +0.4084936490535855 0.09150635094626534 1.0 +0.4084936490536801 0.9084936490536802 1.0 +0.09150635094588741 0.09150635094590549 1.0 +0.09150635094587027 0.9084936490539626 1.0 +0.3000915333581194 0.2061138508397521 1.0 +0.1939393929793829 0.7136238347383141 1.0 +0.5 0.894375868655086 0.4329463619958044 +0.5 0.1082531754729179 0.4375000000015332 +0.5 0.571487698846054 0.8875756189756092 +0.5 0.5741214795607102 0.119168998004383 +0.5 0.1082531754726617 0.6875000000006011 +0.5 0.8955068139140753 0.687189134016003 +0.5 0.3197922505576257 0.1040429926499073 +0.5 0.301659293160697 0.8879629843806891 +0.5 0.8945379619477741 0.1923343918252038 +0.5 0.8130906423761161 0.8792304632365157 +0.5 0.1058465056323024 0.1880296711609376 +0.5 0.1078520638329425 0.3125882785282355 +0.5 0.2156916159879368 0.3763100844746096 +0.5 0.2163705617858266 0.5002183474134949 +0.5 0.3238532227140545 0.4390501100342684 +0.5 0.3245858442739504 0.5627947429086161 +0.5 0.4328327042491723 0.5003074754916487 +0.5 0.4337567545923268 0.383789569085849 +0.5 0.5422849894654955 0.4406184993672195 +0.5 0.5414057297730945 0.5630709958106961 +0.5 0.6426639661546197 0.5006149158643687 +0.5 0.6463285523409318 0.3723688648781147 +0.5 0.648399847123551 0.6251976519466464 +0.5 0.5391261956262033 0.6842047854264401 +0.5 0.7577722283079495 0.562500000001865 +0.5 0.6405275871638941 0.7667461501933093 +0.5 0.2051526528523045 0.2494497819928434 +0.5 0.8917468245265333 0.3125000000012341 +0.5 0.7865878827518458 0.246252093966229 +0.5 0.7967657096187415 0.1144752799106367 +0.5 0.6845448607408022 0.1829759527931888 +0.5 0.77497207487972 0.3656319521113494 +0.5 0.6904888009978722 0.8896169901127362 +0.5 0.2164800353582474 0.6250436301299697 +0.5 0.3268195465283675 0.6839306016384717 +0.5 0.886605030024842 0.5601540574339758 +0.5 0.4406522883523321 0.1093707820909126 +0.5 0.499999999998945 0.2165063509456713 +0.5 0.3843067512265817 0.204745046250173 +0.5 0.435412354136021 0.8726062721352169 +0.5 0.1082261580149423 0.5625436629248126 +0.5 0.2274878048591829 0.7689954711295537 +0.5 0.105817634796763 0.8133737215264445 +0.5 0.7577722283077662 0.6875000000012509 +0.5 0.4330127018902896 0.6250000000010248 +0.5 0.4335162638730714 0.7414078267985001 +0.5 0.9070743513264905 0.8081462788187956 +0.5 0.1864502994599095 0.8961323635725842 +0.5 0.1966413099094998 0.1143890515604102 +0.5 0.3268406767874762 0.3045175021914229 +0.5 0.5255800299577877 0.3224943783845682 +0.5 0.7454468522921446 0.7944116060301174 +0.5 0.5255549114478966 0.7909229690181839 +0.5 0.6860864099837212 0.0833240461416417 +0.5 0.3427906503117716 0.7909133756662735 +0.5 0.8039633961749467 0.4720126736401245 +0.5 0.6972086508126907 0.2810836401448817 +0.5 0.09150635094584145 0.908493649053964 +0.5 0.9084936490536776 0.9084936490536318 +0.5 0.09150635094641765 0.09150635094661799 +0.5 0.9084936490535275 0.09150635094652358 +0.5 0.6014818206490755 0.245982451653818 +0.5 0.8247185888439466 0.6313154736608839 +0.5 0.4374999999988745 0.283493649055028 +0.5 0.7223808150397706 0.4497326295000922 +0.5 0.8253550026150741 0.7489644266067749 +0.5 0.2876237401402548 0.194784274447189 +0.1023166325302679 0.3195843214106644 0.0 +0.1082531754730394 0.5624999999988518 0.0 +0.3846839867109718 0.426516278510507 0.0 +0.3976833674697007 0.680415678587349 0.0 +0.1923343918249383 0.8945379619482097 0.0 +0.3076656081763919 0.1054620380513241 0.0 +0.3122639629403485 0.894323024094841 0.0 +0.2530741344588454 0.7945981967011437 0.0 +0.1154498571236755 0.7979100401832013 0.0 +0.1932392117872833 0.6775841068210785 0.0 +0.1877360370611273 0.1056769759046468 0.0 +0.2469258655419687 0.2054018032976537 0.0 +0.3845501428770587 0.2020899598158116 0.0 +0.3077361438196501 0.3207815340793625 0.0 +0.3917468245270575 0.5624999999988518 0.0 +0.2905799906466582 0.5083224996962605 0.0 +0.1017775495552221 0.434767294664808 0.0 +0.1174891827227065 0.197649373831149 0.0 +0.3825108172776542 0.8023506261678207 0.0 +0.2069930716957313 0.3749999999988985 0.0 +0.2980792419915447 0.6184941615069081 0.0 +0.08338844887679964 0.6825988294002967 0.0 +0.4162658773658106 0.3124999999992288 0.0 +0.2166508075061639 0.5686840429211586 0.0 +0.091506350946844 0.09150635094664367 0.0 +0.09150635094646144 0.9084936490535899 0.0 +0.4084936490542588 0.09150635094572702 0.0 +0.408493649054128 0.9084936490539932 0.0 +0.1954634844235517 0.4765633337193599 0.0 +0.1939393929787376 0.2863761652592501 0.0 +0.3060606070205576 0.7136238347384073 0.0 +0.2845212715646553 0.4147109486860401 0.0 +0.3874424230244282 1.0 0.6879859346131602 +0.3917468245269385 1.0 0.4375000000016365 +0.108253175472739 1.0 0.3124999999992288 +0.1104767907802638 1.0 0.5495034007864623 +0.3941534943676811 1.0 0.1880296711609176 +0.1058465056321822 1.0 0.8119703288394109 +0.3204049132914262 1.0 0.9011617204021374 +0.1795950867088213 1.0 0.098838279597838 +0.3921479361669236 1.0 0.3125882785281645 +0.2888568906084722 1.0 0.3750147130896808 +0.2855166394278844 1.0 0.497289094523428 +0.1752404735809824 1.0 0.4375000000021429 +0.2980878577804649 1.0 0.2493377043688435 +0.108253175473279 1.0 0.6874999999992826 +0.2028728677521019 1.0 0.7506794609007601 +0.2024306704562215 1.0 0.6333638102810374 +0.3946405315505428 1.0 0.556458537272301 +0.4027268679816423 1.0 0.8199103159451206 +0.09655573176801091 1.0 0.1801706731567673 +0.3035071324072291 1.0 0.1118491644533768 +0.1965628913197734 1.0 0.8881520866595967 +0.2791806972154096 1.0 0.6919651983778353 +0.07647483778489925 1.0 0.4374999999988696 +0.2120651139228778 1.0 0.3146474036946574 +0.408493649053709 1.0 0.9084936490536635 +0.4084936490535773 1.0 0.09150635094660889 +0.0915063509465559 1.0 0.09150635094635556 +0.09150635094628991 1.0 0.908493649053543 +0.1999010530413667 1.0 0.2052734479857873 +0.3000202225202909 1.0 0.7968205916019147 +0.2969510646885307 1.0 0.5956725561948821 +0.2031529418856968 1.0 0.5371732103368083 +10 113 157 +9 157 168 +9 10 157 +1 9 168 +49 1 168 +150 49 168 +50 49 150 +116 50 150 +149 116 150 +157 150 168 +149 150 157 +113 149 157 +51 50 116 +51 116 151 +52 51 151 +110 52 151 +122 110 151 +137 122 151 +116 137 151 +137 116 149 +153 138 163 +138 149 163 +138 137 149 +124 138 152 +124 137 138 +124 122 137 +123 122 124 +123 124 125 +128 125 152 +125 124 152 +152 138 153 +132 152 153 +132 153 161 +113 144 163 +149 113 163 +10 11 113 +113 11 144 +11 12 144 +144 153 163 +153 144 161 +80 99 88 +12 99 80 +11 99 12 +88 99 79 +79 99 11 +10 79 11 +79 105 100 +100 105 89 +79 100 88 +88 100 86 +87 88 86 +87 108 88 +293 290 302 +274 290 293 +293 302 305 +290 275 302 +52 275 290 +51 52 290 +274 51 290 +50 51 274 +50 274 291 +291 274 303 +274 293 303 +293 287 303 +287 285 303 +285 291 303 +285 284 291 +291 284 298 +279 284 285 +49 50 291 +49 291 298 +1 49 298 +95 103 84 +28 103 1 +28 1 298 +1 103 9 +9 95 10 +10 95 79 +9 103 95 +84 105 95 +95 105 79 +96 105 84 +27 96 84 +84 103 28 +279 27 284 +284 28 298 +27 84 28 +27 28 284 +26 96 27 +26 27 279 +26 102 96 +26 279 300 +4 102 26 +4 26 300 +25 4 266 +25 102 4 +4 70 266 +70 4 300 +286 70 300 +70 255 266 +70 71 255 +71 70 286 +286 285 287 +279 286 300 +279 285 286 +233 255 273 +233 217 255 +255 217 266 +217 25 266 +24 25 217 +24 81 25 +81 102 25 +85 89 81 +96 102 81 +89 96 81 +89 105 96 +86 100 89 +86 89 85 +78 86 85 +78 87 86 +78 93 87 +22 93 78 +78 85 23 +208 23 218 +22 23 208 +22 78 23 +208 219 220 +208 218 219 +219 218 233 +219 233 256 +218 217 233 +24 217 218 +24 85 81 +23 85 24 +23 24 218 +244 257 270 +257 224 270 +224 256 270 +256 245 270 +221 219 256 +224 221 256 +220 219 221 +223 221 224 +223 224 225 +243 244 245 +245 244 270 +213 243 245 +213 245 273 +245 256 273 +256 233 273 +255 213 273 +287 276 296 +287 293 305 +286 287 296 +71 286 296 +71 213 255 +72 71 296 +71 72 213 +276 72 296 +213 72 243 +73 72 276 +72 73 243 +73 276 288 +288 276 289 +289 276 305 +276 287 305 +302 289 305 +294 289 297 +277 288 294 +288 289 294 +73 210 243 +74 73 288 +73 74 210 +277 74 288 +210 74 260 +75 74 277 +74 75 260 +75 236 260 +75 277 292 +292 277 304 +283 281 304 +294 283 304 +277 294 304 +236 237 260 +237 235 263 +237 263 268 +237 210 260 +210 237 268 +244 210 268 +243 210 244 +228 225 257 +225 224 257 +227 225 228 +227 228 271 +228 238 271 +238 228 263 +263 228 268 +228 257 268 +257 244 268 +44 43 234 +43 44 314 +44 310 314 +43 207 234 +215 44 234 +215 234 235 +235 234 238 +235 238 263 +234 207 238 +238 207 262 +238 262 271 +262 231 271 +207 242 262 +207 42 242 +42 43 307 +43 42 207 +307 43 314 +42 307 322 +307 316 322 +307 315 316 +307 314 315 +315 314 318 +315 318 329 +318 325 334 +318 310 325 +325 310 331 +314 310 318 +310 45 331 +44 45 310 +45 44 215 +45 215 267 +215 236 267 +215 235 236 +236 235 237 +281 280 292 +292 280 301 +278 280 281 +281 292 304 +76 75 292 +75 76 236 +236 76 267 +76 292 301 +8 76 301 +76 8 267 +45 8 331 +8 45 267 +46 8 301 +8 46 331 +280 46 301 +46 325 331 +278 47 280 +47 46 280 +46 47 325 +48 47 278 +47 313 325 +313 48 332 +47 48 313 +48 278 299 +278 282 299 +278 281 282 +282 281 283 +325 313 334 +313 324 334 +324 308 334 +30 308 324 +29 324 332 +29 30 324 +54 55 282 +282 55 299 +55 5 299 +48 5 332 +5 48 299 +324 313 332 +5 29 332 +5 55 167 +29 5 167 +156 29 167 +30 29 156 +156 135 175 +119 135 156 +119 156 167 +55 119 167 +55 54 119 +54 53 120 +119 54 120 +119 120 135 +120 121 135 +135 121 158 +120 110 121 +121 110 122 +53 110 120 +53 52 110 +52 53 275 +275 53 295 +275 297 302 +297 289 302 +283 294 297 +275 283 297 +283 275 295 +282 283 295 +54 282 295 +53 54 295 +126 125 127 +123 125 126 +123 126 158 +121 122 123 +121 123 158 +148 158 171 +158 126 171 +126 159 171 +159 147 171 +147 148 171 +147 146 148 +146 114 148 +158 148 175 +148 114 175 +114 156 175 +135 158 175 +114 30 156 +31 30 114 +31 114 146 +32 31 146 +32 309 328 +31 32 328 +317 308 328 +309 317 328 +30 31 308 +308 31 328 +308 329 334 +308 317 329 +329 318 334 +317 315 329 +316 315 317 +316 317 337 +321 336 337 +309 321 337 +309 319 321 +317 309 337 +321 320 327 +319 320 321 +34 311 319 +33 34 319 +309 33 319 +32 33 309 +112 32 146 +33 32 112 +34 33 162 +33 112 162 +141 34 162 +140 142 164 +142 141 162 +112 142 162 +164 142 170 +142 112 170 +112 146 147 +112 147 170 +147 159 170 +159 130 170 +130 164 170 +130 143 164 +143 130 173 +130 129 173 +127 129 130 +127 130 159 +126 127 159 +61 60 179 +61 179 195 +195 179 206 +186 184 206 +196 186 206 +179 196 206 +179 187 196 +196 188 198 +187 188 196 +179 60 187 +60 59 187 +59 60 109 +59 109 145 +145 109 165 +109 143 165 +165 143 173 +133 165 173 +143 140 164 +109 139 143 +139 140 143 +139 117 140 +61 117 139 +109 60 139 +60 61 139 +61 62 117 +117 62 169 +141 117 169 +140 117 141 +140 141 142 +35 34 141 +35 141 169 +6 35 169 +62 6 169 +35 6 333 +36 6 204 +6 36 333 +6 62 204 +62 195 204 +62 61 195 +34 35 311 +311 35 333 +319 311 320 +320 311 326 +326 311 333 +320 326 335 +184 195 206 +184 183 195 +195 183 204 +180 183 184 +180 37 183 +183 36 204 +36 326 333 +36 37 326 +37 36 183 +38 37 180 +37 312 326 +312 38 330 +38 180 202 +37 38 312 +38 7 330 +7 38 202 +323 312 330 +39 7 265 +7 39 330 +7 69 265 +69 7 202 +185 69 202 +69 216 265 +69 68 216 +68 69 185 +216 68 239 +185 184 186 +180 185 202 +180 184 185 +216 258 272 +253 216 272 +216 253 265 +40 39 253 +253 39 265 +39 40 323 +39 323 330 +40 306 323 +323 306 335 +312 323 335 +326 312 335 +327 320 335 +321 327 336 +327 306 336 +306 327 335 +336 316 337 +306 322 336 +322 316 336 +306 41 322 +41 212 242 +42 41 242 +41 42 322 +242 231 262 +231 242 269 +242 212 269 +250 231 269 +250 269 272 +258 250 272 +269 212 272 +212 253 272 +212 40 253 +41 40 212 +40 41 306 +230 229 232 +230 232 259 +232 229 250 +232 250 258 +229 231 250 +229 227 231 +231 227 271 +226 225 227 +223 225 226 +226 230 251 +226 229 230 +226 227 229 +209 246 259 +232 209 259 +209 232 239 +239 232 258 +216 239 258 +186 177 197 +185 186 197 +68 185 197 +68 67 239 +67 68 197 +67 209 239 +177 67 197 +67 66 209 +66 67 177 +209 66 246 +190 177 198 +177 186 198 +186 196 198 +188 190 198 +190 189 199 +177 190 200 +66 177 200 +66 65 246 +65 66 200 +190 176 200 +65 214 246 +176 65 200 +65 64 214 +64 65 176 +176 199 205 +199 192 205 +176 190 199 +214 248 261 +246 214 261 +252 246 261 +246 252 259 +222 223 251 +223 226 251 +222 221 223 +220 221 222 +220 222 240 +240 222 241 +241 222 251 +240 241 248 +248 241 261 +241 252 261 +241 251 252 +251 230 252 +252 230 259 +20 21 211 +20 77 21 +211 21 247 +211 240 248 +240 211 247 +220 240 247 +208 220 247 +22 208 247 +21 93 22 +21 22 247 +77 93 21 +93 107 87 +77 107 93 +107 108 87 +77 106 98 +98 107 77 +92 107 98 +98 106 91 +97 106 83 +83 106 94 +94 106 77 +20 94 77 +19 101 94 +19 94 20 +19 20 249 +20 211 249 +19 249 264 +249 254 264 +249 248 254 +211 248 249 +248 214 254 +182 193 205 +194 182 205 +193 176 205 +64 176 193 +214 64 254 +64 63 254 +63 64 193 +254 63 264 +63 193 201 +193 182 201 +3 63 201 +63 3 264 +3 101 19 +3 19 264 +94 101 83 +18 3 201 +18 101 3 +17 83 18 +17 18 182 +182 18 201 +83 101 18 +17 97 83 +17 182 194 +16 97 17 +16 17 194 +16 194 203 +16 104 97 +91 106 97 +90 91 82 +91 97 82 +97 104 82 +82 104 15 +14 82 15 +15 104 2 +2 16 203 +2 104 16 +56 2 203 +181 56 203 +57 56 181 +194 181 203 +192 181 194 +192 194 205 +189 192 199 +189 191 192 +191 181 192 +57 181 191 +58 57 191 +178 58 191 +59 58 178 +59 178 187 +187 178 188 +188 178 189 +188 189 190 +189 178 191 +88 108 80 +92 108 107 +80 108 92 +80 92 90 +92 98 91 +90 92 91 +14 90 82 +13 90 14 +80 90 13 +12 80 13 +111 134 161 +144 111 161 +12 111 144 +12 13 111 +111 13 136 +13 14 136 +136 118 160 +134 136 160 +134 111 136 +131 132 134 +134 132 161 +129 128 131 +131 128 132 +132 128 152 +127 125 128 +127 128 129 +129 133 173 +129 131 133 +133 131 154 +154 134 160 +131 134 154 +115 172 174 +154 160 174 +172 154 174 +133 154 172 +115 145 172 +145 133 172 +133 145 165 +58 59 145 +115 58 145 +57 58 115 +57 115 155 +155 115 174 +160 118 174 +118 155 174 +56 57 155 +56 155 166 +155 118 166 +2 56 166 +15 2 166 +14 15 118 +118 15 166 +14 118 136 diff --git a/examples/assets/M2.in b/examples/assets/M2.in new file mode 100644 index 00000000..45a98a3a --- /dev/null +++ b/examples/assets/M2.in @@ -0,0 +1,574 @@ +1 +192 380 +-0.5 0.0 0.0 +-0.5 0.0 1.0 +0.0 0.0 1.0 +0.0 0.0 0.0 +-0.5 1.0 0.0 +-0.5 1.0 1.0 +0.0 1.0 1.0 +0.0 1.0 0.0 +-0.5 0.0 0.1666666666663218 +-0.5 0.0 0.3333333333325029 +-0.5 0.0 0.4999999999986942 +-0.5 0.0 0.6666666666657899 +-0.5 0.0 0.8333333333328949 +-0.3333333333337485 0.0 1.0 +-0.1666666666671051 0.0 1.0 +0.0 0.0 0.8333333333331017 +0.0 0.0 0.6666666666675913 +0.0 0.0 0.5000000000020592 +0.0 0.0 0.3333333333347197 +0.0 0.0 0.1666666666673599 +-0.16666666666620428 0.0 0.0 +-0.3333333333326401 0.0 0.0 +-0.5 1.0 0.1666666666663218 +-0.5 1.0 0.3333333333325029 +-0.5 1.0 0.4999999999986942 +-0.5 1.0 0.6666666666657899 +-0.5 1.0 0.8333333333328949 +-0.3333333333337485 1.0 1.0 +-0.1666666666671051 1.0 1.0 +0.0 1.0 0.8333333333331017 +0.0 1.0 0.6666666666675913 +0.0 1.0 0.5000000000020592 +0.0 1.0 0.3333333333347197 +0.0 1.0 0.1666666666673599 +-0.16666666666620428 1.0 0.0 +-0.3333333333326401 1.0 0.0 +-0.5 0.1666666666663218 0.0 +-0.5 0.3333333333325029 0.0 +-0.5 0.4999999999986942 0.0 +-0.5 0.6666666666657899 0.0 +-0.5 0.8333333333328949 0.0 +-0.5 0.1666666666663218 1.0 +-0.5 0.3333333333325029 1.0 +-0.5 0.4999999999986942 1.0 +-0.5 0.6666666666657899 1.0 +-0.5 0.8333333333328949 1.0 +0.0 0.1666666666663218 1.0 +0.0 0.3333333333325029 1.0 +0.0 0.4999999999986942 1.0 +0.0 0.6666666666657899 1.0 +0.0 0.8333333333328949 1.0 +0.0 0.1666666666663218 0.0 +0.0 0.3333333333325029 0.0 +0.0 0.4999999999986942 0.0 +0.0 0.6666666666657899 0.0 +0.0 0.8333333333328949 0.0 +-0.12758290745376222 0.0 0.749233318682745 +-0.14433756729720648 0.0 0.4166666666683895 +-0.2499999999999593 0.0 0.1563666820692418 +-0.3556624327026136 0.0 0.583333333332242 +-0.3556624327030097 0.0 0.4166666666655985 +-0.3724170925460668 0.0 0.7492333186820921 +-0.140254853105448 0.0 0.5833000710948419 +-0.1277243392043234 0.0 0.2548742700117366 +-0.3722756607959292 0.0 0.2548742700107636 +-0.2500000000002154 0.0 0.8561293975766916 +-0.2499999999988981 0.0 0.6666666666678633 +-0.24999999999907008 0.0 0.5000000000013808 +-0.25000000000049016 0.0 0.3337127944750626 +-0.37799153207151803 0.0 0.1220084679282038 +-0.12200846792768633 0.0 0.1220084679279961 +-0.1211598774260445 0.0 0.878840122574012 +-0.3788401225744618 0.0 0.8788401225742289 +-0.5 0.5797429994424209 0.832554293016586 +-0.5 0.5774318462589461 0.1587006338148569 +-0.5 0.143551118310792 0.5819711637299791 +-0.5 0.8508651158937737 0.5823730257250648 +-0.5 0.2653042698421442 0.1509495412396307 +-0.5 0.8486919098558521 0.2623742252839392 +-0.5 0.2444948570943373 0.8570246023043264 +-0.5 0.143420043479498 0.4178018080007029 +-0.5 0.28839113912686 0.4999621619540175 +-0.5 0.3022135546980175 0.6915391893516348 +-0.5 0.4330127018914469 0.5833333333312153 +-0.5 0.4329653693130474 0.4166603603242623 +-0.5 0.5743994663660459 0.4999989489408719 +-0.5 0.5773502691887096 0.6666666666638472 +-0.5 0.5743328095552369 0.3363888904820139 +-0.5 0.7258696540860385 0.4187894882451219 +-0.5 0.4258960160381263 0.251279709339806 +-0.5 0.8607386135250659 0.7553478405345799 +-0.5 0.401590711830649 0.8503519556208952 +-0.5 0.2825992851963176 0.3324710997450823 +-0.5 0.7505700937546201 0.8636886363796377 +-0.5 0.1365896778738224 0.7486799101409657 +-0.5 0.7489285336822704 0.1374375424148716 +-0.5 0.1367163526849468 0.2551007332358406 +-0.5 0.8850853359671329 0.4193740145170646 +-0.5 0.6868830780615857 0.5784008611301219 +-0.5 0.6950509506876689 0.2627381560481607 +-0.5 0.7176916949776959 0.7131718872416397 +-0.5 0.4166666666655987 0.1116454968456831 +-0.5 0.452108162661239 0.7220076636675707 +-0.5 0.8779915320715334 0.8779915320715334 +-0.5 0.1220084679278423 0.1220084679278423 +-0.5 0.1220084679277092 0.8779915320720809 +-0.5 0.8779915320720809 0.1220084679277092 +-0.3556624327030097 0.4166666666655986 1.0 +-0.3724170925462842 0.749233318681713 1.0 +-0.2500000000002106 0.1563666820689724 1.0 +-0.14433756729738628 0.583333333332242 1.0 +-0.14433756729699032 0.4166666666655985 1.0 +-0.1275829074539802 0.7492333186816733 1.0 +-0.35974514689443937 0.5833000710926574 1.0 +-0.1277243392042693 0.2548742700107182 1.0 +-0.3722756607958772 0.2548742700106801 1.0 +-0.25000000000032974 0.8561293975764704 1.0 +-0.25 0.4999999999986692 1.0 +-0.2500000000000653 0.6666666666654585 1.0 +-0.2500000000000596 0.3332414259033728 1.0 +-0.12200846792841458 0.1220084679282047 1.0 +-0.3779915320720428 0.122008467927976 1.0 +-0.12115987742600282 0.8788401225739974 1.0 +-0.3788401225744688 0.8788401225742359 1.0 +0.0 0.8556624327027218 0.4166666666681411 +0.0 0.150842647608969 0.4184890431676929 +0.0 0.5977876091177665 0.8506173125076427 +0.0 0.5977876091175213 0.1493826874931945 +0.0 0.1513080901440255 0.7376257747159785 +0.0 0.858871325824756 0.7492937717869144 +0.0 0.2477062749973538 0.1388379787973146 +0.0 0.8588713258236131 0.2507062282145385 +0.0 0.6975287124468637 0.307435353338931 +0.0 0.7090255065795987 0.4956836700021359 +0.0 0.5669872981090046 0.4166666666688204 +0.0 0.5638129342172751 0.5777795531772322 +0.0 0.4250635842228545 0.4990743699759462 +0.0 0.4212660308645567 0.6600894341827865 +0.0 0.4226497308120571 0.3333333333363592 +0.0 0.2722221709521693 0.5800999919032748 +0.0 0.4220429753558262 0.8308249698881572 +0.0 0.4209213154059258 0.1681987915372704 +0.0 0.8558140217522868 0.5824962402991329 +0.0 0.6967684076373145 0.6910350462068958 +0.0 0.7514051943308058 0.8611729007695287 +0.0 0.1369493419814799 0.2471168955184743 +0.0 0.7514957068078485 0.1386450039386763 +0.0 0.2555622040073565 0.8607216437582962 +0.0 0.1148745817410327 0.5805762952913194 +0.0 0.3131019839193649 0.4215655857139594 +0.0 0.3044802942647868 0.7338723628896986 +0.0 0.2820285491208584 0.2879236046785119 +0.0 0.5502404735825772 0.2790063509475008 +0.0 0.5403355914385477 0.7220692631925427 +0.0 0.1220084679277514 0.8779915320720942 +0.0 0.8779915320721717 0.8779915320722271 +0.0 0.8779915320713725 0.1220084679286958 +0.0 0.1220084679276659 0.1220084679279441 +-0.14433756729699032 0.4166666666655986 0.0 +-0.1275829074536758 0.7492333186817307 0.0 +-0.249999999999715 0.15636668206895 0.0 +-0.3556624327026136 0.583333333332242 0.0 +-0.3556624327030097 0.4166666666655985 0.0 +-0.37241709254598176 0.7492333186816766 0.0 +-0.1402548531055589 0.5833000710926582 0.0 +-0.37227566079570706 0.2548742700107145 0.0 +-0.1277243392040947 0.2548742700106629 0.0 +-0.24999999999955658 0.8561293975764912 0.0 +-0.2500000000000001 0.4999999999986692 0.0 +-0.24999999999993472 0.6666666666654587 0.0 +-0.2499999999999195 0.3332414259033656 0.0 +-0.3779915320715181 0.1220084679282038 0.0 +-0.12200846792786268 0.1220084679278942 0.0 +-0.3788401225739197 0.8788401225739894 0.0 +-0.12115987742544199 0.878840122574312 0.0 +-0.12758290745376222 1.0 0.749233318682745 +-0.14433756729720648 1.0 0.4166666666683895 +-0.2499999999999593 1.0 0.1563666820692418 +-0.3556624327026136 1.0 0.583333333332242 +-0.3556624327030097 1.0 0.4166666666655985 +-0.3724170925460668 1.0 0.7492333186820921 +-0.140254853105448 1.0 0.5833000710948419 +-0.1277243392043234 1.0 0.2548742700117366 +-0.3722756607959292 1.0 0.2548742700107636 +-0.2500000000002154 1.0 0.8561293975766916 +-0.2499999999988981 1.0 0.6666666666678633 +-0.24999999999907008 1.0 0.5000000000013808 +-0.25000000000049016 1.0 0.3337127944750626 +-0.37799153207151803 1.0 0.1220084679282038 +-0.12200846792768633 1.0 0.1220084679279961 +-0.1211598774260445 1.0 0.878840122574012 +-0.3788401225744618 1.0 0.8788401225742289 +9 97 105 +1 9 105 +37 1 105 +78 37 105 +97 78 105 +93 78 97 +38 37 78 +38 78 102 +39 38 102 +75 39 102 +90 75 102 +78 90 102 +90 78 93 +85 90 93 +85 88 90 +85 84 86 +82 84 85 +82 85 93 +81 93 97 +9 10 97 +10 81 97 +10 11 81 +11 76 81 +81 82 93 +81 76 82 +11 61 60 +10 61 11 +10 65 61 +9 65 10 +65 69 61 +61 69 68 +61 68 60 +163 169 171 +163 162 169 +39 162 163 +38 39 163 +38 163 166 +37 38 166 +166 163 171 +161 166 171 +166 161 172 +37 166 172 +1 37 172 +22 70 1 +22 1 172 +1 70 9 +9 70 65 +65 70 59 +59 69 65 +59 70 22 +161 22 172 +21 59 22 +21 22 161 +21 71 59 +21 161 173 +4 21 173 +4 71 21 +20 71 4 +20 4 158 +52 4 173 +4 52 158 +167 52 173 +52 131 158 +52 53 131 +167 161 171 +161 167 173 +146 131 152 +131 146 158 +146 20 158 +19 20 146 +64 71 20 +59 71 64 +64 69 59 +58 69 64 +68 69 58 +63 68 58 +18 63 58 +18 58 19 +18 19 126 +18 126 149 +140 126 150 +150 126 152 +126 146 152 +126 19 146 +58 64 19 +19 64 20 +139 142 153 +142 139 152 +139 150 152 +135 137 139 +139 137 150 +131 142 152 +159 167 171 +53 52 167 +131 53 142 +159 53 167 +54 53 159 +53 54 142 +54 159 165 +165 159 169 +169 159 171 +165 169 170 +54 128 142 +54 55 128 +55 54 165 +160 55 165 +128 55 147 +56 55 160 +160 165 170 +133 128 147 +128 133 153 +142 128 153 +135 136 137 +135 134 136 +133 134 135 +133 135 153 +135 139 153 +33 34 183 +34 33 132 +177 33 183 +33 125 132 +132 125 133 +133 125 134 +134 125 143 +125 32 143 +33 32 125 +32 33 177 +32 177 182 +182 177 187 +187 177 188 +177 183 188 +183 178 188 +178 183 190 +183 34 190 +34 132 157 +132 147 157 +132 133 147 +160 168 175 +168 160 170 +55 56 147 +147 56 157 +56 160 175 +56 8 157 +8 56 175 +8 34 157 +34 8 190 +8 35 190 +35 8 175 +168 35 175 +35 178 190 +36 35 168 +35 36 178 +178 36 189 +36 168 174 +168 164 174 +164 168 170 +178 184 188 +184 178 189 +23 184 189 +164 41 174 +41 5 174 +5 36 174 +36 5 189 +5 23 189 +5 41 107 +23 5 107 +79 23 107 +79 96 100 +96 79 107 +41 96 107 +41 40 96 +40 75 96 +96 75 100 +88 75 90 +40 39 75 +39 40 162 +169 162 170 +162 164 170 +40 41 164 +162 40 164 +88 86 89 +85 86 88 +75 88 100 +88 89 100 +89 86 99 +89 77 98 +89 79 100 +79 89 98 +24 23 79 +24 79 98 +25 24 98 +24 25 180 +25 179 180 +23 24 184 +24 180 184 +184 180 188 +180 179 187 +180 187 188 +179 186 187 +179 181 186 +179 26 181 +26 27 181 +25 26 179 +77 25 98 +26 25 77 +26 77 91 +91 77 101 +77 99 101 +77 89 99 +99 87 101 +74 87 103 +87 74 101 +86 84 87 +86 87 99 +109 45 114 +46 45 109 +109 114 119 +114 118 119 +45 44 114 +44 45 74 +74 94 101 +74 45 94 +45 46 94 +94 46 104 +91 94 104 +94 91 101 +27 26 91 +27 91 104 +6 27 104 +46 6 104 +27 6 192 +6 28 192 +28 6 124 +6 46 124 +46 109 124 +181 27 192 +185 181 192 +181 185 186 +117 109 119 +109 117 124 +117 28 124 +28 185 192 +28 29 185 +29 28 117 +185 29 191 +29 117 123 +7 29 123 +29 7 191 +7 30 191 +30 7 156 +51 7 123 +7 51 156 +113 51 123 +51 145 156 +51 50 145 +113 117 119 +117 113 123 +144 130 145 +145 130 156 +130 30 156 +30 176 191 +176 185 191 +185 176 186 +176 182 186 +186 182 187 +31 32 182 +32 31 143 +134 143 144 +143 130 144 +31 130 143 +176 31 182 +31 30 130 +30 31 176 +138 136 154 +136 144 154 +136 134 144 +127 141 154 +144 127 154 +127 144 145 +111 113 119 +50 51 113 +50 127 145 +111 50 113 +49 50 111 +50 49 127 +127 49 141 +118 111 119 +112 118 120 +112 111 118 +49 111 112 +49 48 141 +48 49 112 +48 112 115 +141 48 148 +47 48 115 +48 47 148 +115 112 120 +141 148 151 +137 136 138 +137 140 150 +137 138 140 +140 138 151 +138 141 151 +141 138 154 +16 57 17 +16 17 129 +57 63 17 +129 17 149 +129 140 151 +140 129 149 +126 140 149 +17 18 149 +17 63 18 +67 68 63 +57 67 63 +66 67 57 +57 72 66 +16 72 57 +16 129 155 +129 148 155 +148 129 151 +115 110 121 +110 115 120 +148 47 155 +47 115 121 +47 3 155 +3 47 121 +3 16 155 +3 72 16 +15 72 3 +15 3 121 +110 15 121 +66 72 15 +14 15 110 +14 66 15 +14 110 122 +14 73 66 +62 67 66 +66 73 62 +62 73 13 +13 73 2 +2 14 122 +2 73 14 +42 2 122 +116 42 122 +110 116 122 +116 110 120 +108 116 120 +43 42 116 +108 43 116 +44 43 108 +44 108 114 +114 108 118 +118 108 120 +60 68 67 +60 67 62 +12 62 13 +60 62 12 +11 60 12 +82 76 83 +11 12 76 +76 12 95 +12 13 95 +83 76 95 +82 83 84 +87 84 103 +84 83 103 +83 80 92 +83 92 103 +92 74 103 +44 74 92 +43 44 92 +80 43 92 +80 83 95 +80 95 106 +42 43 80 +42 80 106 +2 42 106 +13 2 106 +95 13 106 From a75e2d0cec6d632edb128008484f04176b535576 Mon Sep 17 00:00:00 2001 From: azuccott Date: Wed, 15 May 2024 14:41:14 +0200 Subject: [PATCH 367/528] minor modifications --- examples/tdacusticsinglelayer.jl | 7 ++++--- src/maxwell/timedomain/acustictdops.jl | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/tdacusticsinglelayer.jl b/examples/tdacusticsinglelayer.jl index b952de14..a55aa005 100644 --- a/examples/tdacusticsinglelayer.jl +++ b/examples/tdacusticsinglelayer.jl @@ -1,10 +1,10 @@ using CompScienceMeshes, BEAST, LinearAlgebra #Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) -Γ = CompScienceMeshes.meshsphere(1.0,0.3)#CompScienceMeshes.meshcuboid(1.0,1.0,1.0,0.3) +Γ = CompScienceMeshes.meshsphere(1.0,0.4)#CompScienceMeshes.meshcuboid(1.0,1.0,1.0,0.3) X = lagrangecxd0(Γ) -Δt, Nt = 0.08, 600 +Δt, Nt = 0.5, 200 T = timebasisshiftedlagrange(Δt, Nt, 0) U = timebasisdelta(Δt, Nt) @@ -25,10 +25,11 @@ tdacusticsl = @discretise SL[j′,j] == -1.0e[j′] j∈V j′∈W xacusticsl = solve(tdacusticsl) Z = assemble(SL,W,V) + #corregere da qui import Plots -Plots.plot(xacusticsl[1,1:300],label="Current") +Plots.plot(xacusticsl[1,1:300],label="Current",ylim=[-0.0001,0.0001]) Plots.xlabel!("t") Plots.savefig("stablecurrent.png") diff --git a/src/maxwell/timedomain/acustictdops.jl b/src/maxwell/timedomain/acustictdops.jl index 4a865d87..f82e12bd 100644 --- a/src/maxwell/timedomain/acustictdops.jl +++ b/src/maxwell/timedomain/acustictdops.jl @@ -31,7 +31,7 @@ end #of the module export TDAcustic3D -defaultquadstrat(::AcusticSingleLayerTDIO, tfs, bfs) = nothing # AllAnalyticalQStrat(1) +defaultquadstrat(::AcusticSingleLayerTDIO, tfs, bfs) = AllAnalyticalQStrat(1) #nothing goes in hybrid qr, allanalytical goes in zuccottirule @@ -189,8 +189,9 @@ function momintegrals!(z, op::AcusticSingleLayerTDIO, g::LagrangeRefSpace{T,0,3} elseif hits==1 a2index,a3index=mod1(a1index+1,3),mod1(a1index+2,3) b2index,b3index=mod1(b1index+1,3),mod1(b1index+2,3) - #print("\np1=",τ[a1index],"\np2=",τ[a2index],"\np3=",τ[a3index],"\nv1=",σ[b1index],"\nv2=",σ[b2index],"\nv3=",σ[b3index],"\nt1=",t1,"\nt2=",t2) + print("\na1=",τ[a1index],"\na2=",τ[a2index],"\na3=",τ[a3index],"\nb1=",σ[b1index],"\nb2=",σ[b2index],"\nb3=",σ[b3index],"\nt1=",t1,"\nt2=",t2) z[1,1,1]+=(TimeDomainBEMInt.intcommonvertex(τ[a1index],τ[a2index],τ[a3index],σ[b2index],σ[b3index],t1,t2,qpc))/(4*π) + else #print("\np1=",τ[1],"\np2=",τ[2],"\np3=",τ[3],"\nv1=",σ[1],"\nv2=",σ[2],"\nv3=",σ[3],"\nt1=",t1,"\nt2=",t2) z[1,1,1]+=(inttriangletriangle(τ[1],τ[2],τ[3],σ[1],σ[2],σ[3],t1,t2,qpc))/(4*π) @@ -290,7 +291,7 @@ function momintegrals!(z, op::AcusticSingleLayerTDIO, g::LagrangeRefSpace{T,0,3} #print("\np1=",τ[a1index],"\np2=",τ[a2index],"\np3=",τ[a3index],"\nv1=",σ[b1index],"\nv2=",σ[b2index],"\nv3=",σ[b3index],"\nt1=",t1,"\nt2=",t2) entry=(TimeDomainBEMInt.intcommonvertex(τ[a1index],τ[a2index],τ[a3index],σ[b2index],σ[b3index],t1,t2,qpc))/(4*π) if norm(entry)>10.0 || entry<-0.0001 - println("quadrature ",τ[a1index]," ",τ[a2index]," ",τ[a3index]," ",σ[b2index]," ",σ[b3index], t1," ",t2," ",entry) + println("quadrature ",τ[a1index]," ",τ[a2index]," ",τ[a3index]," ",σ[b2index]," ",σ[b3index], t1," ",t2," ",entry, " ",) XW = qr.outer_quad_points for p in 1 : length(XW) From 800f451690b322d891d662d7ba702ba440df259c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 21 May 2024 13:15:30 +0200 Subject: [PATCH 368/528] removed tests in nonconforming qrules that rurned out too sensitive --- src/quadrature/nonconformingoverlapqrule.jl | 8 ++++++-- src/quadrature/nonconformingtouchqrule.jl | 13 ++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/quadrature/nonconformingoverlapqrule.jl b/src/quadrature/nonconformingoverlapqrule.jl index 88c2b958..3e2e3a26 100644 --- a/src/quadrature/nonconformingoverlapqrule.jl +++ b/src/quadrature/nonconformingoverlapqrule.jl @@ -22,8 +22,12 @@ function momintegrals!(op, test_local_space, basis_local_space, test_charts = [ch for ch in test_charts if volume(ch) .> 1e6 * eps(T)] bsis_charts = [ch for ch in bsis_charts if volume(ch) .> 1e6 * eps(T)] - @assert volume(test_chart) ≈ sum(volume.(test_charts)) - @assert volume(basis_chart) ≈ sum(volume.(bsis_charts)) + # @assert volume(test_chart) ≈ sum(volume.(test_charts)) + # if volume(basis_chart) ≈ sum(volume.(bsis_charts)) else + # @show volume(basis_chart) + # @show sum(volume.(bsis_charts)) + # error() + # end qstrat = CommonFaceOverlappingEdgeQStrat(qrule.conforming_qstrat) qdata = quaddata(op, test_local_space, basis_local_space, diff --git a/src/quadrature/nonconformingtouchqrule.jl b/src/quadrature/nonconformingtouchqrule.jl index e930d4c8..d0c13696 100644 --- a/src/quadrature/nonconformingtouchqrule.jl +++ b/src/quadrature/nonconformingtouchqrule.jl @@ -18,6 +18,9 @@ function momintegrals!(op, test_locspace, bsis_locspace, @assert volume(σ) > eps(T) * 1e3 τs, σs = _conforming_refinement_touching_triangles(τ,σ,i,j) + isempty(τs) && return + isempty(σs) && return + # test conformity for a in τs for b in σs @@ -27,10 +30,14 @@ function momintegrals!(op, test_locspace, bsis_locspace, end end - volume(σ) ≈ sum(volume.(σs)) || @infiltrate + # volume(σ) ≈ sum(volume.(σs)) || @infiltrate - @assert volume(τ) ≈ sum(volume.(τs)) - @assert volume(σ) ≈ sum(volume.(σs)) + # if volume(τ) ≈ sum(volume.(τs)) else + # @show volume(τ) + # @show sum(volume.(τs)) + # error() + # end + # @assert volume(σ) ≈ sum(volume.(σs)) @assert all(volume.(τs) .> 1e3 * eps(T) * (volume(τ))) @assert all(volume.(σs) .> 1e3 * eps(T) * (volume(σ))) From 7050132de63f34f00e0a443b26b34830e6d15a61 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 22 May 2024 18:07:06 +0200 Subject: [PATCH 369/528] gmsh independent nonconf meshes --- examples/nonconfmeshes_general.jl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/nonconfmeshes_general.jl b/examples/nonconfmeshes_general.jl index 23de52d7..13af3ab8 100644 --- a/examples/nonconfmeshes_general.jl +++ b/examples/nonconfmeshes_general.jl @@ -4,10 +4,12 @@ using BEAST # Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) # Γref = CompScienceMeshes.barycentric_refinement(Γ) -h = 0.2*0.7 -M1 = meshcuboid(0.5, 1.0, 1.0, h) -M2 = meshcuboid(0.5, 1.0, 1.0, 1.2*h) -M2 = CompScienceMeshes.translate(M2, point(-0.5, 0, 0)) +# h = 0.2*0.7 +# M1 = meshcuboid(0.5, 1.0, 1.0, h) +# M2 = meshcuboid(0.5, 1.0, 1.0, 1.2*h) +# M2 = CompScienceMeshes.translate(M2, point(-0.5, 0, 0)) +M1 = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/assets/M1.in")) +M2 = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/assets/M2.in")) import Plotly Plotly.plot([ @@ -42,6 +44,7 @@ qs2 = BEAST.NonConformingIntegralOpQStrat(qs1) # A1 = assemble(t,Y,X, quadstrat=qs1, threading=BEAST.Threading{:single}) A2 = assemble(t,Y,X, quadstrat=qs2, threading=BEAST.Threading{:single}) +A3 = assemble(t,X,Y, quadstrat=qs2, threading=BEAST.Threading{:single}) error() From 6b15a13055f193f657e5c8bddc8c03f8ba2b24ef Mon Sep 17 00:00:00 2001 From: Paula Respondek Date: Thu, 6 Jun 2024 10:49:33 +0200 Subject: [PATCH 370/528] Change assembly of BilForm LinearMaps are no longer added but a LinearCombination is made, because the summation of LinearMaps results in a Tuple, which for large DirectProductSpaces causes extremely long compile times. --- src/solvers/solver.jl | 15 +++++++++++++-- test/test_directproduct.jl | 5 +++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index aa9bb0e4..8d7715ce 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -219,14 +219,17 @@ lift(a::ConvolutionOperators.AbstractConvOp ,I,J,U,V) = function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; materialize=BEAST.assemble) + T = Int32 @assert !isempty(bf.terms) spaceTimeBasis = isa(X.factors[1], BEAST.SpaceTimeBasis) if spaceTimeBasis p = [numstages(temporalbasis(ch)) for ch in X.factors] + lincombv = ConvolutionOperators.LiftedConvOp[] else p = 1 + lincombv = LinearMap[] end M = numfunctions.(spatialbasis(X).factors) .* p @@ -237,7 +240,7 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; U = BlockArrays.blockedrange(M) V = BlockArrays.blockedrange(N) - sum(bf.terms) do term + for term in bf.terms x = X.factors[term.test_id] for op in reverse(term.test_ops) @@ -251,7 +254,15 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; a = term.coeff * term.kernel z = materialize(a, x, y) - lift(z, Block(term.test_id), Block(term.trial_id), U, V) + + Smap = lift(z, Block(term.test_id), Block(term.trial_id), U, V) + T = promote_type(T, eltype(Smap)) + push!(lincombv, Smap) + end + if spaceTimeBasis + return sum(lincombv) + else + return LinearMaps.LinearCombination{T}(lincombv) end end diff --git a/test/test_directproduct.jl b/test/test_directproduct.jl index f8619f0f..b79f6501 100644 --- a/test/test_directproduct.jl +++ b/test/test_directproduct.jl @@ -25,4 +25,9 @@ for U in [Float32, Float64] t = assemble(T, X, X) @test size(t) == (nt,nt) + + bilterms = [BEAST.Variational.BilTerm(1,1,Any[],Any[],1,T)] + + BilForm = BEAST.Variational.BilForm(:i, :j, bilterms) + @test typeof(assemble(BilForm, X, X)) == LinearMaps.LinearCombination{U, Vector{LinearMap}} end \ No newline at end of file From 9d2f90b2e7e90c9b60ac49ef41ae0136ecf47625 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 11 Jun 2024 14:53:33 +0200 Subject: [PATCH 371/528] partial work --- Project.toml | 4 +- examples/efie.jl | 13 ---- examples/tdmfie.jl | 2 +- src/BEAST.jl | 3 + src/bases/basis.jl | 2 + src/bases/local/ndlcdlocal.jl | 2 +- src/bases/local/rtlocal.jl | 60 ++++++----------- src/bases/local/rtqlocal.jl | 68 +++++++++++++++++++ src/bases/rtqspace.jl | 47 +++++++++++++ src/helmholtz3d/timedomain/tdhh3dexc.jl | 4 +- src/maxwell/mwops.jl | 2 + src/maxwell/timedomain/mwtdexc.jl | 1 + src/quadrature/sauterschwabints.jl | 27 ++++++++ test/Manifest.toml | 90 +++++++++++++++++++------ test/Project.toml | 3 + test/test_fastintegrnds.jl | 31 +++++++++ test/test_interpolate_and_restrict.jl | 71 ++++++++++++++++++- test/test_ndjunction.jl | 2 +- test/test_ndspace.jl | 4 +- 19 files changed, 354 insertions(+), 82 deletions(-) create mode 100644 src/bases/local/rtqlocal.jl create mode 100644 src/bases/rtqspace.jl create mode 100644 test/test_fastintegrnds.jl diff --git a/Project.toml b/Project.toml index 8a7484b3..68687d7d 100644 --- a/Project.toml +++ b/Project.toml @@ -29,6 +29,7 @@ SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +TestItems = "1c621080-faea-4a02-84b6-bbd5e436b8fe" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] @@ -43,8 +44,8 @@ ExtendableSparse = "1.4" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4, 0.5, 1" FillArrays = "0.11, 0.12, 0.13, 1" -IterativeSolvers = "0.9" Infiltrator = "1.8.2" +IterativeSolvers = "0.9" LiftedMaps = "0.5.1" LinearMaps = "3.7 - 3.9, 3.11.2" NestedUnitRanges = "0.2" @@ -53,5 +54,6 @@ SauterSchwab3D = "0.1" SauterSchwabQuadrature = "2.3.0" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" +TestItems = "0.1.1" WiltonInts84 = "0.2.5" julia = "1.6" diff --git a/examples/efie.jl b/examples/efie.jl index 76d42cb1..84ec0ce9 100644 --- a/examples/efie.jl +++ b/examples/efie.jl @@ -2,14 +2,11 @@ using CompScienceMeshes using BEAST Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -# Γ = meshsphere(radius=1.0, h=0.1) -# Γ = CompScienceMeshes.meshmobius(h=0.035) X = raviartthomas(Γ) κ, η = 1.0, 1.0 t = Maxwell3D.singlelayer(wavenumber=κ) E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) -# E = -η/(im*κ)*BEAST.CurlCurlGreen(κ, ẑ, point(2,0,0)) e = (n × E) × n @hilbertspace j @@ -20,13 +17,3 @@ u, ch = BEAST.gmres_ch(efie; restart=1500) include("utils/postproc.jl") include("utils/plotresults.jl") -qsg(g) = BEAST.DoubleNumWiltonSauterQStrat(6, 7, 6, 7, g, g, g, g) -A1 = assemble(t,X,X;quadstrat=qsg(5)) -A2 = assemble(t,X,X;quadstrat=qsg(10)) -A2 = assemble(t,X,X;quadstrat=qsg(20)) -val, pos = findmax(abs.(A2-A3)) - -import Plots -Plots.plotly() -Plots.plot(abs.(A2[537,:])) -Plots.scatter!(abs.(A3[537,:])) \ No newline at end of file diff --git a/examples/tdmfie.jl b/examples/tdmfie.jl index 57910978..e3ba50ed 100644 --- a/examples/tdmfie.jl +++ b/examples/tdmfie.jl @@ -1,6 +1,6 @@ using CompScienceMeshes, BEAST Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -Γ = meshsphere(radius=1.0, h=0.1) +Γ = meshsphere(radius=1.0, h=0.25) X = raviartthomas(Γ) Y = buffachristiansen(Γ) diff --git a/src/BEAST.jl b/src/BEAST.jl index 26abf040..7c729532 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -19,6 +19,7 @@ using AbstractTrees using NestedUnitRanges using Infiltrator +using TestItems import LinearAlgebra: cross, dot import LinearAlgebra: ×, ⋅ @@ -157,6 +158,7 @@ include("bases/local/ncrossbdmlocal.jl") include("bases/local/ndlcclocal.jl") include("bases/local/ndlcdlocal.jl") include("bases/local/bdm3dlocal.jl") +include("bases/local/rtqlocal.jl") include("bases/lagrange.jl") include("bases/rtspace.jl") @@ -171,6 +173,7 @@ include("bases/ndlccspace.jl") include("bases/ndlcdspace.jl") include("bases/dual3d.jl") include("bases/bdm3dspace.jl") +include("bases/rtqspace.jl") include("bases/subdbasis.jl") diff --git a/src/bases/basis.jl b/src/bases/basis.jl index 40ddedf0..75817307 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -1,4 +1,6 @@ abstract type RefSpace{T,D} end +abstract type DivRefSpace{T,D} <: RefSpace{T,D} end + abstract type AbstractSpace end abstract type Space{T} <: AbstractSpace end diff --git a/src/bases/local/ndlcdlocal.jl b/src/bases/local/ndlcdlocal.jl index aabbca81..ba4cf8ae 100644 --- a/src/bases/local/ndlcdlocal.jl +++ b/src/bases/local/ndlcdlocal.jl @@ -97,7 +97,7 @@ function restrict(ϕ::NDLCDRefSpace{T}, dom1, dom2) where {T} A = volume(face) m = normal(p) - u = carttobary(dom1,p) + # u = carttobary(dom1,cartesian(p)) u = carttobary(dom1, c) x = neighborhood(dom1, u) diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index 85c93291..61e19eac 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -1,4 +1,4 @@ -struct RTRefSpace{T} <: RefSpace{T,3} end +struct RTRefSpace{T} <: DivRefSpace{T,3} end # valuetype(ref::RTRefSpace{T}, charttype) where {T} = SVector{3,Tuple{SVector{universedimension(charttype),T},T}} function valuetype(ref::RTRefSpace{T}, charttype::Type) where {T} @@ -44,42 +44,6 @@ function ntrace(x::RTRefSpace, el, q, fc) return t end -# function restrict(ϕ::RTRefSpace{T}, dom1, dom2) where T - -# K = numfunctions(ϕ) -# D = dimension(dom1) - -# @assert K == 3 -# @assert D == 2 -# @assert D == dimension(dom2) - -# Q = zeros(T,K,K) -# for i in 1:K - -# # find the center of edge i of dom2 -# a = dom2.vertices[mod1(i+1,D+1)] -# b = dom2.vertices[mod1(i+2,D+1)] -# c = (a + b) / 2 - -# # find the outer binormal there -# t = b - a -# l = norm(t) -# n = dom2.normals[1] -# m = cross(t, n) / l - -# u = carttobary(dom1, c) -# x = neighborhood(dom1, u) - -# y = ϕ(x) - -# for j in 1:K -# Q[j,i] = dot(y[j][1], m) * l -# end -# end - -# return Q -# end - const _vert_perms_rt = [ (1,2,3), @@ -135,6 +99,19 @@ function interpolate(interpolant::RefSpace, chart1, interpolee::RefSpace, chart2 interpolate(fields, interpolant, chart1) end + +function interpolate(interpolant::RefSpace, chart1, interpolee::RefSpace, chart2, ch1toch2) + function fields(p1) + u1 = parametric(p1) + u2 = cartesian(ch1toch2, u1) + p2 = neighborhood(chart2, u2) + fieldvals = [f.value for f in interpolee(p2)] + end + + interpolate(fields, interpolant, chart1) +end + + function interpolate(fields, interpolant::RTRefSpace, chart) Q = map(faces(chart)) do face p = center(face) @@ -159,6 +136,10 @@ function restrict(ϕ::RefSpace, dom1, dom2) interpolate(ϕ, dom2, ϕ, dom1) end +function restrict(ϕ::RefSpace, dom1, dom2, dom2todom1) + interpolate(ϕ, dom2, ϕ, dom1, dom2todom1) +end + const _dof_rtperm_matrix = [ @SMatrix[1 0 0; # 1. {1,2,3} 0 1 0; @@ -183,4 +164,7 @@ const _dof_rtperm_matrix = [ @SMatrix[0 0 1; # 6. {3,2,1} 0 1 0; 1 0 0] -] \ No newline at end of file +] + + +# Support for zeroth order elements on quadrilaterals diff --git a/src/bases/local/rtqlocal.jl b/src/bases/local/rtqlocal.jl new file mode 100644 index 00000000..4e9b7196 --- /dev/null +++ b/src/bases/local/rtqlocal.jl @@ -0,0 +1,68 @@ +struct RTQuadRefSpace{T} <: DivRefSpace{T,3} end + +function (ϕ::RTQuadRefSpace{T})(p) where {T} + + u, v = parametric(p) + j = jacobian(p) + + D = tangents(p) + + eu = point(T,1,0) + ev = point(T,0,1) + + i = one(T) + vals = SVector( + (value=(v-1)*ev, divergence=i), + (value=u*eu, divergence=i), + (value=v*ev, divergence=i), + (value=(u-1)*eu, divergence=i), + ) + + map(vals) do f + (value=D*f.value/j, divergence=f.divergence/j) end +end + + +function interpolate(fields, interpolant::RTQuadRefSpace{T}, chart) where {T} + + refchart = domain(chart) + Q = map(zip(faces(chart), faces(refchart))) do (edge,refedge) + s = T(0.5) + p_edge = neighborhood(edge, s) + p_refedge = neighborhood(refedge, s) + + p_refchart = neighborhood(refchart, cartesian(p_refedge)) + p_chart = neighborhood(chart, cartesian(p_refchart)) + n_chart = normal(p_chart) + + t_edge = tangents(p_edge, 1) + m_edge = -cross(t_edge, n_chart) + + fieldvals = fields(p_chart) + map(fv -> dot(fv,m_edge), fieldvals) + end + + return hcat(Q...) +end + +@testitem "interpolate" begin + using CompScienceMeshes + + p1 = point(0,0,0) + p2 = point(2,0,0) + p3 = point(2,4,0) + p4 = point(0,4,0) + + quad = CompScienceMeshes.Quadrilateral(p1,p2,p3,p4) + f(p) = (x = cartesian(p); return [point(x[1]+2, -x[2]-3, 0)]) + + rtq = BEAST.RTQuadRefSpace{Float64}() + Q = BEAST.interpolate(f, rtq, quad) + p = neighborhood(quad, point(0.5, 0.5)) + val1 = f(p)[1] + val2 = sum(Q[1,i] * ϕ.value for (i,ϕ) in zip(axes(Q,2), rtq(p))) + + @show val1 + @show val2 + @test val1 ≈ val2 +end \ No newline at end of file diff --git a/src/bases/rtqspace.jl b/src/bases/rtqspace.jl new file mode 100644 index 00000000..dbdf6924 --- /dev/null +++ b/src/bases/rtqspace.jl @@ -0,0 +1,47 @@ +struct RTQSpace{T,M,P} <: Space{T} + geo::M + fns::Vector{Vector{Shape{T}}} + pos::Vector{P} +end + +# RTQSpace(g::M, fns::Vector{}) + +function positions(s::RTQSpace) s.pos end +function refspace(s::RTQSpace{T}) where {T} RTQuadRefSpace{T}() end +function subset(rt::RTQSpace,I) RTQSpace(rt.geo, rt.fns[I], rt.pos[I]) end + +function raviartthomas( + mesh::CompScienceMeshes.QuadMesh{T}, + edges::CompScienceMeshes.AbstractMesh{3,2,T}, + connectivity, orientations) where {T<:Any} + + fns = Vector{Vector{Shape{T}}}(undef, length(edges)) + pos = Vector{SVector{3,T}}(undef, length(edges)) + + rows = rowvals(connectivity) + vals = nonzeros(connectivity) + for (i,edge) in enumerate(edges) + σ = orientations[i] + fn = map(zip(nzrange(connectivity, i),(σ,-σ))) do (j, α) + Shape{T}(j, abs(vals[j]), α) + end + fns[i] = fn + pos[i] = cartesian(CompScienceMeshes.center(chart(edges, edge))) + end + + return RTQSpace(mesh, fns, pos) +end + +@testitem "RTQSpace construction" begin + using CompScienceMeshes + + m = CompScienceMeshes.meshrectangle(2.0, 2.0, 1.0; structured=:quadrilateral) + edges = skeleton(m, 1) + edges_int = submesh(!in(boundary(edges)), edges) + + c = CompScienceMeshes.connectivity(edges_int, m) + @test size(c) == (length(m), length(edges_int)) + o = ones(length(edges_int)) + s = raviartthomas(m, edges_int, c, o) + @test numfunctions(s) == 4 +end \ No newline at end of file diff --git a/src/helmholtz3d/timedomain/tdhh3dexc.jl b/src/helmholtz3d/timedomain/tdhh3dexc.jl index 045ea0b8..91287032 100644 --- a/src/helmholtz3d/timedomain/tdhh3dexc.jl +++ b/src/helmholtz3d/timedomain/tdhh3dexc.jl @@ -46,6 +46,6 @@ dot(::NormalVector, field::TDFunctional) = DotTraceHH{scalartype(field), typeof( function (f::DotTraceHH)(p,t) n = normal(p) - # x = cartesian(p) - return dot(n, f.field(p,t)) + x = cartesian(p) + return dot(n, f.field(x,t)) end diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index d1de4035..dbac76e5 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -170,6 +170,8 @@ function (igd::Integrand{<:MWSingleLayer3D})(x,y,f,g) end end + + function (igd::Integrand{<:MWSingleLayer3DReg})(x,y,f,g) α = igd.operator.α β = igd.operator.β diff --git a/src/maxwell/timedomain/mwtdexc.jl b/src/maxwell/timedomain/mwtdexc.jl index b2f651f4..42cff3c0 100644 --- a/src/maxwell/timedomain/mwtdexc.jl +++ b/src/maxwell/timedomain/mwtdexc.jl @@ -31,6 +31,7 @@ cross(k, pw::PlaneWaveMWTD) = PlaneWaveMWTD( function (f::PlaneWaveMWTD)(r,t) t = cartesian(t)[1] + r = cartesian(r) dr = zero(typeof(t)) for i in 1 : 3 dr += r[i]*f.direction[i] diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index 2de5f2de..3aa75368 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -18,6 +18,33 @@ function (igd::Integrand)(u,v) return jacobian(x) * jacobian(y) * igd(x,y,f,g) end +# For divergence conforming basis and trial functions, an alternative evaluation +# of the integrand is possible that avoids the computation of the chart jacobian +# determinants. +function (igd::Integrand{<:IntegralOperator,<:DivRefSpace,<:DivRefSpace})(u,v) + test_domain = CompScienceMeshes.domain(igd.test_chart) + bsis_domain = CompScienceMeshes.domain(igd.trial_chart) + + x = CompScienceMeshes.neighborhood_lazy(igd.test_chart,u) + y = CompScienceMeshes.neighborhood_lazy(igd.trial_chart,v) + + p = neighborhood(test_domain, u) + q = neighborhood(bsis_domain, v) + + f̂ = igd.local_test_space(p) + ĝ = igd.local_trial_space(q) + + Dx = tangents(x) + Dy = tangents(y) + + f = map(f̂) do fi + (value = Dx * fi.value, divergence = fi.divergence) end + g = map(ĝ) do gi + (value = Dy * gi.value, divergence = gi.divergence) end + + igd(x,y,f,g) +end + getvalue(a::SVector{N}) where {N} = SVector{N}(getvalue(a.data)) getvalue(a::NTuple{1}) = (a[1].value,) getvalue(a::NTuple{N}) where {N} = tuple(a[1].value, getvalue(Base.tail(a))...) diff --git a/test/Manifest.toml b/test/Manifest.toml index c7a420e8..476c9617 100644 --- a/test/Manifest.toml +++ b/test/Manifest.toml @@ -1,8 +1,8 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.9.0" +julia_version = "1.10.2" manifest_format = "2.0" -project_hash = "5eac3002246ba098277ed23880bcbc5af87a54be" +project_hash = "571829245fb8bd97876fa91a4e7d398091f62379" [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" @@ -74,7 +74,7 @@ weakdeps = ["Dates", "LinearAlgebra"] [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.0.2+0" +version = "1.1.0+0" [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] @@ -155,7 +155,7 @@ version = "9.0.1+0" [[deps.GMP_jll]] deps = ["Artifacts", "Libdl"] uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" -version = "6.2.1+2" +version = "6.2.1+6" [[deps.Gettext_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] @@ -214,24 +214,35 @@ git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" version = "2.10.1+0" +[[deps.LegendrePolynomials]] +deps = ["OffsetArrays", "SpecialFunctions"] +git-tree-sha1 = "78e5288b179f2ae90ccbaa1799e9b0cb82ef5e04" +uuid = "3db4a2ba-fc88-11e8-3e01-49c72059a882" +version = "0.4.4" + [[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" -version = "0.6.3" +version = "0.6.4" [[deps.LibCURL_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "7.84.0+0" +version = "8.4.0+0" [[deps.LibGit2]] -deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +[[deps.LibGit2_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.6.4+0" + [[deps.LibSSH2_jll]] deps = ["Artifacts", "Libdl", "MbedTLS_jll"] uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" -version = "1.10.2+0" +version = "1.11.0+1" [[deps.Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" @@ -326,14 +337,14 @@ uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[deps.MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.2+0" +version = "2.28.2+1" [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" [[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2022.10.11" +version = "2023.1.10" [[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" @@ -345,15 +356,26 @@ git-tree-sha1 = "acc8099ae8ed10226dc8424fb256ec9fe367a1f0" uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" version = "7.6.2+2" +[[deps.OffsetArrays]] +git-tree-sha1 = "e64b4f5ea6b7389f6f046d13d4896a8f9c1ba71e" +uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" +version = "1.14.0" + + [deps.OffsetArrays.extensions] + OffsetArraysAdaptExt = "Adapt" + + [deps.OffsetArrays.weakdeps] + Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" + [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.21+4" +version = "0.3.23+4" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+0" +version = "0.8.1+2" [[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -375,7 +397,7 @@ version = "1.6.0" [[deps.PCRE2_jll]] deps = ["Artifacts", "Libdl"] uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" -version = "10.42.0+0" +version = "10.42.0+1" [[deps.Pixman_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -386,7 +408,7 @@ version = "0.40.1+0" [[deps.Pkg]] deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.9.0" +version = "1.10.0" [[deps.Preferences]] deps = ["TOML"] @@ -403,7 +425,7 @@ deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.Random]] -deps = ["SHA", "Serialization"] +deps = ["SHA"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [[deps.Requires]] @@ -437,6 +459,7 @@ uuid = "6462fe0b-24de-5631-8697-dd941f90decc" [[deps.SparseArrays]] deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +version = "1.10.0" [[deps.SpecialFunctions]] deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] @@ -450,6 +473,18 @@ version = "2.2.0" [deps.SpecialFunctions.weakdeps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +[[deps.SphericalScattering]] +deps = ["LegendrePolynomials", "LinearAlgebra", "SpecialFunctions", "StaticArrays"] +git-tree-sha1 = "6acefb2a8f8142e601f68e4bda5b37f6731e1455" +uuid = "1a9ea918-b599-4f1f-bd9a-d681e8bb5b3e" +version = "0.7.2" + + [deps.SphericalScattering.extensions] + SphericalScatteringExt = "PlotlyJS" + + [deps.SphericalScattering.weakdeps] + PlotlyJS = "f0f68f2c-4968-5e81-91da-67840de0976a" + [[deps.StaticArrays]] deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] git-tree-sha1 = "8982b3607a212b070a5e46eea83eb62b4744ae12" @@ -464,12 +499,12 @@ version = "1.4.0" [[deps.Statistics]] deps = ["LinearAlgebra", "SparseArrays"] uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -version = "1.9.0" +version = "1.10.0" [[deps.SuiteSparse_jll]] -deps = ["Artifacts", "Libdl", "Pkg", "libblastrampoline_jll"] +deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "5.10.1+6" +version = "7.2.1+1" [[deps.TOML]] deps = ["Dates"] @@ -485,6 +520,17 @@ version = "1.10.0" deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +[[deps.TestItemRunner]] +deps = ["Pkg", "TOML", "Test", "TestItems", "UUIDs"] +git-tree-sha1 = "cb2b53fd36a8fe20c0b9f55da6244eb4818779f5" +uuid = "f8b46487-2199-4994-9208-9a1283c18c0a" +version = "0.2.3" + +[[deps.TestItems]] +git-tree-sha1 = "8621ba2637b49748e2dc43ba3d84340be2938022" +uuid = "1c621080-faea-4a02-84b6-bbd5e436b8fe" +version = "0.1.1" + [[deps.UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" @@ -579,7 +625,7 @@ version = "1.4.0+3" [[deps.Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.2.13+0" +version = "1.2.13+1" [[deps.gmsh_jll]] deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] @@ -590,7 +636,7 @@ version = "4.11.1+0" [[deps.libblastrampoline_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.7.0+0" +version = "5.8.0+1" [[deps.libpng_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] @@ -601,9 +647,9 @@ version = "1.6.38+0" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.48.0+0" +version = "1.52.0+1" [[deps.p7zip_jll]] deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.4.0+0" +version = "17.4.0+2" diff --git a/test/Project.toml b/test/Project.toml index d48999af..4676299c 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,5 +1,6 @@ [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" +Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" @@ -10,4 +11,6 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SphericalScattering = "1a9ea918-b599-4f1f-bd9a-d681e8bb5b3e" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a" +TestItems = "1c621080-faea-4a02-84b6-bbd5e436b8fe" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" diff --git a/test/test_fastintegrnds.jl b/test/test_fastintegrnds.jl new file mode 100644 index 00000000..2affa6ea --- /dev/null +++ b/test/test_fastintegrnds.jl @@ -0,0 +1,31 @@ +using Test + +using CompScienceMeshes +using BEAST + +s = simplex( + point(5,1,0), + point(-1,3,0), + point(0,0,2), +) + +locspace = BEAST.RTRefSpace{Float64}() +t = Maxwell3D.singlelayer(wavenumber=1.0) + +igd = BEAST.Integrand(t, locspace, locspace, s, s) + +u = point(1/3,1/3) +v = point(1/4,2/3) + +igd(u,v) + +dom = CompScienceMeshes.domain(s) +p = CompScienceMeshes.neighborhood(dom, u) +f̂ = locspace(p) +Dx = tangents(s, u) +f = map(f̂) do fi + (value = Dx * fi.value, divergence = fi.divergence) end + +q = neighborhood(s, u) +g = locspace(q) +J = jacobian(q) \ No newline at end of file diff --git a/test/test_interpolate_and_restrict.jl b/test/test_interpolate_and_restrict.jl index fd6d4882..dcc9bdf6 100644 --- a/test/test_interpolate_and_restrict.jl +++ b/test/test_interpolate_and_restrict.jl @@ -26,4 +26,73 @@ end ctr = center(chart2) vals = [f.value for f in X(ctr)] itpol = sum(w*val for (w,val) in zip(Q3,vals)) -@test itpol ≈ constant_vector_field \ No newline at end of file +@test itpol ≈ constant_vector_field + +using TestItems +@testitem "restrict RT0" begin + using CompScienceMeshes + + ref_vertices = [ + point(1,0), + point(0,1), + point(0,0), + ] + vertices = [ + point(1,0,0), + point(0,1,0), + point(0,0,0), + ] + chart1 = simplex(vertices...) + for I in BEAST._dof_perms_rt + chart2 = simplex( + chart1.vertices[I[1]], + chart1.vertices[I[2]], + chart1.vertices[I[3]],) + chart2tochart1 = CompScienceMeshes.simplex(ref_vertices[collect(I)]...) + rs = BEAST.RTRefSpace{Float64}() + Q1 = BEAST.dof_perm_matrix(rs, I) + Q2 = BEAST.restrict(rs, chart1, chart2) + Q3 = BEAST.restrict(rs, chart1, chart2, chart2tochart1) + @test Q1 ≈ Q2 + @test Q1 ≈ Q3 + end +end + + +@testitem "restrict RTQ0" begin + using CompScienceMeshes + using Combinatorics + + ref_vertices = [ + point(0,0), + point(1,0), + point(1,1), + point(0,1)] + vertices = [ + point(0,0,0), + point(1,0,0), + point(1,1,0), + point(0,1,0)] + chart1 = CompScienceMeshes.Quadrilateral(vertices...) + P = [ + [1,2,3,4], + [2,3,4,1], + [3,4,1,2], + [4,1,2,3], + [2,1,4,3], + [1,4,3,2], + [4,3,2,1], + [3,2,1,4], + ] + for I in P + chart2 = CompScienceMeshes.Quadrilateral( + vertices[I[1]], + vertices[I[2]], + vertices[I[3]], + vertices[I[4]]) + chart2tochart1 = CompScienceMeshes.Quadrilateral(ref_vertices[I]...) + rs = BEAST.RTQuadRefSpace{Float64}() + Q2 = BEAST.restrict(rs, chart1, chart2, chart2tochart1) + @show Q2 + end +end \ No newline at end of file diff --git a/test/test_ndjunction.jl b/test/test_ndjunction.jl index 95cc2b26..a3c7c36f 100644 --- a/test/test_ndjunction.jl +++ b/test/test_ndjunction.jl @@ -29,7 +29,7 @@ function interior(mesh::Mesh, edges=skeleton(mesh,1)) @assert size(C) == (numcells(mesh), numcells(edges)) nn = vec(sum(abs.(C), dims=1)) - T = CompScienceMeshes.celltype(edges) + T = CompScienceMeshes.indextype(edges) interior_edges = Vector{T}() for (i,edge) in pairs(cells(edges)) nn[i] > 1 && push!(interior_edges, edge) diff --git a/test/test_ndspace.jl b/test/test_ndspace.jl index 4a326c42..c400c039 100644 --- a/test/test_ndspace.jl +++ b/test/test_ndspace.jl @@ -26,8 +26,8 @@ for T in [Float32, Float64] # center of the diagonal ctr = center(charts[3]) face_charts = [chart(mesh,fce) for fce in mesh] - nbd1 = neighborhood(face_charts[1], carttobary(face_charts[1], ctr)) - nbd2 = neighborhood(face_charts[2], carttobary(face_charts[2], ctr)) + nbd1 = neighborhood(face_charts[1], carttobary(face_charts[1], cartesian(ctr))) + nbd2 = neighborhood(face_charts[2], carttobary(face_charts[2], cartesian(ctr))) t = tangents(ctr,1) ut = t / norm(t) From 80a3ba2c4f9d3439e1f9f403fb4e3ded920827f3 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 12 Jun 2024 17:25:55 +0200 Subject: [PATCH 372/528] support for quadrilateral meshes --- Project.toml | 4 +- examples/efie_quad.jl | 42 +++++++++++++++++ src/bases/local/rtqlocal.jl | 61 ++++++++++++++++++++++++- src/bases/rtqspace.jl | 30 ++++++++++-- src/quadrature/doublenumsauterqstrat.jl | 56 ++++++++++++++++------- src/quadrature/sauterschwabints.jl | 4 +- test/runtests.jl | 3 ++ test/test_interpolate_and_restrict.jl | 37 --------------- test/test_tdefie_irk.jl | 3 +- 9 files changed, 178 insertions(+), 62 deletions(-) create mode 100644 examples/efie_quad.jl diff --git a/Project.toml b/Project.toml index 68687d7d..b7ca37a7 100644 --- a/Project.toml +++ b/Project.toml @@ -37,7 +37,7 @@ AbstractTrees = "0.4.4" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" CollisionDetection = "0.1.5" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.7" +CompScienceMeshes = "0.8" Compat = "2, 3, 4" ConvolutionOperators = "0.4" ExtendableSparse = "1.4" @@ -51,7 +51,7 @@ LinearMaps = "3.7 - 3.9, 3.11.2" NestedUnitRanges = "0.2" Requires = "1" SauterSchwab3D = "0.1" -SauterSchwabQuadrature = "2.3.0" +SauterSchwabQuadrature = "2.4.0" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" TestItems = "0.1.1" diff --git a/examples/efie_quad.jl b/examples/efie_quad.jl new file mode 100644 index 00000000..8eea732b --- /dev/null +++ b/examples/efie_quad.jl @@ -0,0 +1,42 @@ +using CompScienceMeshes +using BEAST + +Γ = CompScienceMeshes.meshrectangle(2.0, 2.0, 0.1; structured=:quadrilateral) +edges = skeleton(Γ, 1) +edges_bnd = boundary(Γ) +pred = !in(edges_bnd) +edges_int = submesh(pred, edges) +c = CompScienceMeshes.connectivity(edges_int, Γ, identity) +o = ones(length(edges_int)) +X = raviartthomas(Γ, edges_int, c, o) +@show numfunctions(X) + +κ, η = 1.0, 1.0 +t = Maxwell3D.singlelayer(wavenumber=κ) +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +e = (n × E) × n + +assemble(t, X, X) + +@hilbertspace j +@hilbertspace k +efie = @discretise t[k,j]==e[k] j∈X k∈X +u, ch = BEAST.gmres_ch(efie; restart=1500) + +Φ, Θ = [0.0], range(0,stop=π,length=100) +pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] +ffd = potential(MWFarField3D(wavenumber=κ), pts, u, X) + +xs, y, zs = range(-4,stop=6,length=200), 1.0, range(-5,stop=5,length=200) +gridpoints = [point(x,y,z) for z in zs, x in xs] +nfd = potential(MWSingleLayerField3D(wavenumber = κ), gridpoints, u, X) +# nfd = reshape(nfd, (nx,nz)) +nfd .-= E.(gridpoints) + +import Plots +using LinearAlgebra +p1 = Plots.scatter(Θ, real.(norm.(ffd))) +p2 = Plots.heatmap(-clamp.(real.(getindex.(nfd,1)), -3.0, 3.0), clims=(-3,3), colormap=:viridis) +p3 = Plots.contour(-clamp.(real.(getindex.(nfd,1)), -3.0, 3.0), clims=(-3,3)) +Plots.plot(p1,p2,p3,layout=(3,1)) + diff --git a/src/bases/local/rtqlocal.jl b/src/bases/local/rtqlocal.jl index 4e9b7196..bc196d95 100644 --- a/src/bases/local/rtqlocal.jl +++ b/src/bases/local/rtqlocal.jl @@ -1,4 +1,4 @@ -struct RTQuadRefSpace{T} <: DivRefSpace{T,3} end +struct RTQuadRefSpace{T} <: DivRefSpace{T,4} end function (ϕ::RTQuadRefSpace{T})(p) where {T} @@ -65,4 +65,63 @@ end @show val1 @show val2 @test val1 ≈ val2 +end + + +const _vrtperm_matrix_rtq = [ + (1,2,3,4), + (2,3,4,1), + (3,4,1,2), + (4,1,2,3), + (2,1,4,3), + (1,4,3,2), + (4,3,2,1), + (3,2,1,4), +] + +const _dofperm_matrix_rtq = [ + @SMatrix[1.0 0.0 0.0 0.0; 0.0 1.0 0.0 0.0; 0.0 0.0 1.0 0.0; 0.0 0.0 0.0 1.0], + @SMatrix[0.0 0.0 0.0 1.0; 1.0 0.0 0.0 0.0; 0.0 1.0 0.0 0.0; 0.0 0.0 1.0 0.0], + @SMatrix[0.0 0.0 1.0 0.0; 0.0 0.0 0.0 1.0; 1.0 0.0 0.0 0.0; 0.0 1.0 0.0 0.0], + @SMatrix[0.0 1.0 0.0 0.0; 0.0 0.0 1.0 0.0; 0.0 0.0 0.0 1.0; 1.0 0.0 0.0 0.0], + @SMatrix[1.0 0.0 0.0 0.0; 0.0 0.0 0.0 1.0; 0.0 0.0 1.0 0.0; 0.0 1.0 0.0 0.0], + @SMatrix[0.0 0.0 0.0 1.0; 0.0 0.0 1.0 0.0; 0.0 1.0 0.0 0.0; 1.0 0.0 0.0 0.0], + @SMatrix[0.0 0.0 1.0 0.0; 0.0 1.0 0.0 0.0; 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 1.0], + @SMatrix[0.0 1.0 0.0 0.0; 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 1.0; 0.0 0.0 1.0 0.0], +] + +function dof_perm_matrix(::RTQuadRefSpace, vert_permutation) + i = findfirst(==(tuple(vert_permutation...)), _vrtperm_matrix_rtq) + @assert i != nothing + return _dofperm_matrix_rtq[i] +end + +@testitem "restrict RTQ0" begin + using CompScienceMeshes + using Combinatorics + + ref_vertices = [ + point(0,0), + point(1,0), + point(1,1), + point(0,1)] + vertices = [ + point(0,0,0), + point(1,0,0), + point(1,1,0), + point(0,1,0)] + chart1 = CompScienceMeshes.Quadrilateral(vertices...) + for I in BEAST._vrtperm_matrix_rtq + @show I + chart2 = CompScienceMeshes.Quadrilateral( + vertices[I[1]], + vertices[I[2]], + vertices[I[3]], + vertices[I[4]]) + chart2tochart1 = CompScienceMeshes.Quadrilateral(ref_vertices[collect(I)]...) + rs = BEAST.RTQuadRefSpace{Float64}() + Q1 = BEAST.dof_perm_matrix(rs, I) + Q2 = BEAST.restrict(rs, chart1, chart2, chart2tochart1) + @test Q1 ≈ Q1 + end end \ No newline at end of file diff --git a/src/bases/rtqspace.jl b/src/bases/rtqspace.jl index dbdf6924..441c15cf 100644 --- a/src/bases/rtqspace.jl +++ b/src/bases/rtqspace.jl @@ -23,7 +23,7 @@ function raviartthomas( for (i,edge) in enumerate(edges) σ = orientations[i] fn = map(zip(nzrange(connectivity, i),(σ,-σ))) do (j, α) - Shape{T}(j, abs(vals[j]), α) + Shape{T}(rows[j], abs(vals[j]), α) end fns[i] = fn pos[i] = cartesian(CompScienceMeshes.center(chart(edges, edge))) @@ -37,11 +37,35 @@ end m = CompScienceMeshes.meshrectangle(2.0, 2.0, 1.0; structured=:quadrilateral) edges = skeleton(m, 1) - edges_int = submesh(!in(boundary(edges)), edges) + edges_bnd = boundary(m) + # @show length(edges_bnd) + @test length(edges_bnd) == 8 + pred = !in(edges_bnd) + edges_int = submesh(pred, edges) - c = CompScienceMeshes.connectivity(edges_int, m) + c = CompScienceMeshes.connectivity(edges_int, m, identity) @test size(c) == (length(m), length(edges_int)) o = ones(length(edges_int)) s = raviartthomas(m, edges_int, c, o) @test numfunctions(s) == 4 +end + +@testitem "RTQSpace assembly data" begin + using CompScienceMeshes + m = CompScienceMeshes.meshrectangle(2.0, 2.0, 1.0; structured=:quadrilateral) + edges = skeleton(m, 1) + edges_bnd = boundary(m) + pred = !in(edges_bnd) + edges_int = submesh(pred, edges) + c = CompScienceMeshes.connectivity(edges_int, m, identity) + o = ones(length(edges_int)) + s = raviartthomas(m, edges_int, c, o) + + num_cells = numcells(m) + num_bfs = numfunctions(s) + r = refspace(s) + num_refs = numfunctions(r) + celltonum = BEAST.make_celltonum(num_cells, num_refs, num_bfs, s) + + els, ad, a2g = BEAST.assemblydata(s) end \ No newline at end of file diff --git a/src/quadrature/doublenumsauterqstrat.jl b/src/quadrature/doublenumsauterqstrat.jl index 8e1bc94d..f869393f 100644 --- a/src/quadrature/doublenumsauterqstrat.jl +++ b/src/quadrature/doublenumsauterqstrat.jl @@ -27,23 +27,12 @@ function quaddata(op::IntegralOperator, end -function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, - qs::DoubleNumSauterQstrat) - - T = eltype(eltype(τ.vertices)) - hits = 0 - dtol = 1.0e3 * eps(T) - dmin2 = floatmax(T) - for t in τ.vertices - for s in σ.vertices - d2 = LinearAlgebra.norm_sqr(t-s) - d = norm(t-s) - dmin2 = min(dmin2, d2) - # hits += (d2 < dtol) - hits += (d < dtol) - end - end +function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, + i, τ::CompScienceMeshes.Simplex{<:Any, 2}, + j, σ::CompScienceMeshes.Simplex{<:Any, 2}, + qd, qs::DoubleNumSauterQstrat) + hits = _numhits(τ, σ) @assert hits <= 3 hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) @@ -53,4 +42,39 @@ function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, return DoubleQuadRule( qd.tpoints[1,i], qd.bpoints[1,j],) +end + +function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, + i, τ::CompScienceMeshes.Quadrilateral, + j, σ::CompScienceMeshes.Quadrilateral, + qd, qs::DoubleNumSauterQstrat) + + hits = _numhits(τ, σ) + @assert hits != 3 + @assert hits <= 4 + + hits == 4 && return SauterSchwabQuadrature.CommonFaceQuad(qd.gausslegendre[3]) + hits == 2 && return SauterSchwabQuadrature.CommonEdgeQuad(qd.gausslegendre[2]) + hits == 1 && return SauterSchwabQuadrature.CommonVertexQuad(qd.gausslegendre[1]) + + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j],) +end + + +function _numhits(τ, σ) + T = coordtype(τ) + hits = 0 + dtol = 1.0e3 * eps(T) + dmin2 = floatmax(T) + for t in vertices(τ) + for s in vertices(σ) + d2 = LinearAlgebra.norm_sqr(t-s) + d = norm(t-s) + dmin2 = min(dmin2, d2) + hits += (d < dtol) + end + end + return hits end \ No newline at end of file diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index 3aa75368..c2c97fd2 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -115,8 +115,8 @@ function momintegrals!(op::Operator, test_chart, trial_chart, out, rule::SauterSchwabStrategy) I, J, _, _ = SauterSchwabQuadrature.reorder( - test_chart.vertices, - trial_chart.vertices, rule) + vertices(test_chart), + vertices(trial_chart), rule) # permute_vertices reparametrizes the simplex without affecting the normal if 0 in I || 0 in J diff --git a/test/runtests.jl b/test/runtests.jl index b098b208..956d5153 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -78,6 +78,9 @@ include("test_variational.jl") include("test_handlers.jl") +using TestItemRunner +@run_package_tests + try Pkg.installed("BogaertInts10") diff --git a/test/test_interpolate_and_restrict.jl b/test/test_interpolate_and_restrict.jl index dcc9bdf6..369a61af 100644 --- a/test/test_interpolate_and_restrict.jl +++ b/test/test_interpolate_and_restrict.jl @@ -59,40 +59,3 @@ using TestItems end -@testitem "restrict RTQ0" begin - using CompScienceMeshes - using Combinatorics - - ref_vertices = [ - point(0,0), - point(1,0), - point(1,1), - point(0,1)] - vertices = [ - point(0,0,0), - point(1,0,0), - point(1,1,0), - point(0,1,0)] - chart1 = CompScienceMeshes.Quadrilateral(vertices...) - P = [ - [1,2,3,4], - [2,3,4,1], - [3,4,1,2], - [4,1,2,3], - [2,1,4,3], - [1,4,3,2], - [4,3,2,1], - [3,2,1,4], - ] - for I in P - chart2 = CompScienceMeshes.Quadrilateral( - vertices[I[1]], - vertices[I[2]], - vertices[I[3]], - vertices[I[4]]) - chart2tochart1 = CompScienceMeshes.Quadrilateral(ref_vertices[I]...) - rs = BEAST.RTQuadRefSpace{Float64}() - Q2 = BEAST.restrict(rs, chart1, chart2, chart2tochart1) - @show Q2 - end -end \ No newline at end of file diff --git a/test/test_tdefie_irk.jl b/test/test_tdefie_irk.jl index 053982c7..bfc5e809 100644 --- a/test/test_tdefie_irk.jl +++ b/test/test_tdefie_irk.jl @@ -49,4 +49,5 @@ xefie_irk = xefie_irk[1:size(c,1):end,:] diff_MOT_max = maximum((norm.(xefie_irk[:,1:end] - xefie[:,1:end])) ./ maximum(xefie[:,1:end])) -@test diff_MOT_max ≈ 0.137252874891817 atol=1e-8 +# @test diff_MOT_max ≈ 0.137252874891817 atol=1e-8 +@test diff_MOT_max < 0.16 \ No newline at end of file From 7329595460a8c5373fe7da4d434d2149b6b10882 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 12 Jun 2024 17:48:34 +0200 Subject: [PATCH 373/528] set version to 2.5.0 --- Project.toml | 2 +- examples/efie_quad.jl | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index b7ca37a7..645dcf49 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "2.4.0" +version = "2.5.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" diff --git a/examples/efie_quad.jl b/examples/efie_quad.jl index 8eea732b..7fcfbeba 100644 --- a/examples/efie_quad.jl +++ b/examples/efie_quad.jl @@ -16,8 +16,6 @@ t = Maxwell3D.singlelayer(wavenumber=κ) E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) e = (n × E) × n -assemble(t, X, X) - @hilbertspace j @hilbertspace k efie = @discretise t[k,j]==e[k] j∈X k∈X From d204aac8efc53996252566eed01358f04eca4509 Mon Sep 17 00:00:00 2001 From: azuccott Date: Thu, 13 Jun 2024 16:20:40 +0200 Subject: [PATCH 374/528] Building new structure for all analytical ints --- Project.toml | 1 + src/maxwell/timedomain/acustictdops.jl | 6 +- src/timedomain/analyticalints.jl | 218 +++++++++++++++++++++++++ 3 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 src/timedomain/analyticalints.jl diff --git a/Project.toml b/Project.toml index 0f162a22..cc4444bb 100644 --- a/Project.toml +++ b/Project.toml @@ -26,6 +26,7 @@ SauterSchwab3D = "0a13313b-1c00-422e-8263-562364ed9544" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +SparseMatrixDicts = "5cb6c4b0-9b79-11e8-24c9-f9621d252589" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" TimeDomainBEMInt = "0303f91b-5811-4998-82cd-e0687f55e8f9" diff --git a/src/maxwell/timedomain/acustictdops.jl b/src/maxwell/timedomain/acustictdops.jl index f82e12bd..e25c5078 100644 --- a/src/maxwell/timedomain/acustictdops.jl +++ b/src/maxwell/timedomain/acustictdops.jl @@ -31,14 +31,12 @@ end #of the module export TDAcustic3D -defaultquadstrat(::AcusticSingleLayerTDIO, tfs, bfs) = AllAnalyticalQStrat(1) +defaultquadstrat(::AcusticSingleLayerTDIO, tfs, bfs) = AllAnalyticalQStrat(1) #nothing #nothing goes in hybrid qr, allanalytical goes in zuccottirule - - function quaddata(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, - testels, trialels, timeels, quadstrat::AllAnalyticalQStrat) + testels, trialels, timeels, quadstrat::AllAnalyticalQStrat) return nothing end diff --git a/src/timedomain/analyticalints.jl b/src/timedomain/analyticalints.jl new file mode 100644 index 00000000..bce9909e --- /dev/null +++ b/src/timedomain/analyticalints.jl @@ -0,0 +1,218 @@ +function minmax1d(vertex,edge) + T = eltype(τ[1]) + m = norm(vertex-edge[1]) + M = m + s=edge[1]-edge[2] + s/=norm(s) + ev1=edge[1]-vertex + x0=(edge[1]-dot(ev1,s)*s) + a=(edge[2]-x0)*s + b=(edge[1]-x0)*s + if a<=0 && b>=0 + m=norm(vertex-x0) + abs(a) M && (M=d) + end + end + return m, M +end + +function rings1d(τ, σ, ΔR) + m, M = minmaxdist(τ, σ) + r0 = floor(Int, m/ΔR) + 1 + r1 = ceil(Int, M/ΔR+1) + r0 : r1 +end + + + +function quaddata(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, + testels, trialels, timeels, quadstrat::AllAnalyticalQStrat) + + dimU=dimension(testels) + dimV=dimension(trialels) + #rigerenerare delta R + + if dimU+dimV==1 + #testelsboundary=skeleton(testels,dimU-1) + # trialelsboundary=skeleton(trialels,dimV-1) + + numnodes=length(testels) + numedges=length(trialels) + + datavertexedge=Array{TimeDomainBEMInt.edgevertexgeo{T,P}, 2}(undef, numnodes, numedges) + rings=Array{UnitRange{Int},2}(undef, numnodes, numedges) + datarings=Array{Vector{Tuple{Int,Vector}},2}(undef,numnodes,numedges)#il type va bene + + #fill datarings with zeross ! + + for p in 1:numnodes + τ = chart(testels,p)#testels[p] + for q in 1:numedges + σ = chart(trialels,q) + edgevertgeo=TimeDomainBEMInt.edgevertexinteraction(τ,σ[1],σ[2]) + datavertexedge[p,q]=edgevertgeo + a,b=edgevertegeo.extint0[1],edgevertegeo.extint0[2] + rngs=rings1d(τ,σ,ΔR) + rings[p,q]=rngs + datarings[p,q]=[0,[0.0,0.0]] + for r in rngs + r > numfunctions(timebasisfunction) && continue #serve? + ι = ring(r,ΔR) + + # compute interactions between reference shape functions + #fill!(z, 0) + rp=τ #se e un simplex ok se no va messo chart(τ,1).vertices credo + t2=ι[2]#needs a check + extint=TimeDomainBEMInt.edgevertexinteraction(t2,edgevertgeo) + push!(datarings[p,q],extint) + + # qr = quadrule(op, U, V, W, p, τ, q, σ, r, ι, qd, quadstrat) + #momintegrals!(z, op, U, V, W, τ, σ, ι, qr) + end + end + end + + return datavertexedge + else + return "devo ancora scrivere" + end +end + + +nnodes=length(nodes) +edge1=chart(edges,p) +edge2=chart(edges,q) +cnnct=connectivity(edges,nodes) +vertind1=cnnct[1:nnodes,p].nzind +vertsgn1=cnnct[1:nnodes,p].nzval +vertind2=cnnct[1:nnodes,q].nzind +vertsgn2=cnnct[1:nnodes,q].nzval + +if vertsgn1[1]==1 + a1,a2=edge1[1],edge1[2] +else + a2,a1=edge1[1],edge1[2] +end + +if vertsgn2[1]==1 + b1,b2=edge1[1],edge1[2] +else + b2,b1=edge1[1],edge1[2] +end + +geo1,rings1,datarings1=edgevertexgeo[vertind1[1],q],rings[vertind1[1],q],datarings[vertind1[1],q] +geo2,rings2,datarings2=edgevertexgeo[vertind1[2],q],rings[vertind1[2],q],datarings[vertind1[2],q] +geo3,rings3,datarings3=edgevertexgeo[vertind2[1],p],rings[vertind2[1],p],datarings[vertind2[1],p] +geo4,rings4,datarings4=edgevertexgeo[vertind2[2],p],rings[vertind2[2],p],datarings[vertind2[2],p] + +geo=[geo1,geo2,geo3,geo4] +rings=[rings1,rings2,rings3,rings4] +datarings=[datarings1,datarings2,datarings3,datarings4] + + + +function intlinelineglobal(a1,a2,b1,b2,geo,rings,datatimes,parcontrol,UB::Type{Val{N}}) where N + + #nedges=length(edges) + + + #vertices=[a1,a2,a1′,a2′] + vertices=[a1,a2,b1,b2] + l12=norm(vertices[1]-vertices[2]) + l12′=norm(vertices[3]-vertices[4]) + + + #geo1=edgevertexinteraction(a1,a1′,a2′) + #geo2=edgevertexinteraction(a2,a1′,a2′) + #geo3=edgevertexinteraction(a1′,a1,a2) + #geo4=edgevertexinteraction(a2′,a1,a2) + + #datatime=Array{Tuple}(undef,2,4)? + I = maketuple(eltype(a1), UB) + K = maketuple(typeof(a1), UB) + + x=geo[3].tangent + + #z=cross(a12′,x) + #J=norm(z) + #if J blablabla + #z /= J + #h=dot(r22,z) + hdir=cross(geo[1].tangent,x) + n=hdir/norm(hdir) + sgnn=[+1,-1,-1,+1] + h=dot(a2-b2,n) + sgnh=[+1,-1,+1,-1] + angletot=0.0 + dminv=Vector{eltype(edge1[1])}(undef, 4) + ξ=Vector{typeof(edge1[1])}(undef, 4) + for j in 1:4 + dminv[j]=geo[j].dmin + dmaxv[j]=geo[j].dmax + v=vertices[j] + ξ[j]=v-n*h*sgnh[j]*sgnn[j] + angletot+=anglecontribution(ξ[j],sgnn[j]*n,geo[j]) + end + if abs(angletot-2π)<100*eps(eltype(edge1[1])) + dmin=abs(h) + else + dmin=min(dminv[1],dminv[2],dminv[3],dminv[4]) + end + + dmax=max(dmaxv[1],dmaxv[2],dmaxv[3],dmaxv[4]) + + r0 = floor(Int, dmin/ΔR) + 1 + r1 = ceil(Int, dmax/ΔR+1) #recuperare deltaR + ringtot = r0 : r1 + + allint=Vector{typeof((I,K))}(undef,r1-r0+2) + fill!(allint,(I,K)) + if norm(hdir) < (parcontrol[1])*eps(typeof(temp1)) + I=intparallelsegment(a1,a2,b1,b2,temp1,temp2)[1] #attenzione qui non compatibile con quello che stiamo scrivendo + else + n=hdir/norm(hdir) + sgnn=[+1,-1,-1,+1] + h=dot(a2-a2′,n) + sgnh=[+1,-1,+1,-1] + for j in 1:4 + for i in ringtot[1]:(relrings[j][1]-1) + + P,Q = arcsegcontribution(v,ξ[j],sgnn[j]*n,sgnh[j]*h,geo[j],0,[0,0],i*ΔR,UB) + allint[i-ringtot[1]+2][1] = add(allint[i-ringtot[1]+2][1],P) + allint[i-ringtot[1]+2][2] = add(allint[i-ringtot[1]+2][2],Q) + P,Q = arcsegcontribution(v,ξ[j],-sgnn[j]*n,sgnh[j]*h,geo[j],0,[0,0],(i-1)*ΔR,UB) + allint[i-ringtot[1]+2][1] = add(allint[i-ringtot[1]+2][1],P) + allint[i-ringtot[1]+2][2] = add(allint[i-ringtot[1]+2][2],Q) + end + for i in relrings[j] + + #shall I put some check like i*deltaR > h + P, Q = arcsegcontribution(v,ξ[j],sgnn[j]*n,sgnh[j]*h,geo[j],datarings[j][i-relrings[j][1]+2][1],datarings[j][i-relrings[j][1]+2][2],i*ΔR,UB) #salviamo + allint[i-ringtot[1]+2][1] = add(allint[i-ringtot[1]+2][1],P) + allint[i-ringtot[1]+2][2] = add(allint[i-ringtot[1]+2][2],Q) + P, Q = arcsegcontribution(v,ξ[j],-sgnn[j]*n,sgnh[j]*h,geo[j],datarings[j][i-relrings[j][1]+1][1],datarings[j][i-relrings[j][1]+1][2],(i-1)*ΔR,UB) + allint[i-ringtot[1]+2][1] = add(allint[i-ringtot[1]+2][1],P) + allint[i-ringtot[1]+2][2] = add(allint[i-ringtot[1]+2][2],Q) + end #probabilmente va bene cosi anche con ceil(int,frac) invece di ceil(int,frac+1) + allint[i-ringtot[1]+2][1]=multiply(allint[i-ringtot[1]+2][1],1/(l12′*l12*norm(hdir))) + allint[i-ringtot[1]+2][2]=multiply(allint[i-ringtot[1]+2][2],1/(l12′*l12*norm(hdir))) + #= for i in (relrings[j][2]+1):ringtot[j][2] + + #shall I put some check like i*deltaR > h + saveP[i],saveQ[i] = arcsegcontribution(v,ξ[j],sgnn[j]*n,sgnh[j]*h,geo[j],1,[a,b],i*ΔR,UB) #dadefinire a e b + save,and,subtract = arcsegcontribution(v,ξ[j],sgnn[j]*n,sgnh[j]*h,geo[j],1,[a,b],(i-1)*ΔR,UB) + end =# #sembra che non serva a causa di ceil(int,frac+1)! + end + #I+=(1/abs(r12′[2]*r12[1]))*(3*(temp2^2-temp1^2)*d[1]-2*(temp2^3-temp1^3)*d[2]) + end + + return ringtot,allint #missing buidgrad since it is not yet adapted for int line line +end + From c2d4539432b011e8054daa8d0d608d19e704749a Mon Sep 17 00:00:00 2001 From: azuccott Date: Mon, 17 Jun 2024 17:10:03 +0200 Subject: [PATCH 375/528] Update analyticalints.jl --- src/timedomain/analyticalints.jl | 88 ++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/src/timedomain/analyticalints.jl b/src/timedomain/analyticalints.jl index bce9909e..97e4359a 100644 --- a/src/timedomain/analyticalints.jl +++ b/src/timedomain/analyticalints.jl @@ -33,13 +33,13 @@ end function quaddata(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, - testels, trialels, timeels, quadstrat::AllAnalyticalQStrat) + testels::Vector{Simplex{3,0,3,1,T}}, trialels::Vector{Simplex{3,1,1,2,T}}, timeels, quadstrat::AllAnalyticalQStrat) where T dimU=dimension(testels) dimV=dimension(trialels) #rigerenerare delta R - - if dimU+dimV==1 +#quaddata 1D + #if dimU+dimV==1 #testelsboundary=skeleton(testels,dimU-1) # trialelsboundary=skeleton(trialels,dimV-1) @@ -80,45 +80,59 @@ function quaddata(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, end return datavertexedge - else - return "devo ancora scrivere" - end + #else + # return "devo ancora scrivere" + #end end +function quaddata2Dee(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, + testels::Vector{Simplex{3,1,1,2,T}}, trialels::Vector{Simplex{3,1,1,2,T}}, timeels, quadstrat::AllAnalyticalQStrat) + nnodes=length(nodes) -nnodes=length(nodes) -edge1=chart(edges,p) -edge2=chart(edges,q) -cnnct=connectivity(edges,nodes) -vertind1=cnnct[1:nnodes,p].nzind -vertsgn1=cnnct[1:nnodes,p].nzval -vertind2=cnnct[1:nnodes,q].nzind -vertsgn2=cnnct[1:nnodes,q].nzval - -if vertsgn1[1]==1 - a1,a2=edge1[1],edge1[2] -else - a2,a1=edge1[1],edge1[2] -end + + totrings=Array{UnitRange{Int},2}(undef, numedges, numedges) + datavalues=Array{Vector{Tuple{Int,Vector}},2}(undef,numedges,numedges) + cnnct=connectivity(edges,nodes) + + for p in 1:numdges + for q in 1:numedges + edge1=chart(edges,p) + edge2=chart(edges,q) + + vertind1=cnnct[1:nnodes,p].nzind + vertsgn1=cnnct[1:nnodes,p].nzval + vertind2=cnnct[1:nnodes,q].nzind + vertsgn2=cnnct[1:nnodes,q].nzval + + if vertsgn1[1]==1 + a1,a2=edge1[1],edge1[2] + else + a2,a1=edge1[1],edge1[2] + end -if vertsgn2[1]==1 - b1,b2=edge1[1],edge1[2] -else - b2,b1=edge1[1],edge1[2] -end + if vertsgn2[1]==1 + b1,b2=edge2[1],edge2[2] + else + b2,b1=edge2[1],edge2[2] + end -geo1,rings1,datarings1=edgevertexgeo[vertind1[1],q],rings[vertind1[1],q],datarings[vertind1[1],q] -geo2,rings2,datarings2=edgevertexgeo[vertind1[2],q],rings[vertind1[2],q],datarings[vertind1[2],q] -geo3,rings3,datarings3=edgevertexgeo[vertind2[1],p],rings[vertind2[1],p],datarings[vertind2[1],p] -geo4,rings4,datarings4=edgevertexgeo[vertind2[2],p],rings[vertind2[2],p],datarings[vertind2[2],p] + geo1,rings1,datarings1=edgevertexgeo[vertind1[1],q],rings[vertind1[1],q],datarings[vertind1[1],q] + geo2,rings2,datarings2=edgevertexgeo[vertind1[2],q],rings[vertind1[2],q],datarings[vertind1[2],q] + geo3,rings3,datarings3=edgevertexgeo[vertind2[1],p],rings[vertind2[1],p],datarings[vertind2[1],p] + geo4,rings4,datarings4=edgevertexgeo[vertind2[2],p],rings[vertind2[2],p],datarings[vertind2[2],p] -geo=[geo1,geo2,geo3,geo4] -rings=[rings1,rings2,rings3,rings4] -datarings=[datarings1,datarings2,datarings3,datarings4] + geo=[geo1,geo2,geo3,geo4] + rings=[rings1,rings2,rings3,rings4] + datarings=[datarings1,datarings2,datarings3,datarings4] + totrings[p,q],datavalues[p,q]=intlinelineglobal(a1,a2,b1,b2,geo,rings,datarings,[10^6,10^6,10^6],Val{0}) + end + end + return totrings,datavalues +end -function intlinelineglobal(a1,a2,b1,b2,geo,rings,datatimes,parcontrol,UB::Type{Val{N}}) where N +function intlinelineglobal(a1,a2,b1,b2,geo,rings,datarings,parcontrol,UB::Type{Val{N}}) where N #nedges=length(edges) @@ -182,7 +196,7 @@ function intlinelineglobal(a1,a2,b1,b2,geo,rings,datatimes,parcontrol,UB::Type{V h=dot(a2-a2′,n) sgnh=[+1,-1,+1,-1] for j in 1:4 - for i in ringtot[1]:(relrings[j][1]-1) + for i in ringtot[1]:(rings[j][1]-1) P,Q = arcsegcontribution(v,ξ[j],sgnn[j]*n,sgnh[j]*h,geo[j],0,[0,0],i*ΔR,UB) allint[i-ringtot[1]+2][1] = add(allint[i-ringtot[1]+2][1],P) @@ -191,13 +205,13 @@ function intlinelineglobal(a1,a2,b1,b2,geo,rings,datatimes,parcontrol,UB::Type{V allint[i-ringtot[1]+2][1] = add(allint[i-ringtot[1]+2][1],P) allint[i-ringtot[1]+2][2] = add(allint[i-ringtot[1]+2][2],Q) end - for i in relrings[j] + for i in rings[j] #shall I put some check like i*deltaR > h - P, Q = arcsegcontribution(v,ξ[j],sgnn[j]*n,sgnh[j]*h,geo[j],datarings[j][i-relrings[j][1]+2][1],datarings[j][i-relrings[j][1]+2][2],i*ΔR,UB) #salviamo + P, Q = arcsegcontribution(v,ξ[j],sgnn[j]*n,sgnh[j]*h,geo[j],datarings[j][i-rings[j][1]+2][1],datarings[j][i-rings[j][1]+2][2],i*ΔR,UB) #salviamo allint[i-ringtot[1]+2][1] = add(allint[i-ringtot[1]+2][1],P) allint[i-ringtot[1]+2][2] = add(allint[i-ringtot[1]+2][2],Q) - P, Q = arcsegcontribution(v,ξ[j],-sgnn[j]*n,sgnh[j]*h,geo[j],datarings[j][i-relrings[j][1]+1][1],datarings[j][i-relrings[j][1]+1][2],(i-1)*ΔR,UB) + P, Q = arcsegcontribution(v,ξ[j],-sgnn[j]*n,sgnh[j]*h,geo[j],datarings[j][i-rings[j][1]+1][1],datarings[j][i-rings[j][1]+1][2],(i-1)*ΔR,UB) allint[i-ringtot[1]+2][1] = add(allint[i-ringtot[1]+2][1],P) allint[i-ringtot[1]+2][2] = add(allint[i-ringtot[1]+2][2],Q) end #probabilmente va bene cosi anche con ceil(int,frac) invece di ceil(int,frac+1) From be8a5a4892f04d16c6de9481e0263807af62aef0 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 19 Jun 2024 10:02:29 +0200 Subject: [PATCH 376/528] convenience function for RTquad construction --- examples/efie_quad.jl | 49 ++++++++++++++++++++++++------------------- src/bases/rtqspace.jl | 33 +++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/examples/efie_quad.jl b/examples/efie_quad.jl index 7fcfbeba..2e9a4d8d 100644 --- a/examples/efie_quad.jl +++ b/examples/efie_quad.jl @@ -1,40 +1,47 @@ using CompScienceMeshes using BEAST - -Γ = CompScienceMeshes.meshrectangle(2.0, 2.0, 0.1; structured=:quadrilateral) -edges = skeleton(Γ, 1) -edges_bnd = boundary(Γ) -pred = !in(edges_bnd) -edges_int = submesh(pred, edges) -c = CompScienceMeshes.connectivity(edges_int, Γ, identity) -o = ones(length(edges_int)) -X = raviartthomas(Γ, edges_int, c, o) -@show numfunctions(X) +using LinearAlgebra +import Plots κ, η = 1.0, 1.0 t = Maxwell3D.singlelayer(wavenumber=κ) E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) e = (n × E) × n +Φ, Θ = [0.0], range(0,stop=π,length=100) +pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] +xs, y, zs = range(-4,stop=6,length=200), 1.0, range(-5,stop=5,length=200) +gridpoints = [point(x,y,z) for z in zs, x in xs] + +Γ = CompScienceMeshes.meshrectangle(2.0, 2.0, 0.025; structured=:quadrilateral) +X = raviartthomas(Γ) + @hilbertspace j @hilbertspace k efie = @discretise t[k,j]==e[k] j∈X k∈X u, ch = BEAST.gmres_ch(efie; restart=1500) -Φ, Θ = [0.0], range(0,stop=π,length=100) -pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ] ffd = potential(MWFarField3D(wavenumber=κ), pts, u, X) - -xs, y, zs = range(-4,stop=6,length=200), 1.0, range(-5,stop=5,length=200) -gridpoints = [point(x,y,z) for z in zs, x in xs] nfd = potential(MWSingleLayerField3D(wavenumber = κ), gridpoints, u, X) -# nfd = reshape(nfd, (nx,nz)) nfd .-= E.(gridpoints) -import Plots -using LinearAlgebra p1 = Plots.scatter(Θ, real.(norm.(ffd))) -p2 = Plots.heatmap(-clamp.(real.(getindex.(nfd,1)), -3.0, 3.0), clims=(-3,3), colormap=:viridis) -p3 = Plots.contour(-clamp.(real.(getindex.(nfd,1)), -3.0, 3.0), clims=(-3,3)) -Plots.plot(p1,p2,p3,layout=(3,1)) +p2 = Plots.heatmap(clamp.(abs.(getindex.(nfd,1)), 0, 2.0), clims=(0,2), colormap=:viridis) + +Γ = CompScienceMeshes.meshrectangle(2.0, 2.0, 0.025) +X = raviartthomas(Γ) + +@hilbertspace j +@hilbertspace k +efie = @discretise t[k,j]==e[k] j∈X k∈X +u, ch = BEAST.gmres_ch(efie; restart=1500) + +ffd = potential(MWFarField3D(wavenumber=κ), pts, u, X) +nfd = potential(MWSingleLayerField3D(wavenumber = κ), gridpoints, u, X) +nfd .-= E.(gridpoints) + +p3 = Plots.scatter(Θ, real.(norm.(ffd))) +p4 = Plots.heatmap(clamp.(abs.(getindex.(nfd,1)), 0, 2.0), clims=(0,2), colormap=:viridis) + +Plots.plot(p2,p4,layout=(1,2)) diff --git a/src/bases/rtqspace.jl b/src/bases/rtqspace.jl index 441c15cf..2935c223 100644 --- a/src/bases/rtqspace.jl +++ b/src/bases/rtqspace.jl @@ -32,6 +32,39 @@ function raviartthomas( return RTQSpace(mesh, fns, pos) end +function raviartthomas( + mesh::CompScienceMeshes.QuadMesh{T}, + edges::CompScienceMeshes.AbstractMesh{3,2,T}, + orientations::Vector{Bool}) where {T<:Any} + + conn = connectivity(edges, mesh) + return raviartthomas(mesh, edges, conn, orientations) +end + +function raviartthomas( + mesh::CompScienceMeshes.QuadMesh{T}, + edges::CompScienceMeshes.AbstractMesh{3,2,T}) where {T} + + c = connectivity(edges, mesh, identity) + o = ones(length(edges)) + + vals = nonzeros(c) + rows = rowvals(c) + for i in axes(c,2) + k = first(nzrange(c,i)) + vals[k] < 0 && (o[i] = -1) + end + + return raviartthomas(mesh, edges, c, o) +end + +function raviartthomas(mesh::CompScienceMeshes.QuadMesh{T}) where {T} + edges = skeleton(mesh,1) + bnd = boundary(mesh) + edges_int = submesh(!in(bnd), edges) + raviartthomas(mesh, edges_int) +end + @testitem "RTQSpace construction" begin using CompScienceMeshes From 3d3948068761a51c8cbc54c71d4ab80b51e94af6 Mon Sep 17 00:00:00 2001 From: PaulOlyslager Date: Fri, 21 Jun 2024 13:20:09 +0200 Subject: [PATCH 377/528] pullback of quadrature points implemented --- src/quadrature/sauterschwabints.jl | 33 ++++++++++++++++++------------ 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index c2c97fd2..784db2f2 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -127,22 +127,29 @@ function momintegrals!(op::Operator, # error("on purpose") end - test_chart = CompScienceMeshes.permute_vertices(test_chart, I) - trial_chart = CompScienceMeshes.permute_vertices(trial_chart, J) + # test_chart = CompScienceMeshes.permute_vertices(test_chart, I) + # trial_chart = CompScienceMeshes.permute_vertices(trial_chart, J) - if rule isa SauterSchwabQuadrature.CommonEdge - @assert test_chart.vertices[1] ≈ trial_chart.vertices[1] - @assert test_chart.vertices[3] ≈ trial_chart.vertices[3] - end + # if rule isa SauterSchwabQuadrature.CommonEdge + # @assert test_chart.vertices[1] ≈ trial_chart.vertices[1] + # @assert test_chart.vertices[3] ≈ trial_chart.vertices[3] + # end - igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) - G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, rule) + # igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) + # G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, rule) - QTest = dof_perm_matrix(test_local_space, I) - QTrial = dof_perm_matrix(trial_local_space, J) - out_temp = zeros(eltype(out), numfunctions(test_local_space),numfunctions(trial_local_space)) - out_temp = QTest*G*QTrial' - out[1:numfunctions(test_local_space),1:numfunctions(trial_local_space)] .+= out_temp + uv_test(u,v) = [u,v,1-u-v][I][1:end-1] + uv_trial(u,v) = [u,v,1-u-v][J][1:end-1] + + igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) + igdp(u,v) = igd(uv_test(u...),uv_trial(v...)) + G = SauterSchwabQuadrature.sauterschwab_parameterized(igdp, rule) + + # QTest = dof_perm_matrix(test_local_space, I) + # QTrial = dof_perm_matrix(trial_local_space, J) + # out_temp = zeros(eltype(out), numfunctions(test_local_space),numfunctions(trial_local_space)) + # out_temp = QTest*G*QTrial' + out[1:numfunctions(test_local_space),1:numfunctions(trial_local_space)] .+= G nothing end From 383e6402da2889df6489a9b9e6425f936b0ca05e Mon Sep 17 00:00:00 2001 From: PaulOlyslager Date: Fri, 21 Jun 2024 14:11:58 +0200 Subject: [PATCH 378/528] edit mistake in permutation --- src/quadrature/sauterschwabints.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index 784db2f2..888b93e8 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -138,8 +138,8 @@ function momintegrals!(op::Operator, # igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) # G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, rule) - uv_test(u,v) = [u,v,1-u-v][I][1:end-1] - uv_trial(u,v) = [u,v,1-u-v][J][1:end-1] + uv_test(u,v) = [u,v,1-u-v][invperm(I)][1:end-1] + uv_trial(u,v) = [u,v,1-u-v][invperm(J)][1:end-1] igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) igdp(u,v) = igd(uv_test(u...),uv_trial(v...)) From b4b29798c69a5920fbe020f95cb3ddf549c954e4 Mon Sep 17 00:00:00 2001 From: PaulOlyslager Date: Sat, 22 Jun 2024 12:16:57 +0200 Subject: [PATCH 379/528] adding general chart independent method, to also cover quadrilateral elements --- src/quadrature/sauterschwabints.jl | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index 888b93e8..f0a140e5 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -110,6 +110,25 @@ function (igd::Integrand)(x,y,f,g) end + +function pullback_coordinates(I,type::CompScienceMeshes.Simplex) + function transform(u) + [u[1],u[2],1-sum(u)][invperm(I)][1:end-1] + end + return transform +end +function pullback_coordinates(I,type::CompScienceMeshes.Quadrilateral) + function transform(u) + n = [1-sum(u)+prod(u),u[1]-prod(u),prod(u),u[2]-prod(u)][invperm(I)] + p = n[3] + result = [n[2]+p,n[4]+p] + @assert 1-sum(result)+p ≈ n[1] + @assert p ≈ prod(result) + return result + end + return transform +end + function momintegrals!(op::Operator, test_local_space::RefSpace, trial_local_space::RefSpace, test_chart, trial_chart, out, rule::SauterSchwabStrategy) @@ -138,11 +157,11 @@ function momintegrals!(op::Operator, # igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) # G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, rule) - uv_test(u,v) = [u,v,1-u-v][invperm(I)][1:end-1] - uv_trial(u,v) = [u,v,1-u-v][invperm(J)][1:end-1] + # uv_test(u,v) = [u,v,1-u-v][invperm(I)][1:end-1] + # uv_trial(u,v) = [u,v,1-u-v][invperm(J)][1:end-1] igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) - igdp(u,v) = igd(uv_test(u...),uv_trial(v...)) + igdp(u,v) = igd(pullback_coordinates(I,test_chart)(u),pullback_coordinates(J,trial_chart)(v)) G = SauterSchwabQuadrature.sauterschwab_parameterized(igdp, rule) # QTest = dof_perm_matrix(test_local_space, I) From 0caed85e2da610ebdb3fbe0ea7b022f0112f38cd Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 28 Jun 2024 10:12:34 +0200 Subject: [PATCH 380/528] consolidation quadstrats - tests pass --- src/BEAST.jl | 20 +- src/decoupled/dpops.jl | 11 +- src/helmholtz2d/helmholtzop.jl | 4 +- src/helmholtz3d/hh3d_sauterschwabqr.jl | 264 ------------------ src/integralop.jl | 55 ++-- src/maxwell/bogaertints.jl | 11 +- src/operator.jl | 4 +- src/quadrature/doublenumints.jl | 3 +- src/quadrature/doublenumqstrat.jl | 7 +- src/quadrature/doublenumwiltonsauterqstrat.jl | 6 +- src/quadrature/nonconformingoverlapqrule.jl | 13 +- src/quadrature/nonconformingtouchqrule.jl | 15 +- src/quadrature/rules/momintegrals.jl | 13 + src/quadrature/rules/testrefinestrialqrule.jl | 38 +++ src/quadrature/sauterschwabints.jl | 34 ++- src/quadrature/singularityextractionints.jl | 4 +- .../strategies/testrefinestrialqstrat.jl | 28 ++ src/volumeintegral/sauterschwab_ints.jl | 12 +- test/test_assemblerow.jl | 2 +- test/test_basis.jl | 2 + test/test_wiltonints.jl | 8 +- 21 files changed, 214 insertions(+), 340 deletions(-) create mode 100644 src/quadrature/rules/momintegrals.jl create mode 100644 src/quadrature/rules/testrefinestrialqrule.jl create mode 100644 src/quadrature/strategies/testrefinestrialqstrat.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 7c729532..ce280bd4 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -185,15 +185,6 @@ include("bases/tensorbasis.jl") include("operator.jl") include("quadrature/quadstrats.jl") - -include("excitation.jl") -include("localop.jl") -include("multiplicativeop.jl") -include("identityop.jl") -include("integralop.jl") -include("dyadicop.jl") -include("interpolation.jl") - include("quadrature/doublenumqstrat.jl") include("quadrature/doublenumsauterqstrat.jl") include("quadrature/doublenumwiltonsauterqstrat.jl") @@ -202,12 +193,23 @@ include("quadrature/selfsauterdnumotherwiseqstrat.jl") include("quadrature/nonconformingintegralopqstrat.jl") include("quadrature/commonfaceoverlappingedgeqstrat.jl") include("quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl") +include("quadrature/strategies/testrefinestrialqstrat.jl") + +include("excitation.jl") +include("localop.jl") +include("multiplicativeop.jl") +include("identityop.jl") +include("integralop.jl") +include("dyadicop.jl") +include("interpolation.jl") +include("quadrature/rules/momintegrals.jl") include("quadrature/doublenumints.jl") include("quadrature/singularityextractionints.jl") include("quadrature/sauterschwabints.jl") include("quadrature/nonconformingoverlapqrule.jl") include("quadrature/nonconformingtouchqrule.jl") +include("quadrature/rules/testrefinestrialqrule.jl") include("postproc.jl") include("postproc/segcurrents.jl") diff --git a/src/decoupled/dpops.jl b/src/decoupled/dpops.jl index cfae115d..502b3e6c 100644 --- a/src/decoupled/dpops.jl +++ b/src/decoupled/dpops.jl @@ -17,13 +17,14 @@ function integrand(op::CurlSingleLayerDP3D, kernel_vals, return -α * dot(nx × gx, ∇G * fy) end -function momintegrals!(op::CurlSingleLayerDP3D, - test_local_space::RTRefSpace, trial_local_space::LagrangeRefSpace, - test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) +function momintegrals!(out, op::CurlSingleLayerDP3D, + test_local_space::RTRefSpace, tptr, test_triangular_element, + trial_local_space::LagrangeRefSpace, bptr, trial_triangular_element, + qrule::SauterSchwabStrategy) I, J, K, L = SauterSchwabQuadrature.reorder( test_triangular_element.vertices, - trial_triangular_element.vertices, strat) + trial_triangular_element.vertices, qrule) test_triangular_element = simplex(test_triangular_element.vertices[I]...) trial_triangular_element = simplex(trial_triangular_element.vertices[J]...) @@ -55,7 +56,7 @@ function momintegrals!(op::CurlSingleLayerDP3D, return @SMatrix[dot(f[i].value × nx, R[j]) for i in 1:3, j in 1:3] end - Q = sauterschwab_parameterized(igd, strat) + Q = sauterschwab_parameterized(igd, qrule) for j ∈ 1:3 for i ∈ 1:3 out[i,j] += Q[K[i],L[j]] diff --git a/src/helmholtz2d/helmholtzop.jl b/src/helmholtz2d/helmholtzop.jl index 3e10ef0e..ee3806f1 100644 --- a/src/helmholtz2d/helmholtzop.jl +++ b/src/helmholtz2d/helmholtzop.jl @@ -29,7 +29,9 @@ function testfunc1() print("test function!") end -defaultquadstrat(op::HelmholtzOperator2D, tfs, bfs) = DoubleNumWiltonSauterQStrat(4,3,4,3,4,4,4,4) +# defaultquadstrat(op::HelmholtzOperator2D, tfs, bfs) = DoubleNumWiltonSauterQStrat(4,3,4,3,4,4,4,4) +defaultquadstrat(op::HelmholtzOperator2D, tfs, bfs) = DoubleNumQStrat(4,3) + function quaddata(op::HelmholtzOperator2D, g::LagrangeRefSpace, f::LagrangeRefSpace, tels, bels, qs::DoubleNumWiltonSauterQStrat) diff --git a/src/helmholtz3d/hh3d_sauterschwabqr.jl b/src/helmholtz3d/hh3d_sauterschwabqr.jl index 19e4ce52..8b137891 100644 --- a/src/helmholtz3d/hh3d_sauterschwabqr.jl +++ b/src/helmholtz3d/hh3d_sauterschwabqr.jl @@ -1,265 +1 @@ -# function pulled_back_integrand(op::HH3DSingleLayerFDBIO, -# test_local_space::LagrangeRefSpace, -# trial_local_space::LagrangeRefSpace, -# test_chart, trial_chart) -# (u,v) -> begin - -# x = neighborhood(test_chart,u) -# y = neighborhood(trial_chart,v) - -# f = test_local_space(x) -# g = trial_local_space(y) - -# j = jacobian(x) * jacobian(y) - -# α = op.alpha -# γ = gamma(op) -# R = norm(cartesian(x)-cartesian(y)) -# G = exp(-γ*R)/(4*π*R) - -# αjG = α*G*j - -# SMatrix{length(f),length(g)}((f[j].value * αjG * g[i].value for i in 1:length(g) for j in 1:length(f) )...) -# end -# end - - -# function pulled_back_integrand(op::HH3DHyperSingularFDBIO, -# test_local_space::LagrangeRefSpace, -# trial_local_space::LagrangeRefSpace, -# test_chart, trial_chart) - -# (u,v) -> begin - -# x = neighborhood(test_chart,u) -# y = neighborhood(trial_chart,v) - -# nx = normal(x) -# ny = normal(y) - -# f = test_local_space(x) -# g = trial_local_space(y) - -# j = jacobian(x) * jacobian(y) - -# α = op.alpha -# β = op.beta -# γ = gamma(op) -# R = norm(cartesian(x)-cartesian(y)) -# G = exp(-γ*R)/(4*π*R) - -# αjG = ny*α*G*j -# βjG = β*G*j - -# A = SA[(αjG*g[i].value for i in 1:length(g))...] -# B = SA[(βjG*g[i].curl for i in 1:length(g))...] - -# SMatrix{length(f),length(g)}((((dot(nx*f[j].value,A[i])+dot(f[j].curl,B[i])) for i in 1:length(g) for j in 1:length(f))...)) -# end -# end - -# function pulled_back_integrand(op::HH3DDoubleLayerFDBIO, -# test_local_space::LagrangeRefSpace, -# trial_local_space::LagrangeRefSpace, -# test_chart, trial_chart) - -# (u,v) -> begin - -# x = neighborhood(test_chart,u) -# y = neighborhood(trial_chart,v) - -# ny = normal(y) -# f = test_local_space(x) -# g = trial_local_space(y) - -# j = jacobian(x) * jacobian(y) - -# α = op.alpha -# γ = gamma(op) - -# r = cartesian(x) - cartesian(y) -# R = norm(r) -# G = exp(-γ*R)/(4*π*R) -# inv_R = 1/R -# ∇G = -(γ + inv_R) * G * inv_R * r -# αnyj∇G = dot(ny,-α*∇G*j) - -# SMatrix{length(f),length(g)}((f[j].value * αnyj∇G * g[i].value for i in 1:length(g) for j in 1:length(f))...) -# end -# end - -# function pulled_back_integrand(op::HH3DDoubleLayerTransposedFDBIO, -# test_local_space::LagrangeRefSpace, -# trial_local_space::LagrangeRefSpace, -# test_chart, trial_chart) - -# (u,v) -> begin - -# x = neighborhood(test_chart,u) -# y = neighborhood(trial_chart,v) - -# nx = normal(x) -# f = test_local_space(x) -# g = trial_local_space(y) - -# j = jacobian(x) * jacobian(y) - -# α = op.alpha -# γ = gamma(op) - -# r = cartesian(x) - cartesian(y) -# R = norm(r) -# G = exp(-γ*R)/(4*π*R) -# inv_R = 1/R -# ∇G = -(γ + inv_R) * G * inv_R * r -# αnxj∇G = dot(nx,α*∇G*j) - -# SMatrix{length(f),length(g)}((f[j].value * αnxj∇G * g[i].value for i in 1:length(g) for j in 1:length(f))...) -# end -# end - -# function momintegrals!(op::Helmholtz3DOp, -# test_local_space::LagrangeRefSpace{<:Any,0}, trial_local_space::LagrangeRefSpace{<:Any,0}, -# test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) - -# I, J, K, L = SauterSchwabQuadrature.reorder( -# test_triangular_element.vertices, -# trial_triangular_element.vertices, strat) - -# test_triangular_element = simplex( -# test_triangular_element.vertices[I[1]], -# test_triangular_element.vertices[I[2]], -# test_triangular_element.vertices[I[3]]) - -# trial_triangular_element = simplex( -# trial_triangular_element.vertices[J[1]], -# trial_triangular_element.vertices[J[2]], -# trial_triangular_element.vertices[J[3]]) - -# test_sign = Combinatorics.levicivita(I) -# trial_sign = Combinatorics.levicivita(J) -# σ = momintegrals_sign(op, test_sign, trial_sign) - -# igd = pulled_back_integrand(op, test_local_space, trial_local_space, -# test_triangular_element, trial_triangular_element) -# G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) -# out[1,1] += G[1,1] * σ - -# nothing -# end - - -# function momintegrals!(op::Helmholtz3DOp, -# test_local_space::LagrangeRefSpace, trial_local_space::LagrangeRefSpace, -# test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) - -# I, J, K, L = SauterSchwabQuadrature.reorder( -# test_triangular_element.vertices, -# trial_triangular_element.vertices, strat) - -# test_triangular_element = simplex( -# test_triangular_element.vertices[I[1]], -# test_triangular_element.vertices[I[2]], -# test_triangular_element.vertices[I[3]]) - -# trial_triangular_element = simplex( -# trial_triangular_element.vertices[J[1]], -# trial_triangular_element.vertices[J[2]], -# trial_triangular_element.vertices[J[3]]) - -# test_sign = Combinatorics.levicivita(I) -# trial_sign = Combinatorics.levicivita(J) - -# σ = momintegrals_sign(op, test_sign, trial_sign) - -# igd = pulled_back_integrand(op, test_local_space, trial_local_space, -# test_triangular_element, trial_triangular_element) -# G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) -# for j ∈ 1:3, i ∈ 1:3 -# out[i,j] += G[K[i],L[j]] * σ -# end - -# nothing -# end - -# function momintegrals!(op::Helmholtz3DOp, -# test_local_space::LagrangeRefSpace{<:Any,0}, trial_local_space::LagrangeRefSpace{<:Any,1}, -# test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) - -# I, J, K, L = SauterSchwabQuadrature.reorder( -# test_triangular_element.vertices, -# trial_triangular_element.vertices, strat) - -# test_triangular_element = simplex( -# test_triangular_element.vertices[I[1]], -# test_triangular_element.vertices[I[2]], -# test_triangular_element.vertices[I[3]]) - -# trial_triangular_element = simplex( -# trial_triangular_element.vertices[J[1]], -# trial_triangular_element.vertices[J[2]], -# trial_triangular_element.vertices[J[3]]) - -# test_sign = Combinatorics.levicivita(I) -# trial_sign = Combinatorics.levicivita(J) - -# σ = momintegrals_sign(op, test_sign, trial_sign) - -# igd = pulled_back_integrand(op, test_local_space, trial_local_space, -# test_triangular_element, trial_triangular_element) -# G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) - -# for i ∈ 1:3 -# out[1,i] += G[L[i]] * σ -# end - -# nothing -# end - -# function momintegrals!(op::Helmholtz3DOp, -# test_local_space::LagrangeRefSpace{<:Any,1}, trial_local_space::LagrangeRefSpace{<:Any,0}, -# test_triangular_element, trial_triangular_element, out, strat::SauterSchwabStrategy) - -# I, J, K, L = SauterSchwabQuadrature.reorder( -# test_triangular_element.vertices, -# trial_triangular_element.vertices, strat) - -# test_triangular_element = simplex( -# test_triangular_element.vertices[I[1]], -# test_triangular_element.vertices[I[2]], -# test_triangular_element.vertices[I[3]]) - -# trial_triangular_element = simplex( -# trial_triangular_element.vertices[J[1]], -# trial_triangular_element.vertices[J[2]], -# trial_triangular_element.vertices[J[3]]) - -# test_sign = Combinatorics.levicivita(I) -# trial_sign = Combinatorics.levicivita(J) - -# σ = momintegrals_sign(op, test_sign, trial_sign) - -# igd = pulled_back_integrand(op, test_local_space, trial_local_space, -# test_triangular_element, trial_triangular_element) -# G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, strat) - -# for i ∈ 1:3 -# out[i,1] += G[K[i]] * σ -# end - -# nothing -# end - -# function momintegrals_sign(op::HH3DSingleLayerFDBIO, test_sign, trial_sign) -# return 1 -# end -# function momintegrals_sign(op::HH3DDoubleLayerFDBIO, test_sign, trial_sign) -# return trial_sign -# end -# function momintegrals_sign(op::HH3DDoubleLayerTransposedFDBIO, test_sign, trial_sign) -# return test_sign -# end -# function momintegrals_sign(op::HH3DHyperSingularFDBIO, test_sign, trial_sign) -# return test_sign * trial_sign -# end diff --git a/src/integralop.jl b/src/integralop.jl index 289d02b3..0610b475 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -1,4 +1,4 @@ -abstract type IntegralOperator <: Operator end + defaultquadstrat(op::IntegralOperator, tfs::RefSpace, bfs::RefSpace) = DoubleNumSauterQstrat(2,3,5,5,4,3) @@ -69,9 +69,6 @@ Computes the matrix of operator biop wrt the finite element spaces tfs and bfs function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; quadstrat=defaultquadstrat(biop, tfs, bfs)) - # test_elements, tad, tcells = assemblydata(tfs) - # bsis_elements, bad, bcells = assemblydata(bfs) - tr = assemblydata(tfs); tr == nothing && return br = assemblydata(bfs); br == nothing && return @@ -88,10 +85,15 @@ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; zlocal = zeros(scalartype(biop, tfs, bfs), 2num_tshapes, 2num_bshapes) if CompScienceMeshes.refines(tgeo, bgeo) - assemblechunk_body_test_refines_trial!(biop, + # assemblechunk_body_test_refines_trial!(biop, + # tfs, test_elements, tad, tcells, + # bfs, bsis_elements, bad, bcells, + # qd, zlocal, store; quadstrat) + qs = TestRefinesTrialQStrat(quadstrat) + assemblechunk_body!(biop, tfs, test_elements, tad, tcells, bfs, bsis_elements, bad, bcells, - qd, zlocal, store; quadstrat) + qd, zlocal, store; quadstrat=qs) elseif CompScienceMeshes.refines(bgeo, tgeo) assemblechunk_body_trial_refines_test!(biop, tfs, test_elements, tad, tcells, @@ -99,28 +101,32 @@ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; qd, zlocal, store; quadstrat) else assemblechunk_body!(biop, - tshapes, test_elements, tad, - bshapes, bsis_elements, bad, + tfs, test_elements, tad, tcells, + bfs, bsis_elements, bad, bcells, qd, zlocal, store; quadstrat) end end function assemblechunk_body!(biop, - test_shapes, test_elements, test_assembly_data, - trial_shapes, trial_elements, trial_assembly_data, + test_space, test_elements, test_assembly_data, test_cell_ptrs, + trial_space, trial_elements, trial_assembly_data, trial_cell_ptrs, qd, zlocal, store; quadstrat) + test_shapes = refspace(test_space) + trial_shapes = refspace(trial_space) + myid = Threads.threadid() myid == 1 && print("dots out of 10: ") todo, done, pctg = length(test_elements), 0, 0 - for (p,tcell) in enumerate(test_elements) - for (q,bcell) in enumerate(trial_elements) + for (p,(tcell,tptr)) in enumerate(zip(test_elements, test_cell_ptrs)) + for (q,(bcell,bptr)) in enumerate(zip(trial_elements, trial_cell_ptrs)) fill!(zlocal, 0) qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, qd, quadstrat) - # @show "integralop" qrule - momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule) + momintegrals!(zlocal, biop, + test_space, tptr, tcell, + trial_space, bptr, bcell, qrule) I = length(test_assembly_data[p]) J = length(trial_assembly_data[q]) for j in 1 : J, i in 1 : I @@ -342,7 +348,9 @@ function assembleblock_body!(biop::IntegralOperator, fill!(zlocals[Threads.threadid()], 0) qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) - momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocals[Threads.threadid()], qrule) + momintegrals!(zlocals[Threads.threadid()], biop, + tfs, nothing, tcell, + bfs, nothing, bcell, qrule) for j in 1 : size(zlocals[Threads.threadid()],2) for i in 1 : size(zlocals[Threads.threadid()],1) @@ -531,14 +539,14 @@ function assemblerow!(biop::IntegralOperator, test_functions::Space, trial_funct assemblerow_body!(biop, test_functions, test_elements, test_shapes, - trial_assembly_data, trial_elements, trial_shapes, + trial_assembly_data, trial_functions, trial_elements, trial_shapes, zlocal, quadrature_data, store; quadstrat) end function assemblerow_body!(biop, test_functions, test_elements, test_shapes, - trial_assembly_data, trial_elements, trial_shapes, + trial_assembly_data, trial_functions, trial_elements, trial_shapes, zlocal, quadrature_data, store; quadstrat) test_function = test_functions.fns[1] @@ -551,7 +559,10 @@ function assemblerow_body!(biop, fill!(zlocal, 0) qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) - momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule) + momintegrals!(zlocal, biop, + test_functions, nothing, tcell, + trial_functions, nothing, bcell, + qrule) for j in 1:size(zlocal,2) for (n,b) in trial_assembly_data[q,j] @@ -580,14 +591,14 @@ function assemblecol!(biop::IntegralOperator, test_functions::Space, trial_funct @assert numfunctions(trial_functions) == 1 assemblecol_body!(biop, - test_assembly_data, test_elements, test_shapes, + test_assembly_data, test_functions, test_elements, test_shapes, trial_functions, trial_elements, trial_shapes, zlocal, quadrature_data, store; quadstrat) end function assemblecol_body!(biop, - test_assembly_data, test_elements, test_shapes, + test_assembly_data, test_functions, test_elements, test_shapes, trial_functions, trial_elements, trial_shapes, zlocal, quadrature_data, store; quadstrat) @@ -602,7 +613,9 @@ function assemblecol_body!(biop, fill!(zlocal, 0) qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) - momintegrals!(biop, test_shapes, trial_shapes, tcell, bcell, zlocal, qrule) + momintegrals!(zlocal, biop, + test_functions, nothing, tcell, + trial_functions, nothing, bcell, qrule) for i in 1:size(zlocal,1) for (m,a) in test_assembly_data[p,i] diff --git a/src/maxwell/bogaertints.jl b/src/maxwell/bogaertints.jl index d5f08e49..9307bb25 100644 --- a/src/maxwell/bogaertints.jl +++ b/src/maxwell/bogaertints.jl @@ -1,4 +1,7 @@ -function momintegrals!(op::MWSingleLayer3D, g::RTRefSpace, f::RTRefSpace, t, s, z, strat::BogaertStrategy) +function momintegrals!(z, op::MWSingleLayer3D, + g::RTRefSpace, tptr, t, + f::RTRefSpace, bptr, s, + strat::BogaertStrategy) T, GG = GetIntegrals(t, s, op.gamma, strat) @@ -52,8 +55,10 @@ end -function momintegrals!(op::MWDoubleLayer3D, g::RTRefSpace, f::RTRefSpace, - τ, σ, z, strat::BogaertStrategy) +function momintegrals!(z, op::MWDoubleLayer3D, + g::RTRefSpace, tptr, τ, + f::RTRefSpace, bptr, σ, + strat::BogaertStrategy) # Get the primitives r = τ.vertices diff --git a/src/operator.jl b/src/operator.jl index e1958954..b54b2a74 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -6,13 +6,11 @@ struct Threading{T} end import Base: transpose, +, -, * abstract type AbstractOperator end - -#@linearspace AbstractOperator{T} T - """ *Atomic operator*: one that assemblechunk can deal with """ abstract type Operator <: AbstractOperator end +abstract type IntegralOperator <: Operator end mutable struct TransposedOperator <: AbstractOperator op::AbstractOperator diff --git a/src/quadrature/doublenumints.jl b/src/quadrature/doublenumints.jl index 1efdf398..0efc2ccb 100644 --- a/src/quadrature/doublenumints.jl +++ b/src/quadrature/doublenumints.jl @@ -9,7 +9,8 @@ momintegrals!(biop, tshs, bshs, tcell, bcell, interactions, strat) Function for the computation of moment integrals using simple double quadrature. """ -function momintegrals!(biop, tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) +function momintegrals!(biop, + tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) igd = Integrand(biop, tshs, bshs, tcell, bcell) diff --git a/src/quadrature/doublenumqstrat.jl b/src/quadrature/doublenumqstrat.jl index 2c940e31..90a6d766 100644 --- a/src/quadrature/doublenumqstrat.jl +++ b/src/quadrature/doublenumqstrat.jl @@ -1,7 +1,10 @@ function quaddata(operator::IntegralOperator, - local_test_basis::RefSpace, local_trial_basis::RefSpace, + local_test_basis, local_trial_basis, test_elements, trial_elements, qs::DoubleNumQStrat) + # local_test_basis = refspace(test_basis) + # local_trial_basis = refspace(trial_basis) + test_quad_data = quadpoints(local_test_basis, test_elements, (qs.outer_rule,)) trial_quad_data = quadpoints(local_trial_basis, trial_elements, (qs.inner_rule,)) @@ -10,7 +13,7 @@ end function quadrule(operator::IntegralOperator, - local_test_basis::RefSpace, local_trial_basis::RefSpace, + local_test_basis, local_trial_basis, test_id, test_element, trial_id, trial_element, quad_data, qs::DoubleNumQStrat) diff --git a/src/quadrature/doublenumwiltonsauterqstrat.jl b/src/quadrature/doublenumwiltonsauterqstrat.jl index 2cdde450..70918398 100644 --- a/src/quadrature/doublenumwiltonsauterqstrat.jl +++ b/src/quadrature/doublenumwiltonsauterqstrat.jl @@ -1,8 +1,10 @@ function quaddata(op::IntegralOperator, - test_local_space::RefSpace, trial_local_space::RefSpace, + test_local_space, trial_local_space, test_charts, trial_charts, qs::DoubleNumWiltonSauterQStrat) T = coordtype(test_charts[1]) + # test_local_space = refspace(test_space) + # trial_local_space = refspace(trial_space) tqd = quadpoints(test_local_space, test_charts, (qs.outer_rule_far,qs.outer_rule_near)) bqd = quadpoints(trial_local_space, trial_charts, (qs.inner_rule_far,qs.inner_rule_near)) @@ -16,7 +18,7 @@ function quaddata(op::IntegralOperator, end -function quadrule(op::IntegralOperator, g::RefSpace, f::RefSpace, i, τ, j, σ, qd, +function quadrule(op::IntegralOperator, g, f, i, τ, j, σ, qd, qs::DoubleNumWiltonSauterQStrat) T = eltype(eltype(τ.vertices)) diff --git a/src/quadrature/nonconformingoverlapqrule.jl b/src/quadrature/nonconformingoverlapqrule.jl index 3e2e3a26..9276b7ce 100644 --- a/src/quadrature/nonconformingoverlapqrule.jl +++ b/src/quadrature/nonconformingoverlapqrule.jl @@ -3,7 +3,8 @@ struct NonConformingOverlapQRule{S} end -function momintegrals!(op, test_local_space, basis_local_space, +function momintegrals!(op, + test_local_space, basis_local_space, test_chart::CompScienceMeshes.Simplex, basis_chart::CompScienceMeshes.Simplex, out, qrule::NonConformingOverlapQRule) @@ -22,12 +23,17 @@ function momintegrals!(op, test_local_space, basis_local_space, test_charts = [ch for ch in test_charts if volume(ch) .> 1e6 * eps(T)] bsis_charts = [ch for ch in bsis_charts if volume(ch) .> 1e6 * eps(T)] + isempty(test_charts) && return + isempty(bsis_charts) && return + # @assert volume(test_chart) ≈ sum(volume.(test_charts)) # if volume(basis_chart) ≈ sum(volume.(bsis_charts)) else # @show volume(basis_chart) # @show sum(volume.(bsis_charts)) # error() # end + # test_local_space = refspace(test_functions) + # basis_local_space = refspace(basis_functions) qstrat = CommonFaceOverlappingEdgeQStrat(qrule.conforming_qstrat) qdata = quaddata(op, test_local_space, basis_local_space, @@ -43,8 +49,9 @@ function momintegrals!(op, test_local_space, basis_local_space, Q = restrict(basis_local_space, basis_chart, bchart) zlocal = zero(out) - momintegrals!(op, test_local_space, basis_local_space, - tchart, bchart, zlocal, qrule) + momintegrals!(zlocal, op, + test_local_space, nothing, tchart, + basis_local_space, nothing, bchart, qrule) for i in axes(P,1) for j in axes(Q,1) diff --git a/src/quadrature/nonconformingtouchqrule.jl b/src/quadrature/nonconformingtouchqrule.jl index d0c13696..3c31236d 100644 --- a/src/quadrature/nonconformingtouchqrule.jl +++ b/src/quadrature/nonconformingtouchqrule.jl @@ -4,9 +4,13 @@ struct NonConformingTouchQRule{S} bsis_overlapping_edge_index::Int end -function momintegrals!(op, test_locspace, bsis_locspace, - τ::CompScienceMeshes.Simplex, σ::CompScienceMeshes.Simplex, - out, qrule::NonConformingTouchQRule) +function momintegrals!(op, + test_locspace, bsis_locspace, + τ::CompScienceMeshes.Simplex, σ::CompScienceMeshes.Simplex, + out, qrule::NonConformingTouchQRule) + + # test_locspace = refspace(test_functions) + # bsis_locspace = refspace(bsis_functions) T = coordtype(τ) P = eltype(τ.vertices) @@ -58,8 +62,9 @@ function momintegrals!(op, test_locspace, bsis_locspace, Q = restrict(bsis_locspace, σ, bchart) zlocal = zero(out) - momintegrals!(op, test_locspace, bsis_locspace, - tchart, bchart, zlocal, qrule) + momintegrals!(zlocal, op, + test_locspace, nothing, tchart, + bsis_locspace, nothing, bchart, qrule) # out .+= P * zlocal * Q' for i in axes(P,1) diff --git a/src/quadrature/rules/momintegrals.jl b/src/quadrature/rules/momintegrals.jl new file mode 100644 index 00000000..6b749033 --- /dev/null +++ b/src/quadrature/rules/momintegrals.jl @@ -0,0 +1,13 @@ +function momintegrals!(out, op, + test_functions::Space, test_cellptr, test_chart, + trial_functions::Space, trial_cellptr, trial_chart, + quadrule) + + local_test_space = refspace(test_functions) + local_trial_space = refspace(trial_functions) + + momintegrals!(op, + local_test_space, local_trial_space, + test_chart, trial_chart, + out, quadrule) +end \ No newline at end of file diff --git a/src/quadrature/rules/testrefinestrialqrule.jl b/src/quadrature/rules/testrefinestrialqrule.jl new file mode 100644 index 00000000..08bcec5a --- /dev/null +++ b/src/quadrature/rules/testrefinestrialqrule.jl @@ -0,0 +1,38 @@ +struct TestRefinesTrialQRule{S} + conforming_qstrat::S +end + +function momintegrals!(out, op, + test_functions::Space, test_cell, test_chart, + trial_functions::Space, trial_cell, trial_chart, + qr::TestRefinesTrialQRule) + + test_local_space = refspace(test_functions) + trial_local_space = refspace(trial_functions) + + test_mesh = geometry(test_functions) + trial_mesh = geometry(trial_functions) + + parent_mesh = CompScienceMeshes.parent(test_mesh) + trial_charts = [chart(test_mesh, p) for p in CompScienceMeshes.children(parent_mesh, trial_cell)] + + quadstrat = qr.conforming_qstrat + qd = quaddata(op, test_local_space, trial_local_space, + [test_chart], trial_charts, quadstrat) + + for (q,chart) in enumerate(trial_charts) + qr = quadrule(op, test_local_space, trial_local_space, + 1, test_chart, q ,chart, qd, quadstrat) + + Q = restrict(trial_local_space, trial_chart, chart) + zlocal = zero(out) + + momintegrals!(zlocal, op, + test_functions, nothing, test_chart, + trial_functions, nothing, chart, qr) + + for j in 1:numfunctions(trial_local_space) + for i in 1:numfunctions(test_local_space) + for k in 1:size(Q, 2) + out[i,j] += zlocal[i,k] * Q[j,k] +end end end end end \ No newline at end of file diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index c2c97fd2..ab3da50b 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -111,8 +111,12 @@ end function momintegrals!(op::Operator, - test_local_space::RefSpace, trial_local_space::RefSpace, - test_chart, trial_chart, out, rule::SauterSchwabStrategy) + test_local_space, trial_local_space, + test_chart, trial_chart, + out, rule::SauterSchwabStrategy) + + # test_local_space = refspace(test_space) + # trial_local_space = refspace(trial_space) I, J, _, _ = SauterSchwabQuadrature.reorder( vertices(test_chart), @@ -153,11 +157,12 @@ function momintegrals_test_refines_trial!(out, op, trial_functions, trial_cell, trial_chart, quadrule, quadstrat) - test_local_space = refspace(test_functions) - trial_local_space = refspace(trial_functions) + # test_local_space = refspace(test_functions) + # trial_local_space = refspace(trial_functions) - momintegrals!(op, test_local_space, trial_local_space, - test_chart, trial_chart, out, quadrule) + momintegrals!(out, op, + test_functions, test_cell, test_chart, + trial_functions, trial_cell, trial_chart, quadrule) end # const MWOperator3D = Union{MWSingleLayer3D, MWDoubleLayer3D} @@ -170,7 +175,7 @@ function momintegrals_test_refines_trial!(out, op, trial_local_space = refspace(trial_functions) test_mesh = geometry(test_functions) - trial_mesh = geometry(trial_functions) + # trial_mesh = geometry(trial_functions) parent_mesh = CompScienceMeshes.parent(test_mesh) trial_charts = [chart(test_mesh, p) for p in CompScienceMeshes.children(parent_mesh, trial_cell)] @@ -186,8 +191,9 @@ function momintegrals_test_refines_trial!(out, op, Q = restrict(trial_local_space, trial_chart, chart) zlocal = zero(out) - momintegrals!(op, test_local_space, trial_local_space, - test_chart, chart, zlocal, qr) + momintegrals!(zlocal, op, + test_functions, test_cell, test_chart, + trial_functions, nothing, chart, qr) for j in 1:numfunctions(trial_local_space) for i in 1:numfunctions(test_local_space) @@ -205,8 +211,9 @@ function momintegrals_trial_refines_test!(out, op, test_local_space = refspace(test_functions) trial_local_space = refspace(trial_functions) - momintegrals!(op, test_local_space, trial_local_space, - test_chart, trial_chart, out, quadrule) + momintegrals!(out, op, + test_functions, test_cell, test_chart, + trial_functions, trial_cell, trial_chart, quadrule) end @@ -233,8 +240,9 @@ function momintegrals_trial_refines_test!(out, op, Q = restrict(test_local_space, test_chart, chart) zlocal = zero(out) - momintegrals!(op, test_local_space, trial_local_space, - chart, trial_chart, zlocal, qr) + momintegrals!(zlocal, op, + test_functions, nothing, chart, + trial_functions, trial_cell, trial_chart, qr) for j in 1:numfunctions(trial_local_space) for i in 1:numfunctions(test_local_space) diff --git a/src/quadrature/singularityextractionints.jl b/src/quadrature/singularityextractionints.jl index 10c4685f..6cb3377b 100644 --- a/src/quadrature/singularityextractionints.jl +++ b/src/quadrature/singularityextractionints.jl @@ -1,7 +1,9 @@ abstract type SingularityExtractionRule end regularpart_quadrule(qr::SingularityExtractionRule) = qr.regularpart_quadrule -function momintegrals!(op, g, f, t, s, z, qrule::SingularityExtractionRule) +function momintegrals!(op, + g, f,t, s, + z, qrule::SingularityExtractionRule) womps = qrule.outer_quad_points diff --git a/src/quadrature/strategies/testrefinestrialqstrat.jl b/src/quadrature/strategies/testrefinestrialqstrat.jl new file mode 100644 index 00000000..3e4e34e9 --- /dev/null +++ b/src/quadrature/strategies/testrefinestrialqstrat.jl @@ -0,0 +1,28 @@ +struct TestRefinesTrialQStrat{S} + conforming_qstrat::S +end + +function quaddata(a, X, Y, tels, bels, qs::TestRefinesTrialQStrat) + return quaddata(a, X, Y, tels, bels, qs.conforming_qstrat) +end + +function quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, + qs::TestRefinesTrialQStrat) + + hits = _numhits(τ, σ) + if hits > 0 + return TestRefinesTrialQRule(qs.conforming_qstrat) + end + + # if CompScienceMeshes.overlap(τ, σ) + # return NonConformingOverlapQRule(qs.conforming_qstrat) + # end + + # for (i,λ) in pairs(faces(τ)) + # for (j,μ) in pairs(faces(σ)) + # if CompScienceMeshes.overlap(λ, μ) + # return NonConformingOverlapQRule(qs.conforming_qstrat) + # end end end + + return quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, qs.conforming_qstrat) +end \ No newline at end of file diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index 31164385..68385e7a 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -101,9 +101,10 @@ function reorder_dof(space::LagrangeRefSpace{T,1,4,4},I) where T return SVector(K),SVector{4,Int64}(1,1,1,1) end -function momintegrals!(op::VIEOperator, - test_local_space::RefSpace, trial_local_space::RefSpace, - test_tetrahedron_element, trial_tetrahedron_element, out, strat::SauterSchwab3DStrategy) +function momintegrals!(out, op::VIEOperator, + test_local_space::RefSpace, test_ptr, test_tetrahedron_element, + trial_local_space::RefSpace, trial_ptr, trial_tetrahedron_element, + strat::SauterSchwab3DStrategy) #Find permutation of vertices to match location of singularity to SauterSchwab J, I= SauterSchwab3D.reorder(strat.sing) @@ -159,7 +160,10 @@ function momintegrals!(op::VIEOperator, nothing end -function momintegrals!(biop::VIEOperator, tshs, bshs, tcell, bcell, z, strat::DoubleQuadRule) +function momintegrals!(z, biop::VIEOperator, + tshs, tptr, tcell, + bshs, bptr, bcell, + strat::DoubleQuadRule) # memory allocation here is a result from the type instability on strat # which is on purpose, i.e. the momintegrals! method is chosen based diff --git a/test/test_assemblerow.jl b/test/test_assemblerow.jl index 8e82630a..948e1b93 100644 --- a/test/test_assemblerow.jl +++ b/test/test_assemblerow.jl @@ -12,7 +12,7 @@ for T in [Float32, Float64] numfunctions(X) ## - X1 = subset(X,1:1) + local X1 = subset(X,1:1) numfunctions(X1) T1 = assemble(t,X1,X) diff --git a/test/test_basis.jl b/test/test_basis.jl index 38e153b7..c6f36017 100644 --- a/test/test_basis.jl +++ b/test/test_basis.jl @@ -17,6 +17,8 @@ for T in [Float32, Float64] identityop = Identity() doublelayer = DoubleLayer(κ) + @show BEAST.defaultquadstrat(hypersingular, X, X) + # @show @which BEAST.defaultquadstrat(hypersingular, X, X) @time N = assemble(hypersingular, X, X) @time I = assemble(identityop, X, X) diff --git a/test/test_wiltonints.jl b/test/test_wiltonints.jl index aba1fd27..ed0acfbe 100644 --- a/test/test_wiltonints.jl +++ b/test/test_wiltonints.jl @@ -343,7 +343,9 @@ tqd = BE.quadpoints(x, [t], (12,)) bqd = BE.quadpoints(x, [s], (13,)) DQ_strategy = BE.DoubleQuadRule(tqd[1,1], bqd[1,1]) -BEAST.momintegrals!(op, x, x, t, s, z1, DQ_strategy) +BEAST.momintegrals!(z1, op, + X, nothing, t, + X, nothing, s, DQ_strategy) SE_strategy = BE.WiltonSERule( tqd[1,1], @@ -352,6 +354,8 @@ SE_strategy = BE.WiltonSERule( bqd[1,1], ), ) -BEAST.momintegrals!(op, x, x, t, s, z2, SE_strategy) +BEAST.momintegrals!(z2, op, + X, nothing, t, + X, nothing, s, SE_strategy) @test norm(z1-z2) < 1.0e-7 From 4577125267d0d4f71a6b62c338bcf5abacf824d6 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 28 Jun 2024 11:58:27 +0200 Subject: [PATCH 381/528] dispatch on refined meshes all via quadstrat --- src/BEAST.jl | 2 + src/integralop.jl | 439 ++++++++++-------- src/quadrature/rules/trialrefinestestqrule.jl | 37 ++ .../strategies/testrefinestrialqstrat.jl | 10 - .../strategies/trialrefinestestqstrat.jl | 18 + 5 files changed, 292 insertions(+), 214 deletions(-) create mode 100644 src/quadrature/rules/trialrefinestestqrule.jl create mode 100644 src/quadrature/strategies/trialrefinestestqstrat.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index ce280bd4..14daed8a 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -194,6 +194,7 @@ include("quadrature/nonconformingintegralopqstrat.jl") include("quadrature/commonfaceoverlappingedgeqstrat.jl") include("quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl") include("quadrature/strategies/testrefinestrialqstrat.jl") +include("quadrature/strategies/trialrefinestestqstrat.jl") include("excitation.jl") include("localop.jl") @@ -210,6 +211,7 @@ include("quadrature/sauterschwabints.jl") include("quadrature/nonconformingoverlapqrule.jl") include("quadrature/nonconformingtouchqrule.jl") include("quadrature/rules/testrefinestrialqrule.jl") +include("quadrature/rules/trialrefinestestqrule.jl") include("postproc.jl") include("postproc/segcurrents.jl") diff --git a/src/integralop.jl b/src/integralop.jl index 0610b475..2989812a 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -81,30 +81,47 @@ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; tgeo = geometry(tfs) bgeo = geometry(bfs) - qd = quaddata(biop, tshapes, bshapes, test_elements, bsis_elements, quadstrat) - zlocal = zeros(scalartype(biop, tfs, bfs), 2num_tshapes, 2num_bshapes) - - if CompScienceMeshes.refines(tgeo, bgeo) - # assemblechunk_body_test_refines_trial!(biop, - # tfs, test_elements, tad, tcells, - # bfs, bsis_elements, bad, bcells, - # qd, zlocal, store; quadstrat) - qs = TestRefinesTrialQStrat(quadstrat) - assemblechunk_body!(biop, - tfs, test_elements, tad, tcells, - bfs, bsis_elements, bad, bcells, - qd, zlocal, store; quadstrat=qs) + qs = if CompScienceMeshes.refines(tgeo, bgeo) + TestRefinesTrialQStrat(quadstrat) elseif CompScienceMeshes.refines(bgeo, tgeo) - assemblechunk_body_trial_refines_test!(biop, - tfs, test_elements, tad, tcells, - bfs, bsis_elements, bad, bcells, - qd, zlocal, store; quadstrat) + TrialRefinesTestQStrat(quadstrat) else - assemblechunk_body!(biop, - tfs, test_elements, tad, tcells, - bfs, bsis_elements, bad, bcells, - qd, zlocal, store; quadstrat) + quadstrat end + + qd = quaddata(biop, tshapes, bshapes, test_elements, bsis_elements, qs) + zlocal = zeros(scalartype(biop, tfs, bfs), 2num_tshapes, 2num_bshapes) + assemblechunk_body!(biop, + tfs, test_elements, tad, tcells, + bfs, bsis_elements, bad, bcells, + qd, zlocal, store; quadstrat=qs) + + # if CompScienceMeshes.refines(tgeo, bgeo) + # assemblechunk_body_test_refines_trial!(biop, + # tfs, test_elements, tad, tcells, + # bfs, bsis_elements, bad, bcells, + # qd, zlocal, store; quadstrat) + # qs = TestRefinesTrialQStrat(quadstrat) + # # assemblechunk_body!(biop, + # # tfs, test_elements, tad, tcells, + # # bfs, bsis_elements, bad, bcells, + # # qd, zlocal, store; quadstrat=qs) + # elseif CompScienceMeshes.refines(bgeo, tgeo) + # qs = TrialRefinesTestQStrat(quadstrat) + # assemblechunk_body!(biop, + # tfs, test_elements, tad, tcells, + # bfs, bsis_elements, bad, bcells, + # qd, zlocal, store; quadstrat=qs) + # # assemblechunk_body_trial_refines_test!(biop, + # # tfs, test_elements, tad, tcells, + # # bfs, bsis_elements, bad, bcells, + # # qd, zlocal, store; quadstrat) + # else + # assemblechunk_body!(biop, + # tfs, test_elements, tad, tcells, + # bfs, bsis_elements, bad, bcells, + # qd, zlocal, store; quadstrat) + # end end @@ -147,122 +164,136 @@ function assemblechunk_body!(biop, end -function assemblechunk_body_test_refines_trial!(biop, - test_functions, test_charts, test_assembly_data, test_cells, - trial_functions, trial_charts, trial_assembly_data, trial_cells, - qd, zlocal, store; quadstrat) - - test_shapes = refspace(test_functions) - trial_shapes = refspace(trial_functions) - - myid = Threads.threadid() - myid == 1 && print("dots out of 10: ") - todo, done, pctg = length(test_charts), 0, 0 - for (p,(tcell,tchart)) in enumerate(zip(test_cells, test_charts)) - for (q,(bcell,bchart)) in enumerate(zip(trial_cells, trial_charts)) +# function assemblechunk_body_test_refines_trial!(biop, +# test_functions, test_charts, test_assembly_data, test_cells, +# trial_functions, trial_charts, trial_assembly_data, trial_cells, +# qd, zlocal, store; quadstrat) - fill!(zlocal, 0) - qrule = quadrule(biop, test_shapes, trial_shapes, p, tchart, q, bchart, qd, quadstrat) - # @show ("1", qrule) - momintegrals_test_refines_trial!(zlocal, biop, - test_functions, tcell, tchart, - trial_functions, bcell, bchart, - qrule, quadstrat) - - I = length(test_assembly_data[p]) - J = length(trial_assembly_data[q]) - for j in 1 : J, i in 1 : I - zij = zlocal[i,j] - for (n,b) in trial_assembly_data[q][j] - zb = zij*b - for (m,a) in test_assembly_data[p][i] - store(a*zb, m, n) - end end end end +# test_shapes = refspace(test_functions) +# trial_shapes = refspace(trial_functions) - done += 1 - new_pctg = round(Int, done / todo * 100) - if new_pctg > pctg + 9 - myid == 1 && print(".") - pctg = new_pctg - end end - myid == 1 && println("") -end - - -function assemblechunk_body_trial_refines_test!(biop, - test_functions, test_charts, test_assembly_data, test_cells, - trial_functions, trial_charts, trial_assembly_data, trial_cells, - qd, zlocal, store; quadstrat) - - test_shapes = refspace(test_functions) - trial_shapes = refspace(trial_functions) +# myid = Threads.threadid() +# myid == 1 && print("dots out of 10: ") +# todo, done, pctg = length(test_charts), 0, 0 +# for (p,(tcell,tchart)) in enumerate(zip(test_cells, test_charts)) +# for (q,(bcell,bchart)) in enumerate(zip(trial_cells, trial_charts)) - myid = Threads.threadid() - myid == 1 && print("dots out of 10: ") - todo, done, pctg = length(test_charts), 0, 0 - for (p,(tcell,tchart)) in enumerate(zip(test_cells, test_charts)) - for (q,(bcell,bchart)) in enumerate(zip(trial_cells, trial_charts)) +# fill!(zlocal, 0) +# qrule = quadrule(biop, test_shapes, trial_shapes, p, tchart, q, bchart, qd, quadstrat) +# # @show ("1", qrule) +# momintegrals_test_refines_trial!(zlocal, biop, +# test_functions, tcell, tchart, +# trial_functions, bcell, bchart, +# qrule, quadstrat) - fill!(zlocal, 0) - qrule = quadrule(biop, test_shapes, trial_shapes, p, tchart, q, bchart, qd, quadstrat) - momintegrals_trial_refines_test!(zlocal, biop, - test_functions, tcell, tchart, - trial_functions, bcell, bchart, - qrule, quadstrat) +# I = length(test_assembly_data[p]) +# J = length(trial_assembly_data[q]) +# for j in 1 : J, i in 1 : I +# zij = zlocal[i,j] +# for (n,b) in trial_assembly_data[q][j] +# zb = zij*b +# for (m,a) in test_assembly_data[p][i] +# store(a*zb, m, n) +# end end end end + +# done += 1 +# new_pctg = round(Int, done / todo * 100) +# if new_pctg > pctg + 9 +# myid == 1 && print(".") +# pctg = new_pctg +# end end +# myid == 1 && println("") +# end + + +# function assemblechunk_body_trial_refines_test!(biop, +# test_functions, test_charts, test_assembly_data, test_cells, +# trial_functions, trial_charts, trial_assembly_data, trial_cells, +# qd, zlocal, store; quadstrat) + +# test_shapes = refspace(test_functions) +# trial_shapes = refspace(trial_functions) + +# myid = Threads.threadid() +# myid == 1 && print("dots out of 10: ") +# todo, done, pctg = length(test_charts), 0, 0 +# for (p,(tcell,tchart)) in enumerate(zip(test_cells, test_charts)) +# for (q,(bcell,bchart)) in enumerate(zip(trial_cells, trial_charts)) + +# fill!(zlocal, 0) +# qrule = quadrule(biop, test_shapes, trial_shapes, p, tchart, q, bchart, qd, quadstrat) +# momintegrals_trial_refines_test!(zlocal, biop, +# test_functions, tcell, tchart, +# trial_functions, bcell, bchart, +# qrule, quadstrat) - I = length(test_assembly_data[p]) - J = length(trial_assembly_data[q]) - for j in 1 : J, i in 1 : I - zij = zlocal[i,j] - for (n,b) in trial_assembly_data[q][j] - zb = zij*b - for (m,a) in test_assembly_data[p][i] - store(a*zb, m, n) - end end end end - - done += 1 - new_pctg = round(Int, done / todo * 100) - if new_pctg > pctg + 9 - myid == 1 && print(".") - pctg = new_pctg - end end - myid == 1 && println("") -end +# I = length(test_assembly_data[p]) +# J = length(trial_assembly_data[q]) +# for j in 1 : J, i in 1 : I +# zij = zlocal[i,j] +# for (n,b) in trial_assembly_data[q][j] +# zb = zij*b +# for (m,a) in test_assembly_data[p][i] +# store(a*zb, m, n) +# end end end end + +# done += 1 +# new_pctg = round(Int, done / todo * 100) +# if new_pctg > pctg + 9 +# myid == 1 && print(".") +# pctg = new_pctg +# end end +# myid == 1 && println("") +# end function blockassembler(biop::IntegralOperator, tfs::Space, bfs::Space; quadstrat=defaultquadstrat(biop, tfs, bfs)) - test_elements, test_assembly_data, - trial_elements, trial_assembly_data, - quadrature_data, zlocals = assembleblock_primer(biop, tfs, bfs; quadstrat) - tgeo = geometry(tfs) bgeo = geometry(bfs) - if CompScienceMeshes.refines(tgeo, bgeo) - return (test_ids, trial_ids, store) -> begin - assembleblock_body_test_refines_trial!(biop, - tfs, test_ids, test_elements, test_assembly_data, - bfs, trial_ids, trial_elements, trial_assembly_data, - quadrature_data, zlocals, store; quadstrat) - end + qs = if CompScienceMeshes.refines(tgeo, bgeo) + TestRefinesTrialQStrat(quadstrat) elseif CompScienceMeshes.refines(bgeo, tgeo) - return (test_ids, trial_ids, store) -> begin - assembleblock_body_trial_refines_test!(biop, - tfs, test_ids, test_elements, test_assembly_data, - bfs, trial_ids, trial_elements, trial_assembly_data, - quadrature_data, zlocals, store; quadstrat) - end + TrialRefinesTestQStrat(quadstrat) else - return (test_ids, trial_ids, store) -> begin - assembleblock_body!(biop, - tfs, test_ids, test_elements, test_assembly_data, - bfs, trial_ids, trial_elements, trial_assembly_data, - quadrature_data, zlocals, store; quadstrat) - end + quadstrat end + + test_elements, test_assembly_data, + trial_elements, trial_assembly_data, + quadrature_data, zlocals = assembleblock_primer(biop, tfs, bfs; quadstrat=qs) + + return (test_ids, trial_ids, store) -> + assembleblock_body!(biop, + tfs, test_ids, test_elements, test_assembly_data, + bfs, trial_ids, trial_elements, trial_assembly_data, + quadrature_data, zlocals, store; quadstrat=qs) + + # if CompScienceMeshes.refines(tgeo, bgeo) + # return (test_ids, trial_ids, store) -> begin + # assembleblock_body_test_refines_trial!(biop, + # tfs, test_ids, test_elements, test_assembly_data, + # bfs, trial_ids, trial_elements, trial_assembly_data, + # quadrature_data, zlocals, store; quadstrat) + # end + # elseif CompScienceMeshes.refines(bgeo, tgeo) + # return (test_ids, trial_ids, store) -> begin + # assembleblock_body_trial_refines_test!(biop, + # tfs, test_ids, test_elements, test_assembly_data, + # bfs, trial_ids, trial_elements, trial_assembly_data, + # quadrature_data, zlocals, store; quadstrat) + # end + # else + # return (test_ids, trial_ids, store) -> begin + # assembleblock_body!(biop, + # tfs, test_ids, test_elements, test_assembly_data, + # bfs, trial_ids, trial_elements, trial_assembly_data, + # quadrature_data, zlocals, store; quadstrat) + # end + # end end @@ -349,8 +380,8 @@ function assembleblock_body!(biop::IntegralOperator, fill!(zlocals[Threads.threadid()], 0) qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) momintegrals!(zlocals[Threads.threadid()], biop, - tfs, nothing, tcell, - bfs, nothing, bcell, qrule) + tfs, p, tcell, + bfs, q, bcell, qrule) for j in 1 : size(zlocals[Threads.threadid()],2) for i in 1 : size(zlocals[Threads.threadid()],1) @@ -363,109 +394,109 @@ function assembleblock_body!(biop::IntegralOperator, store(a*zlocals[Threads.threadid()][i,j]*b, m′, n′) end end end end end end end -function assembleblock_body_trial_refines_test!(biop::IntegralOperator, - tfs, test_ids, test_elements, test_assembly_data, - bfs, trial_ids, bsis_elements, trial_assembly_data, - quadrature_data, zlocals, store; quadstrat) +# function assembleblock_body_trial_refines_test!(biop::IntegralOperator, +# tfs, test_ids, test_elements, test_assembly_data, +# bfs, trial_ids, bsis_elements, trial_assembly_data, +# quadrature_data, zlocals, store; quadstrat) - test_shapes = refspace(tfs) - trial_shapes = refspace(bfs) +# test_shapes = refspace(tfs) +# trial_shapes = refspace(bfs) - # Enumerate all the active test elements - active_test_el_ids = Vector{Int}() - active_trial_el_ids = Vector{Int}() +# # Enumerate all the active test elements +# active_test_el_ids = Vector{Int}() +# active_trial_el_ids = Vector{Int}() - test_id_in_blk = Dict{Int,Int}() - trial_id_in_blk = Dict{Int,Int}() +# test_id_in_blk = Dict{Int,Int}() +# trial_id_in_blk = Dict{Int,Int}() - for (i,m) in enumerate(test_ids); test_id_in_blk[m] = i; end - for (i,m) in enumerate(trial_ids); trial_id_in_blk[m] = i; end +# for (i,m) in enumerate(test_ids); test_id_in_blk[m] = i; end +# for (i,m) in enumerate(trial_ids); trial_id_in_blk[m] = i; end - for m in test_ids, sh in tfs.fns[m]; push!(active_test_el_ids, sh.cellid); end - for m in trial_ids, sh in bfs.fns[m]; push!(active_trial_el_ids, sh.cellid); end +# for m in test_ids, sh in tfs.fns[m]; push!(active_test_el_ids, sh.cellid); end +# for m in trial_ids, sh in bfs.fns[m]; push!(active_trial_el_ids, sh.cellid); end - active_test_el_ids = unique!(sort!(active_test_el_ids)) - active_trial_el_ids = unique!(sort!(active_trial_el_ids)) +# active_test_el_ids = unique!(sort!(active_test_el_ids)) +# active_trial_el_ids = unique!(sort!(active_trial_el_ids)) - @assert length(active_test_el_ids) <= length(test_elements) - @assert length(active_trial_el_ids) <= length(bsis_elements) +# @assert length(active_test_el_ids) <= length(test_elements) +# @assert length(active_trial_el_ids) <= length(bsis_elements) - @assert maximum(active_test_el_ids) <= length(test_elements) "$(maximum(active_test_el_ids)), $(length(test_elements))" - @assert maximum(active_trial_el_ids) <= length(bsis_elements) "$(maximum(active_trial_el_ids)), $(length(bsis_elements))" +# @assert maximum(active_test_el_ids) <= length(test_elements) "$(maximum(active_test_el_ids)), $(length(test_elements))" +# @assert maximum(active_trial_el_ids) <= length(bsis_elements) "$(maximum(active_trial_el_ids)), $(length(bsis_elements))" - for p in active_test_el_ids - tcell = test_elements[p] - for q in active_trial_el_ids - bcell = bsis_elements[q] +# for p in active_test_el_ids +# tcell = test_elements[p] +# for q in active_trial_el_ids +# bcell = bsis_elements[q] - fill!(zlocals[Threads.threadid()], 0) - qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) - momintegrals_trial_refines_test!(zlocals[Threads.threadid()], biop, - tfs, p, tcell, - bfs, q, bcell, - qrule, quadstrat) - for j in 1 : size(zlocals[Threads.threadid()],2) - for i in 1 : size(zlocals[Threads.threadid()],1) - for (n,b) in trial_assembly_data[q,j] - n′ = get(trial_id_in_blk, n, 0) - n′ == 0 && continue - for (m,a) in test_assembly_data[p,i] - m′ = get(test_id_in_blk, m, 0) - m′ == 0 && continue - store(a*zlocals[Threads.threadid()][i,j]*b, m′, n′) -end end end end end end end +# fill!(zlocals[Threads.threadid()], 0) +# qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) +# momintegrals_trial_refines_test!(zlocals[Threads.threadid()], biop, +# tfs, p, tcell, +# bfs, q, bcell, +# qrule, quadstrat) +# for j in 1 : size(zlocals[Threads.threadid()],2) +# for i in 1 : size(zlocals[Threads.threadid()],1) +# for (n,b) in trial_assembly_data[q,j] +# n′ = get(trial_id_in_blk, n, 0) +# n′ == 0 && continue +# for (m,a) in test_assembly_data[p,i] +# m′ = get(test_id_in_blk, m, 0) +# m′ == 0 && continue +# store(a*zlocals[Threads.threadid()][i,j]*b, m′, n′) +# end end end end end end end -function assembleblock_body_test_refines_trial!(biop::IntegralOperator, - tfs, test_ids, test_elements, test_assembly_data, - bfs, trial_ids, bsis_elements, trial_assembly_data, - quadrature_data, zlocals, store; quadstrat) +# function assembleblock_body_test_refines_trial!(biop::IntegralOperator, +# tfs, test_ids, test_elements, test_assembly_data, +# bfs, trial_ids, bsis_elements, trial_assembly_data, +# quadrature_data, zlocals, store; quadstrat) - test_shapes = refspace(tfs) - trial_shapes = refspace(bfs) +# test_shapes = refspace(tfs) +# trial_shapes = refspace(bfs) - # Enumerate all the active test elements - active_test_el_ids = Vector{Int}() - active_trial_el_ids = Vector{Int}() +# # Enumerate all the active test elements +# active_test_el_ids = Vector{Int}() +# active_trial_el_ids = Vector{Int}() - test_id_in_blk = Dict{Int,Int}() - trial_id_in_blk = Dict{Int,Int}() +# test_id_in_blk = Dict{Int,Int}() +# trial_id_in_blk = Dict{Int,Int}() - for (i,m) in enumerate(test_ids); test_id_in_blk[m] = i; end - for (i,m) in enumerate(trial_ids); trial_id_in_blk[m] = i; end +# for (i,m) in enumerate(test_ids); test_id_in_blk[m] = i; end +# for (i,m) in enumerate(trial_ids); trial_id_in_blk[m] = i; end - for m in test_ids, sh in tfs.fns[m]; push!(active_test_el_ids, sh.cellid); end - for m in trial_ids, sh in bfs.fns[m]; push!(active_trial_el_ids, sh.cellid); end +# for m in test_ids, sh in tfs.fns[m]; push!(active_test_el_ids, sh.cellid); end +# for m in trial_ids, sh in bfs.fns[m]; push!(active_trial_el_ids, sh.cellid); end - active_test_el_ids = unique!(sort!(active_test_el_ids)) - active_trial_el_ids = unique!(sort!(active_trial_el_ids)) +# active_test_el_ids = unique!(sort!(active_test_el_ids)) +# active_trial_el_ids = unique!(sort!(active_trial_el_ids)) - @assert length(active_test_el_ids) <= length(test_elements) - @assert length(active_trial_el_ids) <= length(bsis_elements) +# @assert length(active_test_el_ids) <= length(test_elements) +# @assert length(active_trial_el_ids) <= length(bsis_elements) - @assert maximum(active_test_el_ids) <= length(test_elements) "$(maximum(active_test_el_ids)), $(length(test_elements))" - @assert maximum(active_trial_el_ids) <= length(bsis_elements) "$(maximum(active_trial_el_ids)), $(length(bsis_elements))" +# @assert maximum(active_test_el_ids) <= length(test_elements) "$(maximum(active_test_el_ids)), $(length(test_elements))" +# @assert maximum(active_trial_el_ids) <= length(bsis_elements) "$(maximum(active_trial_el_ids)), $(length(bsis_elements))" - for p in active_test_el_ids - tcell = test_elements[p] - for q in active_trial_el_ids - bcell = bsis_elements[q] +# for p in active_test_el_ids +# tcell = test_elements[p] +# for q in active_trial_el_ids +# bcell = bsis_elements[q] - fill!(zlocals[Threads.threadid()], 0) - qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) - momintegrals_test_refines_trial!(zlocals[Threads.threadid()], biop, - tfs, p, tcell, - bfs, q, bcell, - qrule, quadstrat) - for j in 1 : size(zlocals[Threads.threadid()],2) - for i in 1 : size(zlocals[Threads.threadid()],1) - for (n,b) in trial_assembly_data[q,j] - n′ = get(trial_id_in_blk, n, 0) - n′ == 0 && continue - for (m,a) in test_assembly_data[p,i] - m′ = get(test_id_in_blk, m, 0) - m′ == 0 && continue - store(a*zlocals[Threads.threadid()][i,j]*b, m′, n′) -end end end end end end end +# fill!(zlocals[Threads.threadid()], 0) +# qrule = quadrule(biop, test_shapes, trial_shapes, p, tcell, q, bcell, quadrature_data, quadstrat) +# momintegrals_test_refines_trial!(zlocals[Threads.threadid()], biop, +# tfs, p, tcell, +# bfs, q, bcell, +# qrule, quadstrat) +# for j in 1 : size(zlocals[Threads.threadid()],2) +# for i in 1 : size(zlocals[Threads.threadid()],1) +# for (n,b) in trial_assembly_data[q,j] +# n′ = get(trial_id_in_blk, n, 0) +# n′ == 0 && continue +# for (m,a) in test_assembly_data[p,i] +# m′ = get(test_id_in_blk, m, 0) +# m′ == 0 && continue +# store(a*zlocals[Threads.threadid()][i,j]*b, m′, n′) +# end end end end end end end # function assembleblock_body_nested!(biop::IntegralOperator, diff --git a/src/quadrature/rules/trialrefinestestqrule.jl b/src/quadrature/rules/trialrefinestestqrule.jl new file mode 100644 index 00000000..3bbb4c7a --- /dev/null +++ b/src/quadrature/rules/trialrefinestestqrule.jl @@ -0,0 +1,37 @@ +struct TrialRefinesTestQRule{S} + conforming_qstrat::S +end + +function momintegrals!(out, op, + test_functions::Space, test_cell, test_chart, + trial_functions::Space, trial_cell, trial_chart, + qr::TrialRefinesTestQRule) + + test_local_space = refspace(test_functions) + trial_local_space = refspace(trial_functions) + + test_mesh = geometry(test_functions) + trial_mesh = geometry(trial_functions) + + parent_mesh = CompScienceMeshes.parent(trial_mesh) + test_charts = [chart(trial_mesh, p) for p in CompScienceMeshes.children(parent_mesh, test_cell)] + + quadstrat = qr.conforming_qstrat + qd = quaddata(op, test_local_space, trial_local_space, + test_charts, [trial_chart], quadstrat) + + for (p,chart) in enumerate(test_charts) + qr = quadrule(op, test_local_space, trial_local_space, + p, chart, 1, trial_chart, qd, quadstrat) + + Q = restrict(test_local_space, test_chart, chart) + zlocal = zero(out) + momintegrals!(zlocal, op, + test_functions, nothing, chart, + trial_functions, trial_cell, trial_chart, qr) + + for j in 1:numfunctions(trial_local_space) + for i in 1:numfunctions(test_local_space) + for k in 1:size(Q, 2) + out[i,j] += Q[i,k] * zlocal[k,j] +end end end end end \ No newline at end of file diff --git a/src/quadrature/strategies/testrefinestrialqstrat.jl b/src/quadrature/strategies/testrefinestrialqstrat.jl index 3e4e34e9..f36b48c6 100644 --- a/src/quadrature/strategies/testrefinestrialqstrat.jl +++ b/src/quadrature/strategies/testrefinestrialqstrat.jl @@ -14,15 +14,5 @@ function quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, return TestRefinesTrialQRule(qs.conforming_qstrat) end - # if CompScienceMeshes.overlap(τ, σ) - # return NonConformingOverlapQRule(qs.conforming_qstrat) - # end - - # for (i,λ) in pairs(faces(τ)) - # for (j,μ) in pairs(faces(σ)) - # if CompScienceMeshes.overlap(λ, μ) - # return NonConformingOverlapQRule(qs.conforming_qstrat) - # end end end - return quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, qs.conforming_qstrat) end \ No newline at end of file diff --git a/src/quadrature/strategies/trialrefinestestqstrat.jl b/src/quadrature/strategies/trialrefinestestqstrat.jl new file mode 100644 index 00000000..b7f69546 --- /dev/null +++ b/src/quadrature/strategies/trialrefinestestqstrat.jl @@ -0,0 +1,18 @@ +struct TrialRefinesTestQStrat{S} + conforming_qstrat::S +end + +function quaddata(a, X, Y, tels, bels, qs::TrialRefinesTestQStrat) + return quaddata(a, X, Y, tels, bels, qs.conforming_qstrat) +end + +function quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, + qs::TrialRefinesTestQStrat) + + hits = _numhits(τ, σ) + if hits > 0 + return TrialRefinesTestQRule(qs.conforming_qstrat) + end + + return quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, qs.conforming_qstrat) +end \ No newline at end of file From fada046f98007355a7f95aef74aee79d5a0b2138 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 28 Jun 2024 13:51:49 +0200 Subject: [PATCH 382/528] update BlockArrays compat entry --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 645dcf49..41c86d54 100644 --- a/Project.toml +++ b/Project.toml @@ -34,7 +34,7 @@ WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] AbstractTrees = "0.4.4" -BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16" +BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 1" CollisionDetection = "0.1.5" Combinatorics = "0.7, 1" CompScienceMeshes = "0.8" @@ -44,8 +44,8 @@ ExtendableSparse = "1.4" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4, 0.5, 1" FillArrays = "0.11, 0.12, 0.13, 1" -Infiltrator = "1.8.2" IterativeSolvers = "0.9" +Infiltrator = "1.8.2" LiftedMaps = "0.5.1" LinearMaps = "3.7 - 3.9, 3.11.2" NestedUnitRanges = "0.2" From 6c6d8feeff54625933bb986cc80f1be3823418be Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 28 Jun 2024 14:26:16 +0200 Subject: [PATCH 383/528] update with upstream --- test/Manifest.toml | 18 +++++++++++++++++- test/Project.toml | 1 + test/test_directproduct.jl | 3 ++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/test/Manifest.toml b/test/Manifest.toml index 476c9617..8479a59f 100644 --- a/test/Manifest.toml +++ b/test/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.10.2" manifest_format = "2.0" -project_hash = "571829245fb8bd97876fa91a4e7d398091f62379" +project_hash = "1b78102b70e82631772273acf6687b12f1d8fa74" [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" @@ -299,6 +299,22 @@ git-tree-sha1 = "71e8ee0f9fe0e86a8f8c7f28361e5118eab2f93f" uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" version = "5.0.0+0" +[[deps.LinearMaps]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "ee79c3208e55786de58f8dcccca098ced79f743f" +uuid = "7a12625a-238d-50fd-b39a-03d52299707e" +version = "3.11.3" + + [deps.LinearMaps.extensions] + LinearMapsChainRulesCoreExt = "ChainRulesCore" + LinearMapsSparseArraysExt = "SparseArrays" + LinearMapsStatisticsExt = "Statistics" + + [deps.LinearMaps.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + [[deps.LogExpFunctions]] deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] git-tree-sha1 = "c3ce8e7420b3a6e071e0fe4745f5d4300e37b13f" diff --git a/test/Project.toml b/test/Project.toml index 4676299c..c9ab94c0 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -5,6 +5,7 @@ CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" diff --git a/test/test_directproduct.jl b/test/test_directproduct.jl index b79f6501..fcede8c8 100644 --- a/test/test_directproduct.jl +++ b/test/test_directproduct.jl @@ -2,6 +2,7 @@ using CompScienceMeshes using BEAST using Test +import LinearMaps for U in [Float32, Float64] m1 = meshrectangle(U(1.0), U(1.0), U(0.5)) @@ -29,5 +30,5 @@ for U in [Float32, Float64] bilterms = [BEAST.Variational.BilTerm(1,1,Any[],Any[],1,T)] BilForm = BEAST.Variational.BilForm(:i, :j, bilterms) - @test typeof(assemble(BilForm, X, X)) == LinearMaps.LinearCombination{U, Vector{LinearMap}} + @test typeof(assemble(BilForm, X, X)) == LinearMaps.LinearCombination{U, Vector{LinearMaps.LinearMap}} end \ No newline at end of file From b3c31cf0fefaae1fd6c46de9f059073822c773b2 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 3 Jul 2024 12:09:09 +0200 Subject: [PATCH 384/528] type stable reparametrisation of Sauter-Schwab integrals --- Project.toml | 2 +- src/quadrature/sauterschwabints.jl | 224 +++++++++++++---------------- src/solvers/solver.jl | 6 +- test/test_directproduct.jl | 32 +++-- test/test_ss_nested_meshes.jl | 10 +- 5 files changed, 131 insertions(+), 143 deletions(-) diff --git a/Project.toml b/Project.toml index 41c86d54..1c73256d 100644 --- a/Project.toml +++ b/Project.toml @@ -37,7 +37,7 @@ AbstractTrees = "0.4.4" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 1" CollisionDetection = "0.1.5" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.8" +CompScienceMeshes = "0.8.1" Compat = "2, 3, 4" ConvolutionOperators = "0.4" ExtendableSparse = "1.4" diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index 54d19097..e978d84c 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -109,171 +109,153 @@ function (igd::Integrand)(x,y,f,g) end +# TODO: Remove this when the next version of CSM is relased! +function CompScienceMeshes.permute_vertices( + ch::CompScienceMeshes.RefQuadrilateral, I) + V = vertices(ch) + return Quadrilateral(V[I[1]], V[I[2]], V[I[3]], V[I[4]]) +end -function pullback_coordinates(I,type::CompScienceMeshes.Simplex) - function transform(u) - [u[1],u[2],1-sum(u)][invperm(I)][1:end-1] - end - return transform +struct PulledBackIntegrand{I,C} + igd::I + chart1::C + chart2::C end -function pullback_coordinates(I,type::CompScienceMeshes.Quadrilateral) - function transform(u) - n = [1-sum(u)+prod(u),u[1]-prod(u),prod(u),u[2]-prod(u)][invperm(I)] - p = n[3] - result = [n[2]+p,n[4]+p] - @assert 1-sum(result)+p ≈ n[1] - @assert p ≈ prod(result) - return result - end - return transform + +function (f::PulledBackIntegrand)(u,v) + # In general I think a Jacobian determinant needs to be included. For Simplical and + # Quadrilateral charts this is not needed because they are 1. + f.igd(cartesian(f.chart1,u), cartesian(f.chart2,v)) end +function pulledback_integrand(igd, + I, chart1, + J, chart2) + + dom1 = domain(chart1) + dom2 = domain(chart2) + + ichart1 = CompScienceMeshes.permute_vertices(dom1, I) + ichart2 = CompScienceMeshes.permute_vertices(dom2, J) + + PulledBackIntegrand(igd, ichart1, ichart2) +end + function momintegrals!(op::Operator, test_local_space, trial_local_space, test_chart, trial_chart, out, rule::SauterSchwabStrategy) - # test_local_space = refspace(test_space) - # trial_local_space = refspace(trial_space) - I, J, _, _ = SauterSchwabQuadrature.reorder( vertices(test_chart), vertices(trial_chart), rule) - # permute_vertices reparametrizes the simplex without affecting the normal - if 0 in I || 0 in J - @show typeof(rule) I J - @show test_chart.vertices - @show trial_chart.vertices - @infiltrate - # error("on purpose") - end - - # test_chart = CompScienceMeshes.permute_vertices(test_chart, I) - # trial_chart = CompScienceMeshes.permute_vertices(trial_chart, J) - - # if rule isa SauterSchwabQuadrature.CommonEdge - # @assert test_chart.vertices[1] ≈ trial_chart.vertices[1] - # @assert test_chart.vertices[3] ≈ trial_chart.vertices[3] - # end - - # igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) - # G = SauterSchwabQuadrature.sauterschwab_parameterized(igd, rule) - - # uv_test(u,v) = [u,v,1-u-v][invperm(I)][1:end-1] - # uv_trial(u,v) = [u,v,1-u-v][invperm(J)][1:end-1] - igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) - igdp(u,v) = igd(pullback_coordinates(I,test_chart)(u),pullback_coordinates(J,trial_chart)(v)) + igdp = pulledback_integrand(igd, I, test_chart, J, trial_chart) G = SauterSchwabQuadrature.sauterschwab_parameterized(igdp, rule) - - # QTest = dof_perm_matrix(test_local_space, I) - # QTrial = dof_perm_matrix(trial_local_space, J) - # out_temp = zeros(eltype(out), numfunctions(test_local_space),numfunctions(trial_local_space)) - # out_temp = QTest*G*QTrial' out[1:numfunctions(test_local_space),1:numfunctions(trial_local_space)] .+= G nothing end -function momintegrals_test_refines_trial!(out, op, - test_functions, test_cell, test_chart, - trial_functions, trial_cell, trial_chart, - quadrule, quadstrat) +# function momintegrals_test_refines_trial!(out, op, +# test_functions, test_cell, test_chart, +# trial_functions, trial_cell, trial_chart, +# quadrule, quadstrat) - # test_local_space = refspace(test_functions) - # trial_local_space = refspace(trial_functions) +# # test_local_space = refspace(test_functions) +# # trial_local_space = refspace(trial_functions) - momintegrals!(out, op, - test_functions, test_cell, test_chart, - trial_functions, trial_cell, trial_chart, quadrule) -end +# momintegrals!(out, op, +# test_functions, test_cell, test_chart, +# trial_functions, trial_cell, trial_chart, quadrule) +# end -# const MWOperator3D = Union{MWSingleLayer3D, MWDoubleLayer3D} -function momintegrals_test_refines_trial!(out, op, - test_functions, test_cell, test_chart, - trial_functions, trial_cell, trial_chart, - qr::SauterSchwabStrategy, quadstrat) +# # const MWOperator3D = Union{MWSingleLayer3D, MWDoubleLayer3D} +# function momintegrals_test_refines_trial!(out, op, +# test_functions, test_cell, test_chart, +# trial_functions, trial_cell, trial_chart, +# qr::SauterSchwabStrategy, quadstrat) - test_local_space = refspace(test_functions) - trial_local_space = refspace(trial_functions) +# test_local_space = refspace(test_functions) +# trial_local_space = refspace(trial_functions) - test_mesh = geometry(test_functions) - # trial_mesh = geometry(trial_functions) +# test_mesh = geometry(test_functions) +# # trial_mesh = geometry(trial_functions) - parent_mesh = CompScienceMeshes.parent(test_mesh) - trial_charts = [chart(test_mesh, p) for p in CompScienceMeshes.children(parent_mesh, trial_cell)] +# parent_mesh = CompScienceMeshes.parent(test_mesh) +# trial_charts = [chart(test_mesh, p) for p in CompScienceMeshes.children(parent_mesh, trial_cell)] - qd = quaddata(op, test_local_space, trial_local_space, - [test_chart], trial_charts, quadstrat) +# qd = quaddata(op, test_local_space, trial_local_space, +# [test_chart], trial_charts, quadstrat) - for (q,chart) in enumerate(trial_charts) - qr = quadrule(op, test_local_space, trial_local_space, - 1, test_chart, q ,chart, qd, quadstrat) - # @show qr +# for (q,chart) in enumerate(trial_charts) +# qr = quadrule(op, test_local_space, trial_local_space, +# 1, test_chart, q ,chart, qd, quadstrat) +# # @show qr - Q = restrict(trial_local_space, trial_chart, chart) - zlocal = zero(out) +# Q = restrict(trial_local_space, trial_chart, chart) +# zlocal = zero(out) - momintegrals!(zlocal, op, - test_functions, test_cell, test_chart, - trial_functions, nothing, chart, qr) +# momintegrals!(zlocal, op, +# test_functions, test_cell, test_chart, +# trial_functions, nothing, chart, qr) - for j in 1:numfunctions(trial_local_space) - for i in 1:numfunctions(test_local_space) - for k in 1:size(Q, 2) - out[i,j] += zlocal[i,k] * Q[j,k] -end end end end end +# for j in 1:numfunctions(trial_local_space) +# for i in 1:numfunctions(test_local_space) +# for k in 1:size(Q, 2) +# out[i,j] += zlocal[i,k] * Q[j,k] +# end end end end end -function momintegrals_trial_refines_test!(out, op, - test_functions, test_cell, test_chart, - trial_functions, trial_cell, trial_chart, - quadrule, quadstrat) +# function momintegrals_trial_refines_test!(out, op, +# test_functions, test_cell, test_chart, +# trial_functions, trial_cell, trial_chart, +# quadrule, quadstrat) - test_local_space = refspace(test_functions) - trial_local_space = refspace(trial_functions) +# test_local_space = refspace(test_functions) +# trial_local_space = refspace(trial_functions) - momintegrals!(out, op, - test_functions, test_cell, test_chart, - trial_functions, trial_cell, trial_chart, quadrule) -end +# momintegrals!(out, op, +# test_functions, test_cell, test_chart, +# trial_functions, trial_cell, trial_chart, quadrule) +# end -function momintegrals_trial_refines_test!(out, op, - test_functions, test_cell, test_chart, - trial_functions, trial_cell, trial_chart, - qr::SauterSchwabStrategy, quadstrat) +# function momintegrals_trial_refines_test!(out, op, +# test_functions, test_cell, test_chart, +# trial_functions, trial_cell, trial_chart, +# qr::SauterSchwabStrategy, quadstrat) - test_local_space = refspace(test_functions) - trial_local_space = refspace(trial_functions) +# test_local_space = refspace(test_functions) +# trial_local_space = refspace(trial_functions) - test_mesh = geometry(test_functions) - trial_mesh = geometry(trial_functions) +# test_mesh = geometry(test_functions) +# trial_mesh = geometry(trial_functions) - parent_mesh = CompScienceMeshes.parent(trial_mesh) - test_charts = [chart(trial_mesh, p) for p in CompScienceMeshes.children(parent_mesh, test_cell)] +# parent_mesh = CompScienceMeshes.parent(trial_mesh) +# test_charts = [chart(trial_mesh, p) for p in CompScienceMeshes.children(parent_mesh, test_cell)] - qd = quaddata(op, test_local_space, trial_local_space, - test_charts, [trial_chart], quadstrat) +# qd = quaddata(op, test_local_space, trial_local_space, +# test_charts, [trial_chart], quadstrat) - for (p,chart) in enumerate(test_charts) - qr = quadrule(op, test_local_space, trial_local_space, - p, chart, 1, trial_chart, qd, quadstrat) +# for (p,chart) in enumerate(test_charts) +# qr = quadrule(op, test_local_space, trial_local_space, +# p, chart, 1, trial_chart, qd, quadstrat) - Q = restrict(test_local_space, test_chart, chart) - zlocal = zero(out) - momintegrals!(zlocal, op, - test_functions, nothing, chart, - trial_functions, trial_cell, trial_chart, qr) +# Q = restrict(test_local_space, test_chart, chart) +# zlocal = zero(out) +# momintegrals!(zlocal, op, +# test_functions, nothing, chart, +# trial_functions, trial_cell, trial_chart, qr) - for j in 1:numfunctions(trial_local_space) - for i in 1:numfunctions(test_local_space) - for k in 1:size(Q, 2) - out[i,j] += Q[i,k] * zlocal[k,j] - end end end - end -end +# for j in 1:numfunctions(trial_local_space) +# for i in 1:numfunctions(test_local_space) +# for k in 1:size(Q, 2) +# out[i,j] += Q[i,k] * zlocal[k,j] +# end end end +# end +# end diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 8d7715ce..d1aa30e6 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -262,7 +262,11 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; if spaceTimeBasis return sum(lincombv) else - return LinearMaps.LinearCombination{T}(lincombv) + if length(lincombv) == 1 + return lincombv[1] + else + return LinearMaps.LinearCombination{T}(lincombv) + end end end diff --git a/test/test_directproduct.jl b/test/test_directproduct.jl index fcede8c8..49f63deb 100644 --- a/test/test_directproduct.jl +++ b/test/test_directproduct.jl @@ -5,30 +5,32 @@ using Test import LinearMaps for U in [Float32, Float64] - m1 = meshrectangle(U(1.0), U(1.0), U(0.5)) - m2 = CompScienceMeshes.translate!(meshrectangle(U(1.0), U(1.0), U(0.5)), point(U,0,0,1)) - m3 = CompScienceMeshes.translate!(meshrectangle(U(1.0), U(1.0), U(0.5)), point(U,0,0,2)) + local m1 = meshrectangle(U(1.0), U(1.0), U(0.5)) + local m2 = CompScienceMeshes.translate!(meshrectangle(U(1.0), U(1.0), U(0.5)), point(U,0,0,1)) + local m3 = CompScienceMeshes.translate!(meshrectangle(U(1.0), U(1.0), U(0.5)), point(U,0,0,2)) - X1 = raviartthomas(m1) - X2 = raviartthomas(m2) - X3 = raviartthomas(m3) + local X1 = raviartthomas(m1) + local X2 = raviartthomas(m2) + local X3 = raviartthomas(m3) local X = X1 × X2 × X3 - n1 = numfunctions(X1) - n2 = numfunctions(X2) - n3 = numfunctions(X3) + local n1 = numfunctions(X1) + local n2 = numfunctions(X2) + local n3 = numfunctions(X3) @test numfunctions(X) == n1 + n2 + n3 - nt = numfunctions(X) + local nt = numfunctions(X) - T = MWSingleLayer3D(U(1.0)) - t = assemble(T, X, X) + local T = MWSingleLayer3D(U(1.0)) + local t = assemble(T, X, X) @test size(t) == (nt,nt) - bilterms = [BEAST.Variational.BilTerm(1,1,Any[],Any[],1,T)] + local bilterms = [BEAST.Variational.BilTerm(1,1,Any[],Any[],1,T)] - BilForm = BEAST.Variational.BilForm(:i, :j, bilterms) - @test typeof(assemble(BilForm, X, X)) == LinearMaps.LinearCombination{U, Vector{LinearMaps.LinearMap}} + local BilForm = BEAST.Variational.BilForm(:i, :j, bilterms) + local BXX = BEAST.assemble(BilForm, X, X) + @assert BXX isa BEAST.LiftedMaps.LiftedMap + # @test typeof(assemble(BilForm, X, X)) == LinearMaps.LinearCombination{U, Vector{LinearMaps.LinearMap}} end \ No newline at end of file diff --git a/test/test_ss_nested_meshes.jl b/test/test_ss_nested_meshes.jl index 8eff8cb2..48bf2c98 100644 --- a/test/test_ss_nested_meshes.jl +++ b/test/test_ss_nested_meshes.jl @@ -53,20 +53,20 @@ end qs_strat = BEAST.DoubleNumWiltonSauterQStrat(1,1,6,7,10,10,10,10) -sauterschwab = BEAST.SauterSchwabQuadrature.CommonFace(BEAST._legendre(10,0.0,1.0)) +# sauterschwab = BEAST.SauterSchwabQuadrature.CommonFace(BEAST._legendre(10,0.0,1.0)) out_ss = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) -BEAST.momintegrals_test_refines_trial!(out_ss, 𝒜, +BEAST.momintegrals!(out_ss, 𝒜, Y, p, test_chart, X, 1, trial_chart, - sauterschwab, qs_strat) + BEAST.TestRefinesTrialQRule(qs_strat)) wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) out_dw = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) -BEAST.momintegrals_test_refines_trial!(out_dw, 𝒜, +BEAST.momintegrals!(out_dw, 𝒜, Y, p, test_chart, X, 1, trial_chart, - wiltonsingext, qs_strat) + wiltonsingext) @show norm(out_ss-out_dw) / norm(out_dw) From 82f03a7934ecc359b943477d7e86a201910811bf Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Mon, 8 Jul 2024 00:43:22 +0000 Subject: [PATCH 385/528] CompatHelper: bump compat for TestItems to 1, (keep existing compat) --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 1c73256d..125727b3 100644 --- a/Project.toml +++ b/Project.toml @@ -44,8 +44,8 @@ ExtendableSparse = "1.4" FFTW = "0.2.3, 1" FastGaussQuadrature = "0.3, 0.4, 0.5, 1" FillArrays = "0.11, 0.12, 0.13, 1" -IterativeSolvers = "0.9" Infiltrator = "1.8.2" +IterativeSolvers = "0.9" LiftedMaps = "0.5.1" LinearMaps = "3.7 - 3.9, 3.11.2" NestedUnitRanges = "0.2" @@ -54,6 +54,6 @@ SauterSchwab3D = "0.1" SauterSchwabQuadrature = "2.4.0" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" -TestItems = "0.1.1" +TestItems = "0.1.1, 1" WiltonInts84 = "0.2.5" julia = "1.6" From eddd2d6f2d9eb3c6d177f0326d2564182df90161 Mon Sep 17 00:00:00 2001 From: Simon Adrian Date: Sat, 6 Jul 2024 15:57:45 +0200 Subject: [PATCH 386/528] Extend docstrings to functions related to basis - Some of the docstrings where outdated due to interface changes - Added new docstrings for some, so far, undocumented functions --- src/bases/basis.jl | 99 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 21 deletions(-) diff --git a/src/bases/basis.jl b/src/bases/basis.jl index 75817307..833cb946 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -55,6 +55,13 @@ in `basis` are defined. The order the elements are encountered needs correspond to the element indices used in the data structure returned by `assemblydata`. """ geometry(s::Space) = s.geo + +""" + basisfunction(basis, i) + +Returns a vector of the shape functions defining the `i`th function +of the `basis`. +""" basisfunction(s::Space, i) = s.fns[i] numfunctions(space::Space) = length(space.fns) @@ -150,30 +157,46 @@ end """ - charts, admap = assemblydata(basis) + charts, admap, act_to_global = assemblydata(basis; onlyactives=true) -Given a Basis this function returns a data structure containing the information -required for matrix assemble. More precise the following expressions are valid -for the returned object `ad`: +Given a `basis` this function returns a data structure containing the information +required for matrix assemble, that is, the vector `charts` containing `Simplex` elements, +a variable `admap` of type `AssemblyData`, and a mapping from indices of actively +used simplices to global simplices. + +When `onlyactives` is `true`, another layer of indices is introduced to filter out all +cells of the mesh that are not in the union of the support of the basis functions +(i.e., when the basis functions are defined only on a part of the mesh). + +`admap` is, in essence, a three-dimensional array of named tuples, which, +by wrapping it in the struct `AssemblyData`, allows the definition of iterators. +The tuple consists of the two entries ``` -ad[c,r,i].globalindex -ad[c,r,i].coefficient +admap[i,r,c].globalindex +admap[i,r,c].coefficient ``` -Here, `c` and `r` are indices in the iterable set of geometric elements and the -set of local shape functions on each element. `i` ranges from 1 to the maximum -number of basis functions local shape function `r` on element `r` contributes -to. - -For a triplet `(c,r,i)`, `globalindex` is the index in the Basis of the -`i`-th basis function that has a contribution from local shape function `r` on -element `r`. `coefficient` is the coefficient of that contribution in the -linear combination defining that basis function in terms of local shape +Here, `c` and `r` are indices in the iterable set of (active) simplices and the +set of shape functions on each cell/simplex: `r` ranges from 1 to the number of +shape functions on a cell/simplex, `c` ranges from 1 to the number of active +simplices, and `i` ranges from 1 to the number of maximal number of basis functions, +where any of the shape functions contributes to. + +For example, for continuous piecewise linear lagrange functions (c0d1), each of the +three shape functions on a triangle are associated with exactly one Lagrange function, +and therefore `i` is limited to 1. + +*Note*: When `onlyactives=false`, the indices `c` correspond to +the position of the corresponding cell/simplex whilst iterating over `geometry(basis)`. +When `onlyactives=true`, then `act_to_global(c)` correspond to the position of the +corresponding cell/simplex whilst iterating over `geometry(basis)`. + +For a triplet `(i,r,c)`, `globalindex` is the index in the `basis` of the +`i`th basis function that has a contribution from shape function `r` on +(active) cell/simplex `c`. `coefficient` is the coefficient of that contribution in the +linear combination defining that basis function in terms of shape function. - -*Note*: the indices `c` correspond to the position of the corresponding -element whilst iterating over `geometry(basis)`. """ function assemblydata(basis::Space; onlyactives=true) @@ -187,11 +210,15 @@ function assemblydata(basis::Space; onlyactives=true) num_bfs = numfunctions(basis) num_refs = numfunctions(refspace(basis)) - # # determine the maximum number of function defined over a given cell + # Determine the number of functions defined over a given cell + # and per local shape function. celltonum = make_celltonum(num_cells, num_refs, num_bfs, basis) - # mark the active elements, i.e. elements over which - # at least one function is defined. + # In general, a basis function space might only be defined + # over a small portion of the underlying mesh. To avoid + # the inefficient iterating of cells, which are not in the support of + # any of the basis functions, we filter out only those cells, over + # which at least one basis function is defined. if onlyactives active, index_among_actives, num_active_cells, act_to_global = index_actives(num_cells, celltonum) @@ -203,8 +230,13 @@ function assemblydata(basis::Space; onlyactives=true) end num_active_cells == 0 && return nothing + + # Generate the a vector of Simplexes associated + # with the active cells only elements = instantiate_charts(geo, num_active_cells, active) + # Determine the maximal number of functions associated with a + # local shape function max_celltonum = maximum(celltonum) fill!(celltonum, 0) data = fill((0,zero(T)), max_celltonum, num_refs, num_active_cells) @@ -223,7 +255,14 @@ function assemblydata(basis::Space; onlyactives=true) return elements, AssemblyData(data), act_to_global end +""" + celltonum = make_celltonum(num_cells, num_refs, num_bfs, basis) +Computes the array `celltonum[c,r]` that, given the index +`c` of a cell of the mesh associated with the `basis` and the index `r` +of the shape function in the refspace associated with `basis`, provides the number +of basis functions where the `r`th shape function on cell `c` is used in their definition. +""" function make_celltonum(num_cells, num_refs, num_bfs, basis) celltonum = zeros(Int, num_cells, num_refs) for b in 1 : num_bfs @@ -237,6 +276,20 @@ function make_celltonum(num_cells, num_refs, num_bfs, basis) return celltonum end +""" + active, index_among_actives, num_active_cells, act_to_global = + index_actives(num_cells, celltonum) + +Given the number of cells of the mesh `num_cells` and an array indicating, which +shape functions of each cell are used in the definition of the basis, `celltonum`, +`index_actives(num_cells, celltonum)` computes a Boolean vector `active` that indicates for +each cell index if the cell is in the support of any basis function. For this reduced set +of active cells, a new index set is introduced, where `index_among_actives` is a vector +providing the mapping from the cells of the mesh to the indices of the active cells, +`num_active_cells` provides the number of all active cells, and `act_to_global` is a +vector providing the mapping from the indices of the active cells to the indices of +cells of the underlying mesh of the basis function spce. +""" function index_actives(num_cells, celltonum) @assert num_cells == size(celltonum,1) active = falses(num_cells) @@ -255,7 +308,11 @@ function index_actives(num_cells, celltonum) return active, index_among_actives, num_active_cells, act_to_global end +""" + instantiate_charts(geo, num_active_cells, active) +Returns a vector of all actively used simplices. +""" function instantiate_charts(geo, num_active_cells, active) @assert length(geo) != 0 E = typeof(chart(geo, first(geo))) From 1078fddcf0d394adfdce27b7230ae0ec55094491 Mon Sep 17 00:00:00 2001 From: Simon Adrian Date: Sat, 6 Jul 2024 15:57:20 +0200 Subject: [PATCH 387/528] Add baseline concept of FEMFunctions FEMFunction is a type that combines expansion coefficients and function space. Also added is a routine Lp_integrate that integrates FEMFunctions or linear combinations over their domain. Moreover, in order to measure errors, analytic functions can be wrapped in the type GlobalFunction, which can be linearly combined with FEMFunctions. Thus, the Lp-integral over the difference of a FEMFunction and an analytic function can be computed to measure, for example, an error (including interpolation error). TODO: - It might come in handy if the returntype of a GlobalFunction is predicted - FEMFunction could be integrated into solve functions etc. Maybe an extension for time-domain is, however, necessary. --- src/BEAST.jl | 1 + src/gridfunction.jl | 328 ++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 1 + test/test_gridfunction.jl | 116 ++++++++++++++ 4 files changed, 446 insertions(+) create mode 100644 src/gridfunction.jl create mode 100644 test/test_gridfunction.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 14daed8a..d14a5ee2 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -197,6 +197,7 @@ include("quadrature/strategies/testrefinestrialqstrat.jl") include("quadrature/strategies/trialrefinestestqstrat.jl") include("excitation.jl") +include("gridfunction.jl") include("localop.jl") include("multiplicativeop.jl") include("identityop.jl") diff --git a/src/gridfunction.jl b/src/gridfunction.jl new file mode 100644 index 00000000..05da678f --- /dev/null +++ b/src/gridfunction.jl @@ -0,0 +1,328 @@ +abstract type AbstractMeshFunction end + +Base.:*(b::Number, fun::AbstractMeshFunction) = LinearCombinationOfAbstractMeshFunctions([b], [fun]) + +function Base.:+(fun1::AbstractMeshFunction, fun2::AbstractMeshFunction) + if geometry(fun1) != geometry(fun2) + error("Functions must be defined over the same geometry.") + end + return LinearCombinationOfAbstractMeshFunctions([1.0; 1.0], [fun1; fun2]) +end + +function Base.:-(fun1::AbstractMeshFunction, fun2::AbstractMeshFunction) + if geometry(fun1) != geometry(fun2) + error("Functions must be defined over the same geometry.") + end + return LinearCombinationOfAbstractMeshFunctions([1.0; -1.0], [fun1; fun2]) +end + +struct GlobalFunction{F,X,I} <: AbstractMeshFunction + fun::F + geo::X + cells::Vector{I} +end + +defaultquadstrat(field::GlobalFunction) = SingleNumQStrat(7) +quaddata(fn::GlobalFunction, simplexes, qs::SingleNumQStrat) = quadpoints(fn, simplexes, [qs.quad_rule]) +quadrule(fn::GlobalFunction, refs, p, cell, qd, qs::SingleNumQStrat) = qd[1,p] +geometry(fn::GlobalFunction) = fn.geo + +function (f::GlobalFunction{F, X, I})(mp::CompScienceMeshes.MeshPointNM) where {F, X, I} + + # Predict return type + #PT = promote_type(coordtype(chart(mp))) + + return f.fun(cartesian(mp)) +end + +function (f::GlobalFunction{F, X, I})(x) where {F, X, I} + return f.fun(x) +end + +struct FEMFunction{T, X} <: AbstractMeshFunction + coeffs::AbstractVector{T} + space::X +end + +defaultquadstrat(field::FEMFunction) = SingleNumQStrat(7) +returntype(f::FEMFunction{T,X}) where {T,X} = T + +quaddata(fn::FEMFunction, refs, cells, qs::SingleNumQStrat) = quadpoints(refs, cells, [qs.quad_rule]) +quadrule(fn::FEMFunction, refs, p, cell, qd, qs::SingleNumQStrat) = qd[1,p] +geometry(fn::FEMFunction) = geometry(fn.space) + +mutable struct LinearCombinationOfAbstractMeshFunctions{T} + coeffs::Vector{T} + gfs::Vector +end + +geometry(lincombfun::LinearCombinationOfAbstractMeshFunctions) = geometry(first(lincombfun.gfs)) + +Base.length(lc::LinearCombinationOfAbstractMeshFunctions) = length(lc.coeffs) + +Base.:*(b::Number, lcb::LinearCombinationOfAbstractMeshFunctions) = + LinearCombinationOfAbstractMeshFunctions(b .* lcb.coeffs, lcb.gfs) + +function Base.:+(lcb::LinearCombinationOfAbstractMeshFunctions{T}, fun::AbstractMeshFunction) where {T} + + if geometry(lcb) != geometry(fun) + error("Functions must be defined over the same geometry.") + end + + return LinearCombinationOfAbstractMeshFunctions([lcb.coeffs[:]; T(1)], [lcb.gfs[:]; fun]) +end + +function Base.:-(lcb::LinearCombinationOfAbstractMeshFunctions{T}, fun::AbstractMeshFunction) where {T} + + if geometry(lcb) != geometry(fun) + error("Functions must be defined over the same geometry.") + end + + return LinearCombinationOfAbstractMeshFunctions([lcb.coeffs[:]; -T(1)], [lcb.gfs[:]; fun]) +end + +Base.:+(fun::AbstractMeshFunction, lcb::LinearCombinationOfAbstractMeshFunctions) = lcb + fun + +Base.:-(fun::AbstractMeshFunction, lcb::LinearCombinationOfAbstractMeshFunctions) = lcb - fun + +function Base.:+(lcb1::LinearCombinationOfAbstractMeshFunctions, lcb2::LinearCombinationOfAbstractMeshFunctions) + + if geometry(lcb1) != geometry(lcb2) + error("Functions must be defined over the same geometry.") + end + + return LinearCombinationOfAbstractMeshFunctions([lcb1.coeffs[:]; lcb2.coeffs[:]], [lcb1.gfs[:]; lcb2.gfs[:]]) +end + +function Base.:-(lcb1::LinearCombinationOfAbstractMeshFunctions{T1}, lcb2::LinearCombinationOfAbstractMeshFunctions{T2}) where {T1, T2} + + if geometry(lcb1) != geometry(lcb2) + error("Functions must be defined over the same geometry.") + end + + return LinearCombinationOfAbstractMeshFunctions([lcb1.coeffs[:]; -T2(1) .* lcb2.coeffs[:]], [lcb1.gfs[:]; lcb2.gfs[:]]) +end + +Base.iterate(lc::LinearCombinationOfAbstractMeshFunctions, state=1) = + state > length(lc) ? nothing : ((lc.coeffs[state], lc.gfs[state]), state+1) + +defaultquadstrat(field::LinearCombinationOfAbstractMeshFunctions) = SingleNumQStrat(7) + +""" + Lp_integrate + +Integrates FEMFunctions and GlobalFunctions as well as linear combinations. +NOTE: FEMFunctions on the dual mesh are currently not supported. +""" + +function Lp_integrate(gf::FEMFunction; p=2, quadstrat=defaultquadstrat(gf)) + return Lp_integrate(LinearCombinationOfAbstractMeshFunctions([1.0], [gf]); p=p, quadstrat=quadstrat) +end + +function Lp_integrate( + glf::GlobalFunction{F, X, I}; + p=2, + quadstrat=defaultquadstrat(glf) +) where {F, X, I} + return Lp_integrate(LinearCombinationOfAbstractMeshFunctions([1.0], [glf]); p=p, quadstrat=quadstrat) +end + +""" + indices_splitfemglobal(lincombgfs) + +Given a linear combination of FEMFunctions and/or GlobalFunctions, return the indices +of the fem and the global functions +""" +function indices_splitfemglobal(lincombgfs::LinearCombinationOfAbstractMeshFunctions{T}) where T + + indices_fem = Int64[] + indices_global = Int64[] + + for (i, gf) in enumerate(lincombgfs.gfs) + if gf isa FEMFunction + push!(indices_fem, i) + else + push!(indices_global, i) + end + end + + return indices_fem, indices_global +end + +function Lp_integrate( + lincombgfs::LinearCombinationOfAbstractMeshFunctions{T}; + p=2, + quadstrat=SingleNumQStrat(7) +) where {T} + + # TODO: Add routine that geos are consistent + indices_fem, indices_global = indices_splitfemglobal(lincombgfs) + + if length(indices_fem) >= 1 + geo = geometry(lincombgfs.gfs[indices_fem[1]].space) + elseif length(indices_global) >= 1 + geo = lincombgfs.gfs[indices_global[1]].geo + else + error("Error: empty linear combination") + end + + asmdata = [ assemblydata(lincombgfs.gfs[i].space, onlyactives=false) for i in indices_fem] + + qds_fem = Any[] + trefs_fem = Any[] + + numquadpoints = 0 + for (i, j) in enumerate(indices_fem) + push!(trefs_fem, refspace(lincombgfs.gfs[j].space)) + tels = asmdata[i][1] + qd = quaddata(lincombgfs.gfs[j], trefs_fem[i], tels, quadstrat) + push!(qds_fem, qd) + end + + qds_global = Any[] + active_global = Any[] + for (i, j) in enumerate(indices_global) + push!(active_global, falses(numcells(lincombgfs.gfs[j].geo))) + + for cellindex in lincombgfs.gfs[j].cells + active_global[i][cellindex] = true + end + + tels = instantiate_charts(lincombgfs.gfs[j].geo, numcells(lincombgfs.gfs[j].geo), trues(numcells(lincombgfs.gfs[j].geo))) + qd = quaddata(lincombgfs.gfs[j], tels, quadstrat) + push!(qds_global, qd) + end + + if length(indices_fem) >= 1 + numquadpoints = length(qds_fem[1][1, 1]) + elseif length(indices_global) >= 1 + numquadpoints = length(qds_global[1][1, 1]) + else + error("Error: empty linear combination") + end + + # It is difficult to predict the return type of an arbitrary function + quadrature_sampling_gf_res = zeros(ComplexF64, numquadpoints, numcells(geo)) + + retval = Float64(0) #returntype(first(lincombgfs.gfs))(0) + + tels = instantiate_charts(geo, numcells(geo), trues(numcells(geo))) + + for (t, tcell) in enumerate(tels) + + qrs_fem = [quadrule(lincombgfs.gfs[j], trefs_fem[i], t, tcell, qds_fem[i], quadstrat) for (i, j) in enumerate(indices_fem)] + + # Loop over FEM functions first + + #for (i, (~, gftad, ~)) in enumerate(asmdata) + for (i, j) in enumerate(indices_fem) + ~, gftad, ~ = asmdata[i] + cellquadsamplingvaluesvalues!( + quadrature_sampling_gf_res, + t, + trefs_fem[i], + gftad, + lincombgfs.coeffs[j], + lincombgfs.gfs[j], + qrs_fem[i] + ) + end + + qrs_global = [ + quadrule(lincombgfs.gfs[j], nothing, t, tcell, qds_global[i], quadstrat) + for (i, j) in enumerate(indices_global) + ] + + # TODO: loop over grid functions + + for (i, j) in enumerate(indices_global) + + # We only integrate, if the cell is part of the domain + active_global[i][t] == false && continue + + cellquadsamplingvaluesvalues!( + quadrature_sampling_gf_res, + t, + nothing, + nothing, + lincombgfs.coeffs[j], + lincombgfs.gfs[j], + qrs_global[i] + ) + end + + # The weights are all the same since we use the same quadrature + # Just pick any from the no-empty sets + if length(indices_fem) >= 1 + qr = first(qrs_fem) + elseif length(indices_global) >= 1 + qr = first(qrs_global) + else + error("Error: empty linear combination") + end + + for i = 1:numquadpoints + retval += abs(quadrature_sampling_gf_res[i, t])^p*qr[i].weight + end + end + + return retval^(1/p) +end + +function cellquadsamplingvaluesvalues!( + quadsampling, + cellindex, + tshs::RefSpace, + tad, + lincombcoeff, + gf, + qr +) + + num_tshs = numfunctions(tshs) + + num_oqp = length(qr) + + data = tad.data + + for p in 1 : num_oqp + + tvals = qr[p].value + + for m in 1 : num_tshs + tval = tvals[m] + + for k = 1:size(data, 1) + # w is the is weight of shape function + # of the global basis function, omitted via ~ + b, w = data[k, m, cellindex] + + igd = tval[1]*w*lincombcoeff*gf.coeffs[b] + quadsampling[p, cellindex] += igd + end + end + end + +end + +function cellquadsamplingvaluesvalues!( + quadsampling, + cellindex, + tshs, + tad::Nothing, + lincombcoeff, + gf::GlobalFunction, + qr +) + + num_oqp = length(qr) + + for p in 1 : num_oqp + igd = qr[p].value + if lincombcoeff isa Number + igd *= lincombcoeff + end + quadsampling[p, cellindex] += igd + end + +end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 956d5153..bbcee67f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -77,6 +77,7 @@ include("test_td_tensoroperator.jl") include("test_variational.jl") include("test_handlers.jl") +include("test_gridfunction.jl") using TestItemRunner @run_package_tests diff --git a/test/test_gridfunction.jl b/test/test_gridfunction.jl new file mode 100644 index 00000000..95bfd434 --- /dev/null +++ b/test/test_gridfunction.jl @@ -0,0 +1,116 @@ +using BEAST +using CompScienceMeshes +using StaticArrays +using LinearAlgebra +using Test +U = Float64 + +a = U(1) + +# Cube between (3,3,3) and (4,4,4) +Γ = translate(CompScienceMeshes.meshcuboid(a,a,a,U(1.0)), SVector(3.0,3.0,3.0)) + +C0 = lagrangec0d1(Γ) + +# We interpolate the function f(x, y, z) = z +coeffs = [v[3] for v in vertices(Γ)] + +gfc0 = BEAST.FEMFunction(coeffs, C0) + +# We integrate on a cube between the vertices (3,3,3) and (4,4,4) +# the function f(x, y, z) = z +# L1 result is = 4 + 3 + 4 * (16/2 - 9/2) = 21 +@test BEAST.Lp_integrate(gfc0; p=1) ≈ 21 + +# L2 result is sqrt(16 + 9 + 4 * (4^3/3 - 3^3/2)) +@test BEAST.Lp_integrate(gfc0) ≈ sqrt(16 + 9 + 4 * (4^3/3 - 3^3/3)) + +#lincombgfs = BEAST.LinearCombinationOfAbstractMeshFunctions([1.0; -0.5], [gfc0, gfc0]) +lincombgfs = -0.5*gfc0 + gfc0 + +# We integrate on a cube between the vertices (3,3,3) and (4,4,4) +# the function f(x, y, z) = z +# L1 result is = 4 + 3 + 4 * (16/2 - 9/2) = 21 +@test BEAST.Lp_integrate(lincombgfs; p=1) ≈ 21/2 + +# L2 result is sqrt(16 + 9 + 4 * (4^3/3 - 3^3/2)) +@test BEAST.Lp_integrate(lincombgfs) ≈ sqrt(16 + 9 + 4 * (4^3/3 - 3^3/3))/2 + +CX = lagrangecxd0(Γ) + +# We interpolate the function f(x, y, z) = 2 +# with piecewise constant lagrange elements +constgfcx = BEAST.FEMFunction([2.0 for c in cells(Γ)], CX) +@test BEAST.Lp_integrate(constgfcx; p=1) ≈ 12 +@test BEAST.Lp_integrate(constgfcx) ≈ sqrt(24) + +# We interpolate the function f(x, y, z) = z +# with piecewise constant lagrange elements +coeffsCx = [((Γ.vertices[c[1]] + Γ.vertices[c[2]] + Γ.vertices[c[3]])/3)[3] for c in cells(Γ)] + +gfcx = BEAST.FEMFunction(coeffsCx, CX) + +# Even though the function, should be able to interpolate it +@test BEAST.Lp_integrate(gfcx; p=1) ≈ 21 + +# Compute the difference in approximation between constant and linear elements +#difflincomb1 = BEAST.LinearCombinationOfAbstractMeshFunctions([1.0; -1.0], [gfcx, gfc0]) +difflincomb1 = gfcx - gfc0 + +@test BEAST.Lp_integrate(difflincomb1)/BEAST.Lp_integrate(gfc0) ≈ 0.03866223365135301 + +## Test GlobalFunction +f(x) = x[3] +glbf = BEAST.GlobalFunction(f, Γ, Vector(1:numcells(Γ))) + +@test BEAST.Lp_integrate(glbf; p=1) ≈ 21 + +g(x) = x[3]^2 +glbf2 = BEAST.GlobalFunction(g, Γ, Vector(1:numcells(Γ))) + +@test BEAST.Lp_integrate(glbf2; p=1) ≈ 74.33333333333334 + +## Test linear combination of global and fem functions +idxfem, idxglobal = BEAST.indices_splitfemglobal(gfc0 - glbf + 0.0gfcx) + +@test length(idxfem) == 2 +@test length(idxglobal) == 1 + +## +@test BEAST.Lp_integrate(gfc0 - glbf) / BEAST.Lp_integrate(glbf) ≈ 0 atol=1e-16 + +@test BEAST.Lp_integrate(gfcx - glbf) / BEAST.Lp_integrate(glbf) ≈ 0.038662233651353003 + +## Global function on limited supported +f(x) = x[3] + +idx_topplate = Int64[] + +for (i, face) in enumerate(cells(Γ)) + center = (Γ.vertices[face[1]] + Γ.vertices[face[2]] + Γ.vertices[face[3]]) / 3.0 + if center[3] > 3.99 + push!(idx_topplate, i) + end +end + +glbf_toppplate = BEAST.GlobalFunction(f, Γ, idx_topplate) +@test BEAST.Lp_integrate(glbf_toppplate, p=1) ≈ 4.000000000000003 + +@test BEAST.Lp_integrate(gfc0 - glbf_toppplate, p=1) ≈ 21 - 4 # Topplate accounts for 4 + +## + +Γ2 = CompScienceMeshes.meshcuboid(a,a,a,U(1.0)) + +coeffsΓ2 = [v[3] for v in vertices(Γ2)] + +C0Γ2 = lagrangec0d1(Γ2) + +gfc0Γ2 = BEAST.FEMFunction(coeffsΓ2, C0Γ2) + +@test_throws ErrorException("Functions must be defined over the same geometry.") gfc0 + gfc0Γ2 + +@test_throws ErrorException("Functions must be defined over the same geometry.") lincombgfs + 1.0gfc0Γ2 + +## Test mixed combinations +@test BEAST.Lp_integrate(gfc0 + 1im * gfc0; p=1) ≈ sqrt(2) * 21 \ No newline at end of file From 1d71ae86c6b2cf90d6e5301616ee7fc3ffcef639 Mon Sep 17 00:00:00 2001 From: lvchien Date: Mon, 19 Aug 2024 15:16:54 +0200 Subject: [PATCH 388/528] Fix the tail issue of the identity operator in TD-MFIE --- src/timedomain/tdtimeops.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timedomain/tdtimeops.jl b/src/timedomain/tdtimeops.jl index 61bf39d7..cdb3e99e 100644 --- a/src/timedomain/tdtimeops.jl +++ b/src/timedomain/tdtimeops.jl @@ -72,7 +72,7 @@ function allocatestorage(op::TensorOperator, test_functions, trial_functions, tail = zeros(T, M, N) Nt = numfunctions(temporalbasis(trial_functions)) - Z = ConvolutionOperators.ConvOp(data, K0, K1, tail, Nt) + Z = ConvolutionOperators.ConvOp(data, K0, K1, tail, bandwidth) function store1(v,m,n,k) if Z.k0[m,n] ≤ k ≤ Z.k1[m,n] Z.data[k - Z.k0[m,n] + 1,m,n] += v From 7f75ba9288edaaa535ff145a9f742c2d44f62a6f Mon Sep 17 00:00:00 2001 From: lvchien Date: Mon, 19 Aug 2024 21:28:58 +0200 Subject: [PATCH 389/528] Add LaplaceDomainOperator --- examples/tdpmchwt_cq.jl | 81 +++++++++++++++++++++++++++++++++++++++++ src/timedomain/rkcq.jl | 72 ++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 examples/tdpmchwt_cq.jl diff --git a/examples/tdpmchwt_cq.jl b/examples/tdpmchwt_cq.jl new file mode 100644 index 00000000..e19e8746 --- /dev/null +++ b/examples/tdpmchwt_cq.jl @@ -0,0 +1,81 @@ +using BEAST, LinearAlgebra, CompScienceMeshes, ConvolutionOperators, Printf + +# Physical parameters +ϵ, μ = 1.0, 1.0 +ϵ′, μ′ = 1.0, 1.0 +η, η′ = √(μ/ϵ), √(μ′/ϵ′) + +T = BEAST.LaplaceDomainOperator(s::ComplexF64 -> MWSingleLayer3D(s*√(ϵ*μ), -s*√(ϵ*μ), ComplexF64(1/√(ϵ*μ))/s)) +K = BEAST.LaplaceDomainOperator(s::ComplexF64 -> MWDoubleLayer3D(s*√(ϵ*μ))) +T′ = BEAST.LaplaceDomainOperator(s::ComplexF64 -> MWSingleLayer3D(s*√(ϵ′*μ′), -s*√(ϵ′*μ′), ComplexF64(1/√(ϵ′*μ′))/s)) +K′ = BEAST.LaplaceDomainOperator(s::ComplexF64 -> MWDoubleLayer3D(s*√(ϵ′*μ′))) + +Γ = meshsphere(1.0, 0.3) +X = raviartthomas(Γ) +Y = buffachristiansen(Γ) + +@hilbertspace k l +@hilbertspace j m + +Nt, Δt = 200, 10.0 +(A, b, c) = butcher_tableau_radau_2stages() +CQ = StagedTimeStep(Δt, Nt, c, A, b, 30, 1.0001) + +duration = 40 * Δt +delay = 60 * Δt +amplitude = 1.0 +gaussian = creategaussian(duration, delay, amplitude) +fgaussian = fouriertransform(gaussian) +polarisation, direction = x̂, ẑ +E = planewave(polarisation, direction, gaussian, 1.0) +H = direction × E + +BEAST.@defaultquadstrat (T, X⊗CQ, X⊗CQ) BEAST.DoubleNumWiltonSauterQStrat(6, 7, 6, 7, 7, 7, 7, 7) +BEAST.@defaultquadstrat (T′, X⊗CQ, X⊗CQ) BEAST.DoubleNumWiltonSauterQStrat(6, 7, 6, 7, 7, 7, 7, 7) +BEAST.@defaultquadstrat (K, X⊗CQ, X⊗CQ) BEAST.DoubleNumWiltonSauterQStrat(6, 7, 6, 7, 7, 7, 7, 7) +BEAST.@defaultquadstrat (K′, X⊗CQ, X⊗CQ) BEAST.DoubleNumWiltonSauterQStrat(6, 7, 6, 7, 7, 7, 7, 7) + +pmchwt = @discretise(η*T[k,j] + η′*T′[k,j] + K[l,j] + K′[l,j] + - K[k,m] - K′[k,m] + 1/η*T[l,m] + 1/η′*T′[l,m] == E[k] + H[l], + k∈X⊗CQ, l∈X⊗CQ, j∈X⊗CQ, m∈X⊗CQ) +je = solve(pmchwt) + + +# Exact solution +N = NCross() +Nyx = assemble(N, Y, X) +iNyx = inv(Matrix(Nyx)) +Zr = zeros(size(iNyx)) + +δ = timebasisdelta(Δt, Nt) +linform = @discretise(H[k] + E[l], k∈Y⊗δ, l∈Y⊗δ) +rhs = BEAST.assemble(linform.linform, linform.test_space_dict) +j_ext = [iNyx Zr; Zr iNyx] * rhs + + +# Plot the numerical solutions +using Plots +x = 1e-2 * Δt/3 * [1:1:Nt;] + +plt = Plots.plot( + size = (600, 400), + grid = false, + xscale = :identity, + # xlims = (0, 1620), + # xticks = [400, 800, 1200, 1600], + xtickfont = Plots.font(10, "Computer Modern"), + yscale = :log10, + ylims = (1e-15, 1e0), + yticks = [1e-20, 1e-15, 1e-10, 1e-5, 1e0, 1e5], + ytickfont = Plots.font(10, "Computer Modern"), + xlabel = "Time ({\\mu}s)", + ylabel = "Current density intensity (A/m)", + titlefont = Plots.font(10, "Computer Modern"), + guidefont = Plots.font(11, "Computer Modern"), + colorbar_titlefont = Plots.font(10, "Computer Modern"), + legendfont = Plots.font(11, "Computer Modern"), + legend = :topright, + dpi = 300) + +Plots.plot!(x, abs.(j_ext[1, :]), label="Exact solution", linecolor=1, lw=1.2) +Plots.plot!(x, abs.(je[1, :]), label="TD-PMCHWT", linecolor=2, lw=1.2) \ No newline at end of file diff --git a/src/timedomain/rkcq.jl b/src/timedomain/rkcq.jl index 13ae3b24..6dcfd3ec 100644 --- a/src/timedomain/rkcq.jl +++ b/src/timedomain/rkcq.jl @@ -106,3 +106,75 @@ function assemble(rkcq :: RungeKuttaConvolutionQuadrature, return ZC end + + +struct LaplaceDomainOperator + coeff::ComplexF64 + kernel::Any +end + +scalartype(op::LaplaceDomainOperator) = ComplexF64 + +*(a::Number, op::LaplaceDomainOperator) = LaplaceDomainOperator(a*op.coeff, op.kernel) + +LaplaceDomainOperator(kernel) = LaplaceDomainOperator(Complex(1.0), kernel) + +defaultquadstrat(op::LaplaceDomainOperator, tfs::SpaceTimeBasis, bfs::SpaceTimeBasis) = DoubleNumWiltonSauterQStrat(2,3,6,7,5,5,4,3) + +function assemble(op::LaplaceDomainOperator, testfns::SpaceTimeBasis, trialfns::SpaceTimeBasis; quadstrat=defaultquadstrat(op, testfns, trialfns)) + laplaceKernel = op.kernel + + A = testfns.time.A + b = testfns.time.b + Δt = testfns.time.Δt + Q = testfns.time.zTransformedTermCount + rho = testfns.time.contourRadius + p = length(b) # stage count + + test_spatial_basis = testfns.space + trial_spatial_basis = trialfns.space + + # Compute the Z transformed sequence. + # Assume that the operator applied on the conjugate of s is the same as the + # conjugate of the operator applied on s, + # so that only half of the values are computed + Qmax = Q>>1+1 + M = numfunctions(test_spatial_basis) + N = numfunctions(trial_spatial_basis) + Tz = ComplexF64 + Zz = Vector{Array{Tz,2}}(undef,Qmax) + blocksEigenvalues = Vector{Array{Tz,2}}(undef,p) + tmpDiag = Vector{Tz}(undef,p) + + for q = 0:Qmax-1 + # Build a temporary matrix for each eigenvalue + s = laplace_to_z(rho, q, Q, Δt, A, b) + sFactorized = diagonalizedmatrix(s) + for (i,sD) in enumerate(sFactorized.D) + blocksEigenvalues[i] = op.coeff * assemble(laplaceKernel(sD), test_spatial_basis, trial_spatial_basis, quadstrat=quadstrat) + end + + # Compute the Z transformed matrix by block + Zz[q+1] = zeros(Tz, M*p, N*p) + for m = 1:M + for n = 1:N + for i = 1:p + tmpDiag[i] = blocksEigenvalues[i][m,n] + end + D = SVector{p,Tz}(tmpDiag); + Zz[q+1][(m-1)*p.+(1:p),(n-1)*p.+(1:p)] = sFactorized.H * diagm(D) * sFactorized.invH + end + end + end + + # return the inverse Z transform + kmax = Q + T = real(Tz) + Z = zeros(T, M*p, N*p, kmax) + for q = 0:kmax-1 + Z[:,:,q+1] = real_inverse_z_transform(q, rho, Q, Zz) + end + ZC = ConvolutionOperators.DenseConvOp(Z) + + return ZC +end \ No newline at end of file From 92770b1e124fac62b34a23b7b6c1a821fadaaec7 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 9 Sep 2024 14:50:05 +0200 Subject: [PATCH 390/528] PulledBackIntegrand supports mixed Chart types --- Project.toml | 2 +- examples/efie.jl | 2 + src/bases/local/rt2local.jl | 32 ++++++++- src/quadrature/sauterschwabints.jl | 104 +---------------------------- test/test_restrict.jl | 10 +-- 5 files changed, 40 insertions(+), 110 deletions(-) diff --git a/Project.toml b/Project.toml index 1c73256d..8a088f9d 100644 --- a/Project.toml +++ b/Project.toml @@ -35,7 +35,7 @@ WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] AbstractTrees = "0.4.4" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 1" -CollisionDetection = "0.1.5" +CollisionDetection = "0.1.6" Combinatorics = "0.7, 1" CompScienceMeshes = "0.8.1" Compat = "2, 3, 4" diff --git a/examples/efie.jl b/examples/efie.jl index 84ec0ce9..89df72d6 100644 --- a/examples/efie.jl +++ b/examples/efie.jl @@ -2,6 +2,8 @@ using CompScienceMeshes using BEAST Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +# Γ = meshsphere(radius=1.0, h=0.1) +@show length(Γ) X = raviartthomas(Γ) κ, η = 1.0, 1.0 diff --git a/src/bases/local/rt2local.jl b/src/bases/local/rt2local.jl index 68f7dbc4..aeaa2c57 100644 --- a/src/bases/local/rt2local.jl +++ b/src/bases/local/rt2local.jl @@ -39,7 +39,7 @@ function interpolate(fields, interpolant::BEAST.RT2RefSpace, chart) T = coordtype(chart) Q = Any[] - refchart = CompScienceMeshes.domain(chart).simplex + refchart = CompScienceMeshes.domain(chart) nfields = length(fields(center(chart))) for (edge, refedge) in zip(faces(chart), faces(refchart)) @@ -48,8 +48,6 @@ function interpolate(fields, interpolant::BEAST.RT2RefSpace, chart) qps = CompScienceMeshes.quadpoints(edge,4) for (p_edge,w) in qps s = parametric(p_edge) - # x = cartesian(p_edge) - # u = carttobary(chart, x) p_refedge = neighborhood(refedge,s) u = cartesian(p_refedge) @@ -174,3 +172,31 @@ const _dof_rt2perm_matrix = [ 0 0 0 0 0 0 -1 1] ] +@testitem "restrict RT2" begin + using CompScienceMeshes + using LinearAlgebra + + ref_vertices = [ + point(1,0), + point(0,1), + point(0,0), + ] + vertices = [ + point(1,0,0), + point(0,1,0), + point(0,0,0), + ] + chart1 = simplex(vertices...) + for I in BEAST._vert_perms_rt + chart2 = simplex( + chart1.vertices[I[1]], + chart1.vertices[I[2]], + chart1.vertices[I[3]],) + chart2tochart1 = CompScienceMeshes.simplex(ref_vertices[collect(I)]...) + rs = BEAST.RT2RefSpace{Float64}() + Q1 = BEAST.dof_perm_matrix(rs, I) + Q3 = BEAST.interpolate(rs, chart2, rs, chart1, chart2tochart1) + @test Q1 ≈ Q3 + @show norm(Q1-Q3) + end +end \ No newline at end of file diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index e978d84c..b514cd3c 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -117,10 +117,10 @@ function CompScienceMeshes.permute_vertices( return Quadrilateral(V[I[1]], V[I[2]], V[I[3]], V[I[4]]) end -struct PulledBackIntegrand{I,C} +struct PulledBackIntegrand{I,C1,C2} igd::I - chart1::C - chart2::C + chart1::C1 + chart2::C2 end function (f::PulledBackIntegrand)(u,v) @@ -160,102 +160,4 @@ function momintegrals!(op::Operator, end -# function momintegrals_test_refines_trial!(out, op, -# test_functions, test_cell, test_chart, -# trial_functions, trial_cell, trial_chart, -# quadrule, quadstrat) -# # test_local_space = refspace(test_functions) -# # trial_local_space = refspace(trial_functions) - -# momintegrals!(out, op, -# test_functions, test_cell, test_chart, -# trial_functions, trial_cell, trial_chart, quadrule) -# end - -# # const MWOperator3D = Union{MWSingleLayer3D, MWDoubleLayer3D} -# function momintegrals_test_refines_trial!(out, op, -# test_functions, test_cell, test_chart, -# trial_functions, trial_cell, trial_chart, -# qr::SauterSchwabStrategy, quadstrat) - -# test_local_space = refspace(test_functions) -# trial_local_space = refspace(trial_functions) - -# test_mesh = geometry(test_functions) -# # trial_mesh = geometry(trial_functions) - -# parent_mesh = CompScienceMeshes.parent(test_mesh) -# trial_charts = [chart(test_mesh, p) for p in CompScienceMeshes.children(parent_mesh, trial_cell)] - -# qd = quaddata(op, test_local_space, trial_local_space, -# [test_chart], trial_charts, quadstrat) - -# for (q,chart) in enumerate(trial_charts) -# qr = quadrule(op, test_local_space, trial_local_space, -# 1, test_chart, q ,chart, qd, quadstrat) -# # @show qr - -# Q = restrict(trial_local_space, trial_chart, chart) -# zlocal = zero(out) - -# momintegrals!(zlocal, op, -# test_functions, test_cell, test_chart, -# trial_functions, nothing, chart, qr) - -# for j in 1:numfunctions(trial_local_space) -# for i in 1:numfunctions(test_local_space) -# for k in 1:size(Q, 2) -# out[i,j] += zlocal[i,k] * Q[j,k] -# end end end end end - - - -# function momintegrals_trial_refines_test!(out, op, -# test_functions, test_cell, test_chart, -# trial_functions, trial_cell, trial_chart, -# quadrule, quadstrat) - -# test_local_space = refspace(test_functions) -# trial_local_space = refspace(trial_functions) - -# momintegrals!(out, op, -# test_functions, test_cell, test_chart, -# trial_functions, trial_cell, trial_chart, quadrule) -# end - - -# function momintegrals_trial_refines_test!(out, op, -# test_functions, test_cell, test_chart, -# trial_functions, trial_cell, trial_chart, -# qr::SauterSchwabStrategy, quadstrat) - -# test_local_space = refspace(test_functions) -# trial_local_space = refspace(trial_functions) - -# test_mesh = geometry(test_functions) -# trial_mesh = geometry(trial_functions) - -# parent_mesh = CompScienceMeshes.parent(trial_mesh) -# test_charts = [chart(trial_mesh, p) for p in CompScienceMeshes.children(parent_mesh, test_cell)] - -# qd = quaddata(op, test_local_space, trial_local_space, -# test_charts, [trial_chart], quadstrat) - -# for (p,chart) in enumerate(test_charts) -# qr = quadrule(op, test_local_space, trial_local_space, -# p, chart, 1, trial_chart, qd, quadstrat) - -# Q = restrict(test_local_space, test_chart, chart) -# zlocal = zero(out) -# momintegrals!(zlocal, op, -# test_functions, nothing, chart, -# trial_functions, trial_cell, trial_chart, qr) - -# for j in 1:numfunctions(trial_local_space) -# for i in 1:numfunctions(test_local_space) -# for k in 1:size(Q, 2) -# out[i,j] += Q[i,k] * zlocal[k,j] -# end end end -# end -# end diff --git a/test/test_restrict.jl b/test/test_restrict.jl index 8877dce0..e06a6a58 100644 --- a/test/test_restrict.jl +++ b/test/test_restrict.jl @@ -44,11 +44,11 @@ for T in [Float32, Float64] # Test restriction of RT elements - ni, no = 6, 7; - ui = transpose([triangleGaussA[ni] triangleGaussB[ni] ]); - uo = transpose([triangleGaussA[no] triangleGaussB[no] ]); - wi = triangleGaussW[ni]; - wo = triangleGaussW[no]; + # ni, no = 6, 7; + # ui = transpose([CompScienceMeshes.triangleGaussA[ni] CompScienceMeshes.triangleGaussB[ni] ]); + # uo = transpose([CompScienceMeshes.triangleGaussA[no] CompScienceMeshes.triangleGaussB[no] ]); + # wi = triangleGaussW[ni]; + # wo = triangleGaussW[no]; # universe = Universe(1.0, ui, wi, uo, wo); p = simplex([T.(2*e0),T.(2*e1),T.(2*e2)], Val{2}) From 52258b4ffff28a4eefe93fb6ef148a66900762cd Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 9 Sep 2024 15:30:36 +0200 Subject: [PATCH 391/528] fix test to next treatment of tails --- test/test_td_tensoroperator.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_td_tensoroperator.jl b/test/test_td_tensoroperator.jl index db480dac..3bddb66d 100644 --- a/test/test_td_tensoroperator.jl +++ b/test/test_td_tensoroperator.jl @@ -28,7 +28,7 @@ using Test op = Id ⊗ Id Z = assemble(op, V, U) A = BEAST.ConvolutionOperators.ConvOpAsArray(Z) - @test size(A) == (1,1,10) + @test size(A) == (1,1,2) for i in 2:10 @test A[1,1,i] ≈ 0 atol=sqrt(eps(eltype(A))) end From 390ebe1ed8270e182da40ce2e7fa4e1c642439c2 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 9 Sep 2024 16:26:44 +0200 Subject: [PATCH 392/528] tests in testitem to fix naming conflict --- test/test_gridfunction.jl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/test_gridfunction.jl b/test/test_gridfunction.jl index 95bfd434..f951c7ea 100644 --- a/test/test_gridfunction.jl +++ b/test/test_gridfunction.jl @@ -1,8 +1,10 @@ -using BEAST +@testitem "gridfunction" begin + +# using BEAST using CompScienceMeshes using StaticArrays using LinearAlgebra -using Test +# using Test U = Float64 a = U(1) @@ -113,4 +115,6 @@ gfc0Γ2 = BEAST.FEMFunction(coeffsΓ2, C0Γ2) @test_throws ErrorException("Functions must be defined over the same geometry.") lincombgfs + 1.0gfc0Γ2 ## Test mixed combinations -@test BEAST.Lp_integrate(gfc0 + 1im * gfc0; p=1) ≈ sqrt(2) * 21 \ No newline at end of file +@test BEAST.Lp_integrate(gfc0 + 1im * gfc0; p=1) ≈ sqrt(2) * 21 + +end \ No newline at end of file From 9d8c8923c7abf4ff8cbd19715466f86aa461e42c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 10 Sep 2024 13:58:39 +0200 Subject: [PATCH 393/528] require CSM 0.8.2 --- Project.toml | 2 +- src/quadrature/sauterschwabints.jl | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index e593bfe8..aea9da92 100644 --- a/Project.toml +++ b/Project.toml @@ -37,7 +37,7 @@ AbstractTrees = "0.4.4" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 1" CollisionDetection = "0.1.6" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.8.1" +CompScienceMeshes = "0.8.2" Compat = "2, 3, 4" ConvolutionOperators = "0.4" ExtendableSparse = "1.4" diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index b514cd3c..423bda9a 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -109,13 +109,12 @@ function (igd::Integrand)(x,y,f,g) end -# TODO: Remove this when the next version of CSM is relased! -function CompScienceMeshes.permute_vertices( - ch::CompScienceMeshes.RefQuadrilateral, I) +# function CompScienceMeshes.permute_vertices( +# ch::CompScienceMeshes.RefQuadrilateral, I) - V = vertices(ch) - return Quadrilateral(V[I[1]], V[I[2]], V[I[3]], V[I[4]]) -end +# V = vertices(ch) +# return Quadrilateral(V[I[1]], V[I[2]], V[I[3]], V[I[4]]) +# end struct PulledBackIntegrand{I,C1,C2} igd::I From 0ebfc70e42cb2936542d9dc4ea9a9104cc86b2a0 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 13 Sep 2024 10:06:21 +0200 Subject: [PATCH 394/528] scalar higher order basis plus superficial tests --- src/bases/lagrange.jl | 26 +++ src/bases/local/laglocal.jl | 175 +++++++++++++------ test/test_higher_order_lagrange_functions.jl | 31 ++++ 3 files changed, 182 insertions(+), 50 deletions(-) create mode 100644 test/test_higher_order_lagrange_functions.jl diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 271caa1a..a5738abb 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -840,3 +840,29 @@ function dual0forms_body(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}, refd, bn LagrangeBasis{1,0,3}(refd, bfs, pos) end + + + +function lagrangecx(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}; order) + + T = coordtype(mesh) + NF = binomial(2+order, 2) + P = vertextype(mesh) + S = Shape{T} + + fns = Vector{Vector{S}}(undef, length(mesh) * NF) + pos = Vector{P}(undef, length(mesh) * NF) + + idx = 1 + u = one(T) + for (c,cell) in enumerate(mesh) + ch = chart(mesh,cell) + for r in 1:NF + fns[idx] = S[S(c,r,u)] + pos[idx] = cartesian(center(ch)) + idx += 1 + end + end + + return LagrangeBasis{order,-1,NF}(mesh, fns, pos) +end \ No newline at end of file diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index aef55c13..3c50d202 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -1,7 +1,7 @@ # T: coeff type # Degree: degree # Dim1: dimension of the support + 1 -mutable struct LagrangeRefSpace{T,Degree,Dim1,NF} <: RefSpace{T,NF} end +struct LagrangeRefSpace{T,Degree,Dim1,NF} <: RefSpace{T,NF} end numfunctions(s::LagrangeRefSpace{T,D,2}) where {T,D} = D+1 numfunctions(s::LagrangeRefSpace{T,0,3}) where {T} = 1 @@ -26,10 +26,6 @@ end # Evaluete linear lagrange elements on a triangle function (f::LagrangeRefSpace{T,1,3})(t) where T u,v,w, = barycentric(t) - # SVector( - # (value=u,), - # (value=v,), - # (value=w,)) j = jacobian(t) p = t.patch @@ -41,24 +37,6 @@ function (f::LagrangeRefSpace{T,1,3})(t) where T end -""" - f(tangent_space, Val{:withcurl}) - -Compute the values of the shape functions together with their curl. -""" -# function (f::LagrangeRefSpace{T,1,3})(t, ::Type{Val{:withcurl}}) where T -# # Evaluete linear Lagrange elements on a triange, together with their curl -# j = jacobian(t) -# u,v,w, = barycentric(t) -# p = t.patch -# σ = sign(dot(normal(t), cross(p[1]-p[3],p[2]-p[3]))) -# SVector( -# (value=u, curl=σ*(p[3]-p[2])/j), -# (value=v, curl=σ*(p[1]-p[3])/j), -# (value=w, curl=σ*(p[2]-p[1])/j) -# ) -# end - # Evaluate constant Lagrange elements on a triangle, with their curls function (f::LagrangeRefSpace{T,0,3})(t, ::Type{Val{:withcurl}}) where T @@ -156,7 +134,6 @@ end function restrict(refs::LagrangeRefSpace{T,0}, dom1, dom2) where T - #Q = eye(T, numfunctions(refs)) Q = Matrix{T}(I, numfunctions(refs), numfunctions(refs)) end @@ -193,10 +170,6 @@ function (f::LagrangeRefSpace{T,2,3})(t) where T j = jacobian(t) p = t.patch - #curl=(p[3]-p[2])/j), - # (value=v, curl=(p[1]-p[3])/j), - # (value=w, curl=(p[2]-p[1])/j) - σ = sign(dot(normal(t), cross(p[1]-p[3],p[2]-p[3]))) SVector( (value=u*(2*u-1), curl=σ*(p[3]-p[2])*(4u-1)/j), @@ -208,22 +181,6 @@ function (f::LagrangeRefSpace{T,2,3})(t) where T ) end -# function (f::LagrangeRefSpace{T,2,3})(t, ::Type{Val{:withcurl}}) where T -# # Evaluete quadratic Lagrange elements on a triange, together with their curl -# j = jacobian(t) -# u,v,w, = barycentric(t) -# p = t.patch - -# σ = sign(dot(normal(t), cross(p[1]-p[3],p[2]-p[3]))) -# SVector( -# (value=u*(2*u-1), curl=σ*(p[3]-p[2])*(4u-1)/j), -# (value=v*(2*v-1), curl=σ*(p[1]-p[3])*(4v-1)/j), -# (value=w*(2*w-1), curl=σ*(p[2]-p[1])*(4w-1)/j), -# (value=4*v*w, curl=4*σ*(w*(p[1]-p[3])+v*(p[2]-p[1]))/j), -# (value=4*w*u, curl=4*σ*(w*(p[3]-p[2])+u*(p[2]-p[1]))/j), -# (value=4*u*v, curl=4*σ*(u*(p[1]-p[3])+v*(p[3]-p[2]))/j), -# ) -# end function curl(ref::LagrangeRefSpace{T,2,3} where {T}, sh, el) #curl of lagc0d2 as combination of bdm functions @@ -293,17 +250,11 @@ end const _dof_lag0perm_matrix = [ @SMatrix[1], # 1. {1,2,3} - @SMatrix[1], # 2. {2,3,1} - @SMatrix[1], # 3. {3,1,2} - @SMatrix[1], # 4. {2,1,3} - @SMatrix[1], # 5. {1,3,2} - @SMatrix[1] # 6. {3,2,1} - ] function (ϕ::LagrangeRefSpace{T, 1, 4, 4})(lag) where T @@ -330,3 +281,127 @@ function (ϕ::LagrangeRefSpace{T, 1, 4, 4})(lag) where T (value = T(1.0)-u-v-w, gradient = A*gr4) )) end + + + +# Evaluate higher order Lagrange elements on triangles +# TODO: Optimise using code generation +function (ϕ::LagrangeRefSpace{T,Degree,3})(p) where {T,Degree} + + u, v = parametric(p) + w = 1 - u - v + idx = 0 + + suppdim = 2 + localdim = binomial(suppdim+Degree, suppdim) + vals = T[] + diffus = T[] + diffvs = T[] + + D1 = Degree + 1 + for i in 0:Degree + ui = i/Degree + for j in 0:Degree + vj = j/Degree + for k in 0:Degree + wk = k/Degree + i + j + k == Degree || continue + + val = one(T) + for p in 0:Degree + p == i && continue + up = p/Degree + for q in 0:Degree + q == j && continue + vq = q/Degree + for r in 0:Degree + r == k && continue + wr = r/Degree + val *= (u-up) * (v-vq) * (w-wr) / ((ui - up)* (vj - vq) * (wk - wr)) + end end end + push!(vals, val) + + diffu = zero(T) + for l in 0:Degree + l == i && continue + ul = l/Degree + terml = one(T) + for p in 0:Degree + p == l && continue + p == i && continue + up = p/Degree + for q in 0:Degree + q == j && continue + vq = q/Degree + for r in 0:Degree + r == k && continue + wr = r/Degree + terml *= (u - up) * (v - vq) * (w - wr) / ((ui - up) * (vj - vq) * (wk - wr)) + end end end + diffu += terml / (ui - ul) + end + for l in 0:Degree + l == k && continue + wl = l/Degree + terml = one(T) + for p in 0:Degree + p == i && continue + up = p/Degree + for q in 0:Degree + q == j && continue + vq = q/Degree + for r in 0:Degree + r == l && continue + r == k && continue + wr = r/Degree + terml *= (u - up) * (v - vq) * (w - wr) / ((ui - up) * (vj - vq) * (wk - wr)) + end end end + diffu -= terml / (wk - wl) + end + push!(diffus, diffu) + + + diffv = zero(T) + for l in 0:Degree + l == j && continue + vl = l/Degree + terml = one(T) + for p in 0:Degree + p == i && continue + up = p/Degree + for q in 0:Degree + q == l && continue + q == j && continue + vq = q/Degree + for r in 0:Degree + r == k && continue + wr = r/Degree + terml *= (u - up) * (v - vq) * (w - wr) / ((ui - up) * (vj - vq) * (wk - wr)) + end end end + diffv += terml / (vj - vl) + end + for l in 0:Degree + l == k && continue + wl = l/Degree + terml = one(T) + for p in 0:Degree + p == i && continue + up = p/Degree + for q in 0:Degree + q == j && continue + vq = q/Degree + for r in 0:Degree + r == l && continue + r == k && continue + wr = r/Degree + terml *= (u - up) * (v - vq) * (w - wr) / ((ui - up) * (vj - vq) * (wk -wr)) + end end end + diffv -= terml / (wk - wl) + end + push!(diffvs, diffv) + + idx += 1 + end end end + + [(value=f, curl=point(T,-dv,du)) for (f,du,dv) in zip(vals, diffus, diffvs)] +end \ No newline at end of file diff --git a/test/test_higher_order_lagrange_functions.jl b/test/test_higher_order_lagrange_functions.jl new file mode 100644 index 00000000..dfb12b1f --- /dev/null +++ b/test/test_higher_order_lagrange_functions.jl @@ -0,0 +1,31 @@ +@testitem "lagrangecx order=3 - global" begin + using CompScienceMeshes + + projectdir = joinpath(dirname(pathof(BEAST)),"..") + m = readmesh(joinpath(projectdir, "test/assets/sphere2.in")) + + lagspace3 = BEAST.lagrangecx(m, order=3) + @test numfunctions(lagspace3) == 10 * length(m) + @test refspace(lagspace3) == BEAST.LagrangeRefSpace{Float64,3,3,10}() +end + +@testitem "lagrangecx order=3 - local" begin + using CompScienceMeshes + + s = simplex( + point(1,0,0), + point(0,1,0), + point(0,0,0), + ) + p = center(s) + + ϕ = BEAST.LagrangeRefSpace{Float64,3,3,10}() + A = ϕ(p) + + # test the dimension + @test length(A) == binomial(5,2) + + # test the partition of unity property + valp = sum(a.value for a in A) + @test valp ≈ 1 +end \ No newline at end of file From 1a5a16d8b7e791856c543bd18fd775eb3a162ec8 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 13 Sep 2024 10:17:43 +0200 Subject: [PATCH 395/528] div-conf push forward --- src/bases/local/laglocal.jl | 5 +++- test/test_higher_order_lagrange_functions.jl | 28 +++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 3c50d202..f19d9e10 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -403,5 +403,8 @@ function (ϕ::LagrangeRefSpace{T,Degree,3})(p) where {T,Degree} idx += 1 end end end - [(value=f, curl=point(T,-dv,du)) for (f,du,dv) in zip(vals, diffus, diffvs)] + tu = tangents(p,1) + tv = tangents(p,2) + j = jacobian(p) + [(value=f, curl=(-dv*tu+du*tv)/j) for (f,du,dv) in zip(vals, diffus, diffvs)] end \ No newline at end of file diff --git a/test/test_higher_order_lagrange_functions.jl b/test/test_higher_order_lagrange_functions.jl index dfb12b1f..5877fea9 100644 --- a/test/test_higher_order_lagrange_functions.jl +++ b/test/test_higher_order_lagrange_functions.jl @@ -9,9 +9,10 @@ @test refspace(lagspace3) == BEAST.LagrangeRefSpace{Float64,3,3,10}() end -@testitem "lagrangecx order=3 - local" begin +@testitem "lagrangecxd3: local, ref" begin using CompScienceMeshes + T = Float64 s = simplex( point(1,0,0), point(0,1,0), @@ -25,6 +26,31 @@ end # test the dimension @test length(A) == binomial(5,2) + # test the partition of unity property + valp = sum(a.value for a in A) + crlp = sum(a.curl for a in A) + @test valp ≈ 1 + @test crlp ≈ point(0,0,0) atol=sqrt(eps(T)) + @show crlp +end + + +@testitem "lagrangecxd3: local, generic simplex" begin + using CompScienceMeshes + + s = simplex( + point(3,0,0), + point(0,2,1), + point(-1,-1,-1), + ) + p = center(s) + + ϕ = BEAST.LagrangeRefSpace{Float64,3,3,10}() + A = ϕ(p) + + # test the dimension + @test length(A) == binomial(5,2) + # test the partition of unity property valp = sum(a.value for a in A) @test valp ≈ 1 From 592026c23760ac7be85dce4fe7a7422ef5e8aa00 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 16 Sep 2024 12:08:13 +0200 Subject: [PATCH 396/528] bug from eval for LagrangeHO TODO: debug curl --- examples/hh3d_dirichlet_order3.jl | 64 ++++++++++++++++++++ src/bases/local/laglocal.jl | 50 +++++++++++---- src/quadrature/quadstrats.jl | 27 ++++++--- src/solvers/itsolver.jl | 2 +- test/test_higher_order_lagrange_functions.jl | 64 ++++++++++++++++++++ 5 files changed, 186 insertions(+), 21 deletions(-) create mode 100644 examples/hh3d_dirichlet_order3.jl diff --git a/examples/hh3d_dirichlet_order3.jl b/examples/hh3d_dirichlet_order3.jl new file mode 100644 index 00000000..ffe7230a --- /dev/null +++ b/examples/hh3d_dirichlet_order3.jl @@ -0,0 +1,64 @@ +using CompScienceMeshes, BEAST + +o, x, y, z = euclidianbasis(3) + + +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) + +X = BEAST.lagrangecx(Γ; order=1) +# X = subdsurface(Γ) +# X = raviartthomas(Γ) +@show numfunctions(X) + +κ = 1.0; γ = im*κ +a = Helmholtz3D.singlelayer(wavenumber=κ) +# b = Helmholtz3D.doublelayer_transposed(gamma=κ*im) +0.5Identity() + +BEAST.@defaultquadstrat (a,X,X) BEAST.DoubleNumSauterQstrat(7,8,6,6,6,6) +BEAST.@defaultquadstrat (f,X) BEAST.SingleNumQStrat(12) + +uⁱ = Helmholtz3D.planewave(wavenumber=κ, direction=z) +f = strace(uⁱ,Γ) +# g = ∂n(uⁱ) + +@hilbertspace u +@hilbertspace v + +eq1 = @discretise a[v,u] == f[v] u∈X v∈X +# eq2 = @discretise b[v,u] == g[v] u∈X v∈X + +x1 = gmres(eq1; tol=1e-3) +# x2 = gmres(eq2) + +fcr1, geo1 = facecurrents(x1, X) +# fcr2, geo2 = facecurrents(x2, X) + +# include(Pkg.dir("CompScienceMeshes","examples","plotlyjs_patches.jl")) +using LinearAlgebra +using Plotly +pt1 = Plotly.plot(patch(geo1, real.(norm.(fcr1)))) +# pt1 = Plotly.plot(patch(geo1, ones(length(geo1)))) +# pt2 = Plotly.plot(patch(geo2, real.(norm.(fcr2)))); +display(pt1) + +# using Test + +# ## test the results +# Z = assemble(a,X,X); +# m1, m2 = 1, numfunctions(X) +# chm, chn = chart(Γ,cells(Γ)[m1]), chart(Γ,cells(Γ)[m2]) +# ctm, ctn = CompScienceMeshes.center(chm), CompScienceMeshes.center(chn) +# R = norm(cartesian(ctm)-cartesian(ctn)) +# G = exp(-im*κ*R)/(4π*R) +# Wmn = volume(chm) * volume(chn) * G +# @show abs(Wmn-Z[m1,m2]) / abs(Z[m1,m2]) +# @test abs(Wmn-Z[m1,m2]) / abs(Z[m1,m2]) < 2.0e-3 + +# r = assemble(f,X) +# m1 = 1 +# chm = chart(Γ,cells(Γ)[m1]) +# ctm = CompScienceMeshes.center(chm) +# sm = volume(chm) * f(ctm) +# r[m1] +# @show abs(sm - r[m1]) / abs(r[m1]) +# @test abs(sm - r[m1]) / abs(r[m1]) < 1e-3 diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index f19d9e10..2427525f 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -308,17 +308,18 @@ function (ϕ::LagrangeRefSpace{T,Degree,3})(p) where {T,Degree} i + j + k == Degree || continue val = one(T) - for p in 0:Degree - p == i && continue - up = p/Degree - for q in 0:Degree - q == j && continue - vq = q/Degree - for r in 0:Degree - r == k && continue - wr = r/Degree - val *= (u-up) * (v-vq) * (w-wr) / ((ui - up)* (vj - vq) * (wk - wr)) - end end end + for p in 0:i-1 + up = p / Degree + val *= (u-up) / (ui-up) + end + for q in 0:j-1 + vq = q / Degree + val *= (v-vq) / (vj-vq) + end + for r in 0:k-1 + wr = r / Degree + val *= (w-wr) / (wk-wr) + end push!(vals, val) diffu = zero(T) @@ -406,5 +407,30 @@ function (ϕ::LagrangeRefSpace{T,Degree,3})(p) where {T,Degree} tu = tangents(p,1) tv = tangents(p,2) j = jacobian(p) - [(value=f, curl=(-dv*tu+du*tv)/j) for (f,du,dv) in zip(vals, diffus, diffvs)] + NF = length(vals) + SVector{NF}([(value=f, curl=(-dv*tu+du*tv)/j) for (f,du,dv) in zip(vals, diffus, diffvs)]) +end + +# fields[i] ≈ sum(Q[j,i] * interpolant[j].value for j in 1:numfunctions(interpolant)) +function interpolate(fields, interpolant::LagrangeRefSpace{T,Degree,3}, chart) where {T,Degree} + + dim = binomial(2+Degree, Degree) + + I = 0:Degree + s = range(0,1,length=Degree+1) + Is = zip(I,s) + idx = 1 + vals = [] + for (i,ui) in Is + for (j,vj) in Is + for (k,wk) in Is + i + j + k == Degree || continue + @assert ui + vj + wk ≈ 1 + p = neighborhood(chart, (ui,vj)) + push!(vals, fields(p)) + idx += 1 + end end end + + Q = hcat(vals...) + return Q end \ No newline at end of file diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index 534f9a46..dba1fc33 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -34,16 +34,27 @@ end defaultquadstrat(op, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bfs)) macro defaultquadstrat(dop, body) @assert dop.head == :tuple - @assert length(dop.args) == 3 - op = dop.args[1] - tfs = dop.args[2] - bfs = dop.args[3] - ex = quote - function BEAST.defaultquadstrat(::typeof($op), ::typeof($tfs), ::typeof($bfs)) - $body + if length(dop.args) == 3 + op = dop.args[1] + tfs = dop.args[2] + bfs = dop.args[3] + ex = quote + function BEAST.defaultquadstrat(::typeof($op), ::typeof($tfs), ::typeof($bfs)) + $body + end end + return esc(ex) + elseif length(dop.args) == 2 + lin = dop.args[1] + tfs = dop.args[2] + ex = quote + function BEAST.defaultquadstrat(::typeof($lin), ::typeof($tfs)) + $body + end + end + return esc(ex) end - return esc(ex) + error("@defaultquadstrat expects a first argument of the for (op,tfs,bfs) or (linform,tfs)") end struct SingleNumQStrat diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index 4ccc1941..ea49e8d5 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -110,7 +110,7 @@ function gmres_ch(eq::DiscreteEquation; maxiter=0, restart=0, tol=0) if tol == 0 invZ = GMRESSolver(Z, maxiter=maxiter, restart=restart) else - invZ = GMRESSolver(Z, maxiter=maxiter, restart=restart, tol=tol) + invZ = GMRESSolver(Z, maxiter=maxiter, restart=restart, reltol=tol) end x, ch = solve(invZ, b) # x = invZ * b diff --git a/test/test_higher_order_lagrange_functions.jl b/test/test_higher_order_lagrange_functions.jl index 5877fea9..32780c11 100644 --- a/test/test_higher_order_lagrange_functions.jl +++ b/test/test_higher_order_lagrange_functions.jl @@ -54,4 +54,68 @@ end # test the partition of unity property valp = sum(a.value for a in A) @test valp ≈ 1 +end + +@testitem "lagrangecxd3: self-interpolate" begin + using CompScienceMeshes + using LinearAlgebra + + ϕ = BEAST.LagrangeRefSpace{Float64,3,3,10}() + ch = simplex( + point(3,0,0), + point(0,2,1), + point(-1,-1,-1)) + ch1toch2 = simplex( + point(1,0), + point(0,1), + point(0,0)) + + Q = BEAST.interpolate(ϕ, ch, ϕ, ch, ch1toch2) + # display(round.(Q, digits=3)) + Id = Matrix{Float64}(I, 10, 10) + @test Q ≈ Id +end + + +@testitem "lagrnacexcd3: interpolate generic poly" begin + + using CompScienceMeshes + using LinearAlgebra + + ϕ = BEAST.LagrangeRefSpace{Float64,3,3,10}() + ch = simplex( + point(3,0,0), + point(0,2,1), + point(-1,-1,-1)) + + function fields(p) + u, v = parametric(p) + return [ + 1 + u + v + u^2 + u*v + v^2 + u^3 + u^2*v + u*v^2 + v^3, + 1 + u + u^2 + u^3, + 1 + u + u^2, + 1 + u, + 1 + ] + end + + Q = BEAST.interpolate(fields, ϕ, ch) + nbds = [ + neighborhood(ch, (0.2, 0.2)), + neighborhood(ch, (0.2, 0.6)), + neighborhood(ch, (0.6, 0.2)), + neighborhood(ch, (1/3, 1/3)), + neighborhood(ch, (0.0, 0.0))] + + for p in nbds + basis = ϕ(p) + + vals = fields(p) + for j in eachindex(vals) + val1 = vals[j] + val2 = sum(Q[j,i] * b.value for (i,b) in enumerate(basis)) + @show val1 val2 + end + println() + end end \ No newline at end of file From 49e78ec9b7de372150f39a5f4cd39803fd01eb83 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 23 Sep 2024 15:57:48 +0200 Subject: [PATCH 397/528] debugged computation of curl for HO Lag space --- examples/hh3d_dirichlet_order3.jl | 11 +- src/bases/basis.jl | 21 +++ src/bases/lagrange.jl | 178 +++++++++++++++++++ src/bases/local/laglocal.jl | 114 ++++-------- src/solvers/solver.jl | 5 + test/test_higher_order_lagrange_functions.jl | 86 ++++++++- 6 files changed, 335 insertions(+), 80 deletions(-) diff --git a/examples/hh3d_dirichlet_order3.jl b/examples/hh3d_dirichlet_order3.jl index ffe7230a..6866a778 100644 --- a/examples/hh3d_dirichlet_order3.jl +++ b/examples/hh3d_dirichlet_order3.jl @@ -5,7 +5,7 @@ o, x, y, z = euclidianbasis(3) Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -X = BEAST.lagrangecx(Γ; order=1) +X = BEAST.lagrangecx(Γ; order=3) # X = subdsurface(Γ) # X = raviartthomas(Γ) @show numfunctions(X) @@ -24,10 +24,15 @@ f = strace(uⁱ,Γ) @hilbertspace u @hilbertspace v -eq1 = @discretise a[v,u] == f[v] u∈X v∈X +# eq1 = @discretise a[v,u] == f[v] u∈X v∈X # eq2 = @discretise b[v,u] == g[v] u∈X v∈X -x1 = gmres(eq1; tol=1e-3) +A = assemble(a[v,u], X, X) +b = assemble(f[v], X) +x1 = AbstractMatrix(A) \ b +# x1 = BEAST.GMRESSolver(A; reltol=1e-10) * b + +x1 = gmres(eq1; tol=1e-6) # x2 = gmres(eq2) fcr1, geo1 = facecurrents(x1, X) diff --git a/src/bases/basis.jl b/src/bases/basis.jl index 833cb946..e2bee21d 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -395,3 +395,24 @@ function functionvals(s::BEAST.Space, index::Int, n=3) return ctrs, vals end + + + +function eval(s::BEAST.Space, i::Int, cellid, u) + + ch = chart(cellid, s.geo) + p = neighborhood(ch, u) + + ϕ = refpsace(s) + ϕp = ϕ(p) + + U = valuetype(ϕ, ch) + + r = zero(U) + for sh in s.fns[i] + sh.cellid == cellid || continue + r += sh.coeff * ϕp[sh.refid].value + end + + return r +end \ No newline at end of file diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index a5738abb..30f476a8 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -865,4 +865,182 @@ function lagrangecx(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}; order) end return LagrangeBasis{order,-1,NF}(mesh, fns, pos) +end + + +function lagrangec0(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}; order) + + verts = skeleton(mesh, 0) + edges = skeleton(mesh, 1) + + T = coordtype(mesh) + P = vertextype(mesh) + S = Shape{T} + NF = binomial(2+order, 2) + ϕ = LagrangeRefSpace{T,order,3,NF}() + tol = sqrt(eps(T)) + z, u = zero(T), one(T) + + fns = Vector{S}[] + pos = P[] + conn20 = connectivity(verts, mesh, abs) + rows = rowvals(conn20) + vals = nonzeros(conn20) + for vert in verts + fn = S[] + vert_ch = chart(verts, vert) + vert_dom = domain(vert_ch) + for k in nzrange(conn20, vert) + face = rows[k] + lidx = vals[k] + face_ch = chart(mesh, face) + face_dom = domain(face_ch) + # injection = faces(face_dom, Val{0})[lidx] + injection = simplex(vertices(face_dom)[lidx]) + p = neighborhood(injection, ()) + u = cartesian(p) + ϕu = ϕ(u) + for i in 1:numfunctions(ϕ) + weight = ϕu[i].value + isapprox(0, weight, atol=sqrt(eps(T))) && continue + push!(fn, S(face, i, weight)) + end + end + push!(fns, fn) + push!(pos, cartesian(center(vert_ch))) + end + + conn21 = connectivity(edges, mesh, abs) + rows = rowvals(conn21) + vals = nonzeros(conn21) + for edge in edges + edge_fns = [S[] for i in 1:order-1] + edge_pos = Vector{P}(undef, order-1) + edge_ch = chart(edges, edge) + edge_dom = domain(edge_ch) + for k in nzrange(conn21, edge) + face = rows[k] + lidx = vals[k] + face_ch = chart(mesh, face) + face_dom = domain(face_ch) + local_edge_ch = faces(face_ch)[lidx] + face_ch_verts = vertices(face_ch) + edge_ch_verts = vertices(edge_ch) + perm = [something(findfirst(w->isapprox(v,w,atol=tol), face_ch_verts)) for v in edge_ch_verts] + injection = simplex(vertices(face_dom)[perm]) + ds = one(T) / order + for (j,s) in enumerate(range(ds, step=ds, length=order-1)) + p = neighborhood(injection, (s,)) + u = cartesian(p) + ϕu = ϕ(u) + for (i,val) in enumerate(ϕu) + weight = val.value + isapprox(0, weight, atol=sqrt(eps(T))) && continue + push!(edge_fns[j], S(face, i, weight)) + end + edge_pos[j] = cartesian(neighborhood(edge_ch, s)) + end + end + append!(fns, edge_fns) + append!(pos, edge_pos) + end + + I = 1:order-1 + ds = one(T) / order + for face in mesh + face_fns = [S[] for i in 1:div((order-1)*(order-2),2)] + face_pos = Vector{P}(undef, div((order-1)*(order-2),2)) + face_ch = chart(mesh, face) + face_dom = domain(face_ch) + idx = 1 + for i in I + for j in I + for k in I + i + j + k == order || continue + p = neighborhood(face_dom, (i*ds, j*ds)) + ϕs = ϕ(p) + for (n,ϕn) in enumerate(ϕs) + weight = ϕn.value + isapprox(0, weight, atol=sqrt(eps(T))) && continue + push!(face_fns[idx], S(face, n, weight)) + end + face_pos[idx] = cartesian(neighborhood(face_ch, (i*ds, j*ds))) + idx += 1 + end + end + end + append!(fns, face_fns) + end + + return LagrangeBasis{order,0,NF}(mesh, fns, pos) +end + +struct _LagrangeGlobalEdgeDoFs end + +function globaldofs_edge(edge_ch, face_ch, fields) + +end + +function lagrangec0_bis(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}; order) + + T = coordtype(mesh) + P = vertextype(mesh) + S = Shape{T} + F = Vector{S} + + verts = skeleton(mesh, 0) + edges = skeleton(mesh, 1) + + C02 = connectivity(mesh, verts); R02 = rowvals(C02) + C12 = connectivity(mesh, edges); R12 = rowvals(C12) + + localspace = LagrangeRefSpace{T,order,3,NF}() + localdim = numfunctions(localspace) + + ne = order-1 + nf = div((order-2)*(order-1), 2) + + nV = length(verts) + nE = length(edges) * ne + nF = length(faces) * nf + function local2global(c) + gids = R02[nzrange(C02,c)] + for e in R12[nzrange(C12,c)] + for p in 1:ne + push!(gids, nV + (e-1)*ne + p) + end + end + for p in 1:nF + push!(gids, nV + nE + (c-1)*nf + p) + end + return gids + end + + # list the vertex supported global DoFs + B = zeros(T, localdim, localdim) + for cell in mesh + i = 1 + gids = localtoglobal(c) + for v in R02[nzrange(C02,c)] + B[i,:] = globaldof(c,v,localspace) + i += 1 + end + for e in R12[nzrange(C12,c)] + for p in 1:ne + B[i,:] = globaldof(c,e,p,localspace) + i += 1 + end + end + for p in 1:nf + B[i,:] = globaldof(c,p,localspace) + i += 1 + end + A = inv(B) + for i in 1:localdim + mi = gids[i] + for j in 1:localdim + push!(fns[mi], S(cell, j, A[i,j])) + end + end + end end \ No newline at end of file diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 2427525f..63808052 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -7,12 +7,15 @@ numfunctions(s::LagrangeRefSpace{T,D,2}) where {T,D} = D+1 numfunctions(s::LagrangeRefSpace{T,0,3}) where {T} = 1 numfunctions(s::LagrangeRefSpace{T,1,3}) where {T} = 3 numfunctions(s::LagrangeRefSpace{T,2,3}) where {T} = 6 +numfunctions(s::LagrangeRefSpace{T,Dg,D1,NF}) where {T,Dg,D1,NF} = NF valuetype(ref::LagrangeRefSpace{T}, charttype) where {T} = SVector{numfunctions(ref), Tuple{T,T}} # Evaluate constant lagrange elements on anything (ϕ::LagrangeRefSpace{T,0})(tp) where {T} = SVector(((value=one(T), derivative=zero(T)),)) +(ϕ::LagrangeRefSpace{T,0,3})(tp) where {T} = SVector(((value=one(T), derivative=zero(T)),)) + # Evaluate linear Lagrange elements on a segment function (f::LagrangeRefSpace{T,1,2})(mp) where T @@ -299,6 +302,7 @@ function (ϕ::LagrangeRefSpace{T,Degree,3})(p) where {T,Degree} diffvs = T[] D1 = Degree + 1 + s = range(zero(T), one(T), length=D1) for i in 0:Degree ui = i/Degree for j in 0:Degree @@ -307,98 +311,58 @@ function (ϕ::LagrangeRefSpace{T,Degree,3})(p) where {T,Degree} wk = k/Degree i + j + k == Degree || continue - val = one(T) + prod_p = one(T) for p in 0:i-1 up = p / Degree - val *= (u-up) / (ui-up) + prod_p *= (u-up) / (ui-up) end + prod_q = one(T) for q in 0:j-1 vq = q / Degree - val *= (v-vq) / (vj-vq) + prod_q *= (v-vq) / (vj-vq) end + prod_r = one(T) for r in 0:k-1 wr = r / Degree - val *= (w-wr) / (wk-wr) + prod_r *= (w-wr) / (wk-wr) end - push!(vals, val) + push!(vals, prod_p * prod_q * prod_r) diffu = zero(T) - for l in 0:Degree - l == i && continue + diffv = zero(T) + for l in 0:i-1 ul = l/Degree - terml = one(T) - for p in 0:Degree + prod_pl = one(T) + for p in 0:i-1 p == l && continue - p == i && continue - up = p/Degree - for q in 0:Degree - q == j && continue - vq = q/Degree - for r in 0:Degree - r == k && continue - wr = r/Degree - terml *= (u - up) * (v - vq) * (w - wr) / ((ui - up) * (vj - vq) * (wk - wr)) - end end end - diffu += terml / (ui - ul) - end - for l in 0:Degree - l == k && continue - wl = l/Degree - terml = one(T) - for p in 0:Degree - p == i && continue up = p/Degree - for q in 0:Degree - q == j && continue - vq = q/Degree - for r in 0:Degree - r == l && continue - r == k && continue - wr = r/Degree - terml *= (u - up) * (v - vq) * (w - wr) / ((ui - up) * (vj - vq) * (wk - wr)) - end end end - diffu -= terml / (wk - wl) + prod_pl *= (u-up) / (ui-up) + end + diffu += prod_pl * prod_q * prod_r / (ui-ul) end - push!(diffus, diffu) - - - diffv = zero(T) - for l in 0:Degree - l == j && continue - vl = l/Degree - terml = one(T) - for p in 0:Degree - p == i && continue - up = p/Degree - for q in 0:Degree - q == l && continue - q == j && continue - vq = q/Degree - for r in 0:Degree - r == k && continue - wr = r/Degree - terml *= (u - up) * (v - vq) * (w - wr) / ((ui - up) * (vj - vq) * (wk - wr)) - end end end - diffv += terml / (vj - vl) + for m in 0:j-1 + vm = m/Degree + prod_qm = one(T) + for q in 0:j-1 + q == m && continue + vq = q/Degree + prod_qm *= (v-vq) / (vj-vq) + end + diffv += prod_p * prod_qm * prod_r / (vj-vm) end - for l in 0:Degree - l == k && continue - wl = l/Degree - terml = one(T) - for p in 0:Degree - p == i && continue - up = p/Degree - for q in 0:Degree - q == j && continue - vq = q/Degree - for r in 0:Degree - r == l && continue - r == k && continue - wr = r/Degree - terml *= (u - up) * (v - vq) * (w - wr) / ((ui - up) * (vj - vq) * (wk -wr)) - end end end - diffv -= terml / (wk - wl) + for n in 0:k-1 + wn = n/Degree + prod_rn = one(T) + for r in 0:k-1 + r == n && continue + wr = r/Degree + prod_rn *= (w-wr) / (wk-wr) + end + diffu -= prod_p * prod_q * prod_rn / (wk-wn) + diffv -= prod_p * prod_q * prod_rn / (wk-wn) end + + push!(diffus, diffu) push!(diffvs, diffv) idx += 1 diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index d1aa30e6..92542222 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -185,6 +185,11 @@ function assemble(lform::LinForm, X::DirectProductSpace) return B end +function assemble(lf::LinForm, X::Space) + @assert length(lf.terms) == 1 + assemble(lf, BEAST.DirectProductSpace([X])) +end + struct SpaceTimeData{T} <: AbstractArray{Vector{T},1} data::Array{T,2} end diff --git a/test/test_higher_order_lagrange_functions.jl b/test/test_higher_order_lagrange_functions.jl index 32780c11..6efa14ef 100644 --- a/test/test_higher_order_lagrange_functions.jl +++ b/test/test_higher_order_lagrange_functions.jl @@ -31,7 +31,29 @@ end crlp = sum(a.curl for a in A) @test valp ≈ 1 @test crlp ≈ point(0,0,0) atol=sqrt(eps(T)) - @show crlp + + u = T(0.2); du = eps(T) * 1000 + v = T(0.6); dv = eps(T) * 1000 + + p00 = neighborhood(s, (u,v)) + p10 = neighborhood(s, (u+du,v)) + p01 = neighborhood(s, (u, v+dv)) + + ϕ00 = ϕ(p00) + ϕ10 = ϕ(p10) + ϕ01 = ϕ(p01) + + tu = tangents(p00,1) + tv = tangents(p00,2) + j = jacobian(p00) + + for (f00, f10, f01) in zip(ϕ00, ϕ10, ϕ01) + dfdu = (f10.value - f00.value)/du + dfdv = (f01.value - f00.value)/dv + curl_num = (-dfdv * tu + dfdu * tv) / j + curl_ana = f00.curl + @test curl_num ≈ curl_ana atol=sqrt(eps(T))*100 + end end @@ -114,8 +136,68 @@ end for j in eachindex(vals) val1 = vals[j] val2 = sum(Q[j,i] * b.value for (i,b) in enumerate(basis)) - @show val1 val2 + @test val1≈val2 atol=1e-8 end println() end +end + +@testitem "lagrangec0 order=3 - global" begin + using CompScienceMeshes + using SparseArrays + order = 3 + + projectdir = joinpath(dirname(pathof(BEAST)),"..") + m = readmesh(joinpath(projectdir, "test/assets/sphere2.in")) + + verts = skeleton(m,0) + edges = skeleton(m,1) + + println(pathof(CompScienceMeshes)) + + lagspace3 = BEAST.lagrangec0(m, order=3) + @test numfunctions(lagspace3) == + length(verts) + + length(edges)*(order-1) + + length(m)*div((order-1)*(order-2),2) + @test refspace(lagspace3) == BEAST.LagrangeRefSpace{Float64,3,3,10}() + + conn20 = connectivity(verts, m) + for i in 1:length(verts) + @test length(lagspace3.fns[i]) == length(nzrange(conn20, i)) + end + + i0 = length(verts) + for i in 1:(order-1)*length(edges) + @test length(lagspace3.fns[i0+i]) == 2 + end + + i0 += (order-1)*length(edges) + for i in 1:length(m)*div((order-1)*(order-2),2) + @test length(lagspace3.fns[i0+i]) == 1 + end +end + + +@testitem "lagrangec0 order=3 - continuity" begin + using CompScienceMeshes + using SparseArrays + order = 3 + + projectdir = joinpath(dirname(pathof(BEAST)),"..") + m = readmesh(joinpath(projectdir, "test/assets/sphere2.in")) + + verts = skeleton(m,0) + edges = skeleton(m,1) + + println(pathof(CompScienceMeshes)) + + lagspace3 = BEAST.lagrangec0(m, order=3) + @test numfunctions(lagspace3) == + length(verts) + + length(edges)*(order-1) + + length(m)*div((order-1)*(order-2),2) + @test refspace(lagspace3) == BEAST.LagrangeRefSpace{Float64,3,3,10}() + + conn20 = connectivity(verts, m) end \ No newline at end of file From 4ec23cf5aebc87bee4438d3bae6fb33fad0f6033 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 27 Sep 2024 14:20:37 +0200 Subject: [PATCH 398/528] continuous higher order scalar spaces --- examples/hh3d_dirichlet_order3.jl | 8 +- examples/hh3d_neumann order3.jl | 56 ++++ src/BEAST.jl | 1 + src/bases/global/globaldofs.jl | 148 ++++++++++ src/bases/lagrange.jl | 284 +++++++++---------- test/test_higher_order_lagrange_functions.jl | 143 ++++++++-- 6 files changed, 474 insertions(+), 166 deletions(-) create mode 100644 examples/hh3d_neumann order3.jl create mode 100644 src/bases/global/globaldofs.jl diff --git a/examples/hh3d_dirichlet_order3.jl b/examples/hh3d_dirichlet_order3.jl index 6866a778..9205026a 100644 --- a/examples/hh3d_dirichlet_order3.jl +++ b/examples/hh3d_dirichlet_order3.jl @@ -14,13 +14,13 @@ X = BEAST.lagrangecx(Γ; order=3) a = Helmholtz3D.singlelayer(wavenumber=κ) # b = Helmholtz3D.doublelayer_transposed(gamma=κ*im) +0.5Identity() -BEAST.@defaultquadstrat (a,X,X) BEAST.DoubleNumSauterQstrat(7,8,6,6,6,6) -BEAST.@defaultquadstrat (f,X) BEAST.SingleNumQStrat(12) - uⁱ = Helmholtz3D.planewave(wavenumber=κ, direction=z) f = strace(uⁱ,Γ) # g = ∂n(uⁱ) +BEAST.@defaultquadstrat (a,X,X) BEAST.DoubleNumSauterQstrat(7,8,6,6,6,6) +BEAST.@defaultquadstrat (f,X) BEAST.SingleNumQStrat(12) + @hilbertspace u @hilbertspace v @@ -32,7 +32,7 @@ b = assemble(f[v], X) x1 = AbstractMatrix(A) \ b # x1 = BEAST.GMRESSolver(A; reltol=1e-10) * b -x1 = gmres(eq1; tol=1e-6) +# x1 = gmres(eq1; tol=1e-6) # x2 = gmres(eq2) fcr1, geo1 = facecurrents(x1, X) diff --git a/examples/hh3d_neumann order3.jl b/examples/hh3d_neumann order3.jl new file mode 100644 index 00000000..14e8070e --- /dev/null +++ b/examples/hh3d_neumann order3.jl @@ -0,0 +1,56 @@ +using CompScienceMeshes, BEAST +using LinearAlgebra, Pkg + +# Pkg.activate(@__DIR__) +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +# Γ = meshrectangle(1.0, 1.0, 0.05, 3) +X = BEAST.lagrangec0(Γ; order=2) +@show numfunctions(X) + +κ = 2π; γ = im*κ +a = Helmholtz3D.hypersingular(gamma=γ) +# b = Helmholtz3D.doublelayer(gamma=γ) - 0.5Identity() + +uⁱ = Helmholtz3D.planewave(wavenumber=κ, direction=ẑ) +# f = strace(uⁱ,Γ) +g = ∂n(uⁱ) + +BEAST.@defaultquadstrat (a,X,X) BEAST.DoubleNumSauterQstrat(7,8,6,6,6,6) +BEAST.@defaultquadstrat (g,X) BEAST.SingleNumQStrat(12) + +@hilbertspace u +@hilbertspace v + +A = assemble(a[v,u], X, X) +b = assemble(g[v], X) +x1 = AbstractMatrix(A) \ b + +# eq1 = @discretise a[v,u] == g[v] u∈X v∈X +# eq2 = @discretise b[v,u] == f[v] u∈X v∈X + +# x1 = gmres(eq1) +# x2 = gmres(eq2) + +# @assert norm(x1-x2)/norm(x1+x2) < 0.5e-2 + +fcr1, geo1 = facecurrents(x1, X) +# fcr2, geo2 = facecurrents(x2, X) + +using Plots +Plots.plot(title="Comparse 1st and 2nd kind eqs.") +Plots.plot!(norm.(fcr1),c=:blue,label="1st") +# Plots.scatter!(norm.(fcr2),c=:red,label="2nd") + +import Plotly +plt1 = Plotly.plot(patch(Γ, norm.(fcr1))) +# plt2 = Plotly.plot(patch(Γ, norm.(fcr2))) +# display([plt1 plt2]) + +# ys = range(-2,2,length=200) +# zs = range(-2,2,length=200) +# pts = [point(0.5, y, z) for y in ys, z in zs] + +# nf = BEAST.HH3DDoubleLayerNear(wavenumber=κ) +# near = BEAST.potential(nf, pts, x1, X, type=ComplexF64) +# inc = uⁱ.(pts) +# tot = near + inc diff --git a/src/BEAST.jl b/src/BEAST.jl index d14a5ee2..10568c72 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -147,6 +147,7 @@ include("bases/lincomb.jl") include("bases/trace.jl") include("bases/restrict.jl") include("bases/divergence.jl") +include("bases/global/globaldofs.jl") include("bases/local/laglocal.jl") include("bases/local/rtlocal.jl") diff --git a/src/bases/global/globaldofs.jl b/src/bases/global/globaldofs.jl new file mode 100644 index 00000000..be974975 --- /dev/null +++ b/src/bases/global/globaldofs.jl @@ -0,0 +1,148 @@ +function trace(edge_ch, face_ch, localspace::RefSpace) + T = coordtype(edge_ch) + atol = sqrt(eps(T)) + + face_dom = domain(face_ch) + + edge_verts = vertices(edge_ch) + face_verts = vertices(face_ch) + + perm = [something(findfirst(w -> isapprox(v, w; atol), face_verts), 0) for v in edge_verts] + # @show perm + injection = simplex(vertices(face_dom)[perm]) + + function f(s) + u = neighborhood(injection, s) + p = neighborhood(face_ch, cartesian(u)) + localspace(p) + end + + return f +end + +function _lagrangepolynomial(nodes, i, s, i1=length(nodes)) + r = one(T) + si = nodes[i] + for j in 1:i1 + j == i && continue + sj = nodes[j] + r *= (s - sj) / (si - sj) + end + return r +end + +struct _LagrangeGlobalEdgeDoFs + order::Int +end +function numfunctions(dof::_LagrangeGlobalEdgeDoFs) dof.order-1 end +function (dof::_LagrangeGlobalEdgeDoFs)(s) + T = typeof(s) + nodes = range(zero(T), one(T), length=order+1) + [_lagrangepolynomial(nodes, i, s) for i in 2:order] +end + +struct _LagrangeGlobalFaceDoFs + order::Int +end +# function numfunctions(dof::_LagrangeGlobalFaceDoFs) div((order-1)*(order-2),2) end +# function (dof::_LagrangeGlobalFaceDoFs)(s) +# T = eltype(s) +# nodes = range(zero(T), one(T), length=order+1) +# r = zeros(T, numfunctions(dof)) +# s1, s2 = s +# s3 = 1 - s1 - s2 +# idx = 1 +# degree = dof.degree +# for i in 0:degree +# prodi = _lagrangepolynomial(nodes, i+1, s1, i) +# for j in 0:degree +# k = degree - i - j +# k < 0 && continue +# prodj = _lagrangepolynomial(nodes, j+1, s2, j) +# prodk = _lagrangepolynomial(nodes, k+1, s3, k) +# r[idx] = prodi * prodj * prodk +# end end +# return r +# end + +function globaldofs(edge_ch, face_ch, localspace, dof::_LagrangeGlobalEdgeDoFs) + + T = coordtype(edge_ch) + f = trace(edge_ch, face_ch, localspace) + # r = zero(numfunctions(localspace), numfunctions(dof)) + # for (s,w) in CompScienceMeshes.quadpoints(edge_dom, 2*order) + # u = neighborhood(injection, s) + # p = neighborhood(face_ch, cartesian(u)) + # r .+= w * [x.value for x in localspace(p)] * dof(s)' + # end + + ds = one(T) / dof.order + return stack(range(ds, step=ds, length=dof.order-1)) do s + [x.value for x in f(s)] + end +end + + +function globaldofs(edge_ch, face_ch, localspace, dof::_LagrangeGlobalFaceDoFs) + + T = coordtype(edge_ch) + f = trace(edge_ch, face_ch, localspace) + # r = zero(numfunctions(localspace), numfunctions(dof)) + # for (s,w) in CompScienceMeshes.quadpoints(edge_dom, 2*order) + # u = neighborhood(injection, s) + # p = neighborhood(face_ch, cartesian(u)) + # r .+= w * [x.value for x in localspace(p)] * dof(s)' + # end + + d = dof.order + S = ((i,j,d-i-j) for i in 0:d for j in 0:d if (i+j < d && i > 0 && j > 0)) + return stack(S) do s + s = (s[1]/d,s[2]/d) + [x.value for x in f(s)] + end +end + +struct _LagrangeGlobalNodesDoFs + order::Int +end + +function globaldofs(edge_ch, face_ch, localspace, dof::_LagrangeGlobalNodesDoFs) + f = trace(edge_ch, face_ch, localspace) + return stack([()]) do s + [x.value for x in f(s)] + end +end + + +@testitem "globaldofs: interpolatory on edge" begin + using CompScienceMeshes + + T = Float64 + face_ch = simplex( + point(3,0,0), + point(2,0,-1), + point(0,0,1), + ) + edge_ch = simplex( + point(0,0,1), + point(2,0,-1), + ) + + localspace = BEAST.LagrangeRefSpace{T,3,3,10}() + L = BEAST._LagrangeGlobalEdgeDoFs(3) + dofs = BEAST.globaldofs(edge_ch, face_ch, localspace, L) + + @test size(dofs) == (10,2) + A = [ + 0 0 + 0 1 + 1 0 + 0 0 + 0 0 + 0 0 + 0 0 + 0 0 + 0 0 + 0 0] + @test dofs ≈ A +end \ No newline at end of file diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 30f476a8..a0bbb336 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -868,122 +868,96 @@ function lagrangecx(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}; order) end -function lagrangec0(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}; order) - verts = skeleton(mesh, 0) - edges = skeleton(mesh, 1) +""" + localindices(dof, chart, i) - T = coordtype(mesh) - P = vertextype(mesh) - S = Shape{T} - NF = binomial(2+order, 2) - ϕ = LagrangeRefSpace{T,order,3,NF}() - tol = sqrt(eps(T)) - z, u = zero(T), one(T) - - fns = Vector{S}[] - pos = P[] - conn20 = connectivity(verts, mesh, abs) - rows = rowvals(conn20) - vals = nonzeros(conn20) - for vert in verts - fn = S[] - vert_ch = chart(verts, vert) - vert_dom = domain(vert_ch) - for k in nzrange(conn20, vert) - face = rows[k] - lidx = vals[k] - face_ch = chart(mesh, face) - face_dom = domain(face_ch) - # injection = faces(face_dom, Val{0})[lidx] - injection = simplex(vertices(face_dom)[lidx]) - p = neighborhood(injection, ()) - u = cartesian(p) - ϕu = ϕ(u) - for i in 1:numfunctions(ϕ) - weight = ϕu[i].value - isapprox(0, weight, atol=sqrt(eps(T))) && continue - push!(fn, S(face, i, weight)) - end - end - push!(fns, fn) - push!(pos, cartesian(center(vert_ch))) - end +Returns a vector of indices into the vector of local shape functions that correspond to +global degrees of freedom supported on sub-entity `i`, where the type of entity (nodes, +edge, face) is encoded in the type of 'dof'. +""" +function localindices(dof::_LagrangeGlobalNodesDoFs, chart::CompScienceMeshes.Simplex, + localspace, i) - conn21 = connectivity(edges, mesh, abs) - rows = rowvals(conn21) - vals = nonzeros(conn21) - for edge in edges - edge_fns = [S[] for i in 1:order-1] - edge_pos = Vector{P}(undef, order-1) - edge_ch = chart(edges, edge) - edge_dom = domain(edge_ch) - for k in nzrange(conn21, edge) - face = rows[k] - lidx = vals[k] - face_ch = chart(mesh, face) - face_dom = domain(face_ch) - local_edge_ch = faces(face_ch)[lidx] - face_ch_verts = vertices(face_ch) - edge_ch_verts = vertices(edge_ch) - perm = [something(findfirst(w->isapprox(v,w,atol=tol), face_ch_verts)) for v in edge_ch_verts] - injection = simplex(vertices(face_dom)[perm]) - ds = one(T) / order - for (j,s) in enumerate(range(ds, step=ds, length=order-1)) - p = neighborhood(injection, (s,)) - u = cartesian(p) - ϕu = ϕ(u) - for (i,val) in enumerate(ϕu) - weight = val.value - isapprox(0, weight, atol=sqrt(eps(T))) && continue - push!(edge_fns[j], S(face, i, weight)) - end - edge_pos[j] = cartesian(neighborhood(edge_ch, s)) - end - end - append!(fns, edge_fns) - append!(pos, edge_pos) + d = dof.order + [div((d+1)*(d+2),2), d+1, 1][[i]] +end + +function localindices(dof::_LagrangeGlobalEdgeDoFs, chart::CompScienceMeshes.Simplex, + localspace, i) + + d = dof.order + lids, lid = Int[], 0 + if i == 1 + for i in 0:d for j in 0:d + k = d - i - j + k < 0 && continue + lid += 1 + i != 0 && continue + j in (0,d) && continue + push!(lids, lid) + end end + elseif i == 2 + for i in 0:d for j in 0:d + k = d - i - j + k < 0 && continue + lid += 1 + i in (0,d) && continue + j != 0 && continue + push!(lids, lid) + end end + elseif i ==3 + for i in 0:d for j in 0:d + k = d - i - j + k < 0 && continue + lid += 1 + j in (0,d) && continue + k != 0 && continue + push!(lids, lid) + end end + else + error("wrong local edge index") end + return lids +end - I = 1:order-1 - ds = one(T) / order - for face in mesh - face_fns = [S[] for i in 1:div((order-1)*(order-2),2)] - face_pos = Vector{P}(undef, div((order-1)*(order-2),2)) - face_ch = chart(mesh, face) - face_dom = domain(face_ch) - idx = 1 - for i in I - for j in I - for k in I - i + j + k == order || continue - p = neighborhood(face_dom, (i*ds, j*ds)) - ϕs = ϕ(p) - for (n,ϕn) in enumerate(ϕs) - weight = ϕn.value - isapprox(0, weight, atol=sqrt(eps(T))) && continue - push!(face_fns[idx], S(face, n, weight)) - end - face_pos[idx] = cartesian(neighborhood(face_ch, (i*ds, j*ds))) - idx += 1 - end - end - end - append!(fns, face_fns) +function localindices(dof::_LagrangeGlobalFaceDoFs, chart::CompScienceMeshes.Simplex, + localspace, i) + + d = dof.order + lids, lid = Int[], 0 + for i in 0:d, j in 0:d + k = d - i - j + k < 0 && continue + lid += 1 + (i == 0 || j == 0 || k == 0) && continue + # @show (i,j,k) + push!(lids, lid) end + return lids +end - return LagrangeBasis{order,0,NF}(mesh, fns, pos) +function localindices(dof::_LagrangeGlobalNodesDoFs, chart::CompScienceMeshes.Simplex, + localspace::LagrangeRefSpace{<:Real,2}, i) + return [i] end -struct _LagrangeGlobalEdgeDoFs end +function localindices(dof::_LagrangeGlobalEdgeDoFs, chart::CompScienceMeshes.Simplex, + localspace::LagrangeRefSpace{<:Real,2}, i) + return [3+i] +end -function globaldofs_edge(edge_ch, face_ch, fields) - +function localindices(dof::_LagrangeGlobalFaceDoFs, chart::CompScienceMeshes.Simplex, + localspace::LagrangeRefSpace{<:Real,2}, i) + return [] end -function lagrangec0_bis(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}; order) + +function lagrangec0(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}; order) T = coordtype(mesh) + atol = sqrt(eps(T)) + P = vertextype(mesh) S = Shape{T} F = Vector{S} @@ -991,56 +965,82 @@ function lagrangec0_bis(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}; order) verts = skeleton(mesh, 0) edges = skeleton(mesh, 1) - C02 = connectivity(mesh, verts); R02 = rowvals(C02) - C12 = connectivity(mesh, edges); R12 = rowvals(C12) - - localspace = LagrangeRefSpace{T,order,3,NF}() - localdim = numfunctions(localspace) + C02 = connectivity(mesh, verts, abs); R02 = rowvals(C02); V02 = nonzeros(C02) + C12 = connectivity(mesh, edges, abs); R12 = rowvals(C12); V12 = nonzeros(C12) ne = order-1 nf = div((order-2)*(order-1), 2) nV = length(verts) nE = length(edges) * ne - nF = length(faces) * nf - function local2global(c) - gids = R02[nzrange(C02,c)] - for e in R12[nzrange(C12,c)] - for p in 1:ne - push!(gids, nV + (e-1)*ne + p) - end - end - for p in 1:nF - push!(gids, nV + nE + (c-1)*nf + p) - end - return gids - end + nF = length(mesh) * nf + N = nV + nE + nF - # list the vertex supported global DoFs - B = zeros(T, localdim, localdim) + localspace = LagrangeRefSpace{T,order,3,binomial(2+order,2)}() + localdim = numfunctions(localspace) + + d = order + fns = [S[] for n in 1:(nV+nE+nF)] + pos = fill(point(0,0,0), nV+nE+nF) for cell in mesh - i = 1 - gids = localtoglobal(c) - for v in R02[nzrange(C02,c)] - B[i,:] = globaldof(c,v,localspace) - i += 1 - end - for e in R12[nzrange(C12,c)] - for p in 1:ne - B[i,:] = globaldof(c,e,p,localspace) - i += 1 + cell_ch = chart(mesh, cell) + V = R02[nzrange(C02,cell)] + I = V02[nzrange(C02,cell)] + for (i,v) in zip(I, V) + vertex_ch = chart(verts, v) + gids = [v] + lids = localindices(_LagrangeGlobalNodesDoFs(d), cell_ch, localspace, i) + v = globaldofs(vertex_ch, cell_ch, localspace, _LagrangeGlobalNodesDoFs(d)) + α = v[lids, :] + β = inv(α') + for i in axes(β,1) + for j in axes(β,2) + isapprox(β[i,j], 0; atol) && continue + push!(fns[gids[i]], S(cell, lids[j], β[i,j])) + end end end - for p in 1:nf - B[i,:] = globaldof(c,p,localspace) - i += 1 - end - A = inv(B) - for i in 1:localdim - mi = gids[i] - for j in 1:localdim - push!(fns[mi], S(cell, j, A[i,j])) + + E = R12[nzrange(C12,cell)] + I = V12[nzrange(C12,cell)] + for (i,e) in zip(I, E) + edge_ch = chart(edges, e) + gids = nV + (e-1)*ne + 1: nV + e*ne + lids = localindices(_LagrangeGlobalEdgeDoFs(d), cell_ch, localspace, i) + @assert length(lids) == length(gids) + v = globaldofs(edge_ch, cell_ch, localspace, _LagrangeGlobalEdgeDoFs(d)) + α = v[lids, :] + β = inv(α') + for i in axes(β,1) + for j in axes(β,2) + isapprox(β[i,j], 0; atol) && continue + push!(fns[gids[i]], S(cell, lids[j], β[i,j])) + end end end - end + + order < 3 && continue + F = [cell] + for (q,f) in enumerate(F) + face_ch = chart(mesh, f) + gids = nV + nE + (f-1)*nf + 1 : nV + nE + f*nf + lids = localindices(_LagrangeGlobalFaceDoFs(d), cell_ch, localspace, 1) + v = globaldofs(face_ch, cell_ch, localspace, _LagrangeGlobalFaceDoFs(d)) + α = v[lids, :] + # @show α + β = inv(α') + for i in axes(β,1) + for j in axes(β,2) + isapprox(β[i,j], 0; atol) && continue + push!(fns[gids[i]], S(cell, lids[j], β[i,j])) + end + end + end + end + + for v in verts pos[v] = cartesian(center(chart(verts, v))) end + for e in edges pos[nV + (e-1)*ne + 1: nV + e*ne] .= Ref(cartesian(center(chart(edges, e)))) end + for f in mesh pos[nV + nE + (f-1)*nf + 1: nV + nE + f*nf] .= Ref(cartesian(center(chart(mesh, f)))) end + + return LagrangeBasis{order,0,localdim}(mesh, fns, pos) end \ No newline at end of file diff --git a/test/test_higher_order_lagrange_functions.jl b/test/test_higher_order_lagrange_functions.jl index 6efa14ef..5f6bab8d 100644 --- a/test/test_higher_order_lagrange_functions.jl +++ b/test/test_higher_order_lagrange_functions.jl @@ -9,6 +9,55 @@ @test refspace(lagspace3) == BEAST.LagrangeRefSpace{Float64,3,3,10}() end +@testitem "lagrangecxd2: local, ref" begin + using CompScienceMeshes + + T = Float64 + s = simplex( + point(1,0,0), + point(0,1,0), + point(0,0,0), + ) + p = CompScienceMeshes.center(s) + + ϕ = BEAST.LagrangeRefSpace{Float64,2,3,6}() + A = ϕ(p) + + # test the dimension + @test length(A) == binomial(4,2) + + # test the partition of unity property + valp = sum(a.value for a in A) + crlp = sum(a.curl for a in A) + @test valp ≈ 1 + @test crlp ≈ point(0,0,0) atol=sqrt(eps(T)) + + u = T(0.2); du = eps(T) * 1000 + v = T(0.6); dv = eps(T) * 1000 + + p00 = neighborhood(s, (u,v)) + p10 = neighborhood(s, (u+du,v)) + p01 = neighborhood(s, (u, v+dv)) + + ϕ00 = ϕ(p00) + ϕ10 = ϕ(p10) + ϕ01 = ϕ(p01) + + # @show [x.value for x in ϕ(neighborhood(s, (0.0, 0.0)))] + + tu = tangents(p00,1) + tv = tangents(p00,2) + j = jacobian(p00) + + for (f00, f10, f01) in zip(ϕ00, ϕ10, ϕ01) + dfdu = (f10.value - f00.value)/du + dfdv = (f01.value - f00.value)/dv + curl_num = (dfdv * tu - dfdu * tv) / j + curl_ana = f00.curl + @test curl_num ≈ curl_ana atol=sqrt(eps(T))*100 + end +end + @testitem "lagrangecxd3: local, ref" begin using CompScienceMeshes @@ -142,11 +191,12 @@ end end end -@testitem "lagrangec0 order=3 - global" begin + +@testitem "lagc0d2: support size" begin using CompScienceMeshes using SparseArrays - order = 3 + order = 2 projectdir = joinpath(dirname(pathof(BEAST)),"..") m = readmesh(joinpath(projectdir, "test/assets/sphere2.in")) @@ -155,31 +205,26 @@ end println(pathof(CompScienceMeshes)) - lagspace3 = BEAST.lagrangec0(m, order=3) + lagspace3 = BEAST.lagrangec0(m, order=order) @test numfunctions(lagspace3) == length(verts) + length(edges)*(order-1) + length(m)*div((order-1)*(order-2),2) - @test refspace(lagspace3) == BEAST.LagrangeRefSpace{Float64,3,3,10}() + @test refspace(lagspace3) == BEAST.LagrangeRefSpace{Float64,2,3,6}() - conn20 = connectivity(verts, m) - for i in 1:length(verts) - @test length(lagspace3.fns[i]) == length(nzrange(conn20, i)) - end - - i0 = length(verts) - for i in 1:(order-1)*length(edges) - @test length(lagspace3.fns[i0+i]) == 2 - end + conn20 = connectivity(verts, m, x -> 1) + num_adjacent_faces = vec(sum(conn20, dims=1)) - i0 += (order-1)*length(edges) - for i in 1:length(m)*div((order-1)*(order-2),2) - @test length(lagspace3.fns[i0+i]) == 1 - end + nv = length(verts) + ne = length(edges) * (order-1) + nf = length(m) * div((order-1) * (order-2),2) + @test length.(lagspace3.fns[1:length(verts)]) == num_adjacent_faces + @test all(length.(lagspace3.fns[nv+1:nv+ne]) .== 2) + @test all(length.(lagspace3.fns[nv+ne+1:nv+ne+nf]) .== 1) end -@testitem "lagrangec0 order=3 - continuity" begin +@testitem "lagc0d3: support size" begin using CompScienceMeshes using SparseArrays order = 3 @@ -199,5 +244,63 @@ end length(m)*div((order-1)*(order-2),2) @test refspace(lagspace3) == BEAST.LagrangeRefSpace{Float64,3,3,10}() - conn20 = connectivity(verts, m) -end \ No newline at end of file + conn20 = connectivity(verts, m, x -> 1) + num_adjacent_faces = vec(sum(conn20, dims=1)) + + nv = length(verts) + ne = length(edges) * (order-1) + nf = length(m) * div((order-1) * (order-2),2) + @test length.(lagspace3.fns[1:nv]) == num_adjacent_faces + @test all(length.(lagspace3.fns[nv+1:nv+ne]) .== 2) + @test all(length.(lagspace3.fns[nv+ne+1:nv+ne+nf]) .== 1) +end + + +# @testitem "lagrangec0 order=3 - continuity" begin +# using CompScienceMeshes +# using SparseArrays +# order = 3 + +# projectdir = joinpath(dirname(pathof(BEAST)),"..") +# m = readmesh(joinpath(projectdir, "test/assets/sphere2.in")) + +# verts = skeleton(m,0) +# edges = skeleton(m,1) + +# println(pathof(CompScienceMeshes)) + +# lagspace3 = BEAST.lagrangec0(m, order=3) +# @test numfunctions(lagspace3) == +# length(verts) + +# length(edges)*(order-1) + +# length(m)*div((order-1)*(order-2),2) +# @test refspace(lagspace3) == BEAST.LagrangeRefSpace{Float64,3,3,10}() + +# conn20 = connectivity(verts, m) +# end + + +# @testitem "Lagc0d3: alternative construction" begin +# using CompScienceMeshes +# using SparseArrays +# import Main.InteractiveUtils +# order = 3 + +# projectdir = joinpath(dirname(pathof(BEAST)),"..") +# m = readmesh(joinpath(projectdir, "test/assets/sphere2.in")) + +# verts = skeleton(m,0) +# edges = skeleton(m,1) + +# println(pathof(CompScienceMeshes)) + +# lagspace3 = BEAST.lagrangec0(m, order=3) +# @test numfunctions(lagspace3) == +# length(verts) + +# length(edges)*(order-1) + +# length(m)*div((order-1)*(order-2),2) +# # @show InteractiveUtils.@which refspace(lagspace3) +# @test refspace(lagspace3) == BEAST.LagrangeRefSpace{Float64,3,3,10}() + +# conn20 = connectivity(verts, m) +# end \ No newline at end of file From b12990ba82b155e0e44a1291ee58b33e4e49e634 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 1 Oct 2024 14:44:10 +0200 Subject: [PATCH 399/528] numfunctions now requires both the refspace and refdomain --- src/BEAST.jl | 17 ++-- src/bases/basis.jl | 18 ++-- src/bases/global/globaldofs.jl | 19 +--- src/bases/local/bdm3dlocal.jl | 2 +- src/bases/local/bdmlocal.jl | 2 +- src/bases/local/gwplocal.jl | 76 +++++++++++++++ src/bases/local/laglocal.jl | 20 ++-- src/bases/local/ncrossbdmlocal.jl | 2 +- src/bases/local/nd2local.jl | 4 +- src/bases/local/ndlcclocal.jl | 6 +- src/bases/local/ndlcdlocal.jl | 6 +- src/bases/local/ndlocal.jl | 5 +- src/bases/local/rt2local.jl | 3 +- src/bases/local/rtlocal.jl | 4 +- src/bases/local/rtqlocal.jl | 2 +- src/bases/localbasis.jl | 29 ++++++ src/bases/subdbasis.jl | 2 +- src/bases/timebasis.jl | 4 +- src/excitation.jl | 10 +- src/helmholtz3d/timedomain/tdhh3dops.jl | 24 ++--- src/helmholtz3d/wiltonints.jl | 92 +++++++++++++------ src/integralop.jl | 31 +++++-- src/localop.jl | 37 +++++++- src/maxwell/timedomain/mwtdops.jl | 14 ++- src/maxwell/wiltonints.jl | 14 ++- src/postproc.jl | 4 +- src/quadrature/rules/testrefinestrialqrule.jl | 10 +- src/quadrature/rules/trialrefinestestqrule.jl | 10 +- src/quadrature/sauterschwabints.jl | 5 +- src/timedomain/tdexcitation.jl | 16 +++- src/timedomain/tdintegralop.jl | 7 +- src/utils/lagpolys.jl | 45 +++++++++ test/test_basis.jl | 6 +- test/test_gwp.jl | 12 +++ test/test_rt.jl | 3 +- test/test_ss_nested_meshes.jl | 28 +++--- test/test_tdassembly.jl | 8 +- test/test_tdhhdbl.jl | 6 +- test/test_wiltonints.jl | 2 +- 39 files changed, 449 insertions(+), 156 deletions(-) create mode 100644 src/bases/local/gwplocal.jl create mode 100644 src/bases/localbasis.jl create mode 100644 src/utils/lagpolys.jl create mode 100644 test/test_gwp.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 10568c72..96d4fc5d 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -141,14 +141,9 @@ include("utils/combinatorics.jl") include("utils/linearspace.jl") include("utils/zeromap.jl") include("utils/rank1map.jl") +include("utils/lagpolys.jl") -include("bases/basis.jl") -include("bases/lincomb.jl") -include("bases/trace.jl") -include("bases/restrict.jl") -include("bases/divergence.jl") -include("bases/global/globaldofs.jl") - +include("bases/localbasis.jl") include("bases/local/laglocal.jl") include("bases/local/rtlocal.jl") include("bases/local/rt2local.jl") @@ -160,6 +155,14 @@ include("bases/local/ndlcclocal.jl") include("bases/local/ndlcdlocal.jl") include("bases/local/bdm3dlocal.jl") include("bases/local/rtqlocal.jl") +include("bases/local/gwplocal.jl") + +include("bases/basis.jl") +include("bases/lincomb.jl") +include("bases/trace.jl") +include("bases/restrict.jl") +include("bases/divergence.jl") +include("bases/global/globaldofs.jl") include("bases/lagrange.jl") include("bases/rtspace.jl") diff --git a/src/bases/basis.jl b/src/bases/basis.jl index e2bee21d..e75a0733 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -1,5 +1,4 @@ -abstract type RefSpace{T,D} end -abstract type DivRefSpace{T,D} <: RefSpace{T,D} end + abstract type AbstractSpace end abstract type Space{T} <: AbstractSpace end @@ -25,12 +24,12 @@ Returns the ReferenceSpace of local shape functions on which the basis is built. function refspace end -""" - numfunctions(r) +# """ +# numfunctions(r) -Return the number of functions in a `Space` or `RefSpace`. -""" -numfunctions(rs::RefSpace{T,D}) where {T,D} = D +# Return the number of functions in a `Space` or `RefSpace`. +# """ +# numfunctions(rs::RefSpace{T,D}) where {T,D} = D """ @@ -208,7 +207,10 @@ function assemblydata(basis::Space; onlyactives=true) num_cells = numcells(geo) num_bfs = numfunctions(basis) - num_refs = numfunctions(refspace(basis)) + + ch = chart(geo, first(geo)) + dom = domain(ch) + num_refs = numfunctions(refspace(basis), dom) # Determine the number of functions defined over a given cell # and per local shape function. diff --git a/src/bases/global/globaldofs.jl b/src/bases/global/globaldofs.jl index be974975..b736bb1c 100644 --- a/src/bases/global/globaldofs.jl +++ b/src/bases/global/globaldofs.jl @@ -20,16 +20,7 @@ function trace(edge_ch, face_ch, localspace::RefSpace) return f end -function _lagrangepolynomial(nodes, i, s, i1=length(nodes)) - r = one(T) - si = nodes[i] - for j in 1:i1 - j == i && continue - sj = nodes[j] - r *= (s - sj) / (si - sj) - end - return r -end + struct _LagrangeGlobalEdgeDoFs order::Int @@ -38,7 +29,7 @@ function numfunctions(dof::_LagrangeGlobalEdgeDoFs) dof.order-1 end function (dof::_LagrangeGlobalEdgeDoFs)(s) T = typeof(s) nodes = range(zero(T), one(T), length=order+1) - [_lagrangepolynomial(nodes, i, s) for i in 2:order] + [_lagpoly(nodes, i, s) for i in 2:order] end struct _LagrangeGlobalFaceDoFs @@ -54,12 +45,12 @@ end # idx = 1 # degree = dof.degree # for i in 0:degree -# prodi = _lagrangepolynomial(nodes, i+1, s1, i) +# prodi = _lagpoly(nodes, i+1, s1, i) # for j in 0:degree # k = degree - i - j # k < 0 && continue -# prodj = _lagrangepolynomial(nodes, j+1, s2, j) -# prodk = _lagrangepolynomial(nodes, k+1, s3, k) +# prodj = _lagpoly(nodes, j+1, s2, j) +# prodk = _lagpoly(nodes, k+1, s3, k) # r[idx] = prodi * prodj * prodk # end end # return r diff --git a/src/bases/local/bdm3dlocal.jl b/src/bases/local/bdm3dlocal.jl index 9b1694ea..02b3b02a 100644 --- a/src/bases/local/bdm3dlocal.jl +++ b/src/bases/local/bdm3dlocal.jl @@ -1,4 +1,4 @@ -struct BDM3DRefSpace{T} <: RefSpace{T,12} end +struct BDM3DRefSpace{T} <: RefSpace{T} end function (f::BDM3DRefSpace)(p) diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index a07e42b6..1eb173a4 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -1,4 +1,4 @@ -struct BDMRefSpace{T} <: RefSpace{T,6} end +struct BDMRefSpace{T} <: RefSpace{T} end function (f::BDMRefSpace)(p) diff --git a/src/bases/local/gwplocal.jl b/src/bases/local/gwplocal.jl new file mode 100644 index 00000000..a0788f4a --- /dev/null +++ b/src/bases/local/gwplocal.jl @@ -0,0 +1,76 @@ +struct GWPCurlRefSpace{T,Degree} <: RefSpace{T} end + +function (ϕ::GWPCurlRefSpace{T,Degree})(p) where {T,Degree} + dom = domain(chart(p)) + u = parametric(p) + vals = ϕ(dom, u) + pushforwardcurl(vals, p) +end + +function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Dim}, + u) where {T,Deg,Dim} + + d = Deg + u, v = u + w = 1-u-v + + s = range(zero(T), one(T), length=d+3) + # rwg1 = point(T, u-1, v) + # rwg2 = point(T, u, v-1) + # rwg3 = point(T, u, v) + nd1 = point(T, -v, u-1) + nd2 = point(T, -v+1, u) + nd3 = point(T, -v, u) + + P = SVector{2,T} + vals = P[] + dffu = T[] + dffv = T[] + + i = 0 + for j in 1:d+1 + k = (d+2)-i-j + Rᵢ = _sylpoly_shift(s, i+1, u) + Rⱼ = _sylpoly(s, j+1, v) + Rₖ = _sylpoly(s, k+1, w) + push!(vals, Rᵢ*Rⱼ*Rₖ*nd1) + + # du = _sylpoly_shift_diff(s, i+1, u)*Rⱼ*Rₖ + end + + for i in 1:d+1 + j = 0 + k = (d+2)-i-j + Rᵢ = _sylpoly(s, i+1, u) + Rⱼ = _sylpoly_shift(s, j+1, v) + Rₖ = _sylpoly(s, k+1, w) + push!(vals, Rᵢ*Rⱼ*Rₖ*nd2) + end + + for i in 1:d+1 + j = (d+2)-i + k = 0 + Rᵢ = _sylpoly(s, i+1, u) + Rⱼ = _sylpoly(s, j+1, v) + Rₖ = _sylpoly_shift(s, k+1, w) + push!(vals, Rᵢ*Rⱼ*Rₖ*nd3) + end + + for i in 1:d+1 + for j in 1:d+1 + k = (d+2)-i-j + k <= 0 && continue + Rsᵢ = _sylpoly_shift(s, i+1, u) + Rsⱼ = _sylpoly_shift(s, j+1, v) + Rᵢ = _sylpoly(s, i+1, u) + Rⱼ = _sylpoly(s, j+1, v) + Rₖ = _sylpoly(s, k+1, w) + push!(vals, Rsᵢ*Rⱼ*Rₖ*nd1) + push!(vals, Rᵢ*Rsⱼ*Rₖ*nd2) + end end + + NF = length(vals) + SVector{NF}([(value=f, curl=zero(T)) for f in vals]) +end + + diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 63808052..cd90f2c3 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -1,13 +1,13 @@ # T: coeff type # Degree: degree # Dim1: dimension of the support + 1 -struct LagrangeRefSpace{T,Degree,Dim1,NF} <: RefSpace{T,NF} end +struct LagrangeRefSpace{T,Degree,Dim1,NF} <: RefSpace{T} end -numfunctions(s::LagrangeRefSpace{T,D,2}) where {T,D} = D+1 -numfunctions(s::LagrangeRefSpace{T,0,3}) where {T} = 1 -numfunctions(s::LagrangeRefSpace{T,1,3}) where {T} = 3 -numfunctions(s::LagrangeRefSpace{T,2,3}) where {T} = 6 -numfunctions(s::LagrangeRefSpace{T,Dg,D1,NF}) where {T,Dg,D1,NF} = NF +numfunctions(s::LagrangeRefSpace{T,D,2}, ch::CompScienceMeshes.ReferenceSimplex{1}) where {T,D} = D+1 +numfunctions(s::LagrangeRefSpace{T,0,3}, ch::CompScienceMeshes.ReferenceSimplex{2}) where {T} = 1 +numfunctions(s::LagrangeRefSpace{T,1,3}, ch::CompScienceMeshes.ReferenceSimplex{2}) where {T} = 3 +numfunctions(s::LagrangeRefSpace{T,2,3}, ch::CompScienceMeshes.ReferenceSimplex{2}) where {T} = 6 +numfunctions(s::LagrangeRefSpace{T,Dg}, ch::CompScienceMeshes.ReferenceSimplex{D}) where {T,Dg,D} = binomial(D+Dg,Dg) valuetype(ref::LagrangeRefSpace{T}, charttype) where {T} = SVector{numfunctions(ref), Tuple{T,T}} @@ -110,7 +110,8 @@ function strace(x::LagrangeRefSpace, cell, localid, face) vals1 = x(P1) vals2 = x(P2) - for j in 1:numfunctions(x) + num_shapes = numfunctions(x, domain(cell)) + for j in 1:num_shapes Q[1,j] = vals1[j].value Q[2,j] = vals2[j].value end @@ -137,12 +138,13 @@ end function restrict(refs::LagrangeRefSpace{T,0}, dom1, dom2) where T - Q = Matrix{T}(I, numfunctions(refs), numfunctions(refs)) + n = numfunctions(refs, domain(dom1)) + Q = Matrix{T}(I, n, n) end function restrict(f::LagrangeRefSpace{T,1}, dom1, dom2) where T - D = numfunctions(f) + D = numfunctions(f, domain(dom1)) Q = zeros(T, D, D) # for each point of the new domain diff --git a/src/bases/local/ncrossbdmlocal.jl b/src/bases/local/ncrossbdmlocal.jl index b7dc7504..5f7fc211 100644 --- a/src/bases/local/ncrossbdmlocal.jl +++ b/src/bases/local/ncrossbdmlocal.jl @@ -1,4 +1,4 @@ -struct NCrossBDMRefSpace{T} <: RefSpace{T,6} end +struct NCrossBDMRefSpace{T} <: RefSpace{T} end function (f::NCrossBDMRefSpace{T})(p) where T diff --git a/src/bases/local/nd2local.jl b/src/bases/local/nd2local.jl index 2a250df0..31dd94c0 100644 --- a/src/bases/local/nd2local.jl +++ b/src/bases/local/nd2local.jl @@ -1,5 +1,5 @@ -mutable struct ND2RefSpace{T} <: RefSpace{T,8} end +mutable struct ND2RefSpace{T} <: RefSpace{T} end function (ϕ::ND2RefSpace)(nbd) @@ -26,3 +26,5 @@ function (ϕ::ND2RefSpace)(nbd) )) end + +numfunctions(x::ND2RefSpace, dom::CompScienceMeshes.ReferenceSimplex{2}) = 8 \ No newline at end of file diff --git a/src/bases/local/ndlcclocal.jl b/src/bases/local/ndlcclocal.jl index ca18947b..37edb79c 100644 --- a/src/bases/local/ndlcclocal.jl +++ b/src/bases/local/ndlcclocal.jl @@ -1,4 +1,4 @@ -struct NDLCCRefSpace{T} <: RefSpace{T,6} end +struct NDLCCRefSpace{T} <: RefSpace{T} end function valuetype(ref::NDLCCRefSpace{T}, charttype::Type) where {T} SVector{universedimension(charttype),T} @@ -58,6 +58,8 @@ function (ϕ::NDLCCRefSpace)(ndlc) ## )) end +numfunctions(x::NDLCCRefSpace, dom::CompScienceMeshes.ReferenceSimplex{3}) = 6 + #check orientation function curl(ref::NDLCCRefSpace, sh, el) a = [4,2,3,4,1,2]##[2,1,1,1,2,4]#[2,1,4,4,3,2]#[4,2,3,4,1,2] @@ -71,7 +73,7 @@ end function restrict(ϕ::NDLCCRefSpace{T}, dom1, dom2) where {T} # dom2 is the smaller of the domains - K = numfunctions(ϕ) + K = numfunctions(ϕ, domain(dom1)) D = dimension(dom1) @assert K == 6 diff --git a/src/bases/local/ndlcdlocal.jl b/src/bases/local/ndlcdlocal.jl index ba4cf8ae..1ecc2ad6 100644 --- a/src/bases/local/ndlcdlocal.jl +++ b/src/bases/local/ndlcdlocal.jl @@ -1,4 +1,4 @@ -struct NDLCDRefSpace{T} <: RefSpace{T,4} end +struct NDLCDRefSpace{T} <: RefSpace{T} end function valuetype(ref::NDLCDRefSpace{T}, charttype::Type) where {T} SVector{universedimension(charttype),T} @@ -30,6 +30,8 @@ function (ϕ::NDLCDRefSpace)(ndlc) )) end +numfunctions(x::NDLCDRefSpace, dom::CompScienceMeshes.ReferenceSimplex{3}) = 4 + function ntrace(x::NDLCDRefSpace, el, q, fc) t = zeros(scalartype(x),1,4) t[q] = 1 / volume(fc) @@ -82,7 +84,7 @@ divergence(ref::NDLCDRefSpace, sh, el) = Shape(sh.cellid, 1, sh.coeff/volume(el) function restrict(ϕ::NDLCDRefSpace{T}, dom1, dom2) where {T} # dom2 is the smaller of the domains - K = numfunctions(ϕ) + K = numfunctions(ϕ, domain(dom1)) D = dimension(dom1) @assert K == 4 diff --git a/src/bases/local/ndlocal.jl b/src/bases/local/ndlocal.jl index 9f0c196f..a4b3bd17 100644 --- a/src/bases/local/ndlocal.jl +++ b/src/bases/local/ndlocal.jl @@ -6,7 +6,7 @@ This is not the edge starting at vertex `r`. The downside of this local numberin scheme is that it cannot be extended to cells that are not simplices because there is no well defined concept of adjacent-ness. """ -mutable struct NDRefSpace{T} <: RefSpace{T,3} end +mutable struct NDRefSpace{T} <: RefSpace{T} end function (ϕ::NDRefSpace)(nbd) @@ -27,10 +27,11 @@ function (ϕ::NDRefSpace)(nbd) end +numfunctions(x::NDRefSpace, dom::CompScienceMeshes.ReferenceSimplex{2}) = 3 function restrict(ϕ::NDRefSpace{T}, dom1, dom2) where T - K = numfunctions(ϕ) + K = numfunctions(ϕ, domain(dom1)) D = dimension(dom1) @assert K == 3 diff --git a/src/bases/local/rt2local.jl b/src/bases/local/rt2local.jl index aeaa2c57..bbecede9 100644 --- a/src/bases/local/rt2local.jl +++ b/src/bases/local/rt2local.jl @@ -1,4 +1,4 @@ -struct RT2RefSpace{T} <: RefSpace{T,8} end +struct RT2RefSpace{T} <: RefSpace{T} end function (f::RT2RefSpace)(p) @@ -33,6 +33,7 @@ function (f::RT2RefSpace)(p) ) end +numfunctions(x::RT2RefSpace, dom::CompScienceMeshes.ReferenceSimplex{2}) = 8 function interpolate(fields, interpolant::BEAST.RT2RefSpace, chart) diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index 61e19eac..7c16d6f8 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -1,4 +1,4 @@ -struct RTRefSpace{T} <: DivRefSpace{T,3} end +struct RTRefSpace{T} <: DivRefSpace{T} end # valuetype(ref::RTRefSpace{T}, charttype) where {T} = SVector{3,Tuple{SVector{universedimension(charttype),T},T}} function valuetype(ref::RTRefSpace{T}, charttype::Type) where {T} @@ -26,6 +26,8 @@ function (ϕ::RTRefSpace)(mp) )) end +numfunctions(x::RTRefSpace, dom::CompScienceMeshes.ReferenceSimplex{2}) = 3 + divergence(ref::RTRefSpace, sh, el) = Shape(sh.cellid, 1, sh.coeff/volume(el)) """ diff --git a/src/bases/local/rtqlocal.jl b/src/bases/local/rtqlocal.jl index bc196d95..f7e38347 100644 --- a/src/bases/local/rtqlocal.jl +++ b/src/bases/local/rtqlocal.jl @@ -1,4 +1,4 @@ -struct RTQuadRefSpace{T} <: DivRefSpace{T,4} end +struct RTQuadRefSpace{T} <: DivRefSpace{T} end function (ϕ::RTQuadRefSpace{T})(p) where {T} diff --git a/src/bases/localbasis.jl b/src/bases/localbasis.jl new file mode 100644 index 00000000..33110983 --- /dev/null +++ b/src/bases/localbasis.jl @@ -0,0 +1,29 @@ +abstract type RefSpace{T} end +abstract type DivRefSpace{T} <: RefSpace{T} end + +function pushforwardcurl(vals, nbhd) + tu = tangents(nbhd, 1) + tv = tangents(nbhd, 2) + j = jacobian(nbhd) + n = normal(nbhd) + map(vals) do v + f = v.value + σ = v.curl + gu = +f[2] + gv = -f[1] + (value=cross(n, (gu*tu + gv*tv)/j), curl=σ/j) + end +end + + +function pushforwarddiv(vals, nbhd) + tu = tangents(nbhd, 1) + tv = tangents(nbhd, 2) + j = jacobian(nbhd) + n = normal(nbhd) + map(vals) do v + f = v.value + σ = v.curl + (value=(f[1]*tu + f[2]*tv)/j, curl=σ/j) + end +end \ No newline at end of file diff --git a/src/bases/subdbasis.jl b/src/bases/subdbasis.jl index bda73d31..9556cd47 100644 --- a/src/bases/subdbasis.jl +++ b/src/bases/subdbasis.jl @@ -14,7 +14,7 @@ function subset(s::S,I) where {S<:subdBasis} end -mutable struct subReferenceSpace{T,D} <: RefSpace{T,D} end +mutable struct subReferenceSpace{T,D} <: RefSpace{T} end refspace(s::subdBasis{T,M,P}) where {T,M,P} = subReferenceSpace{T,12}() # 12 only for regular case diff --git a/src/bases/timebasis.jl b/src/bases/timebasis.jl index dedd754e..4f7fd1ed 100644 --- a/src/bases/timebasis.jl +++ b/src/bases/timebasis.jl @@ -2,7 +2,7 @@ using Compat -mutable struct MonomialBasis{T,Degree,NF} <: RefSpace{T,NF} end +mutable struct MonomialBasis{T,Degree,NF} <: RefSpace{T} end valuetype(::MonomialBasis{T}) where {T} = T degree(::MonomialBasis{T,D}) where {T,D} = D @@ -66,7 +66,7 @@ mutable struct TimeBasisDelta{T} <: AbstractTimeBasisFunction numfunctions::Int end -mutable struct DiracBoundary{T} <: RefSpace{T,1} end +mutable struct DiracBoundary{T} <: RefSpace{T} end numfunctions(x::DiracBoundary) = 1 scalartype(x::TimeBasisDelta{T}) where {T} = T diff --git a/src/excitation.jl b/src/excitation.jl index 165894a7..f194a86c 100644 --- a/src/excitation.jl +++ b/src/excitation.jl @@ -42,13 +42,17 @@ function assemble!(field::Functional, tfs::Space, store; trefs = refspace(tfs) qd = quaddata(field, trefs, tels, quadstrat) + tgeo = geometry(tfs) + tdom = domain(chart(tgeo, first(tgeo))) + num_trefs = numfunctions(trefs, tdom) + for (t, tcell) in enumerate(tels) # compute the testing with the reference elements qr = quadrule(field, trefs, t, tcell, qd, quadstrat) blocal = celltestvalues(trefs, tcell, field, qr) - for i in 1 : numfunctions(trefs) + for i in 1 : num_trefs for (m,a) in tad[t,i] store(a*blocal[i], m) end @@ -82,9 +86,9 @@ function assemble!(field::Functional, tfs::subdBasis, store; end -function celltestvalues(tshs::RefSpace{T,NF}, tcell, field, qr) where {T,NF} +function celltestvalues(tshs::RefSpace{T}, tcell, field, qr) where {T,NF} - num_tshs = numfunctions(tshs) + num_tshs = numfunctions(tshs, domain(tcell)) interactions = zeros(Complex{T}, num_tshs) num_oqp = length(qr) diff --git a/src/helmholtz3d/timedomain/tdhh3dops.jl b/src/helmholtz3d/timedomain/tdhh3dops.jl index afec459a..26911f9b 100644 --- a/src/helmholtz3d/timedomain/tdhh3dops.jl +++ b/src/helmholtz3d/timedomain/tdhh3dops.jl @@ -92,8 +92,8 @@ function innerintegrals!(zlocal, operator::HH3DSingleLayerTDBIO, a = dx / (4*pi) D = operator.num_diffs @assert D == 0 - @assert numfunctions(test_local_space) == 1 - @assert numfunctions(trial_local_space) == 1 + @assert numfunctions(test_local_space, domain(test_element)) == 1 + @assert numfunctions(trial_local_space, domain(trial_element)) == 1 @inline function tmRoR_sl(d, iG) sgn = isodd(d) ? -1 : 1 @@ -139,8 +139,8 @@ function innerintegrals!(zlocal, operator::HH3DHyperSingularTDBIO, trial_element[3], x, r, R, Val{N-1},quad_rule.workspace) - @assert numfunctions(test_local_space) <= 3 - @assert numfunctions(trial_local_space) == 3 + @assert numfunctions(test_local_space, domain(test_element)) <= 3 + @assert numfunctions(trial_local_space, domain(trial_element)) == 3 @inline function tmRoR(d, iG) r = (isodd(d) ? -1 : 1) * iG[d+2] @@ -156,9 +156,9 @@ function innerintegrals!(zlocal, operator::HH3DHyperSingularTDBIO, # weakly singular term α = dx / (4π) * operator.weight_of_weakly_singular_term Ds = operator.num_diffs_on_weakly_singular_term - for i in 1 : numfunctions(test_local_space) + for i in 1 : numfunctions(test_local_space, domain(test_element)) g, curlg = test_values[i] - for j in 1 : numfunctions(trial_local_space) + for j in 1 : numfunctions(trial_local_space, domain(trial_element)) b = trial_element[j] opp_edge = trial_element[mod1(j+2,3)] - trial_element[mod1(j+1,3)] h = norm(opp_edge)/2/volume(trial_element) @@ -175,9 +175,9 @@ function innerintegrals!(zlocal, operator::HH3DHyperSingularTDBIO, # Hyper-singular term β = dx / (4π) * operator.weight_of_hyper_singular_term Dh = operator.num_diffs_on_hyper_singular_term - for i in 1 : numfunctions(test_local_space) + for i in 1 : numfunctions(test_local_space, domain(test_element)) g, curlg = test_values[i] - for j in 1 : numfunctions(trial_local_space) + for j in 1 : numfunctions(trial_local_space, domain(trial_element)) _, curlf = trial_values[j] for k in 1 : numfunctions(time_local_space) d = k - 1 @@ -196,8 +196,8 @@ function innerintegrals!(zlocal, operator::HH3DDoubleLayerTDBIO, test_element, trial_element, time_element, quad_rule, quad_weight) - @assert numfunctions(test_local_space) <= 3 - @assert numfunctions(trial_local_space) == 1 + @assert numfunctions(test_local_space, domain(test_element)) <= 3 + @assert numfunctions(trial_local_space, domain(trial_element)) == 1 dx = quad_weight x = cartesian(test_point) @@ -231,9 +231,9 @@ function innerintegrals!(zlocal, operator::HH3DDoubleLayerTDBIO, # weakly singular term α = dx / (4π) * operator.weight D = operator.num_diffs - for i in 1 : numfunctions(test_local_space) + for i in 1 : numfunctions(test_local_space, domain(test_element)) g, curlg = test_values[i] - for j in 1 : numfunctions(trial_local_space) + for j in 1 : numfunctions(trial_local_space, domain(trial_element)) f, curlf = trial_values[j] for k in 1 : numfunctions(time_local_space) d = k-1 diff --git a/src/helmholtz3d/wiltonints.jl b/src/helmholtz3d/wiltonints.jl index 0921f00a..28772ce1 100644 --- a/src/helmholtz3d/wiltonints.jl +++ b/src/helmholtz3d/wiltonints.jl @@ -52,6 +52,8 @@ function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, s1, s2, s3 = trial_element.vertices t1, t2, t3 = test_elements.vertices + num_tshapes = numfunctions(test_refspace, domain(test_elements)) + num_bshapes = numfunctions(trial_refspace, domain(trial_element)) x = cartesian(test_neighborhood) n = normalize((s1-s3)×(s2-s3)) @@ -60,10 +62,10 @@ function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, scal, vec = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) ∫G = (scal[2] + 0.5*γ^2*scal[4]) / (4π) Atot = 1/2*norm(cross(t3-t1,t3-t2)) - for i in 1:numfunctions(test_refspace) + for i in 1:num_tshapes Ai = 1/2*norm(cross(test_elements.vertices[mod1(i-1,3)]-x,test_elements.vertices[mod1(i+1,3)]-x)) g = Ai/Atot - for j in 1:numfunctions(trial_refspace) + for j in 1:num_bshapes zlocal[i,j] += α * ∫G * g * dx end end @@ -81,6 +83,9 @@ function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, γ = gamma(op) α = op.alpha + num_tshapes = numfunctions(test_refspace, domain(test_elements)) + num_bshapes = numfunctions(trial_refspace, domain(trial_element)) + s1, s2, s3 = trial_element.vertices x = cartesian(test_neighborhood) @@ -90,7 +95,7 @@ function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, ∫Rⁿ, ∫RⁿN = WiltonInts84.higherorder(s1,s2,s3,x,3) ∫G = (∫RⁿN[2] + 0.5*γ^2*∫RⁿN[3]) / (4π) - for i in 1:numfunctions(trial_refspace) + for i in 1:num_bshapes zlocal[1,i] += α * ∫G[i] * dx end @@ -110,6 +115,9 @@ function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, s1, s2, s3 = trial_element.vertices t1, t2, t3 = test_elements.vertices + num_tshapes = numfunctions(test_refspace, domain(test_elements)) + num_bshapes = numfunctions(trial_refspace, domain(trial_element)) + x = cartesian(test_neighborhood) n = normalize((s1-s3)×(s2-s3)) ρ = x - dot(x - s1, n) * n @@ -118,10 +126,10 @@ function innerintegrals!(op::HH3DSingleLayerSng, test_neighborhood, ∫G = (∫RⁿN[2] + 0.5*γ^2*∫RⁿN[3]) / (4π) Atot = 1/2*norm(cross(t3-t1,t3-t2)) - for i in 1:numfunctions(test_refspace) + for i in 1:num_tshapes Ai = 1/2*norm(cross(test_elements.vertices[mod1(i-1,3)]-x,test_elements.vertices[mod1(i+1,3)]-x)) g = Ai/Atot - for j in 1:numfunctions(trial_refspace) + for j in 1:num_bshapes zlocal[i,j] += α * ∫G[j] * g * dx end end @@ -166,12 +174,15 @@ function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, n = normalize((t1-t3)×(t2-t3)) ρ = x - dot(x - s1, n) * n + num_tshapes = numfunctions(test_refspace, domain(test_elements)) + num_bshapes = numfunctions(trial_refspace, domain(trial_element)) + scal, vec, grad = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) ∫∇G = -(grad[1]+0.5*γ^2*grad[3])/(4π) ∫n∇G = dot(n,∫∇G) - for i in 1:numfunctions(test_refspace) - for j in 1:numfunctions(trial_refspace) + for i in 1:num_tshapes + for j in 1:num_bshapes zlocal[i,j] += α * ∫n∇G * dx end end @@ -194,15 +205,18 @@ function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, n = normalize((t1-t3)×(t2-t3)) ρ = x - dot(x - s1, n) * n + num_tshapes = numfunctions(test_refspace, domain(test_elements)) + num_bshapes = numfunctions(trial_refspace, domain(trial_element)) + scal, vec, grad = WiltonInts84.wiltonints(s1, s2, s3, x, Val{1}) ∫∇G = -(grad[1]+0.5*γ^2*grad[3])/(4π) ∫n∇G = dot(n,∫∇G) Atot = 1/2*norm(cross(t3-t1,t3-t2)) - for i in 1:numfunctions(test_refspace) + for i in 1:num_tshapes Ai = 1/2*norm(cross(test_elements.vertices[mod1(i-1,3)]-x,test_elements.vertices[mod1(i+1,3)]-x)) g = Ai/Atot - for j in 1:numfunctions(trial_refspace) + for j in 1:num_bshapes zlocal[i,j] += α * ∫n∇G * g * dx end end @@ -225,11 +239,14 @@ function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, n = normalize((t1-t3)×(t2-t3)) ρ = x - dot(x - s1, n) * n + num_tshapes = numfunctions(test_refspace, domain(test_elements)) + num_bshapes = numfunctions(trial_refspace, domain(trial_element)) + _, _, _, grad = WiltonInts84.higherorder(s1,s2,s3,x,3) ∫∇G = -(grad[1] + 0.5*γ^2*grad[2]) / (4π) - for i in 1:numfunctions(test_refspace) - for j in 1:numfunctions(trial_refspace) + for i in 1:num_tshapes + for j in 1:num_bshapes ∫n∇G = dot(n,∫∇G[j]) zlocal[i,j] += α * ∫n∇G * dx end @@ -253,15 +270,18 @@ function innerintegrals!(op::HH3DDoubleLayerTransposedSng, test_neighborhood, n = normalize((t1-t3)×(t2-t3)) ρ = x - dot(x - s1, n) * n + num_tshapes = numfunctions(test_refspace, domain(test_elements)) + num_bshapes = numfunctions(trial_refspace, domain(trial_element)) + _, _, _, grad = WiltonInts84.higherorder(s1,s2,s3,x,3) ∫∇G = -(grad[1] + 0.5*γ^2*grad[2]) / (4π) Atot = 1/2*norm(cross(t3-t1,t3-t2)) - for i in 1:numfunctions(test_refspace) + for i in 1:num_tshapes Ai = 1/2*norm(cross(test_elements.vertices[mod1(i-1,3)]-x,test_elements.vertices[mod1(i+1,3)]-x)) g = Ai/Atot - for j in 1:numfunctions(trial_refspace) + for j in 1:num_bshapes ∫n∇G = dot(n,∫∇G[j]) zlocal[i,j] += α * ∫n∇G * g * dx end @@ -303,6 +323,9 @@ function innerintegrals!(op::HH3DDoubleLayerSng, p, s1, s2, s3 = s.vertices t1, t2, t3 = t.vertices + num_tshapes = numfunctions(g, domain(t)) + num_bshapes = numfunctions(f, domain(s)) + x = cartesian(p) n = normalize((s1-s3)×(s2-s3)) ρ = x - dot(x - s1, n) * n @@ -312,10 +335,10 @@ function innerintegrals!(op::HH3DDoubleLayerSng, p, ∫∇G = -(grad[1] + 0.5*γ^2*grad[2]) / (4π) Atot = 1/2*norm(cross(t3-t1,t3-t2)) - for i in 1:numfunctions(g) + for i in 1:num_tshapes Ai = 1/2*norm(cross(t.vertices[mod1(i-1,3)]-x,t.vertices[mod1(i+1,3)]-x)) g = Ai/Atot - for j in 1:numfunctions(f) + for j in 1:num_bshapes z[i,j] += α * dot(n,-∫∇G[j]) * g * dx end end @@ -332,6 +355,9 @@ function innerintegrals!(op::HH3DDoubleLayerSng, p, γ = gamma(op) α = op.alpha + num_tshapes = numfunctions(g, domain(t)) + num_bshapes = numfunctions(f, domain(s)) + s1, s2, s3 = s.vertices t1, t2, t3 = t.vertices @@ -343,8 +369,8 @@ function innerintegrals!(op::HH3DDoubleLayerSng, p, ∫∇G = -(grad[1]+0.5*γ^2*grad[3])/(4π) - for i in 1:numfunctions(g) - for j in 1:numfunctions(f) + for i in 1:num_tshapes + for j in 1:num_bshapes z[i,j] += α * dot(n,-∫∇G) * dx #why the minus? end end @@ -363,6 +389,9 @@ function innerintegrals!(op::HH3DDoubleLayerSng, p, s1, s2, s3 = s.vertices t1, t2, t3 = t.vertices + num_tshapes = numfunctions(g, domain(t)) + num_bshapes = numfunctions(f, domain(s)) + x = cartesian(p) n = normalize((s1-s3)×(s2-s3)) ρ = x - dot(x - s1, n) * n @@ -372,13 +401,13 @@ function innerintegrals!(op::HH3DDoubleLayerSng, p, ∫∇G = -(grad[1]+0.5*γ^2*grad[3])/(4π) Atot = 1/2*norm(cross(t3-t1,t3-t2)) -for i in 1:numfunctions(g) - Ai = 1/2*norm(cross(t.vertices[mod1(i-1,3)]-x,t.vertices[mod1(i+1,3)]-x)) - g = Ai/Atot - for j in 1:numfunctions(f) - z[i,j] += α * dot(n,-∫∇G) * g * dx + for i in 1:num_tshapes + Ai = 1/2*norm(cross(t.vertices[mod1(i-1,3)]-x,t.vertices[mod1(i+1,3)]-x)) + g = Ai/Atot + for j in 1:num_bshapes + z[i,j] += α * dot(n,-∫∇G) * g * dx + end end -end return nothing end @@ -402,12 +431,14 @@ function innerintegrals!(op::HH3DDoubleLayerSng, p, ∫∇G = -(grad[1] + 0.5*γ^2*grad[2]) / (4π) + num_tshapes = numfunctions(g, domain(t)) + num_bshapes = numfunctions(f, domain(s)) -for i in 1:numfunctions(g) - for j in 1:numfunctions(f) - z[i,j] += α * dot(n,-∫∇G[j]) * dx + for i in 1:num_tshapes + for j in 1:num_bshapes + z[i,j] += α * dot(n,-∫∇G[j]) * dx + end end -end return nothing end @@ -451,15 +482,18 @@ function innerintegrals!(op::HH3DHyperSingularSng, p, greenconst = (∫Rⁿ[2] + 0.5*γ^2*∫Rⁿ[3]) / (4π) greenlinear = (∫RⁿN[2] + 0.5*γ^2*∫RⁿN[3] ) / (4π) + num_tshapes = numfunctions(g, domain(t)) + num_bshapes = numfunctions(f, domain(s)) + jt = volume(t) * factorial(dimension(t)) js = volume(s) * factorial(dimension(s)) curlt = [(t3-t2)/jt,(t1-t3)/jt,(t2-t1)/jt] curls = [(s3-s2)/js,(s1-s3)/js,(s2-s1)/js] Atot = 1/2*norm(cross(t3-t1,t3-t2)) - for i in 1:numfunctions(g) + for i in 1:num_tshapes Ai = 1/2*norm(cross(t.vertices[mod1(i-1,3)]-x,t.vertices[mod1(i+1,3)]-x)) g = Ai/Atot - for j in 1:numfunctions(f) + for j in 1:num_bshapes z[i,j] += β * dot(curlt[i],curls[j])*greenconst*dx + α*dot(nx,ny) * greenlinear[j]*g*dx end end diff --git a/src/integralop.jl b/src/integralop.jl index 2989812a..32621123 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -75,12 +75,15 @@ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; test_elements, tad, tcells = tr bsis_elements, bad, bcells = br - tshapes = refspace(tfs); num_tshapes = numfunctions(tshapes) - bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes) - tgeo = geometry(tfs) bgeo = geometry(bfs) + tdom = domain(chart(tgeo, first(tgeo))) + bdom = domain(chart(bgeo, first(bgeo))) + + tshapes = refspace(tfs); num_tshapes = numfunctions(tshapes, tdom) + bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes, bdom) + qs = if CompScienceMeshes.refines(tgeo, bgeo) TestRefinesTrialQStrat(quadstrat) elseif CompScienceMeshes.refines(bgeo, tgeo) @@ -328,8 +331,14 @@ function assembleblock_primer(biop, tfs, bfs; test_elements, tad = assemblydata(tfs; onlyactives=false) bsis_elements, bad = assemblydata(bfs; onlyactives=false) - tshapes = refspace(tfs); num_tshapes = numfunctions(tshapes) - bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes) + tgeo = geometry(tfs) + bgeo = geometry(bfs) + + tdom = domain(chart(tgeo, first(tgeo))) + bdom = domain(chart(bgeo, first(bgeo))) + + tshapes = refspace(tfs); num_tshapes = numfunctions(tshapes, tdom) + bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes, bdom) qd = quaddata(biop, tshapes, bshapes, test_elements, bsis_elements, quadstrat) @@ -551,14 +560,20 @@ end end end end end end end function assemblerow!(biop::IntegralOperator, test_functions::Space, trial_functions::Space, store; quadstrat=defaultquadstrat(biop, test_functions, trial_functions)) - test_elements = elements(geometry(test_functions)) + tgeo = geometry(test_functions) + bgeo = geometry(trial_functions) + + tdom = domain(chart(tgeo, first(tgeo))) + bdom = domain(chart(bgeo, first(bgeo))) + + test_elements = elements(tgeo) trial_elements, trial_assembly_data = assemblydata(trial_functions) test_shapes = refspace(test_functions) trial_shapes = refspace(trial_functions) - num_test_shapes = numfunctions(test_shapes) - num_trial_shapes = numfunctions(trial_shapes) + num_test_shapes = numfunctions(test_shapes, tdom) + num_trial_shapes = numfunctions(trial_shapes, bdom) quadrature_data = quaddata(biop, test_shapes, trial_shapes, test_elements, trial_elements, quadstrat) diff --git a/src/localop.jl b/src/localop.jl index 9ce1410b..a169ae7d 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -99,12 +99,21 @@ function assemble_local_matched!(biop::LocalOperator, tfs::Space, bfs::Space, st trefs = refspace(tfs) brefs = refspace(bfs) + tgeo = geometry(tfs) + bgeo = geometry(bfs) + + tdom = domain(chart(tgeo, first(tgeo))) + bdom = domain(chart(bgeo, first(bgeo))) + + num_trefs = numfunctions(trefs, tdom) + num_brefs = numfunctions(brefs, bdom) + qd = quaddata(biop, trefs, brefs, tels, bels, quadstrat) verbose = length(tels) > 10_000 verbose && print("dots out of 20: ") todo, done, pctg = length(tels), 0, 0 - locmat = zeros(scalartype(biop, trefs, brefs), numfunctions(trefs), numfunctions(brefs)) + locmat = zeros(scalartype(biop, trefs, brefs), num_trefs, num_brefs) for (p,cell) in enumerate(tels) P = ta2g[p] q = bg2a[P] @@ -138,6 +147,15 @@ function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, st trefs = refspace(tfs) brefs = refspace(bfs) + tgeo = geometry(tfs) + bgeo = geometry(bfs) + + tdom = domain(chart(tgeo, first(tgeo))) + bdom = domain(chart(bgeo, first(bgeo))) + + num_trefs = numfunctions(trefs, tdom) + num_brefs = numfunctions(brefs, bdom) + tels, tad, ta2g = assemblydata(tfs) bels, bad, ba2g = assemblydata(bfs) @@ -167,8 +185,8 @@ function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, st zlocal = cellinteractions(biop, trefs, brefs, cell, qr) zlocal = Q * zlocal * P' - for i in 1 : numfunctions(trefs) - for j in 1 : numfunctions(brefs) + for i in 1 : num_trefs + for j in 1 : num_brefs for (m,a) in tad[p,i] for (n,b) in bad[q,j] store(a * zlocal[i,j] * b, m, n) @@ -256,6 +274,15 @@ function assemble_local_mixed!(biop::LocalOperator, tfs::Space{T}, bfs::Space{T} trefs = refspace(tfs) brefs = refspace(bfs) + tgeo = geometry(tfs) + bgeo = geometry(bfs) + + tdom = domain(chart(tgeo, first(tgeo))) + bdom = domain(chart(bgeo, first(bgeo))) + + num_trefs = numfunctions(trefs, tdom) + num_brefs = numfunctions(brefs, bdom) + tels, tad = assemblydata(tfs) bels, bad = assemblydata(bfs) @@ -288,8 +315,8 @@ function assemble_local_mixed!(biop::LocalOperator, tfs::Space{T}, bfs::Space{T} zlocal = cellinteractions(biop, trefs, brefs, cell, qr) zlocal = Q * zlocal * P' - for i in 1 : numfunctions(trefs) - for j in 1 : numfunctions(brefs) + for i in 1 : num_trefs + for j in 1 : num_brefs for (m,a) in tad[p,i] for (n,b) in bad[q,j] store(a * zlocal[i,j] * b, m, n) diff --git a/src/maxwell/timedomain/mwtdops.jl b/src/maxwell/timedomain/mwtdops.jl index 962e577f..86f69d2b 100644 --- a/src/maxwell/timedomain/mwtdops.jl +++ b/src/maxwell/timedomain/mwtdops.jl @@ -266,10 +266,13 @@ function innerintegrals!(zl, op::MWSingleLayerTDIO, sol5 = sol4*sol solpowers = (one(sol), sol, sol2, sol3, sol4, sol5) - for i in 1 : numfunctions(U) + udim = numfunctions(U, domain(τ)) + vdim = numfunctions(V, domain(σ)) + + for i in 1 : udim a = τ[i] g = (x-a) - for j in 1 : numfunctions(V) + for j in 1 : vdim b = σ[j]; bξ = ξ-b for k in 1 : numfunctions(W) d = k-1 # ranges from 0 to numfunctions(W)-1 @@ -338,11 +341,14 @@ function innerintegrals!(z, op::MWDoubleLayerTDIO, Ux = U(p) Vx = αf * @SVector[(x-σ[1]), (x-σ[2]), (x-σ[3])] - for i in 1 : numfunctions(U) + udim = numfunctions(U, domain(τ)) + vdim = numfunctions(V, domain(σ)) + + for i in 1 : udim # a = τ[i] # g = αg * (x-τ[i]) g = Ux[i].value - for j in 1 : numfunctions(V) + for j in 1 : vdim # b = σ[j] # f = αf * (x-σ[j]) # f = Vx[j].value diff --git a/src/maxwell/wiltonints.jl b/src/maxwell/wiltonints.jl index 4ffe2d80..9b715446 100644 --- a/src/maxwell/wiltonints.jl +++ b/src/maxwell/wiltonints.jl @@ -29,13 +29,16 @@ function innerintegrals!(op::MWSingleLayer3DSng, p, g, f, t, s, z, c₁ = op.α c₂ = op.β + num_tshapes = numfunctions(g, domain(t)) + num_bshapes = numfunctions(f, domain(s)) + α = 1 / volume(t) / volume(s) / 4 - for i in 1 : numfunctions(g) + for i in 1 : num_bshapes a = t[i] g = x - a dg = 2 - for j in 1 : numfunctions(f) + for j in 1 : num_tshapes b = s[j] ∫Gf = SVector(∫Gy[1]-∫G*b[1], ∫Gy[2]-∫G*b[2], ∫Gy[3]-∫G*b[3]) @@ -60,14 +63,17 @@ function innerintegrals!(op::MWDoubleLayer3DSng, p, g, f, t, s, z, strat::Wilton scal, vec, grad = WiltonInts84.wiltonints(s[1], s[2], s[3], x, Val{1}) + num_tshapes = numfunctions(g, domain(t)) + num_bshapes = numfunctions(f, domain(s)) + # \int \nabla G_s with G_s = \nabla (1/R + 0.5*γ^2*R) / (4\pi) ∫∇G = T.((-grad[1] - 0.5*γ^2*grad[3]) / (4π)) α = 1 / volume(t) / volume(s) / 4 - for i in 1 : numfunctions(g) + for i in 1 : num_tshapes a = t[i] g = (x - a) - for j in 1 : numfunctions(f) + for j in 1 : num_bshapes b = s[j] z[i,j] += ( α * ( (x-b) × g ) ⋅ ∫∇G ) * dx diff --git a/src/postproc.jl b/src/postproc.jl index 961ad2e9..398f4c8f 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -171,7 +171,9 @@ function potential!(store, op, points, basis; els, ad = assemblydata(basis) rs = refspace(basis) - zlocal = Array{type}(undef,numfunctions(rs)) + geo = geometry(basis) + nf = numfunctions(rs, domain(chart(geo, first(geo)))) + zlocal = Array{type}(undef, nf) qdata = quaddata(op,rs,els,quadstrat) print("dots out of 10: ") diff --git a/src/quadrature/rules/testrefinestrialqrule.jl b/src/quadrature/rules/testrefinestrialqrule.jl index 08bcec5a..e2d86b99 100644 --- a/src/quadrature/rules/testrefinestrialqrule.jl +++ b/src/quadrature/rules/testrefinestrialqrule.jl @@ -13,6 +13,12 @@ function momintegrals!(out, op, test_mesh = geometry(test_functions) trial_mesh = geometry(trial_functions) + tdom = domain(test_chart) + bdom = domain(trial_chart) + + num_tshapes = numfunctions(test_local_space, tdom) + num_bshapes = numfunctions(trial_local_space, bdom) + parent_mesh = CompScienceMeshes.parent(test_mesh) trial_charts = [chart(test_mesh, p) for p in CompScienceMeshes.children(parent_mesh, trial_cell)] @@ -31,8 +37,8 @@ function momintegrals!(out, op, test_functions, nothing, test_chart, trial_functions, nothing, chart, qr) - for j in 1:numfunctions(trial_local_space) - for i in 1:numfunctions(test_local_space) + for j in 1:num_bshapes + for i in 1:num_tshapes for k in 1:size(Q, 2) out[i,j] += zlocal[i,k] * Q[j,k] end end end end end \ No newline at end of file diff --git a/src/quadrature/rules/trialrefinestestqrule.jl b/src/quadrature/rules/trialrefinestestqrule.jl index 3bbb4c7a..f0c74f54 100644 --- a/src/quadrature/rules/trialrefinestestqrule.jl +++ b/src/quadrature/rules/trialrefinestestqrule.jl @@ -13,6 +13,12 @@ function momintegrals!(out, op, test_mesh = geometry(test_functions) trial_mesh = geometry(trial_functions) + tdom = domain(chart(test_mesh, first(test_mesh))) + bdom = domain(chart(trial_mesh, first(trial_mesh))) + + num_tshapes = numfunctions(test_local_space, tdom) + num_bshapes = numfunctions(trial_local_space, bdom) + parent_mesh = CompScienceMeshes.parent(trial_mesh) test_charts = [chart(trial_mesh, p) for p in CompScienceMeshes.children(parent_mesh, test_cell)] @@ -30,8 +36,8 @@ function momintegrals!(out, op, test_functions, nothing, chart, trial_functions, trial_cell, trial_chart, qr) - for j in 1:numfunctions(trial_local_space) - for i in 1:numfunctions(test_local_space) + for j in 1:num_bshapes + for i in 1:num_tshapes for k in 1:size(Q, 2) out[i,j] += Q[i,k] * zlocal[k,j] end end end end end \ No newline at end of file diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index 423bda9a..1d56cdb3 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -150,10 +150,13 @@ function momintegrals!(op::Operator, vertices(test_chart), vertices(trial_chart), rule) + num_tshapes = numfunctions(test_local_space, domain(test_chart)) + num_bshapes = numfunctions(trial_local_space, domain(trial_chart)) + igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) igdp = pulledback_integrand(igd, I, test_chart, J, trial_chart) G = SauterSchwabQuadrature.sauterschwab_parameterized(igdp, rule) - out[1:numfunctions(test_local_space),1:numfunctions(trial_local_space)] .+= G + out[1:num_tshapes, 1:num_bshapes] .+= G nothing end diff --git a/src/timedomain/tdexcitation.jl b/src/timedomain/tdexcitation.jl index cf3819af..e6805273 100644 --- a/src/timedomain/tdexcitation.jl +++ b/src/timedomain/tdexcitation.jl @@ -85,9 +85,11 @@ function assemble!(exc::TDFunctional, testST, store; testels, testad = assemblydata(testfns) timeels, timead = assemblydata(timefns) + qd = quaddata(exc, testrefs, timerefs, testels, timeels) - - z = zeros(eltype(exc), numfunctions(testrefs), numfunctions(timerefs)) + + num_testshapes = numfunctions(testrefs, domain(first(testels))) + z = zeros(eltype(exc), num_testshapes, numfunctions(timerefs)) for p in eachindex(testels) τ = testels[p] for r in eachindex(timeels) @@ -97,7 +99,7 @@ function assemble!(exc::TDFunctional, testST, store; qr = quadrule(exc, testrefs, timerefs, p, τ, r, ρ, qd) momintegrals!(z, exc, testrefs, timerefs, τ, ρ, qr) - for i in 1 : numfunctions(testrefs) + for i in 1 : num_testshapes for d in 1 : numfunctions(timerefs) v = z[i,d] @@ -155,13 +157,15 @@ end function timeintegrals!(z, exc::TDFunctional, testrefs, timerefs, testpoint, timeelement, dx, qr, f) + num_tshapes = numfunctions(testrefs, domain(chart(testpoint))) + for p in qr.quad_points t = p.point w = p.weight U = p.value dt = w #* jacobian(t) # * volume(timeelement) - for i in 1 : numfunctions(testrefs) + for i in 1 : num_tshapes for k in 1 : numfunctions(timerefs) z[i,k] += dot(f[i][1]*U[k], exc(testpoint,t)) * dt * dx end @@ -176,12 +180,14 @@ function timeintegrals!(z, exc::TDFunctional, testpoint, timeelement, dx, qr, testvals) + num_tshapes = numfunctions(spacerefs, domain(chart(testpoint))) + # since timeelement uses barycentric coordinates, # the first/left vertex has coords u = 1.0! testtime = neighborhood(timeelement, point(0.0)) @assert cartesian(testtime)[1] ≈ timeelement.vertices[2][1] - for i in 1 : numfunctions(spacerefs) + for i in 1 : num_tshapes z[i,1] += dot(testvals[i][1], exc(testpoint, testtime)) * dx end end diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index bd9820f6..73bcb201 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -251,8 +251,11 @@ function assemble_chunk!(op::RetardedPotential, testST, trialST, store; qd = quaddata(op, U, V, W, testels, trialels, nothing, quadstrat) - udim = numfunctions(U) - vdim = numfunctions(V) + ugeo = geometry(testspace) + vgeo = geometry(trialspace) + + udim = numfunctions(U, domain(chart(ugeo, first(ugeo)))) + vdim = numfunctions(V, domain(chart(vgeo, first(vgeo)))) wdim = numfunctions(W) z = zeros(scalartype(op, testST, trialST), udim, vdim, wdim) diff --git a/src/utils/lagpolys.jl b/src/utils/lagpolys.jl new file mode 100644 index 00000000..65d3c924 --- /dev/null +++ b/src/utils/lagpolys.jl @@ -0,0 +1,45 @@ +function _lagpoly(nodes, i, s, i0=1, i1=length(nodes)) + T = eltype(s) + r = one(T) + si = nodes[i] + for j in i0:i1 + j == i && continue + sj = nodes[j] + r *= (s - sj) / (si - sj) + end + return r +end + +function _lagpoly_diff(nodes, i, s, i0=1, i1=length(nodes)) + r = zero(T) + si = nodes[i] + for p in i0:i1 + p == i && continue + rp = one(T) + for j in i0:i1 + j == i && continue + j == p && continue + sj = nodees[j] + rp *= (s - sj) / (si - sj) + end + rp *= 1 / (si - sp) + r += rp + end + return r +end + +function _sylpoly(nodes, i, s) + _lagpoly(nodes, i, s, 1, i) +end + +function _sylpoly_diff(nodes, i, s) + _lagpoly_diff(nodes, i, s, 1, i) +end + +function _sylpoly_shift(nodes, i, s) + _lagpoly(nodes, i, s, 2, i) +end + +function _sylpoly_shift_diff(nodes, i, s) + _lagpoly_shift(nodes, i, s, 2, i) +end \ No newline at end of file diff --git a/test/test_basis.jl b/test/test_basis.jl index c6f36017..a85c7937 100644 --- a/test/test_basis.jl +++ b/test/test_basis.jl @@ -75,8 +75,9 @@ for T in [Float32, Float64] m = meshrectangle(T(1.0), T(1.0), T(0.5), 3) X = lagrangec0d1(m) x = refspace(X) + dom = domain(chart(m, first(m))) - @test numfunctions(x) == 3 + @test numfunctions(x, dom) == 3 @test numfunctions(X) == 1 @test length(X.fns[1]) == 6 @@ -213,9 +214,10 @@ x = refspace(X) isonjunction = inclosure_gpredicate(j) els, ad = BEAST.assemblydata(X) +num_shapes = numfunctions(x, domain(chart(m, first(m)))) for _p in 1:numcells(m) el = els[_p] - for r in 1:numfunctions(x) + for r in 1:num_shapes vert = el[r] isonjunction(vert) || continue for (i,w) in ad[_p,r] diff --git a/test/test_gwp.jl b/test/test_gwp.jl new file mode 100644 index 00000000..7d1b83b7 --- /dev/null +++ b/test/test_gwp.jl @@ -0,0 +1,12 @@ +@testitem "GWPRefSpace eval" begin + using CompScienceMeshes + + T = Float64 + Dim, Nverts, Degree = 2, 3, 3 + + dom = CompScienceMeshes.ReferenceSimplex{Dim,T,Nverts}() + ϕ = BEAST.GWPCurlRefSpace{T,Degree}() + + u = (one(T)/3, one(T)/3) + vals = ϕ(dom, u) +end \ No newline at end of file diff --git a/test/test_rt.jl b/test/test_rt.jl index 56d9b781..ea46e92c 100644 --- a/test/test_rt.jl +++ b/test/test_rt.jl @@ -13,7 +13,8 @@ tol = eps(T) * 10^3 function shapevals(ϕ, ts) numpoints = length(ts) - numshapes = numfunctions(ϕ) + dom = CompScienceMeshes.domain(CompScienceMeshes.chart(ts[1])) + numshapes = numfunctions(ϕ, dom) y = Array{P}(undef, numshapes, numpoints) for i in 1 : numpoints diff --git a/test/test_ss_nested_meshes.jl b/test/test_ss_nested_meshes.jl index 48bf2c98..b23f725d 100644 --- a/test/test_ss_nested_meshes.jl +++ b/test/test_ss_nested_meshes.jl @@ -54,7 +54,7 @@ end qs_strat = BEAST.DoubleNumWiltonSauterQStrat(1,1,6,7,10,10,10,10) # sauterschwab = BEAST.SauterSchwabQuadrature.CommonFace(BEAST._legendre(10,0.0,1.0)) -out_ss = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) +out_ss = zeros(T, numfunctions(𝒳, domain(test_chart)), numfunctions(𝒳, domain(trial_chart))) BEAST.momintegrals!(out_ss, 𝒜, Y, p, test_chart, X, 1, trial_chart, @@ -62,7 +62,7 @@ BEAST.momintegrals!(out_ss, 𝒜, wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) -out_dw = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) +out_dw = zeros(T, numfunctions(𝒳, domain(test_chart)), numfunctions(𝒳, domain(trial_chart))) BEAST.momintegrals!(out_dw, 𝒜, Y, p, test_chart, X, 1, trial_chart, @@ -77,11 +77,11 @@ test_quadpoints = BEAST.quadpoints(𝒳, [test_chart], (12,))[1,1] trial_quadpoints = BEAST.quadpoints(𝒳, [trial_chart], (13,))[1,1] sauterschwab = BEAST.SauterSchwabQuadrature.CommonFace(BEAST._legendre(10,0.0,1.0)) -out_ss1 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) +out_ss1 = zeros(T, numfunctions(𝒳, domain(test_chart)), numfunctions(𝒳, domain(trial_chart))) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss1,sauterschwab) wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) -out_dw1 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) +out_dw1 = zeros(T, numfunctions(𝒳, domain(test_chart)), numfunctions(𝒳, domain(trial_chart))) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw1,wiltonsingext) @test out_ss1 ≈ out_dw1 rtol=3e-6 @@ -94,11 +94,11 @@ test_quadpoints = BEAST.quadpoints(𝒳, [test_chart], (12,))[1,1] trial_quadpoints = BEAST.quadpoints(𝒳, [trial_chart], (13,))[1,1] sauterschwab = BEAST.SauterSchwabQuadrature.CommonEdge(BEAST._legendre(10,0.0,1.0)) -out_ss2 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) +out_ss2 = zeros(T, numfunctions(𝒳, domain(test_chart)), numfunctions(𝒳, domain(trial_chart))) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss2,sauterschwab) wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) -out_dw2 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) +out_dw2 = zeros(T, numfunctions(𝒳, domain(test_chart)), numfunctions(𝒳, domain(trial_chart))) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw2,wiltonsingext) @test out_ss2 ≈ out_dw2 rtol=4e-6 @@ -111,11 +111,11 @@ test_quadpoints = BEAST.quadpoints(𝒳, [test_chart], (12,))[1,1] trial_quadpoints = BEAST.quadpoints(𝒳, [trial_chart], (13,))[1,1] sauterschwab = BEAST.SauterSchwabQuadrature.CommonEdge(BEAST._legendre(10,0.0,1.0)) -out_ss3 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) +out_ss3 = zeros(T, numfunctions(𝒳, domain(test_chart)), numfunctions(𝒳, domain(trial_chart))) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss3,sauterschwab) wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) -out_dw3 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) +out_dw3 = zeros(T, numfunctions(𝒳, domain(test_chart)), numfunctions(𝒳, domain(trial_chart))) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw3,wiltonsingext) @test out_ss3 ≈ out_dw3 rtol=3e-6 @@ -129,11 +129,11 @@ test_quadpoints = BEAST.quadpoints(𝒳, [test_chart], (12,))[1,1] trial_quadpoints = BEAST.quadpoints(𝒳, [trial_chart], (13,))[1,1] sauterschwab = BEAST.SauterSchwabQuadrature.CommonVertex(BEAST._legendre(10,0.0,1.0)) -out_ss4 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) +out_ss4 = zeros(T, numfunctions(𝒳, domain(test_chart)), numfunctions(𝒳, domain(trial_chart))) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss4,sauterschwab) wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) -out_dw4 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) +out_dw4 = zeros(T, numfunctions(𝒳, domain(test_chart)), numfunctions(𝒳, domain(trial_chart))) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw4,wiltonsingext) @test out_ss4 ≈ out_dw4 rtol=2e-9 @@ -146,11 +146,11 @@ test_quadpoints = BEAST.quadpoints(𝒳, [test_chart], (12,))[1,1] trial_quadpoints = BEAST.quadpoints(𝒳, [trial_chart], (13,))[1,1] sauterschwab = BEAST.SauterSchwabQuadrature.CommonVertex(BEAST._legendre(10,0.0,1.0)) -out_ss5 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) +out_ss5 = zeros(T, numfunctions(𝒳, domain(test_chart)), numfunctions(𝒳, domain(trial_chart))) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss5,sauterschwab) wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) -out_dw5 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) +out_dw5 = zeros(T, numfunctions(𝒳, domain(test_chart)), numfunctions(𝒳, domain(trial_chart))) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw5,wiltonsingext) @test out_ss4 ≈ out_dw4 rtol=3e-8 @@ -163,11 +163,11 @@ test_quadpoints = BEAST.quadpoints(𝒳, [test_chart], (12,))[1,1] trial_quadpoints = BEAST.quadpoints(𝒳, [trial_chart], (13,))[1,1] sauterschwab = BEAST.SauterSchwabQuadrature.CommonVertex(BEAST._legendre(10,0.0,1.0)) -out_ss6 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) +out_ss6 = zeros(T, numfunctions(𝒳, domain(test_chart)), numfunctions(𝒳, domain(trial_chart))) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_ss6,sauterschwab) wiltonsingext = BEAST.WiltonSERule(test_quadpoints, BEAST.DoubleQuadRule(test_quadpoints, trial_quadpoints)) -out_dw6 = zeros(T, numfunctions(𝒳), numfunctions(𝒳)) +out_dw6 = zeros(T, numfunctions(𝒳, domain(test_chart)), numfunctions(𝒳, domain(trial_chart))) BEAST.momintegrals!(𝒜,𝒳,𝒳,test_chart,trial_chart,out_dw6,wiltonsingext) @test out_ss4 ≈ out_dw4 rtol=6e-8 diff --git a/test/test_tdassembly.jl b/test/test_tdassembly.jl index feac5229..00894b9f 100644 --- a/test/test_tdassembly.jl +++ b/test/test_tdassembly.jl @@ -58,14 +58,14 @@ function momintegrals!(z, op::typeof(G), TR = [(-R)^d for d in 0:maxdegree] dxdy = wpv_τ.weight * wpv_σ.weight - for i in 1 : numfunctions(g) - for j in 1 : numfunctions(f) + for i in 1 : numfunctions(g, domain(τ)) + for j in 1 : numfunctions(f, domain(σ)) for k in 1 : numfunctions(T) z[i,j,k] += dxdy * gx[i] * fy[j] * TR[k] / (4π*R) end end end end end end # Compare results for a single monomial -z1 = zeros(numfunctions(x1), numfunctions(x2), numfunctions(q)) +z1 = zeros(numfunctions(x1, domain(τ1)), numfunctions(x2, domain(τ2)), numfunctions(q)) for r in BEAST.rings(τ1, τ2, ΔR) local ι = BEAST.ring(r, ΔR) momintegrals!(z1, G, x1, x2, q, τ1, τ2, ι, DoubleQuadTimeDomainRule()) @@ -73,7 +73,7 @@ end qs = BEAST.defaultquadstrat(G,X1,X2) qd = quaddata(G, x1, x2, q, [τ1], [τ2], nothing, qs) -z2 = zeros(numfunctions(x1), numfunctions(x2), numfunctions(q)) +z2 = zeros(numfunctions(x1, domain(τ1)), numfunctions(x2, domain(τ2)), numfunctions(q)) for r in BEAST.rings(τ1, τ2, ΔR) local ι = BEAST.ring(r, ΔR) quad_rule = quadrule(G, x1, x2, q, 1, τ1, 1, τ2, r, ι, qd, qs) diff --git a/test/test_tdhhdbl.jl b/test/test_tdhhdbl.jl index f56de5e0..864fd295 100644 --- a/test/test_tdhhdbl.jl +++ b/test/test_tdhhdbl.jl @@ -13,11 +13,13 @@ c = 1.0 K = BEAST.HH3DDoubleLayerTDBIO(speed_of_light=c) X = lagrangecxd0(G) -@test numfunctions(refspace(X)) == 1 +@test numfunctions(refspace(X), domain(chart(G, first(G)))) == 1 Y = duallagrangec0d1(G) @test numfunctions(Y) == 1 -@test numfunctions(refspace(Y)) == 3 + +Gref = geometry(Y) +@test numfunctions(refspace(Y), domain(chart(Gref, first(Gref)))) == 3 # X = duallagrangecxd0(G, boundary(G)) # Y = lagrangec0d1(G, dirichlet=false) diff --git a/test/test_wiltonints.jl b/test/test_wiltonints.jl index ed0acfbe..1f1469b7 100644 --- a/test/test_wiltonints.jl +++ b/test/test_wiltonints.jl @@ -333,7 +333,7 @@ x = BEAST.refspace(X) κ = 1.0 op = BEAST.MWSingleLayer3D(κ) -n = BE.numfunctions(x) +n = BE.numfunctions(x, domain(s)) z1 = zeros(ComplexF64, n, n) z2 = zeros(ComplexF64, n, n) From eb7589de612e7a20540b446d9298d22839f65c71 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 2 Oct 2024 17:16:41 +0200 Subject: [PATCH 400/528] self-interpolation verified --- src/bases/global/globaldofs.jl | 4 +- src/bases/local/gwplocal.jl | 90 +++++++++++++++++++++++++++++----- test/test_gwp.jl | 31 +++++++++++- 3 files changed, 111 insertions(+), 14 deletions(-) diff --git a/src/bases/global/globaldofs.jl b/src/bases/global/globaldofs.jl index b736bb1c..ad7908bd 100644 --- a/src/bases/global/globaldofs.jl +++ b/src/bases/global/globaldofs.jl @@ -1,4 +1,4 @@ -function trace(edge_ch, face_ch, localspace::RefSpace) +function trace(edge_ch, face_ch, fields) T = coordtype(edge_ch) atol = sqrt(eps(T)) @@ -14,7 +14,7 @@ function trace(edge_ch, face_ch, localspace::RefSpace) function f(s) u = neighborhood(injection, s) p = neighborhood(face_ch, cartesian(u)) - localspace(p) + fields(p) end return f diff --git a/src/bases/local/gwplocal.jl b/src/bases/local/gwplocal.jl index a0788f4a..6dfa4c7c 100644 --- a/src/bases/local/gwplocal.jl +++ b/src/bases/local/gwplocal.jl @@ -30,9 +30,9 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di i = 0 for j in 1:d+1 k = (d+2)-i-j - Rᵢ = _sylpoly_shift(s, i+1, u) - Rⱼ = _sylpoly(s, j+1, v) - Rₖ = _sylpoly(s, k+1, w) + Rᵢ = _sylpoly(s, i+1, u) + Rⱼ = _sylpoly_shift(s, j+1, v) + Rₖ = _sylpoly_shift(s, k+1, w) push!(vals, Rᵢ*Rⱼ*Rₖ*nd1) # du = _sylpoly_shift_diff(s, i+1, u)*Rⱼ*Rₖ @@ -41,18 +41,18 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di for i in 1:d+1 j = 0 k = (d+2)-i-j - Rᵢ = _sylpoly(s, i+1, u) - Rⱼ = _sylpoly_shift(s, j+1, v) - Rₖ = _sylpoly(s, k+1, w) + Rᵢ = _sylpoly_shift(s, i+1, u) + Rⱼ = _sylpoly(s, j+1, v) + Rₖ = _sylpoly_shift(s, k+1, w) push!(vals, Rᵢ*Rⱼ*Rₖ*nd2) end for i in 1:d+1 j = (d+2)-i k = 0 - Rᵢ = _sylpoly(s, i+1, u) - Rⱼ = _sylpoly(s, j+1, v) - Rₖ = _sylpoly_shift(s, k+1, w) + Rᵢ = _sylpoly_shift(s, i+1, u) + Rⱼ = _sylpoly_shift(s, j+1, v) + Rₖ = _sylpoly(s, k+1, w) push!(vals, Rᵢ*Rⱼ*Rₖ*nd3) end @@ -62,11 +62,18 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di k <= 0 && continue Rsᵢ = _sylpoly_shift(s, i+1, u) Rsⱼ = _sylpoly_shift(s, j+1, v) + Rsₖ = _sylpoly_shift(s, k+1, w) Rᵢ = _sylpoly(s, i+1, u) Rⱼ = _sylpoly(s, j+1, v) Rₖ = _sylpoly(s, k+1, w) - push!(vals, Rsᵢ*Rⱼ*Rₖ*nd1) - push!(vals, Rᵢ*Rsⱼ*Rₖ*nd2) + S1 = Rᵢ*Rsⱼ*Rsₖ*nd1 + S2 = Rsᵢ*Rⱼ*Rsₖ*nd2 + S3 = Rsᵢ*Rsⱼ*Rₖ*nd3 + N1 = (d+2)/(d+2-i) + N2 = (d+2)/(d+2-j) + N3 = (d+2)/(d+2-k) + push!(vals, S2-S3) + push!(vals, S3-S1) end end NF = length(vals) @@ -74,3 +81,64 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di end +function interpolate(fields, interpolant::GWPCurlRefSpace{T,Degree}, chart) where {T,Degree} + + d = Degree + dim = (d+1)*(d+3) + + s = range(zero(T), one(T), length=d+3) + + edges = faces(chart) + + edge = edges[1] + fields_edge = trace(edge, chart, fields) + i = 0 + Q1 = stack(1:d+1) do j + k = (d+2)-i-j + u_edge = s[j+1] + p_edge = neighborhood(edge, (u_edge,)) + @show cartesian(p_edge) + t_edge = -tangents(p_edge, 1) + vals = fields_edge(u_edge) + [dot(t_edge, val) for val in vals] + end + + edge = edges[2] + fields_edge = trace(edge, chart, fields) + Q2 = stack(1:d+1) do i + j = 0 + k = (d+2)-i-j + u_edge = 1 - s[i+1] + p_edge = neighborhood(edge, (u_edge,)) + t_edge = -tangents(p_edge, 1) + vals = fields_edge(u_edge) + [dot(t_edge, val) for val in vals] + end + + edge = edges[3] + fields_edge = trace(edge, chart, fields) + Q3 = stack(1:d+1) do i + j = (d+2)-i + k = 0 + u_edge = 1-s[j+1] + p_edge = neighborhood(edge, (u_edge,)) + t_edge = -tangents(p_edge, 1) + vals = fields_edge(u_edge) + [dot(t_edge, val) for val in vals] + end + + Q = hcat(Q1,Q2,Q3) + if d > 1 + S = ((i,j,d+2-i-j) for i in 1:d+1 for j in 1:d+1 if d+2-i-j > 0) + for (i,j,k) in S + p_chart = neighborhood(chart, (s[i+1],s[j+1])) + t_i = tangents(p_chart, 1) + t_j = tangents(p_chart, 2) + vals = fields(p_chart) + q_i = [dot(t_i, val) for val in vals] + q_j = [dot(t_j, val) for val in vals] + Q = hcat(Q, q_i, q_j) + end + end + return Q +end \ No newline at end of file diff --git a/test/test_gwp.jl b/test/test_gwp.jl index 7d1b83b7..bc47712f 100644 --- a/test/test_gwp.jl +++ b/test/test_gwp.jl @@ -1,4 +1,4 @@ -@testitem "GWPRefSpace eval" begin +@testitem "refspace: dimension" begin using CompScienceMeshes T = Float64 @@ -9,4 +9,33 @@ u = (one(T)/3, one(T)/3) vals = ϕ(dom, u) + + nf = (Degree+1)*(Degree+3) + @test length(vals) == nf +end + + +@testitem "refspace: self-interpolate" begin + using CompScienceMeshes + + T, Dim, Nverts, Degree = Float64, 2, 3, 2 + dom = CompScienceMeshes.ReferenceSimplex{Dim,T,Nverts}() + ϕ = BEAST.GWPCurlRefSpace{T,Degree}() + + fields(p) = [v.value for v in ϕ(dom, p)] + # fields(p) = [ϕ(dom,p)[1].value] + coeffs = BEAST.interpolate(fields, ϕ, dom) + + display(round.(coeffs, digits=3)) + + # pts = [ + # point(T, 0.3, 0.1), + # point(T, 0.1, 0.3), + # point(T, 0.5, 0.0)] + + # for u in pts + # vals = ϕ(dom, u) + # r = sum(c * v.value for (c,v) in zip(coeffs, vals)) + # @show r + # end end \ No newline at end of file From 1813773501794f45b33dac89f4cb9b734e3c6b21 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 7 Oct 2024 15:24:07 +0200 Subject: [PATCH 401/528] divergence tested and efie seems to work --- examples/efie_gwp2.jl | 31 ++++++ examples/mfie_gwp2.jl | 30 ++++++ src/BEAST.jl | 4 + src/bases/global/gwpdivglobal.jl | 39 +++++++ src/bases/global/gwpglobal.jl | 179 +++++++++++++++++++++++++++++++ src/bases/lagrange.jl | 3 +- src/bases/local/gwpdivlocal.jl | 69 ++++++++++++ src/bases/local/gwplocal.jl | 82 +++++++++++--- src/bases/local/rtqlocal.jl | 1 + src/bases/rtqspace.jl | 3 +- src/excitation.jl | 2 +- src/gridfunction.jl | 5 +- src/postproc.jl | 6 +- src/utils/lagpolys.jl | 6 +- test/test_gwp.jl | 48 +++++++-- 15 files changed, 478 insertions(+), 30 deletions(-) create mode 100644 examples/efie_gwp2.jl create mode 100644 examples/mfie_gwp2.jl create mode 100644 src/bases/global/gwpdivglobal.jl create mode 100644 src/bases/global/gwpglobal.jl create mode 100644 src/bases/local/gwpdivlocal.jl diff --git a/examples/efie_gwp2.jl b/examples/efie_gwp2.jl new file mode 100644 index 00000000..b343e991 --- /dev/null +++ b/examples/efie_gwp2.jl @@ -0,0 +1,31 @@ +using CompScienceMeshes +using BEAST + +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +# Γ = meshsphere(radius=1.0, h=0.1) +@show length(Γ) +# X = raviartthomas(Γ) +X = BEAST.gwpdiv(Γ; order=2) + +κ, η = 1.0, 1.0 +t = Maxwell3D.singlelayer(wavenumber=κ) +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +e = (n × E) × n + +BEAST.@defaultquadstrat (t,X,X) BEAST.DoubleNumSauterQstrat(7,8,6,6,6,6) +BEAST.@defaultquadstrat (e,X) BEAST.SingleNumQStrat(12) + +@hilbertspace j +@hilbertspace k +# efie = @discretise t[k,j]==e[k] j∈X k∈X +# u, ch = BEAST.gmres_ch(efie; restart=1500) + +A = assemble(t[k,j], X, X) +b = assemble(e[k], X) +Ai = BEAST.GMRESSolver(A; reltol=1e-8, restart=1500) +u = Ai * b + +include("utils/postproc.jl") +include("utils/plotresults.jl") + +# Id = BEAST.Identity() \ No newline at end of file diff --git a/examples/mfie_gwp2.jl b/examples/mfie_gwp2.jl new file mode 100644 index 00000000..e235f38b --- /dev/null +++ b/examples/mfie_gwp2.jl @@ -0,0 +1,30 @@ +using CompScienceMeshes, BEAST + +# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +Γ = meshsphere(radius=1.0, h=0.1) +# X, Y = raviartthomas(Γ), buffachristiansen(Γ) +# X = brezzidouglasmarini(Γ) +# Y = brezzidouglasmarini(Γ) +# X = raviartthomas(Γ) +# Y = raviartthomas(Γ) +X = BEAST.gwpdiv(Γ; order=2) +Y = BEAST.gwpdiv(Γ; order=2) + +ϵ, μ, ω = 1.0, 1.0, 1.0; κ = ω * √(ϵ*μ) +# κ = 3.0 +NK, Id = BEAST.DoubleLayerRotatedMW3D(1.0, im*κ), Identity() +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +H = -1/(im*μ*ω)*curl(E) +h = n × H + +BEAST.@defaultquadstrat (NK,X,X) BEAST.DoubleNumSauterQstrat(7,8,6,6,6,6) +BEAST.@defaultquadstrat (Id,X,X) BEAST.SingleNumQStrat(7) +BEAST.@defaultquadstrat (h,X) BEAST.SingleNumQStrat(12) + +@hilbertspace j +@hilbertspace m +mfie = @discretise (NK-0.5Id)[m,j] == h[m] j∈X m∈Y +u = gmres(mfie) + +include("utils/postproc.jl") +include("utils/plotresults.jl") diff --git a/src/BEAST.jl b/src/BEAST.jl index 96d4fc5d..879a8e94 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -156,6 +156,8 @@ include("bases/local/ndlcdlocal.jl") include("bases/local/bdm3dlocal.jl") include("bases/local/rtqlocal.jl") include("bases/local/gwplocal.jl") +include("bases/local/gwpdivlocal.jl") + include("bases/basis.jl") include("bases/lincomb.jl") @@ -163,6 +165,8 @@ include("bases/trace.jl") include("bases/restrict.jl") include("bases/divergence.jl") include("bases/global/globaldofs.jl") +include("bases/global/gwpglobal.jl") +include("bases/global/gwpdivglobal.jl") include("bases/lagrange.jl") include("bases/rtspace.jl") diff --git a/src/bases/global/gwpdivglobal.jl b/src/bases/global/gwpdivglobal.jl new file mode 100644 index 00000000..1b97f43d --- /dev/null +++ b/src/bases/global/gwpdivglobal.jl @@ -0,0 +1,39 @@ +struct GWPDivSpace{T,M,P} <: Space{T} + geo::M + fns::Vector{Vector{Shape{T}}} + pos::P + degree::Int +end + +function refspace(s::GWPDivSpace{T}) where {T} GWPDivRefSpace{T,s.degree}() end +function subset(s::S,I) where {S<:GWPDivSpace} S(s.geo, s.fns[I], s.pos[I], s.degree) end + +function gwpdiv(mesh, edges=nothing; order) + + T = coordtype(mesh) + S = Shape{T} + + space = BEAST.gwpcurl(mesh, edges; order) + fns = Vector{Vector{S}}(undef, length(space.fns)) + for (i,fn) in enumerate(space.fns) + fns[i] = [S(s.cellid, s.refid, -s.coeff) for s in fn] + end + + return GWPDivSpace(space.geo, fns, space.pos, order) +end + +@testitem "GWPcurl global: numfunctions" begin + using CompScienceMeshes + + h = 0.5 + mesh = meshrectangle(1.0, 1.0, 0.5) + edges = setminus(skeleton(mesh,1), boundary(mesh)) + + order = 2 + gwp = BEAST.gwpdiv(mesh, edges; order=order) + + ne = order+1 + nf = order * (order+1) + Nt = length(edges)*ne + length(mesh)*nf + @test numfunctions(gwp) == Nt +end \ No newline at end of file diff --git a/src/bases/global/gwpglobal.jl b/src/bases/global/gwpglobal.jl new file mode 100644 index 00000000..3270e4da --- /dev/null +++ b/src/bases/global/gwpglobal.jl @@ -0,0 +1,179 @@ +struct GWPCurlSpace{T,M,P} <: Space{T} + geo::M + fns::Vector{Vector{Shape{T}}} + pos::P + degree::Int +end + +function refspace(s::GWPCurlSpace{T}) where {T} GWPCurlRefSpace{T,s.degree}() end +function subset(s::S,I) where {S<:GWPCurlSpace} S(s.geo, s.fns[I], s.pos[I], s.degree) end + +struct GWPGlobalEdgeDoFs order::Int end +struct GWPGlobalFaceDoFs order::Int end + +function globaldofs(edge_ch, face_ch, localspace, dof::GWPGlobalEdgeDoFs) + f = trace(edge_ch, face_ch, localspace) + T = coordtype(edge_ch) + ds = one(T) / (dof.order+2) + return stack(range(ds, step=ds, length=dof.order+1)) do s + p = neighborhood(edge_ch, s) + t = tangents(p, 1) + [dot(t,x.value) for x in f(s)] + end +end + +@testitem "GWP edge global dofs" begin + using CompScienceMeshes + T = Float64 + face_ch = simplex( + point(3,0,0), + point(2,0,-1), + point(0,0,1), + ) + edge_ch = simplex( + point(0,0,1), + point(2,0,-1), + ) + + degree = 3 + localspace = BEAST.GWPCurlRefSpace{T,degree}() + dofs = BEAST.GWPGlobalEdgeDoFs(degree) + A = BEAST.globaldofs(edge_ch, face_ch, localspace, dofs) + display(round.(A, digits=3)) +end + +function globaldofs(edge_ch, face_ch, localspace, dof::GWPGlobalFaceDoFs) + + d = dof.order + T = coordtype(edge_ch) + ds = one(T) / (d+2) + + f = trace(edge_ch, face_ch, localspace) + Q = zeros(T, numfunctions(localspace, domain(face_ch)), d*(d+1)) + S = ((i,j,d+2-i-j) for i in 1:d+1 for j in 1:d+1 if d+2-i-j > 0) + # return stack(S) do (i,j,k) + idx = 1 + for (i,j,k) in S + u = (i*ds,j*ds) + p = neighborhood(edge_ch, u) + t1 = tangents(p,1) + t2 = tangents(p,2) + vals = f(u) + q1 = [dot(t1,x.value) for x in vals] + q2 = [dot(t2,x.value) for x in vals] + Q[:,idx] = q1; idx += 1 + Q[:,idx] = q2; idx += 1 + end + + return Q +end + + +@testitem "GWP face global dofs" begin + using CompScienceMeshes + T = Float64 + face_ch = simplex( + point(3,0,0), + point(2,0,-1), + point(0,0,1), + ) + # edge_ch = simplex( + # point(0,0,1), + # point(2,0,-1), + # point(3,0,0) + # ) + edge_ch = simplex( + point(3,0,0), + point(2,0,-1), + point(0,0,1), + ) + + degree = 3 + localspace = BEAST.GWPCurlRefSpace{T,degree}() + dofs = BEAST.GWPGlobalFaceDoFs(degree) + A = BEAST.globaldofs(edge_ch, face_ch, localspace, dofs) + display(round.(A, digits=3)) +end + +function _addshapes!(fns, cell, gids, lids, β) + T = eltype(β) + atol = sqrt(eps(T)) + for i in axes(β,1) + fn = fns[gids[i]] + for j in axes(β,2) + isapprox(β[i,j], 0; atol) && continue + push!(fn, Shape{T}(cell, lids[j], β[i,j])) + end + end +end + + +function gwpcurl(mesh, edges=nothing; order) + + T = coordtype(mesh) + atol = sqrt(eps(T)) + + if edges == nothing + edges = setminus(skeleton(mesh, 1), boundary(mesh)) + end + + C12 = connectivity(mesh, edges, abs) + R12 = rowvals(C12) + V12 = nonzeros(C12) + + ne = order+1 + nf = order*(order+1) + + Ne = ne * length(edges) + Nf = nf * length(mesh) + Nt = Ne + Nf + + localspace = GWPCurlRefSpace{T,order}() + refdom = domain(chart(mesh, first(mesh))) + localdim = numfunctions(localspace, refdom) + + fns = [Shape{T}[] for n in 1:Nt] + pos = fill(point(0,0,0), Nt) + for cell in mesh + cell_ch = chart(mesh, cell) + for k in nzrange(C12, cell) + e = R12[k] + i = V12[k] + gids = (e-1)*ne .+ (1:ne) + lids = localindices(localspace, refdom, Val{1}, i) + edge_ch = chart(edges, e) + vals = globaldofs(edge_ch, cell_ch, localspace, GWPGlobalEdgeDoFs(order)) + α = vals[lids,:] + _addshapes!(fns, cell, gids, lids, inv(α')) + end + + order < 2 && continue + face_ch = chart(mesh, cell) + gids = Ne + (cell-1)*nf .+ (1:nf) + lids = localindices(localspace, refdom, Val{2}, 1) + vals = globaldofs(face_ch, face_ch, localspace, GWPGlobalFaceDoFs(order)) + α = vals[lids,:] + _addshapes!(fns, cell, gids, lids, inv(α')) + end + + for e in edges pos[(e-1)*ne .+ (1:ne)] .= Ref(cartesian(center(chart(edges, e)))) end + for f in mesh pos[Ne+(f-1)*nf .+ (1: nf)] .= Ref(cartesian(center(chart(mesh, f)))) end + + return GWPCurlSpace(mesh, fns, pos, order) +end + +@testitem "GWPcurl global: numfunctions" begin + using CompScienceMeshes + + h = 0.5 + mesh = meshrectangle(1.0, 1.0, 0.5) + edges = setminus(skeleton(mesh,1), boundary(mesh)) + + order = 2 + gwp = BEAST.gwpcurl(mesh, edges; order=order) + + ne = order+1 + nf = order * (order+1) + Nt = length(edges)*ne + length(mesh)*nf + @test numfunctions(gwp) == Nt +end \ No newline at end of file diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index a0bbb336..77297b5d 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -977,7 +977,8 @@ function lagrangec0(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}; order) N = nV + nE + nF localspace = LagrangeRefSpace{T,order,3,binomial(2+order,2)}() - localdim = numfunctions(localspace) + dom = domain(chart(mesh, first(mesh))) + localdim = numfunctions(localspace, dom) d = order fns = [S[] for n in 1:(nV+nE+nF)] diff --git a/src/bases/local/gwpdivlocal.jl b/src/bases/local/gwpdivlocal.jl new file mode 100644 index 00000000..8c21b9c4 --- /dev/null +++ b/src/bases/local/gwpdivlocal.jl @@ -0,0 +1,69 @@ +struct GWPDivRefSpace{T,Degree} <: RefSpace{T} end + +function numfunctions(x::GWPDivRefSpace{<:Any,D}, + dom::CompScienceMeshes.ReferenceSimplex{2}) where{D} (D+1)*(D+3) end + +function (ϕ::GWPDivRefSpace{T,Degree})(p) where {T,Degree} + ψ = GWPCurlRefSpace{T,Degree}() + dom = domain(chart(p)) + u = parametric(p) + vals = ψ(dom, u) + vals = pushforwardcurl(vals, p) + n = normal(p) + map(vals) do v + (value = -n × v.value, divergence=v.curl) + end +end + +function interpolate(fields, interpolant::GWPDivRefSpace{T,Degree}, chart) where {T,Degree} + + function nxfields(p) + vals = fields(p) + n = normal(p) + return map(v -> n×v, vals) + end + + return interpolate(nxfields, GWPCurlRefSpace{T,Degree}(), chart) +end + +@testitem "divergence" begin + using CompScienceMeshes, LinearAlgebra + + T = Float64 + s = simplex( + point(3,0,0), + point(0,2,1), + point(-1,-1,-1), + ) + + order = 2 + ϕ = BEAST.GWPDivRefSpace{T,order}() + + u = T(0.2432); dx = sqrt(eps(T)) + v = T(0.5786); dy = sqrt(eps(T)) + + # p00 = neighborhood(s, (u,v)) + # p10 = neighborhood(s, (u+du,v)) + # p01 = neighborhood(s, (u, v+dv)) + + p00 = neighborhood(s, (u,v)) + t1 = normalize(tangents(p00,1)) + t2 = normal(p00) × t1 + + p10 = neighborhood(s, carttobary(s, cartesian(p00) + dx*t1 + 0*t2)) + p01 = neighborhood(s, carttobary(s, cartesian(p00) + 0*t1 + dy*t2)) + + ϕ00 = ϕ(p00) + ϕ10 = ϕ(p10) + ϕ01 = ϕ(p01) + + + for (f00, f10, f01) in zip(ϕ00, ϕ10, ϕ01) + df1dx = dot(t1, f10.value - f00.value) / dx + df2dy = dot(t2, f01.value - f00.value) / dy + curl_num = df1dx + df2dy + curl_ana = f00.divergence + # @show curl_num, curl_ana, abs(curl_num - curl_ana) + @test curl_num ≈ curl_ana atol=sqrt(eps(T))*100 + end +end \ No newline at end of file diff --git a/src/bases/local/gwplocal.jl b/src/bases/local/gwplocal.jl index 6dfa4c7c..1dc4dd4d 100644 --- a/src/bases/local/gwplocal.jl +++ b/src/bases/local/gwplocal.jl @@ -1,5 +1,8 @@ struct GWPCurlRefSpace{T,Degree} <: RefSpace{T} end +function numfunctions(x::GWPCurlRefSpace{<:Any,D}, + dom::CompScienceMeshes.ReferenceSimplex{2}) where{D} (D+1)*(D+3) end + function (ϕ::GWPCurlRefSpace{T,Degree})(p) where {T,Degree} dom = domain(chart(p)) u = parametric(p) @@ -14,18 +17,15 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di u, v = u w = 1-u-v - s = range(zero(T), one(T), length=d+3) - # rwg1 = point(T, u-1, v) - # rwg2 = point(T, u, v-1) - # rwg3 = point(T, u, v) + s = range(zero(T), one(T), length=d+3) + nd1 = point(T, -v, u-1) nd2 = point(T, -v+1, u) nd3 = point(T, -v, u) P = SVector{2,T} vals = P[] - dffu = T[] - dffv = T[] + crls = T[] i = 0 for j in 1:d+1 @@ -35,7 +35,13 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di Rₖ = _sylpoly_shift(s, k+1, w) push!(vals, Rᵢ*Rⱼ*Rₖ*nd1) - # du = _sylpoly_shift_diff(s, i+1, u)*Rⱼ*Rₖ + dRᵢ = _sylpoly_diff(s, i+1, u) + dRⱼ = _sylpoly_shift_diff(s, j+1, v) + dRₖ = _sylpoly_shift_diff(s, k+1, w) + du = dRᵢ*Rⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ + dv = Rᵢ*dRⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ + curl = (du*nd1[2] - dv*nd1[1]) + 2*Rᵢ*Rⱼ*Rₖ + push!(crls, curl) end for i in 1:d+1 @@ -45,6 +51,14 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di Rⱼ = _sylpoly(s, j+1, v) Rₖ = _sylpoly_shift(s, k+1, w) push!(vals, Rᵢ*Rⱼ*Rₖ*nd2) + + dRᵢ = _sylpoly_shift_diff(s, i+1, u) + dRⱼ = _sylpoly_diff(s, j+1, v) + dRₖ = _sylpoly_shift_diff(s, k+1, w) + du = dRᵢ*Rⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ + dv = Rᵢ*dRⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ + curl = (du*nd2[2] - dv*nd2[1]) + 2*Rᵢ*Rⱼ*Rₖ + push!(crls, curl) end for i in 1:d+1 @@ -54,6 +68,16 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di Rⱼ = _sylpoly_shift(s, j+1, v) Rₖ = _sylpoly(s, k+1, w) push!(vals, Rᵢ*Rⱼ*Rₖ*nd3) + + dRᵢ = _sylpoly_shift_diff(s, i+1, u) + dRⱼ = _sylpoly_shift_diff(s, j+1, v) + dRₖ = _sylpoly_diff(s, k+1, w) + + du = dRᵢ*Rⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ + dv = Rᵢ*dRⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ + curl = (du*nd3[2] - dv*nd3[1]) + 2*Rᵢ*Rⱼ*Rₖ + + push!(crls, curl) end for i in 1:d+1 @@ -69,15 +93,34 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di S1 = Rᵢ*Rsⱼ*Rsₖ*nd1 S2 = Rsᵢ*Rⱼ*Rsₖ*nd2 S3 = Rsᵢ*Rsⱼ*Rₖ*nd3 - N1 = (d+2)/(d+2-i) - N2 = (d+2)/(d+2-j) - N3 = (d+2)/(d+2-k) push!(vals, S2-S3) push!(vals, S3-S1) + + dRsᵢ = _sylpoly_shift_diff(s, i+1, u) + dRsⱼ = _sylpoly_shift_diff(s, j+1, v) + dRsₖ = _sylpoly_shift_diff(s, k+1, w) + dRᵢ = _sylpoly_diff(s, i+1, u) + dRⱼ = _sylpoly_diff(s, j+1, v) + dRₖ = _sylpoly_diff(s, k+1, w) + + du = dRᵢ*Rsⱼ*Rsₖ - Rᵢ*Rsⱼ*dRsₖ + dv = Rᵢ*dRsⱼ*Rsₖ - Rᵢ*Rsⱼ*dRsₖ + curlS1 = du*nd1[2] - dv*nd1[1] + 2*Rᵢ*Rsⱼ*Rsₖ + + du = dRsᵢ*Rⱼ*Rsₖ - Rsᵢ*Rⱼ*dRsₖ + dv = Rsᵢ*dRⱼ*Rsₖ - Rsᵢ*Rⱼ*dRsₖ + curlS2 = du*nd2[2] - dv*nd2[1] + 2*Rsᵢ*Rⱼ*Rsₖ + + du = dRsᵢ*Rsⱼ*Rₖ - Rsᵢ*Rsⱼ*dRₖ + dv = Rsᵢ*dRsⱼ*Rₖ - Rsᵢ*Rsⱼ*dRₖ + curlS3 = du*nd3[2] - dv*nd3[1] + 2*Rsᵢ*Rsⱼ*Rₖ + + push!(crls, curlS2 - curlS3) + push!(crls, curlS3 - curlS1) end end NF = length(vals) - SVector{NF}([(value=f, curl=zero(T)) for f in vals]) + SVector{NF}([(value=f, curl=c) for (f,c) in zip(vals, crls)]) end @@ -141,4 +184,19 @@ function interpolate(fields, interpolant::GWPCurlRefSpace{T,Degree}, chart) wher end end return Q -end \ No newline at end of file +end + +function localindices(localspace::GWPCurlRefSpace{<:Any,Degree}, domain, + dim::Type{Val{1}}, i) where {Degree} + + ne = Degree+1 + (i-1)*ne .+ (1:ne) +end + +function localindices(localspace::GWPCurlRefSpace{<:Any,Degree}, domain, + dim::Type{Val{2}}, i) where {Degree} + + ne = Degree+1 + nf = Degree * (Degree + 1) + 3*ne .+ (1:nf) +end diff --git a/src/bases/local/rtqlocal.jl b/src/bases/local/rtqlocal.jl index f7e38347..f1472d52 100644 --- a/src/bases/local/rtqlocal.jl +++ b/src/bases/local/rtqlocal.jl @@ -22,6 +22,7 @@ function (ϕ::RTQuadRefSpace{T})(p) where {T} (value=D*f.value/j, divergence=f.divergence/j) end end +function numfunctions(ϕ::RTQuadRefSpace, dom::CompScienceMeshes.RefQuadrilateral) 4 end function interpolate(fields, interpolant::RTQuadRefSpace{T}, chart) where {T} diff --git a/src/bases/rtqspace.jl b/src/bases/rtqspace.jl index 2935c223..7f701ee6 100644 --- a/src/bases/rtqspace.jl +++ b/src/bases/rtqspace.jl @@ -97,7 +97,8 @@ end num_cells = numcells(m) num_bfs = numfunctions(s) r = refspace(s) - num_refs = numfunctions(r) + dom = domain(chart(m, first(m))) + num_refs = numfunctions(r, dom) celltonum = BEAST.make_celltonum(num_cells, num_refs, num_bfs, s) els, ad, a2g = BEAST.assemblydata(s) diff --git a/src/excitation.jl b/src/excitation.jl index f194a86c..17bc17f0 100644 --- a/src/excitation.jl +++ b/src/excitation.jl @@ -86,7 +86,7 @@ function assemble!(field::Functional, tfs::subdBasis, store; end -function celltestvalues(tshs::RefSpace{T}, tcell, field, qr) where {T,NF} +function celltestvalues(tshs::RefSpace{T}, tcell, field, qr) where {T} num_tshs = numfunctions(tshs, domain(tcell)) interactions = zeros(Complex{T}, num_tshs) diff --git a/src/gridfunction.jl b/src/gridfunction.jl index 05da678f..56931843 100644 --- a/src/gridfunction.jl +++ b/src/gridfunction.jl @@ -279,7 +279,7 @@ function cellquadsamplingvaluesvalues!( qr ) - num_tshs = numfunctions(tshs) + # num_tshs = numfunctions(tshs) num_oqp = length(qr) @@ -289,7 +289,8 @@ function cellquadsamplingvaluesvalues!( tvals = qr[p].value - for m in 1 : num_tshs + for m in 1 : length(tvals) + # for m in 1 : num_tshs tval = tvals[m] for k = 1:size(data, 1) diff --git a/src/postproc.jl b/src/postproc.jl index 398f4c8f..b720a9b5 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -10,12 +10,14 @@ function facecurrents(coeffs, basis) T = eltype(coeffs) RT = real(T) + mesh = geometry(basis) + dom = domain(chart(mesh, first(mesh))) + refs = refspace(basis) - numrefs = numfunctions(refs) + numrefs = numfunctions(refs, dom) cells, tad, a2g = assemblydata(basis) - mesh = geometry(basis) D = dimension(mesh) # U = D+1 U = 3 diff --git a/src/utils/lagpolys.jl b/src/utils/lagpolys.jl index 65d3c924..60620706 100644 --- a/src/utils/lagpolys.jl +++ b/src/utils/lagpolys.jl @@ -11,15 +11,17 @@ function _lagpoly(nodes, i, s, i0=1, i1=length(nodes)) end function _lagpoly_diff(nodes, i, s, i0=1, i1=length(nodes)) + T = typeof(s) r = zero(T) si = nodes[i] for p in i0:i1 p == i && continue + sp = nodes[p] rp = one(T) for j in i0:i1 j == i && continue j == p && continue - sj = nodees[j] + sj = nodes[j] rp *= (s - sj) / (si - sj) end rp *= 1 / (si - sp) @@ -41,5 +43,5 @@ function _sylpoly_shift(nodes, i, s) end function _sylpoly_shift_diff(nodes, i, s) - _lagpoly_shift(nodes, i, s, 2, i) + _lagpoly_diff(nodes, i, s, 2, i) end \ No newline at end of file diff --git a/test/test_gwp.jl b/test/test_gwp.jl index bc47712f..ca2210e8 100644 --- a/test/test_gwp.jl +++ b/test/test_gwp.jl @@ -17,6 +17,7 @@ end @testitem "refspace: self-interpolate" begin using CompScienceMeshes + using LinearAlgebra T, Dim, Nverts, Degree = Float64, 2, 3, 2 dom = CompScienceMeshes.ReferenceSimplex{Dim,T,Nverts}() @@ -26,16 +27,45 @@ end # fields(p) = [ϕ(dom,p)[1].value] coeffs = BEAST.interpolate(fields, ϕ, dom) + nf = numfunctions(ϕ, dom) + @test coeffs ≈ Matrix{T}(I,nf,nf) atol=sqrt(eps(T)) display(round.(coeffs, digits=3)) - # pts = [ - # point(T, 0.3, 0.1), - # point(T, 0.1, 0.3), - # point(T, 0.5, 0.0)] +end + + +@testitem "confspace: interpolate lowest order" begin + using CompScienceMeshes + using LinearAlgebra + + T, Dim, Nverts, Degree = Float64, 2, 3, 2 + supp = simplex( + point(T,3,0,0), + point(T,0,2,0), + point(T,0,0,-1)) + + ϕ = BEAST.GWPCurlRefSpace{T,Degree}() + ψ = BEAST.NDRefSpace{T}() + + fields(p) = [v.value for v in ψ(p)] + coeffs = BEAST.interpolate(fields, ϕ, supp) + + nf1 = numfunctions(ψ, domain(supp)) + nf2 = numfunctions(ϕ, domain(supp)) + @test size(coeffs) == (nf1, nf2) + display(round.(coeffs, digits=3)) + + pts = [ + point(T, 0.3, 0.1), + point(T, 0.1, 0.3), + point(T, 0.5, 0.0)] - # for u in pts - # vals = ϕ(dom, u) - # r = sum(c * v.value for (c,v) in zip(coeffs, vals)) - # @show r - # end + for (i,u) in enumerate(pts) + p = neighborhood(supp, u) + basisvals = ϕ(p) + fieldvals = ψ(p) + r = sum(c * v.value for (c,v) in zip(coeffs[i,:], basisvals)) + s = fieldvals[i].value + @test r ≈ s atol=sqrt(eps(T)) + end end \ No newline at end of file From 3837d9195c235b93dcf3836ef0deb8e6252c9792 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 7 Oct 2024 15:28:38 +0200 Subject: [PATCH 402/528] divergence tested and efie seems to work --- examples/efie_gwp2.jl | 31 ++++++ examples/mfie_gwp2.jl | 30 ++++++ src/BEAST.jl | 4 + src/bases/global/gwpdivglobal.jl | 39 +++++++ src/bases/global/gwpglobal.jl | 179 +++++++++++++++++++++++++++++++ src/bases/lagrange.jl | 3 +- src/bases/local/gwpdivlocal.jl | 69 ++++++++++++ src/bases/local/gwplocal.jl | 82 +++++++++++--- src/bases/local/rtqlocal.jl | 1 + src/bases/rtqspace.jl | 3 +- src/excitation.jl | 2 +- src/gridfunction.jl | 5 +- src/postproc.jl | 6 +- src/utils/lagpolys.jl | 6 +- test/test_gwp.jl | 48 +++++++-- 15 files changed, 478 insertions(+), 30 deletions(-) create mode 100644 examples/efie_gwp2.jl create mode 100644 examples/mfie_gwp2.jl create mode 100644 src/bases/global/gwpdivglobal.jl create mode 100644 src/bases/global/gwpglobal.jl create mode 100644 src/bases/local/gwpdivlocal.jl diff --git a/examples/efie_gwp2.jl b/examples/efie_gwp2.jl new file mode 100644 index 00000000..b343e991 --- /dev/null +++ b/examples/efie_gwp2.jl @@ -0,0 +1,31 @@ +using CompScienceMeshes +using BEAST + +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +# Γ = meshsphere(radius=1.0, h=0.1) +@show length(Γ) +# X = raviartthomas(Γ) +X = BEAST.gwpdiv(Γ; order=2) + +κ, η = 1.0, 1.0 +t = Maxwell3D.singlelayer(wavenumber=κ) +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +e = (n × E) × n + +BEAST.@defaultquadstrat (t,X,X) BEAST.DoubleNumSauterQstrat(7,8,6,6,6,6) +BEAST.@defaultquadstrat (e,X) BEAST.SingleNumQStrat(12) + +@hilbertspace j +@hilbertspace k +# efie = @discretise t[k,j]==e[k] j∈X k∈X +# u, ch = BEAST.gmres_ch(efie; restart=1500) + +A = assemble(t[k,j], X, X) +b = assemble(e[k], X) +Ai = BEAST.GMRESSolver(A; reltol=1e-8, restart=1500) +u = Ai * b + +include("utils/postproc.jl") +include("utils/plotresults.jl") + +# Id = BEAST.Identity() \ No newline at end of file diff --git a/examples/mfie_gwp2.jl b/examples/mfie_gwp2.jl new file mode 100644 index 00000000..e235f38b --- /dev/null +++ b/examples/mfie_gwp2.jl @@ -0,0 +1,30 @@ +using CompScienceMeshes, BEAST + +# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +Γ = meshsphere(radius=1.0, h=0.1) +# X, Y = raviartthomas(Γ), buffachristiansen(Γ) +# X = brezzidouglasmarini(Γ) +# Y = brezzidouglasmarini(Γ) +# X = raviartthomas(Γ) +# Y = raviartthomas(Γ) +X = BEAST.gwpdiv(Γ; order=2) +Y = BEAST.gwpdiv(Γ; order=2) + +ϵ, μ, ω = 1.0, 1.0, 1.0; κ = ω * √(ϵ*μ) +# κ = 3.0 +NK, Id = BEAST.DoubleLayerRotatedMW3D(1.0, im*κ), Identity() +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +H = -1/(im*μ*ω)*curl(E) +h = n × H + +BEAST.@defaultquadstrat (NK,X,X) BEAST.DoubleNumSauterQstrat(7,8,6,6,6,6) +BEAST.@defaultquadstrat (Id,X,X) BEAST.SingleNumQStrat(7) +BEAST.@defaultquadstrat (h,X) BEAST.SingleNumQStrat(12) + +@hilbertspace j +@hilbertspace m +mfie = @discretise (NK-0.5Id)[m,j] == h[m] j∈X m∈Y +u = gmres(mfie) + +include("utils/postproc.jl") +include("utils/plotresults.jl") diff --git a/src/BEAST.jl b/src/BEAST.jl index 96d4fc5d..879a8e94 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -156,6 +156,8 @@ include("bases/local/ndlcdlocal.jl") include("bases/local/bdm3dlocal.jl") include("bases/local/rtqlocal.jl") include("bases/local/gwplocal.jl") +include("bases/local/gwpdivlocal.jl") + include("bases/basis.jl") include("bases/lincomb.jl") @@ -163,6 +165,8 @@ include("bases/trace.jl") include("bases/restrict.jl") include("bases/divergence.jl") include("bases/global/globaldofs.jl") +include("bases/global/gwpglobal.jl") +include("bases/global/gwpdivglobal.jl") include("bases/lagrange.jl") include("bases/rtspace.jl") diff --git a/src/bases/global/gwpdivglobal.jl b/src/bases/global/gwpdivglobal.jl new file mode 100644 index 00000000..1b97f43d --- /dev/null +++ b/src/bases/global/gwpdivglobal.jl @@ -0,0 +1,39 @@ +struct GWPDivSpace{T,M,P} <: Space{T} + geo::M + fns::Vector{Vector{Shape{T}}} + pos::P + degree::Int +end + +function refspace(s::GWPDivSpace{T}) where {T} GWPDivRefSpace{T,s.degree}() end +function subset(s::S,I) where {S<:GWPDivSpace} S(s.geo, s.fns[I], s.pos[I], s.degree) end + +function gwpdiv(mesh, edges=nothing; order) + + T = coordtype(mesh) + S = Shape{T} + + space = BEAST.gwpcurl(mesh, edges; order) + fns = Vector{Vector{S}}(undef, length(space.fns)) + for (i,fn) in enumerate(space.fns) + fns[i] = [S(s.cellid, s.refid, -s.coeff) for s in fn] + end + + return GWPDivSpace(space.geo, fns, space.pos, order) +end + +@testitem "GWPcurl global: numfunctions" begin + using CompScienceMeshes + + h = 0.5 + mesh = meshrectangle(1.0, 1.0, 0.5) + edges = setminus(skeleton(mesh,1), boundary(mesh)) + + order = 2 + gwp = BEAST.gwpdiv(mesh, edges; order=order) + + ne = order+1 + nf = order * (order+1) + Nt = length(edges)*ne + length(mesh)*nf + @test numfunctions(gwp) == Nt +end \ No newline at end of file diff --git a/src/bases/global/gwpglobal.jl b/src/bases/global/gwpglobal.jl new file mode 100644 index 00000000..3270e4da --- /dev/null +++ b/src/bases/global/gwpglobal.jl @@ -0,0 +1,179 @@ +struct GWPCurlSpace{T,M,P} <: Space{T} + geo::M + fns::Vector{Vector{Shape{T}}} + pos::P + degree::Int +end + +function refspace(s::GWPCurlSpace{T}) where {T} GWPCurlRefSpace{T,s.degree}() end +function subset(s::S,I) where {S<:GWPCurlSpace} S(s.geo, s.fns[I], s.pos[I], s.degree) end + +struct GWPGlobalEdgeDoFs order::Int end +struct GWPGlobalFaceDoFs order::Int end + +function globaldofs(edge_ch, face_ch, localspace, dof::GWPGlobalEdgeDoFs) + f = trace(edge_ch, face_ch, localspace) + T = coordtype(edge_ch) + ds = one(T) / (dof.order+2) + return stack(range(ds, step=ds, length=dof.order+1)) do s + p = neighborhood(edge_ch, s) + t = tangents(p, 1) + [dot(t,x.value) for x in f(s)] + end +end + +@testitem "GWP edge global dofs" begin + using CompScienceMeshes + T = Float64 + face_ch = simplex( + point(3,0,0), + point(2,0,-1), + point(0,0,1), + ) + edge_ch = simplex( + point(0,0,1), + point(2,0,-1), + ) + + degree = 3 + localspace = BEAST.GWPCurlRefSpace{T,degree}() + dofs = BEAST.GWPGlobalEdgeDoFs(degree) + A = BEAST.globaldofs(edge_ch, face_ch, localspace, dofs) + display(round.(A, digits=3)) +end + +function globaldofs(edge_ch, face_ch, localspace, dof::GWPGlobalFaceDoFs) + + d = dof.order + T = coordtype(edge_ch) + ds = one(T) / (d+2) + + f = trace(edge_ch, face_ch, localspace) + Q = zeros(T, numfunctions(localspace, domain(face_ch)), d*(d+1)) + S = ((i,j,d+2-i-j) for i in 1:d+1 for j in 1:d+1 if d+2-i-j > 0) + # return stack(S) do (i,j,k) + idx = 1 + for (i,j,k) in S + u = (i*ds,j*ds) + p = neighborhood(edge_ch, u) + t1 = tangents(p,1) + t2 = tangents(p,2) + vals = f(u) + q1 = [dot(t1,x.value) for x in vals] + q2 = [dot(t2,x.value) for x in vals] + Q[:,idx] = q1; idx += 1 + Q[:,idx] = q2; idx += 1 + end + + return Q +end + + +@testitem "GWP face global dofs" begin + using CompScienceMeshes + T = Float64 + face_ch = simplex( + point(3,0,0), + point(2,0,-1), + point(0,0,1), + ) + # edge_ch = simplex( + # point(0,0,1), + # point(2,0,-1), + # point(3,0,0) + # ) + edge_ch = simplex( + point(3,0,0), + point(2,0,-1), + point(0,0,1), + ) + + degree = 3 + localspace = BEAST.GWPCurlRefSpace{T,degree}() + dofs = BEAST.GWPGlobalFaceDoFs(degree) + A = BEAST.globaldofs(edge_ch, face_ch, localspace, dofs) + display(round.(A, digits=3)) +end + +function _addshapes!(fns, cell, gids, lids, β) + T = eltype(β) + atol = sqrt(eps(T)) + for i in axes(β,1) + fn = fns[gids[i]] + for j in axes(β,2) + isapprox(β[i,j], 0; atol) && continue + push!(fn, Shape{T}(cell, lids[j], β[i,j])) + end + end +end + + +function gwpcurl(mesh, edges=nothing; order) + + T = coordtype(mesh) + atol = sqrt(eps(T)) + + if edges == nothing + edges = setminus(skeleton(mesh, 1), boundary(mesh)) + end + + C12 = connectivity(mesh, edges, abs) + R12 = rowvals(C12) + V12 = nonzeros(C12) + + ne = order+1 + nf = order*(order+1) + + Ne = ne * length(edges) + Nf = nf * length(mesh) + Nt = Ne + Nf + + localspace = GWPCurlRefSpace{T,order}() + refdom = domain(chart(mesh, first(mesh))) + localdim = numfunctions(localspace, refdom) + + fns = [Shape{T}[] for n in 1:Nt] + pos = fill(point(0,0,0), Nt) + for cell in mesh + cell_ch = chart(mesh, cell) + for k in nzrange(C12, cell) + e = R12[k] + i = V12[k] + gids = (e-1)*ne .+ (1:ne) + lids = localindices(localspace, refdom, Val{1}, i) + edge_ch = chart(edges, e) + vals = globaldofs(edge_ch, cell_ch, localspace, GWPGlobalEdgeDoFs(order)) + α = vals[lids,:] + _addshapes!(fns, cell, gids, lids, inv(α')) + end + + order < 2 && continue + face_ch = chart(mesh, cell) + gids = Ne + (cell-1)*nf .+ (1:nf) + lids = localindices(localspace, refdom, Val{2}, 1) + vals = globaldofs(face_ch, face_ch, localspace, GWPGlobalFaceDoFs(order)) + α = vals[lids,:] + _addshapes!(fns, cell, gids, lids, inv(α')) + end + + for e in edges pos[(e-1)*ne .+ (1:ne)] .= Ref(cartesian(center(chart(edges, e)))) end + for f in mesh pos[Ne+(f-1)*nf .+ (1: nf)] .= Ref(cartesian(center(chart(mesh, f)))) end + + return GWPCurlSpace(mesh, fns, pos, order) +end + +@testitem "GWPcurl global: numfunctions" begin + using CompScienceMeshes + + h = 0.5 + mesh = meshrectangle(1.0, 1.0, 0.5) + edges = setminus(skeleton(mesh,1), boundary(mesh)) + + order = 2 + gwp = BEAST.gwpcurl(mesh, edges; order=order) + + ne = order+1 + nf = order * (order+1) + Nt = length(edges)*ne + length(mesh)*nf + @test numfunctions(gwp) == Nt +end \ No newline at end of file diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index a0bbb336..77297b5d 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -977,7 +977,8 @@ function lagrangec0(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}; order) N = nV + nE + nF localspace = LagrangeRefSpace{T,order,3,binomial(2+order,2)}() - localdim = numfunctions(localspace) + dom = domain(chart(mesh, first(mesh))) + localdim = numfunctions(localspace, dom) d = order fns = [S[] for n in 1:(nV+nE+nF)] diff --git a/src/bases/local/gwpdivlocal.jl b/src/bases/local/gwpdivlocal.jl new file mode 100644 index 00000000..8c21b9c4 --- /dev/null +++ b/src/bases/local/gwpdivlocal.jl @@ -0,0 +1,69 @@ +struct GWPDivRefSpace{T,Degree} <: RefSpace{T} end + +function numfunctions(x::GWPDivRefSpace{<:Any,D}, + dom::CompScienceMeshes.ReferenceSimplex{2}) where{D} (D+1)*(D+3) end + +function (ϕ::GWPDivRefSpace{T,Degree})(p) where {T,Degree} + ψ = GWPCurlRefSpace{T,Degree}() + dom = domain(chart(p)) + u = parametric(p) + vals = ψ(dom, u) + vals = pushforwardcurl(vals, p) + n = normal(p) + map(vals) do v + (value = -n × v.value, divergence=v.curl) + end +end + +function interpolate(fields, interpolant::GWPDivRefSpace{T,Degree}, chart) where {T,Degree} + + function nxfields(p) + vals = fields(p) + n = normal(p) + return map(v -> n×v, vals) + end + + return interpolate(nxfields, GWPCurlRefSpace{T,Degree}(), chart) +end + +@testitem "divergence" begin + using CompScienceMeshes, LinearAlgebra + + T = Float64 + s = simplex( + point(3,0,0), + point(0,2,1), + point(-1,-1,-1), + ) + + order = 2 + ϕ = BEAST.GWPDivRefSpace{T,order}() + + u = T(0.2432); dx = sqrt(eps(T)) + v = T(0.5786); dy = sqrt(eps(T)) + + # p00 = neighborhood(s, (u,v)) + # p10 = neighborhood(s, (u+du,v)) + # p01 = neighborhood(s, (u, v+dv)) + + p00 = neighborhood(s, (u,v)) + t1 = normalize(tangents(p00,1)) + t2 = normal(p00) × t1 + + p10 = neighborhood(s, carttobary(s, cartesian(p00) + dx*t1 + 0*t2)) + p01 = neighborhood(s, carttobary(s, cartesian(p00) + 0*t1 + dy*t2)) + + ϕ00 = ϕ(p00) + ϕ10 = ϕ(p10) + ϕ01 = ϕ(p01) + + + for (f00, f10, f01) in zip(ϕ00, ϕ10, ϕ01) + df1dx = dot(t1, f10.value - f00.value) / dx + df2dy = dot(t2, f01.value - f00.value) / dy + curl_num = df1dx + df2dy + curl_ana = f00.divergence + # @show curl_num, curl_ana, abs(curl_num - curl_ana) + @test curl_num ≈ curl_ana atol=sqrt(eps(T))*100 + end +end \ No newline at end of file diff --git a/src/bases/local/gwplocal.jl b/src/bases/local/gwplocal.jl index 6dfa4c7c..1dc4dd4d 100644 --- a/src/bases/local/gwplocal.jl +++ b/src/bases/local/gwplocal.jl @@ -1,5 +1,8 @@ struct GWPCurlRefSpace{T,Degree} <: RefSpace{T} end +function numfunctions(x::GWPCurlRefSpace{<:Any,D}, + dom::CompScienceMeshes.ReferenceSimplex{2}) where{D} (D+1)*(D+3) end + function (ϕ::GWPCurlRefSpace{T,Degree})(p) where {T,Degree} dom = domain(chart(p)) u = parametric(p) @@ -14,18 +17,15 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di u, v = u w = 1-u-v - s = range(zero(T), one(T), length=d+3) - # rwg1 = point(T, u-1, v) - # rwg2 = point(T, u, v-1) - # rwg3 = point(T, u, v) + s = range(zero(T), one(T), length=d+3) + nd1 = point(T, -v, u-1) nd2 = point(T, -v+1, u) nd3 = point(T, -v, u) P = SVector{2,T} vals = P[] - dffu = T[] - dffv = T[] + crls = T[] i = 0 for j in 1:d+1 @@ -35,7 +35,13 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di Rₖ = _sylpoly_shift(s, k+1, w) push!(vals, Rᵢ*Rⱼ*Rₖ*nd1) - # du = _sylpoly_shift_diff(s, i+1, u)*Rⱼ*Rₖ + dRᵢ = _sylpoly_diff(s, i+1, u) + dRⱼ = _sylpoly_shift_diff(s, j+1, v) + dRₖ = _sylpoly_shift_diff(s, k+1, w) + du = dRᵢ*Rⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ + dv = Rᵢ*dRⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ + curl = (du*nd1[2] - dv*nd1[1]) + 2*Rᵢ*Rⱼ*Rₖ + push!(crls, curl) end for i in 1:d+1 @@ -45,6 +51,14 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di Rⱼ = _sylpoly(s, j+1, v) Rₖ = _sylpoly_shift(s, k+1, w) push!(vals, Rᵢ*Rⱼ*Rₖ*nd2) + + dRᵢ = _sylpoly_shift_diff(s, i+1, u) + dRⱼ = _sylpoly_diff(s, j+1, v) + dRₖ = _sylpoly_shift_diff(s, k+1, w) + du = dRᵢ*Rⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ + dv = Rᵢ*dRⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ + curl = (du*nd2[2] - dv*nd2[1]) + 2*Rᵢ*Rⱼ*Rₖ + push!(crls, curl) end for i in 1:d+1 @@ -54,6 +68,16 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di Rⱼ = _sylpoly_shift(s, j+1, v) Rₖ = _sylpoly(s, k+1, w) push!(vals, Rᵢ*Rⱼ*Rₖ*nd3) + + dRᵢ = _sylpoly_shift_diff(s, i+1, u) + dRⱼ = _sylpoly_shift_diff(s, j+1, v) + dRₖ = _sylpoly_diff(s, k+1, w) + + du = dRᵢ*Rⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ + dv = Rᵢ*dRⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ + curl = (du*nd3[2] - dv*nd3[1]) + 2*Rᵢ*Rⱼ*Rₖ + + push!(crls, curl) end for i in 1:d+1 @@ -69,15 +93,34 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di S1 = Rᵢ*Rsⱼ*Rsₖ*nd1 S2 = Rsᵢ*Rⱼ*Rsₖ*nd2 S3 = Rsᵢ*Rsⱼ*Rₖ*nd3 - N1 = (d+2)/(d+2-i) - N2 = (d+2)/(d+2-j) - N3 = (d+2)/(d+2-k) push!(vals, S2-S3) push!(vals, S3-S1) + + dRsᵢ = _sylpoly_shift_diff(s, i+1, u) + dRsⱼ = _sylpoly_shift_diff(s, j+1, v) + dRsₖ = _sylpoly_shift_diff(s, k+1, w) + dRᵢ = _sylpoly_diff(s, i+1, u) + dRⱼ = _sylpoly_diff(s, j+1, v) + dRₖ = _sylpoly_diff(s, k+1, w) + + du = dRᵢ*Rsⱼ*Rsₖ - Rᵢ*Rsⱼ*dRsₖ + dv = Rᵢ*dRsⱼ*Rsₖ - Rᵢ*Rsⱼ*dRsₖ + curlS1 = du*nd1[2] - dv*nd1[1] + 2*Rᵢ*Rsⱼ*Rsₖ + + du = dRsᵢ*Rⱼ*Rsₖ - Rsᵢ*Rⱼ*dRsₖ + dv = Rsᵢ*dRⱼ*Rsₖ - Rsᵢ*Rⱼ*dRsₖ + curlS2 = du*nd2[2] - dv*nd2[1] + 2*Rsᵢ*Rⱼ*Rsₖ + + du = dRsᵢ*Rsⱼ*Rₖ - Rsᵢ*Rsⱼ*dRₖ + dv = Rsᵢ*dRsⱼ*Rₖ - Rsᵢ*Rsⱼ*dRₖ + curlS3 = du*nd3[2] - dv*nd3[1] + 2*Rsᵢ*Rsⱼ*Rₖ + + push!(crls, curlS2 - curlS3) + push!(crls, curlS3 - curlS1) end end NF = length(vals) - SVector{NF}([(value=f, curl=zero(T)) for f in vals]) + SVector{NF}([(value=f, curl=c) for (f,c) in zip(vals, crls)]) end @@ -141,4 +184,19 @@ function interpolate(fields, interpolant::GWPCurlRefSpace{T,Degree}, chart) wher end end return Q -end \ No newline at end of file +end + +function localindices(localspace::GWPCurlRefSpace{<:Any,Degree}, domain, + dim::Type{Val{1}}, i) where {Degree} + + ne = Degree+1 + (i-1)*ne .+ (1:ne) +end + +function localindices(localspace::GWPCurlRefSpace{<:Any,Degree}, domain, + dim::Type{Val{2}}, i) where {Degree} + + ne = Degree+1 + nf = Degree * (Degree + 1) + 3*ne .+ (1:nf) +end diff --git a/src/bases/local/rtqlocal.jl b/src/bases/local/rtqlocal.jl index f7e38347..f1472d52 100644 --- a/src/bases/local/rtqlocal.jl +++ b/src/bases/local/rtqlocal.jl @@ -22,6 +22,7 @@ function (ϕ::RTQuadRefSpace{T})(p) where {T} (value=D*f.value/j, divergence=f.divergence/j) end end +function numfunctions(ϕ::RTQuadRefSpace, dom::CompScienceMeshes.RefQuadrilateral) 4 end function interpolate(fields, interpolant::RTQuadRefSpace{T}, chart) where {T} diff --git a/src/bases/rtqspace.jl b/src/bases/rtqspace.jl index 2935c223..7f701ee6 100644 --- a/src/bases/rtqspace.jl +++ b/src/bases/rtqspace.jl @@ -97,7 +97,8 @@ end num_cells = numcells(m) num_bfs = numfunctions(s) r = refspace(s) - num_refs = numfunctions(r) + dom = domain(chart(m, first(m))) + num_refs = numfunctions(r, dom) celltonum = BEAST.make_celltonum(num_cells, num_refs, num_bfs, s) els, ad, a2g = BEAST.assemblydata(s) diff --git a/src/excitation.jl b/src/excitation.jl index f194a86c..17bc17f0 100644 --- a/src/excitation.jl +++ b/src/excitation.jl @@ -86,7 +86,7 @@ function assemble!(field::Functional, tfs::subdBasis, store; end -function celltestvalues(tshs::RefSpace{T}, tcell, field, qr) where {T,NF} +function celltestvalues(tshs::RefSpace{T}, tcell, field, qr) where {T} num_tshs = numfunctions(tshs, domain(tcell)) interactions = zeros(Complex{T}, num_tshs) diff --git a/src/gridfunction.jl b/src/gridfunction.jl index 05da678f..56931843 100644 --- a/src/gridfunction.jl +++ b/src/gridfunction.jl @@ -279,7 +279,7 @@ function cellquadsamplingvaluesvalues!( qr ) - num_tshs = numfunctions(tshs) + # num_tshs = numfunctions(tshs) num_oqp = length(qr) @@ -289,7 +289,8 @@ function cellquadsamplingvaluesvalues!( tvals = qr[p].value - for m in 1 : num_tshs + for m in 1 : length(tvals) + # for m in 1 : num_tshs tval = tvals[m] for k = 1:size(data, 1) diff --git a/src/postproc.jl b/src/postproc.jl index 398f4c8f..b720a9b5 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -10,12 +10,14 @@ function facecurrents(coeffs, basis) T = eltype(coeffs) RT = real(T) + mesh = geometry(basis) + dom = domain(chart(mesh, first(mesh))) + refs = refspace(basis) - numrefs = numfunctions(refs) + numrefs = numfunctions(refs, dom) cells, tad, a2g = assemblydata(basis) - mesh = geometry(basis) D = dimension(mesh) # U = D+1 U = 3 diff --git a/src/utils/lagpolys.jl b/src/utils/lagpolys.jl index 65d3c924..60620706 100644 --- a/src/utils/lagpolys.jl +++ b/src/utils/lagpolys.jl @@ -11,15 +11,17 @@ function _lagpoly(nodes, i, s, i0=1, i1=length(nodes)) end function _lagpoly_diff(nodes, i, s, i0=1, i1=length(nodes)) + T = typeof(s) r = zero(T) si = nodes[i] for p in i0:i1 p == i && continue + sp = nodes[p] rp = one(T) for j in i0:i1 j == i && continue j == p && continue - sj = nodees[j] + sj = nodes[j] rp *= (s - sj) / (si - sj) end rp *= 1 / (si - sp) @@ -41,5 +43,5 @@ function _sylpoly_shift(nodes, i, s) end function _sylpoly_shift_diff(nodes, i, s) - _lagpoly_shift(nodes, i, s, 2, i) + _lagpoly_diff(nodes, i, s, 2, i) end \ No newline at end of file diff --git a/test/test_gwp.jl b/test/test_gwp.jl index bc47712f..ca2210e8 100644 --- a/test/test_gwp.jl +++ b/test/test_gwp.jl @@ -17,6 +17,7 @@ end @testitem "refspace: self-interpolate" begin using CompScienceMeshes + using LinearAlgebra T, Dim, Nverts, Degree = Float64, 2, 3, 2 dom = CompScienceMeshes.ReferenceSimplex{Dim,T,Nverts}() @@ -26,16 +27,45 @@ end # fields(p) = [ϕ(dom,p)[1].value] coeffs = BEAST.interpolate(fields, ϕ, dom) + nf = numfunctions(ϕ, dom) + @test coeffs ≈ Matrix{T}(I,nf,nf) atol=sqrt(eps(T)) display(round.(coeffs, digits=3)) - # pts = [ - # point(T, 0.3, 0.1), - # point(T, 0.1, 0.3), - # point(T, 0.5, 0.0)] +end + + +@testitem "confspace: interpolate lowest order" begin + using CompScienceMeshes + using LinearAlgebra + + T, Dim, Nverts, Degree = Float64, 2, 3, 2 + supp = simplex( + point(T,3,0,0), + point(T,0,2,0), + point(T,0,0,-1)) + + ϕ = BEAST.GWPCurlRefSpace{T,Degree}() + ψ = BEAST.NDRefSpace{T}() + + fields(p) = [v.value for v in ψ(p)] + coeffs = BEAST.interpolate(fields, ϕ, supp) + + nf1 = numfunctions(ψ, domain(supp)) + nf2 = numfunctions(ϕ, domain(supp)) + @test size(coeffs) == (nf1, nf2) + display(round.(coeffs, digits=3)) + + pts = [ + point(T, 0.3, 0.1), + point(T, 0.1, 0.3), + point(T, 0.5, 0.0)] - # for u in pts - # vals = ϕ(dom, u) - # r = sum(c * v.value for (c,v) in zip(coeffs, vals)) - # @show r - # end + for (i,u) in enumerate(pts) + p = neighborhood(supp, u) + basisvals = ϕ(p) + fieldvals = ψ(p) + r = sum(c * v.value for (c,v) in zip(coeffs[i,:], basisvals)) + s = fieldvals[i].value + @test r ≈ s atol=sqrt(eps(T)) + end end \ No newline at end of file From 68804cb5c5df02f78c27ccf933b70a40f9492177 Mon Sep 17 00:00:00 2001 From: azuccott Date: Wed, 9 Oct 2024 12:47:43 +0200 Subject: [PATCH 403/528] Update runtests.jl --- test/runtests.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index ac4f15cb..b642a5d2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -78,7 +78,8 @@ include("test_variational.jl") include("test_handlers.jl") include("test_ncrossbdm.jl") -include("test_curl_lagc0d1_lagc0d2.jl")include("test_gridfunction.jl") +include("test_curl_lagc0d1_lagc0d2.jl") +include("test_gridfunction.jl") using TestItemRunner @run_package_tests From 97f4c1d270894bb215537a4840e6a3b49c84f0c1 Mon Sep 17 00:00:00 2001 From: azuccott Date: Wed, 9 Oct 2024 13:18:40 +0200 Subject: [PATCH 404/528] Cleaning master branch I moved my changes for time domain integration in a new master called time_domain+integration --- Project.toml | 2 - examples/tdacusticsinglelayer.jl | 70 ------ src/BEAST.jl | 1 - src/maxwell/timedomain/acustictdops.jl | 319 ------------------------- src/quadrature/quadstrats.jl | 4 +- src/timedomain/analyticalints.jl | 232 ------------------ src/timedomain/tdintegralop.jl | 10 - 7 files changed, 1 insertion(+), 637 deletions(-) delete mode 100644 examples/tdacusticsinglelayer.jl delete mode 100644 src/maxwell/timedomain/acustictdops.jl delete mode 100644 src/timedomain/analyticalints.jl diff --git a/Project.toml b/Project.toml index 6b722b59..98f53e4e 100644 --- a/Project.toml +++ b/Project.toml @@ -27,10 +27,8 @@ SauterSchwab3D = "0a13313b-1c00-422e-8263-562364ed9544" SauterSchwabQuadrature = "535c7bfe-2023-5c1d-b712-654ef9d93a38" SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -SparseMatrixDicts = "5cb6c4b0-9b79-11e8-24c9-f9621d252589" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" -TimeDomainBEMInt = "0303f91b-5811-4998-82cd-e0687f55e8f9" TestItems = "1c621080-faea-4a02-84b6-bbd5e436b8fe" WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" diff --git a/examples/tdacusticsinglelayer.jl b/examples/tdacusticsinglelayer.jl deleted file mode 100644 index a55aa005..00000000 --- a/examples/tdacusticsinglelayer.jl +++ /dev/null @@ -1,70 +0,0 @@ -using CompScienceMeshes, BEAST, LinearAlgebra -#Γ = readmesh(joinpath(@__DIR__,"sphere2.in")) -Γ = CompScienceMeshes.meshsphere(1.0,0.4)#CompScienceMeshes.meshcuboid(1.0,1.0,1.0,0.3) - -X = lagrangecxd0(Γ) - -Δt, Nt = 0.5, 200 -T = timebasisshiftedlagrange(Δt, Nt, 0) -U = timebasisdelta(Δt, Nt) - -V = X ⊗ T -W = X ⊗ U - -width, delay, scaling = 16.0, 24.0, 1.0 -gaussian = creategaussian(width, delay, scaling) -e = BEAST.planewave(point(0,0,1), 1.0, gaussian) - -@hilbertspace j -@hilbertspace j′ - -SL = TDAcustic3D.acusticsinglelayer(speedofsound=1.0, numdiffs=0) -# BEAST.@defaultquadstrat (SL, W, V) BEAST.OuterNumInnerAnalyticQStrat(7) - -tdacusticsl = @discretise SL[j′,j] == -1.0e[j′] j∈V j′∈W -xacusticsl = solve(tdacusticsl) - -Z = assemble(SL,W,V) - -#corregere da qui -import Plots - -Plots.plot(xacusticsl[1,1:300],label="Current",ylim=[-0.0001,0.0001]) -Plots.xlabel!("t") -Plots.savefig("stablecurrent.png") - -xacusticsl[1,200:300] -#pval=ConvolutionOperators.polyvals(Z) -import Plotly -#fcr, geo = facecurrents(xefie[:,125], X) -#Plotly.plot(patch(geo, norm.(fcr))) - -import BEAST.ConvolutionOperators - -for a in 1:1 - za=ConvolutionOperators.timeslice(Z,a) - zawilton=ConvolutionOperators.timeslice(Zs,a) - for i in 1:10 #numfunctions(X) - #for j in 1:numfunctions(X) - if norm(za[i,i]-zawilton[i,i])>=10^(-6) - println(a," ",i," ",za[i,i]," ",zawilton[i,i]) - end - #end - end -end - - - -name=readline() -parse(Float64, name) - -Xefie, Δω, ω0 = fouriertransform(xefie, Δt, 0.0, 2) -ω = collect(ω0 .+ (0:Nt-1)*Δω) -_, i1 = findmin(abs.(ω.-1.0)) - -ω1 = ω[i1] -ue = Xefie[:,i1] / fouriertransform(gaussian)(ω1) - -fcr, geo = facecurrents(ue, X) -Plotly.plot(patch(geo, norm.(fcr))) - diff --git a/src/BEAST.jl b/src/BEAST.jl index 79b1b6ce..d14a5ee2 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -265,7 +265,6 @@ include("helmholtz3d/timedomain/tdhh3dops.jl") include("helmholtz3d/timedomain/tdhh3dexc.jl") include("helmholtz3d/timedomain/tdhh3dpp.jl") -include("maxwell/timedomain/acustictdops.jl")#support for acusticsinglelayer include("maxwell/timedomain/mwtdops.jl") include("maxwell/timedomain/mwtdexc.jl") include("maxwell/timedomain/tdfarfield.jl") diff --git a/src/maxwell/timedomain/acustictdops.jl b/src/maxwell/timedomain/acustictdops.jl deleted file mode 100644 index e25c5078..00000000 --- a/src/maxwell/timedomain/acustictdops.jl +++ /dev/null @@ -1,319 +0,0 @@ -mutable struct AcusticSingleLayerTDIO{T} <: RetardedPotential{T} - "speed of sound in medium" - speed_of_light::T #for compatibility with the rest of the package I left light instead of sound - "weight" - weight::T - "number of temporal differentiations" - diffs::Int -end - -function Base.:*(a::Number, op::AcusticSingleLayerTDIO) - @info "scalar product a * op (acusticSL)" - AcusticSingleLayerTDIO( - op.speed_of_light, - a * op.weight, - op.diffs) -end - -AcusticSingleLayerTDIO(;speedofsound) = AcusticSingleLayerTDIO(speedofsound, one(speedofsound), 0) - -module TDAcustic3D -import ...BEAST - -function acusticsinglelayer(;speedofsound, numdiffs) - @assert numdiffs >= 0 - #controllare con Kristof che tipo di schema temporale viene utilizzato e come si con integrali rispetto al tempo extra - #return BEAST.integrate(BEAST.MWSingleLayerTDIO(speedoflight,-1/speedoflight,-speedoflight,2,0)) - return BEAST.AcusticSingleLayerTDIO(speedofsound,one(speedofsound),numdiffs) -end - -end #of the module - -export TDAcustic3D - -defaultquadstrat(::AcusticSingleLayerTDIO, tfs, bfs) = AllAnalyticalQStrat(1) #nothing -#nothing goes in hybrid qr, allanalytical goes in zuccottirule - - -function quaddata(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, - testels, trialels, timeels, quadstrat::AllAnalyticalQStrat) - return nothing -end - -quadrule(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, - p, testel, q, trialel, r, timeel, qd, ::AllAnalyticalQStrat) = ZuccottiRule(1.0) - - - -function quaddata(operator::AcusticSingleLayerTDIO, - test_local_space, trial_local_space, time_local_space, - test_element, trial_element, time_element, quadstrat::Nothing) - - dmax = numfunctions(time_local_space)-1 - bn = binomial.((0:dmax),(0:dmax)') - - V = eltype(test_element[1].vertices) - ws = WiltonInts84.workspace(V) - order = 4 - @show order - quadpoints(test_local_space, test_element, (order,)), bn, ws - - end - - - # See: ?BEAST.quadrule for help - function quadrule(operator::AcusticSingleLayerTDIO, - test_local_space, trial_local_space, time_local_space, - p, test_element, q, trial_element, r, time_element, - quad_data, quadstrat::Nothing) - - # WiltonInts84Strat(quad_data[1,p]) - qd = quad_data - HybridZuccottiWiltonStrat(qd[1][1,p],qd[2],qd[3]) - - end - - - function innerintegrals!(zlocal, operator::AcusticSingleLayerTDIO, - test_point, - test_local_space, trial_local_space, time_local_space, - test_element, trial_element, time_element, - quad_rule::HybridZuccottiWiltonStrat, quad_weight) - - # error("Here!!!") - - dx = quad_weight - x = cartesian(test_point) - # n = normal(test_point) - - # a = trial_element[1] - # ξ = x - dot(x -a, n) * n - - r = time_element[1] - R = time_element[2] - @assert r < R - - N = max(degree(time_local_space), 1) - ∫G, ∫vG, ∫∇G = WiltonInts84.wiltonints( - trial_element[1], - trial_element[2], - trial_element[3], - x, r, R, Val{2}, quad_rule.workspace) - - a = dx / (4*pi) - #D = operator.num_diffs - D=0 - @assert D == 0 - @assert numfunctions(test_local_space) == 1 - @assert numfunctions(trial_local_space) == 1 - - @inline function tmRoR_sl(d, iG) - sgn = isodd(d) ? -1 : 1 - r = sgn * iG[d+2] - end - - # bns = quad_rule.binomials - - @assert D == 0 - for k in 1 : numfunctions(time_local_space) - d = k - 1 - d < D && continue - q = reduce(*, d-D+1:d ,init=1) - zlocal[1,1,k] += a * q * tmRoR_sl(d-D, ∫G) - end # k - end - - - -qpclineline=10^8#quasiparallel case controller -qpclinetriang=10^10 -qpctriangtriang=10^13 - -const qpc=[qpclineline, qpclinetriang, qpctriangtriang] - -function momintegrals!(z, op::AcusticSingleLayerTDIO, g::LagrangeRefSpace{T,0,3}, f::LagrangeRefSpace{T,0,3}, t::MonomialBasis{T,0,1}, τ, σ, ι, qr::ZuccottiRule) where T - - t1=ι[1] - t2=ι[2] - - - @assert t2 > t1 - - sos = op.speed_of_light - - a1index,a2index,a3index,b1index,b2index,b3index=0,0,0,0,0,0 - Tt = eltype(eltype(τ.vertices)) - hits = 0 - dtol = 1.0e3 * eps(Tt) - dmin2 = floatmax(Tt) - for t in 1:3 - for s in 1:3 - d2 = LinearAlgebra.norm_sqr(τ[t]-σ[s]) - dmin2 = min(dmin2, d2) - if (d2 < dtol) - hits+=1 - if hits==1 - a1index =t - b1index = s - elseif hits==2 - a2index=t - b2index=s - end - end - end - end - - if hits==3 - #print("\np1=",τ[1],"\np2=",τ[2],"\np3=",τ[3],"\nt1=",t1,"\nt2=",t2) - - z[1,1,1]+=(TimeDomainBEMInt.intcoinctriangles(τ[1],τ[2],τ[3],t1,t2))/(4*π) - elseif hits==2 - #pay attention with double layer and index permutation or in whatever case has nx - if mod1(a1index +1,3)==a2index - a3index=mod1(a1index-1,3) - else - a3index=mod1(a1index+1,3) - end - - if mod1(b1index+1,3)==b2index - b3index=mod1(b1index-1,3) - else - b3index=mod1(b1index+1,3) - end - #print("\np1=",τ[a1index],"\np2=",τ[a2index],"\np3=",τ[a3index],"\nv1=",σ[b1index],"\nv2=",σ[b2index],"\nv3=",σ[b3index],"\nt1=",t1,"\nt2=",t2) - - - z[1,1,1]+=(TimeDomainBEMInt.inttriangletriangleadjacent(τ[a1index],τ[a2index],τ[a3index],σ[b1index],σ[b2index],σ[b3index],t1,t2,qpc))/(4*π) - elseif hits==1 - a2index,a3index=mod1(a1index+1,3),mod1(a1index+2,3) - b2index,b3index=mod1(b1index+1,3),mod1(b1index+2,3) - print("\na1=",τ[a1index],"\na2=",τ[a2index],"\na3=",τ[a3index],"\nb1=",σ[b1index],"\nb2=",σ[b2index],"\nb3=",σ[b3index],"\nt1=",t1,"\nt2=",t2) - z[1,1,1]+=(TimeDomainBEMInt.intcommonvertex(τ[a1index],τ[a2index],τ[a3index],σ[b2index],σ[b3index],t1,t2,qpc))/(4*π) - - else - #print("\np1=",τ[1],"\np2=",τ[2],"\np3=",τ[3],"\nv1=",σ[1],"\nv2=",σ[2],"\nv3=",σ[3],"\nt1=",t1,"\nt2=",t2) - z[1,1,1]+=(inttriangletriangle(τ[1],τ[2],τ[3],σ[1],σ[2],σ[3],t1,t2,qpc))/(4*π) - end - #for the moment sos=1 but I will correct this -end - - -function momintegrals!(z, op::AcusticSingleLayerTDIO, g::LagrangeRefSpace{T,0,3}, f::LagrangeRefSpace{T,0,3}, t::MonomialBasis{T,0,1}, τ, σ, ι, qr::HybridZuccottiWiltonStrat) where T - - t1=ι[1] - t2=ι[2] - - - @assert t2 > t1 - - sos = op.speed_of_light - - a1index,a2index,a3index,b1index,b2index,b3index=0,0,0,0,0,0 - Tt = eltype(eltype(τ.vertices)) - hits = 0 - dtol = 1.0e3 * eps(Tt) - dmin2 = floatmax(Tt) - for t in 1:3 - for s in 1:3 - d2 = LinearAlgebra.norm_sqr(τ[t]-σ[s]) - dmin2 = min(dmin2, d2) - if (d2 < dtol) - hits+=1 - if hits==1 - a1index =t - b1index = s - elseif hits==2 - a2index=t - b2index=s - end - end - end - end - - if hits==3 - #print("\np1=",τ[1],"\np2=",τ[2],"\np3=",τ[3],"\nt1=",t1,"\nt2=",t2) - entry=(TimeDomainBEMInt.intcoinctriangles(τ[1],τ[2],τ[3],t1,t2))/(4*π) - if norm(entry)>10.0 || entry<-0.0001 - println("quadrature ",τ[1]," ",τ[2]," ",τ[3]," ", t1," ",t2," ",entry) - XW = qr.outer_quad_points - for p in 1 : length(XW) - x = XW[p].point - w = XW[p].weight - innerintegrals!(z, op, x, g, f, t, τ, σ, ι, qr, w) - end - else - z[1,1,1]+=max(entry,0.0) - end - elseif hits==2 - #= XW = qr.outer_quad_points - for p in 1 : length(XW) - x = XW[p].point - w = XW[p].weight - innerintegrals!(z, op, x, g, f, t, τ, σ, ι, qr, w) - end=# - #pay attention with double layer and index permutation or whatever case has n cross product - if mod1(a1index +1,3)==a2index - a3index=mod1(a1index-1,3) - else - a3index=mod1(a1index+1,3) - end - - if mod1(b1index+1,3)==b2index - b3index=mod1(b1index-1,3) - else - b3index=mod1(b1index+1,3) - end - #print("\np1=",τ[a1index],"\np2=",τ[a2index],"\np3=",τ[a3index],"\nv1=",σ[b1index],"\nv2=",σ[b2index],"\nv3=",σ[b3index],"\nt1=",t1,"\nt2=",t2) - - entry=(TimeDomainBEMInt.inttriangletriangleadjacent(τ[a1index],τ[a2index],τ[a3index],σ[b1index],σ[b2index],σ[b3index],t1,t2,qpc))/(4*π) - if norm(entry)>10.0 || entry<-0.0001 - println("quadrature ",τ[a1index]," ",τ[a2index]," ",τ[a3index]," ",σ[b1index]," ",σ[b2index]," ",σ[b3index], t1," ",t2," ",entry) - XW = qr.outer_quad_points - for p in 1 : length(XW) - x = XW[p].point - w = XW[p].weight - innerintegrals!(z, op, x, g, f, t, τ, σ, ι, qr, w) - end - else - z[1,1,1]+=max(0.0,entry) - end - elseif hits==1 - #=XW = qr.outer_quad_points - for p in 1 : length(XW) - x = XW[p].point - w = XW[p].weight - innerintegrals!(z, op, x, g, f, t, τ, σ, ι, qr, w) - end=# - a2index,a3index=mod1(a1index+1,3),mod1(a1index+2,3) - b2index,b3index=mod1(b1index+1,3),mod1(b1index+2,3) - #print("\np1=",τ[a1index],"\np2=",τ[a2index],"\np3=",τ[a3index],"\nv1=",σ[b1index],"\nv2=",σ[b2index],"\nv3=",σ[b3index],"\nt1=",t1,"\nt2=",t2) - entry=(TimeDomainBEMInt.intcommonvertex(τ[a1index],τ[a2index],τ[a3index],σ[b2index],σ[b3index],t1,t2,qpc))/(4*π) - if norm(entry)>10.0 || entry<-0.0001 - println("quadrature ",τ[a1index]," ",τ[a2index]," ",τ[a3index]," ",σ[b2index]," ",σ[b3index], t1," ",t2," ",entry, " ",) - - XW = qr.outer_quad_points - for p in 1 : length(XW) - x = XW[p].point - w = XW[p].weight - innerintegrals!(z, op, x, g, f, t, τ, σ, ι, qr, w) - end - else - z[1,1,1]+=max(0.0,entry) - end - else - entry=0.1#(inttriangletriangle(τ[1],τ[2],τ[3],σ[1],σ[2],σ[3],t1,t2,qpc))/(4*π) - #print("\np1=",τ[1],"\np2=",τ[2],"\np3=",τ[3],"\nv1=",σ[1],"\nv2=",σ[2],"\nv3=",σ[3],"\nt1=",t1,"\nt2=",t2) - if norm(entry)>0.0 || entry<-0.0001 - XW = qr.outer_quad_points - for p in 1 : length(XW) - x = XW[p].point - w = XW[p].weight - innerintegrals!(z, op, x, g, f, t, τ, σ, ι, qr, w) - end - - else - z[1,1,1]+=max(0.0,entry) - end - end - #for the moment sos=1 but I will correct this -end \ No newline at end of file diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index 632da895..3187a55b 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -30,9 +30,7 @@ struct OuterNumInnerAnalyticQStrat{R} outer_rule::R end -struct AllAnalyticalQStrat{R} - rule::R -end + defaultquadstrat(op, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bfs)) macro defaultquadstrat(dop, body) diff --git a/src/timedomain/analyticalints.jl b/src/timedomain/analyticalints.jl deleted file mode 100644 index 97e4359a..00000000 --- a/src/timedomain/analyticalints.jl +++ /dev/null @@ -1,232 +0,0 @@ -function minmax1d(vertex,edge) - T = eltype(τ[1]) - m = norm(vertex-edge[1]) - M = m - s=edge[1]-edge[2] - s/=norm(s) - ev1=edge[1]-vertex - x0=(edge[1]-dot(ev1,s)*s) - a=(edge[2]-x0)*s - b=(edge[1]-x0)*s - if a<=0 && b>=0 - m=norm(vertex-x0) - abs(a) M && (M=d) - end - end - return m, M -end - -function rings1d(τ, σ, ΔR) - m, M = minmaxdist(τ, σ) - r0 = floor(Int, m/ΔR) + 1 - r1 = ceil(Int, M/ΔR+1) - r0 : r1 -end - - - -function quaddata(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, - testels::Vector{Simplex{3,0,3,1,T}}, trialels::Vector{Simplex{3,1,1,2,T}}, timeels, quadstrat::AllAnalyticalQStrat) where T - - dimU=dimension(testels) - dimV=dimension(trialels) - #rigerenerare delta R -#quaddata 1D - #if dimU+dimV==1 - #testelsboundary=skeleton(testels,dimU-1) - # trialelsboundary=skeleton(trialels,dimV-1) - - numnodes=length(testels) - numedges=length(trialels) - - datavertexedge=Array{TimeDomainBEMInt.edgevertexgeo{T,P}, 2}(undef, numnodes, numedges) - rings=Array{UnitRange{Int},2}(undef, numnodes, numedges) - datarings=Array{Vector{Tuple{Int,Vector}},2}(undef,numnodes,numedges)#il type va bene - - #fill datarings with zeross ! - - for p in 1:numnodes - τ = chart(testels,p)#testels[p] - for q in 1:numedges - σ = chart(trialels,q) - edgevertgeo=TimeDomainBEMInt.edgevertexinteraction(τ,σ[1],σ[2]) - datavertexedge[p,q]=edgevertgeo - a,b=edgevertegeo.extint0[1],edgevertegeo.extint0[2] - rngs=rings1d(τ,σ,ΔR) - rings[p,q]=rngs - datarings[p,q]=[0,[0.0,0.0]] - for r in rngs - r > numfunctions(timebasisfunction) && continue #serve? - ι = ring(r,ΔR) - - # compute interactions between reference shape functions - #fill!(z, 0) - rp=τ #se e un simplex ok se no va messo chart(τ,1).vertices credo - t2=ι[2]#needs a check - extint=TimeDomainBEMInt.edgevertexinteraction(t2,edgevertgeo) - push!(datarings[p,q],extint) - - # qr = quadrule(op, U, V, W, p, τ, q, σ, r, ι, qd, quadstrat) - #momintegrals!(z, op, U, V, W, τ, σ, ι, qr) - end - end - end - - return datavertexedge - #else - # return "devo ancora scrivere" - #end -end - -function quaddata2Dee(op::AcusticSingleLayerTDIO, testrefs, trialrefs, timerefs, - testels::Vector{Simplex{3,1,1,2,T}}, trialels::Vector{Simplex{3,1,1,2,T}}, timeels, quadstrat::AllAnalyticalQStrat) - nnodes=length(nodes) - - - totrings=Array{UnitRange{Int},2}(undef, numedges, numedges) - datavalues=Array{Vector{Tuple{Int,Vector}},2}(undef,numedges,numedges) - cnnct=connectivity(edges,nodes) - - for p in 1:numdges - for q in 1:numedges - edge1=chart(edges,p) - edge2=chart(edges,q) - - vertind1=cnnct[1:nnodes,p].nzind - vertsgn1=cnnct[1:nnodes,p].nzval - vertind2=cnnct[1:nnodes,q].nzind - vertsgn2=cnnct[1:nnodes,q].nzval - - if vertsgn1[1]==1 - a1,a2=edge1[1],edge1[2] - else - a2,a1=edge1[1],edge1[2] - end - - if vertsgn2[1]==1 - b1,b2=edge2[1],edge2[2] - else - b2,b1=edge2[1],edge2[2] - end - - geo1,rings1,datarings1=edgevertexgeo[vertind1[1],q],rings[vertind1[1],q],datarings[vertind1[1],q] - geo2,rings2,datarings2=edgevertexgeo[vertind1[2],q],rings[vertind1[2],q],datarings[vertind1[2],q] - geo3,rings3,datarings3=edgevertexgeo[vertind2[1],p],rings[vertind2[1],p],datarings[vertind2[1],p] - geo4,rings4,datarings4=edgevertexgeo[vertind2[2],p],rings[vertind2[2],p],datarings[vertind2[2],p] - - geo=[geo1,geo2,geo3,geo4] - rings=[rings1,rings2,rings3,rings4] - datarings=[datarings1,datarings2,datarings3,datarings4] - - totrings[p,q],datavalues[p,q]=intlinelineglobal(a1,a2,b1,b2,geo,rings,datarings,[10^6,10^6,10^6],Val{0}) - end - end - return totrings,datavalues -end - - -function intlinelineglobal(a1,a2,b1,b2,geo,rings,datarings,parcontrol,UB::Type{Val{N}}) where N - - #nedges=length(edges) - - - #vertices=[a1,a2,a1′,a2′] - vertices=[a1,a2,b1,b2] - l12=norm(vertices[1]-vertices[2]) - l12′=norm(vertices[3]-vertices[4]) - - - #geo1=edgevertexinteraction(a1,a1′,a2′) - #geo2=edgevertexinteraction(a2,a1′,a2′) - #geo3=edgevertexinteraction(a1′,a1,a2) - #geo4=edgevertexinteraction(a2′,a1,a2) - - #datatime=Array{Tuple}(undef,2,4)? - I = maketuple(eltype(a1), UB) - K = maketuple(typeof(a1), UB) - - x=geo[3].tangent - - #z=cross(a12′,x) - #J=norm(z) - #if J blablabla - #z /= J - #h=dot(r22,z) - hdir=cross(geo[1].tangent,x) - n=hdir/norm(hdir) - sgnn=[+1,-1,-1,+1] - h=dot(a2-b2,n) - sgnh=[+1,-1,+1,-1] - angletot=0.0 - dminv=Vector{eltype(edge1[1])}(undef, 4) - ξ=Vector{typeof(edge1[1])}(undef, 4) - for j in 1:4 - dminv[j]=geo[j].dmin - dmaxv[j]=geo[j].dmax - v=vertices[j] - ξ[j]=v-n*h*sgnh[j]*sgnn[j] - angletot+=anglecontribution(ξ[j],sgnn[j]*n,geo[j]) - end - if abs(angletot-2π)<100*eps(eltype(edge1[1])) - dmin=abs(h) - else - dmin=min(dminv[1],dminv[2],dminv[3],dminv[4]) - end - - dmax=max(dmaxv[1],dmaxv[2],dmaxv[3],dmaxv[4]) - - r0 = floor(Int, dmin/ΔR) + 1 - r1 = ceil(Int, dmax/ΔR+1) #recuperare deltaR - ringtot = r0 : r1 - - allint=Vector{typeof((I,K))}(undef,r1-r0+2) - fill!(allint,(I,K)) - if norm(hdir) < (parcontrol[1])*eps(typeof(temp1)) - I=intparallelsegment(a1,a2,b1,b2,temp1,temp2)[1] #attenzione qui non compatibile con quello che stiamo scrivendo - else - n=hdir/norm(hdir) - sgnn=[+1,-1,-1,+1] - h=dot(a2-a2′,n) - sgnh=[+1,-1,+1,-1] - for j in 1:4 - for i in ringtot[1]:(rings[j][1]-1) - - P,Q = arcsegcontribution(v,ξ[j],sgnn[j]*n,sgnh[j]*h,geo[j],0,[0,0],i*ΔR,UB) - allint[i-ringtot[1]+2][1] = add(allint[i-ringtot[1]+2][1],P) - allint[i-ringtot[1]+2][2] = add(allint[i-ringtot[1]+2][2],Q) - P,Q = arcsegcontribution(v,ξ[j],-sgnn[j]*n,sgnh[j]*h,geo[j],0,[0,0],(i-1)*ΔR,UB) - allint[i-ringtot[1]+2][1] = add(allint[i-ringtot[1]+2][1],P) - allint[i-ringtot[1]+2][2] = add(allint[i-ringtot[1]+2][2],Q) - end - for i in rings[j] - - #shall I put some check like i*deltaR > h - P, Q = arcsegcontribution(v,ξ[j],sgnn[j]*n,sgnh[j]*h,geo[j],datarings[j][i-rings[j][1]+2][1],datarings[j][i-rings[j][1]+2][2],i*ΔR,UB) #salviamo - allint[i-ringtot[1]+2][1] = add(allint[i-ringtot[1]+2][1],P) - allint[i-ringtot[1]+2][2] = add(allint[i-ringtot[1]+2][2],Q) - P, Q = arcsegcontribution(v,ξ[j],-sgnn[j]*n,sgnh[j]*h,geo[j],datarings[j][i-rings[j][1]+1][1],datarings[j][i-rings[j][1]+1][2],(i-1)*ΔR,UB) - allint[i-ringtot[1]+2][1] = add(allint[i-ringtot[1]+2][1],P) - allint[i-ringtot[1]+2][2] = add(allint[i-ringtot[1]+2][2],Q) - end #probabilmente va bene cosi anche con ceil(int,frac) invece di ceil(int,frac+1) - allint[i-ringtot[1]+2][1]=multiply(allint[i-ringtot[1]+2][1],1/(l12′*l12*norm(hdir))) - allint[i-ringtot[1]+2][2]=multiply(allint[i-ringtot[1]+2][2],1/(l12′*l12*norm(hdir))) - #= for i in (relrings[j][2]+1):ringtot[j][2] - - #shall I put some check like i*deltaR > h - saveP[i],saveQ[i] = arcsegcontribution(v,ξ[j],sgnn[j]*n,sgnh[j]*h,geo[j],1,[a,b],i*ΔR,UB) #dadefinire a e b - save,and,subtract = arcsegcontribution(v,ξ[j],sgnn[j]*n,sgnh[j]*h,geo[j],1,[a,b],(i-1)*ΔR,UB) - end =# #sembra che non serva a causa di ceil(int,frac+1)! - end - #I+=(1/abs(r12′[2]*r12[1]))*(3*(temp2^2-temp1^2)*d[1]-2*(temp2^3-temp1^3)*d[2]) - end - - return ringtot,allint #missing buidgrad since it is not yet adapted for int line line -end - diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 9834e5bc..8ec3cd44 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -1,5 +1,4 @@ using WiltonInts84 -using TimeDomainBEMInt abstract type AbstractSpaceTimeOperator end abstract type SpaceTimeOperator <: AbstractSpaceTimeOperator end # atomic operator @@ -315,15 +314,6 @@ struct WiltonInts84Strat{T,V,W} workspace::W end -struct HybridZuccottiWiltonStrat{T,V,W} - outer_quad_points::T - binomials::V - workspace::W -end - -struct ZuccottiRule{T} - weight::T #Allanalyticalformula -end function momintegrals!(z, op, g, f, T, τ, σ, ι, qr::WiltonInts84Strat) From 68aa02fbe64ffba64fa62ea0b9064c3df4e78529 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 9 Oct 2024 16:00:46 +0200 Subject: [PATCH 405/528] fixed type instability in GWP eval --- examples/efie_gwp2.jl | 10 +++--- src/bases/local/gwplocal.jl | 64 +++++++++++++++++++++++++++---------- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/examples/efie_gwp2.jl b/examples/efie_gwp2.jl index b343e991..53f91e8b 100644 --- a/examples/efie_gwp2.jl +++ b/examples/efie_gwp2.jl @@ -1,18 +1,18 @@ using CompScienceMeshes using BEAST -Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -# Γ = meshsphere(radius=1.0, h=0.1) +# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +Γ = meshsphere(radius=1.0, h=0.4) @show length(Γ) # X = raviartthomas(Γ) X = BEAST.gwpdiv(Γ; order=2) -κ, η = 1.0, 1.0 +κ, η = 3.0, 1.0 t = Maxwell3D.singlelayer(wavenumber=κ) E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) e = (n × E) × n -BEAST.@defaultquadstrat (t,X,X) BEAST.DoubleNumSauterQstrat(7,8,6,6,6,6) +BEAST.@defaultquadstrat (t,X,X) BEAST.DoubleNumSauterQstrat(2,3,4,4,4,4) BEAST.@defaultquadstrat (e,X) BEAST.SingleNumQStrat(12) @hilbertspace j @@ -22,7 +22,7 @@ BEAST.@defaultquadstrat (e,X) BEAST.SingleNumQStrat(12) A = assemble(t[k,j], X, X) b = assemble(e[k], X) -Ai = BEAST.GMRESSolver(A; reltol=1e-8, restart=1500) +Ai = BEAST.GMRESSolver(A; reltol=1e-6, restart=1500) u = Ai * b include("utils/postproc.jl") diff --git a/src/bases/local/gwplocal.jl b/src/bases/local/gwplocal.jl index 1dc4dd4d..6cf2aeea 100644 --- a/src/bases/local/gwplocal.jl +++ b/src/bases/local/gwplocal.jl @@ -2,6 +2,8 @@ struct GWPCurlRefSpace{T,Degree} <: RefSpace{T} end function numfunctions(x::GWPCurlRefSpace{<:Any,D}, dom::CompScienceMeshes.ReferenceSimplex{2}) where{D} (D+1)*(D+3) end +function dimtype(x::GWPCurlRefSpace{<:Any,D}, + dom::CompScienceMeshes.ReferenceSimplex{2}) where{D} Val{(D+1)*(D+3)} end function (ϕ::GWPCurlRefSpace{T,Degree})(p) where {T,Degree} dom = domain(chart(p)) @@ -13,6 +15,12 @@ end function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Dim}, u) where {T,Deg,Dim} + ϕ(dom, u, dimtype(ϕ,dom)) +end + +function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Dim}, + u, ::Type{Val{NF}}) where {T,Deg,Dim,NF} + d = Deg u, v = u w = 1-u-v @@ -24,8 +32,9 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di nd3 = point(T, -v, u) P = SVector{2,T} - vals = P[] - crls = T[] + vals = Vector{P}(undef,NF) + crls = Vector{T}(undef,NF) + idx = 1 i = 0 for j in 1:d+1 @@ -33,15 +42,19 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di Rᵢ = _sylpoly(s, i+1, u) Rⱼ = _sylpoly_shift(s, j+1, v) Rₖ = _sylpoly_shift(s, k+1, w) - push!(vals, Rᵢ*Rⱼ*Rₖ*nd1) - + dRᵢ = _sylpoly_diff(s, i+1, u) dRⱼ = _sylpoly_shift_diff(s, j+1, v) dRₖ = _sylpoly_shift_diff(s, k+1, w) du = dRᵢ*Rⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ dv = Rᵢ*dRⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ curl = (du*nd1[2] - dv*nd1[1]) + 2*Rᵢ*Rⱼ*Rₖ - push!(crls, curl) + + vals[idx] = Rᵢ*Rⱼ*Rₖ*nd1 + crls[idx] = curl + # push!(vals, Rᵢ*Rⱼ*Rₖ*nd1) + # push!(crls, curl) + idx += 1 end for i in 1:d+1 @@ -50,15 +63,20 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di Rᵢ = _sylpoly_shift(s, i+1, u) Rⱼ = _sylpoly(s, j+1, v) Rₖ = _sylpoly_shift(s, k+1, w) - push!(vals, Rᵢ*Rⱼ*Rₖ*nd2) - + dRᵢ = _sylpoly_shift_diff(s, i+1, u) dRⱼ = _sylpoly_diff(s, j+1, v) dRₖ = _sylpoly_shift_diff(s, k+1, w) du = dRᵢ*Rⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ dv = Rᵢ*dRⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ curl = (du*nd2[2] - dv*nd2[1]) + 2*Rᵢ*Rⱼ*Rₖ - push!(crls, curl) + + # push!(vals, Rᵢ*Rⱼ*Rₖ*nd2) + # push!(crls, curl) + + vals[idx] = Rᵢ*Rⱼ*Rₖ*nd2 + crls[idx] = curl + idx += 1 end for i in 1:d+1 @@ -67,17 +85,21 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di Rᵢ = _sylpoly_shift(s, i+1, u) Rⱼ = _sylpoly_shift(s, j+1, v) Rₖ = _sylpoly(s, k+1, w) - push!(vals, Rᵢ*Rⱼ*Rₖ*nd3) - + dRᵢ = _sylpoly_shift_diff(s, i+1, u) dRⱼ = _sylpoly_shift_diff(s, j+1, v) dRₖ = _sylpoly_diff(s, k+1, w) - + du = dRᵢ*Rⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ dv = Rᵢ*dRⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ curl = (du*nd3[2] - dv*nd3[1]) + 2*Rᵢ*Rⱼ*Rₖ - push!(crls, curl) + # push!(vals, Rᵢ*Rⱼ*Rₖ*nd3) + # push!(crls, curl) + + vals[idx] = Rᵢ*Rⱼ*Rₖ*nd3 + crls[idx] = curl + idx += 1 end for i in 1:d+1 @@ -93,8 +115,6 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di S1 = Rᵢ*Rsⱼ*Rsₖ*nd1 S2 = Rsᵢ*Rⱼ*Rsₖ*nd2 S3 = Rsᵢ*Rsⱼ*Rₖ*nd3 - push!(vals, S2-S3) - push!(vals, S3-S1) dRsᵢ = _sylpoly_shift_diff(s, i+1, u) dRsⱼ = _sylpoly_shift_diff(s, j+1, v) @@ -115,12 +135,22 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di dv = Rsᵢ*dRsⱼ*Rₖ - Rsᵢ*Rsⱼ*dRₖ curlS3 = du*nd3[2] - dv*nd3[1] + 2*Rsᵢ*Rsⱼ*Rₖ - push!(crls, curlS2 - curlS3) - push!(crls, curlS3 - curlS1) + # push!(vals, S2-S3) + # push!(crls, curlS2 - curlS3) + vals[idx] = S2-S3 + crls[idx] = curlS2 - curlS3 + idx += 1 + + # push!(vals, S3-S1) + # push!(crls, curlS3 - curlS1) + vals[idx] = S3-S1 + crls[idx] = curlS3 - curlS1 + idx += 1 end end - NF = length(vals) + # NF = length(vals) SVector{NF}([(value=f, curl=c) for (f,c) in zip(vals, crls)]) + # [(value=f, curl=c) for (f,c) in zip(vals, crls)] end From 7537d702461c9c96c95884e846dcc60a6cd66345 Mon Sep 17 00:00:00 2001 From: azuccott Date: Wed, 9 Oct 2024 16:38:45 +0200 Subject: [PATCH 406/528] Update quadstrats.jl --- src/quadrature/quadstrats.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index 3187a55b..534f9a46 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -31,7 +31,6 @@ struct OuterNumInnerAnalyticQStrat{R} end - defaultquadstrat(op, tfs, bfs) = defaultquadstrat(op, refspace(tfs), refspace(bfs)) macro defaultquadstrat(dop, body) @assert dop.head == :tuple From 5b52e37b8fd335513a8d792469e4068e279ec467 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 10 Oct 2024 17:04:19 +0200 Subject: [PATCH 407/528] methods for assembly (Id,GWP,GWP) added --- Project.toml | 2 +- examples/mfie_gwp2.jl | 11 +++-------- src/bases/local/gwplocal.jl | 35 ++++++++--------------------------- src/identityop.jl | 15 +++++++++++++++ 4 files changed, 27 insertions(+), 36 deletions(-) diff --git a/Project.toml b/Project.toml index aea9da92..1b31d623 100644 --- a/Project.toml +++ b/Project.toml @@ -37,7 +37,7 @@ AbstractTrees = "0.4.4" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 1" CollisionDetection = "0.1.6" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.8.2" +CompScienceMeshes = "0.8.3" Compat = "2, 3, 4" ConvolutionOperators = "0.4" ExtendableSparse = "1.4" diff --git a/examples/mfie_gwp2.jl b/examples/mfie_gwp2.jl index e235f38b..ccef8606 100644 --- a/examples/mfie_gwp2.jl +++ b/examples/mfie_gwp2.jl @@ -1,14 +1,9 @@ using CompScienceMeshes, BEAST # Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -Γ = meshsphere(radius=1.0, h=0.1) -# X, Y = raviartthomas(Γ), buffachristiansen(Γ) -# X = brezzidouglasmarini(Γ) -# Y = brezzidouglasmarini(Γ) -# X = raviartthomas(Γ) -# Y = raviartthomas(Γ) -X = BEAST.gwpdiv(Γ; order=2) -Y = BEAST.gwpdiv(Γ; order=2) +Γ = meshsphere(radius=1.0, h=0.2) +X = BEAST.gwpdiv(Γ; order=3) +Y = BEAST.gwpdiv(Γ; order=3) ϵ, μ, ω = 1.0, 1.0, 1.0; κ = ω * √(ϵ*μ) # κ = 3.0 diff --git a/src/bases/local/gwplocal.jl b/src/bases/local/gwplocal.jl index 6cf2aeea..6f0f76e7 100644 --- a/src/bases/local/gwplocal.jl +++ b/src/bases/local/gwplocal.jl @@ -32,8 +32,8 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di nd3 = point(T, -v, u) P = SVector{2,T} - vals = Vector{P}(undef,NF) - crls = Vector{T}(undef,NF) + NT = @NamedTuple{value::P, curl::T} + nts = Vector{NT}(undef, NF) idx = 1 i = 0 @@ -50,10 +50,7 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di dv = Rᵢ*dRⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ curl = (du*nd1[2] - dv*nd1[1]) + 2*Rᵢ*Rⱼ*Rₖ - vals[idx] = Rᵢ*Rⱼ*Rₖ*nd1 - crls[idx] = curl - # push!(vals, Rᵢ*Rⱼ*Rₖ*nd1) - # push!(crls, curl) + nts[idx] = (value=Rᵢ*Rⱼ*Rₖ*nd1, curl=curl) idx += 1 end @@ -71,11 +68,7 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di dv = Rᵢ*dRⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ curl = (du*nd2[2] - dv*nd2[1]) + 2*Rᵢ*Rⱼ*Rₖ - # push!(vals, Rᵢ*Rⱼ*Rₖ*nd2) - # push!(crls, curl) - - vals[idx] = Rᵢ*Rⱼ*Rₖ*nd2 - crls[idx] = curl + nts[idx] = (value=Rᵢ*Rⱼ*Rₖ*nd2, curl=curl) idx += 1 end @@ -93,12 +86,8 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di du = dRᵢ*Rⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ dv = Rᵢ*dRⱼ*Rₖ - Rᵢ*Rⱼ*dRₖ curl = (du*nd3[2] - dv*nd3[1]) + 2*Rᵢ*Rⱼ*Rₖ - - # push!(vals, Rᵢ*Rⱼ*Rₖ*nd3) - # push!(crls, curl) - vals[idx] = Rᵢ*Rⱼ*Rₖ*nd3 - crls[idx] = curl + nts[idx] = (value=Rᵢ*Rⱼ*Rₖ*nd3, curl=curl) idx += 1 end @@ -135,22 +124,14 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di dv = Rsᵢ*dRsⱼ*Rₖ - Rsᵢ*Rsⱼ*dRₖ curlS3 = du*nd3[2] - dv*nd3[1] + 2*Rsᵢ*Rsⱼ*Rₖ - # push!(vals, S2-S3) - # push!(crls, curlS2 - curlS3) - vals[idx] = S2-S3 - crls[idx] = curlS2 - curlS3 + nts[idx] = (value=S2-S3, curl=curlS2 - curlS3) idx += 1 - # push!(vals, S3-S1) - # push!(crls, curlS3 - curlS1) - vals[idx] = S3-S1 - crls[idx] = curlS3 - curlS1 + nts[idx] = (value=S3-S1, curl=curlS3 - curlS1) idx += 1 end end - # NF = length(vals) - SVector{NF}([(value=f, curl=c) for (f,c) in zip(vals, crls)]) - # [(value=f, curl=c) for (f,c) in zip(vals, crls)] + return SVector{NF}(nts) end diff --git a/src/identityop.jl b/src/identityop.jl index 8cea7f61..c55d1dce 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -87,6 +87,21 @@ function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,4} where {T,Deg}, end +defaultquadstrat(::LocalOperator, ::GWPDivRefSpace{<:Real,D1}, + ::GWPDivRefSpace{<:Real,D2}) where {D1,D2} = SingleNumQStrat(7) + + +function quaddata(op::LocalOperator, g::GWPDivRefSpace, f::GWPDivRefSpace, + tels::Vector, bels::Vector, + qs::SingleNumQStrat) + + u, w = trgauss(qs.quad_rule) + qd = [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] + A = _alloc_workspace(qd, g, f, tels, bels) + return qd, A +end + + function quadrule(op::LocalOperator, ψ::RefSpace, ϕ::RefSpace, τ, (qd,A), qs::SingleNumQStrat) for i in eachindex(qd) q = qd[i] From 67496e0d0999e7904be91ed4f53930a4d1ff8704 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 10 Oct 2024 17:47:14 +0200 Subject: [PATCH 408/528] rank on Sparse issues fixed --- test/test_basis.jl | 2 +- test/test_mult.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_basis.jl b/test/test_basis.jl index a85c7937..64a7d0ef 100644 --- a/test/test_basis.jl +++ b/test/test_basis.jl @@ -20,7 +20,7 @@ for T in [Float32, Float64] @show BEAST.defaultquadstrat(hypersingular, X, X) # @show @which BEAST.defaultquadstrat(hypersingular, X, X) @time N = assemble(hypersingular, X, X) - @time I = assemble(identityop, X, X) + @time I = Matrix(assemble(identityop, X, X)) @test size(N) == (numfunctions(X), numfunctions(X)) @test size(I) == (numfunctions(X), numfunctions(X)) diff --git a/test/test_mult.jl b/test/test_mult.jl index 16927330..4c5399df 100644 --- a/test/test_mult.jl +++ b/test/test_mult.jl @@ -22,7 +22,7 @@ for T in [Float32, Float64] divX = divergence(X) Id = BEAST.Identity() - DD = assemble(Id, divX, divX) + DD = Matrix(assemble(Id, divX, divX)) @test rank(DD) == 7 L = divX * Conn for sh in L.fns[1] From fca8afe999497f922ec6adb634023f3d1e1c4622 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 11 Oct 2024 17:18:40 +0200 Subject: [PATCH 409/528] support to take the divergence of GWP space --- examples/divgwp_expansion.jl | 66 +++++++++++++++++++++++++++++ src/bases/divergence.jl | 5 ++- src/bases/global/gwpdivglobal.jl | 58 +++++++++++++++++++++++++ src/bases/lagrange.jl | 1 + src/bases/local/bdmlocal.jl | 2 +- src/bases/local/gwpdivlocal.jl | 72 +++++++++++++++++++++++++++++++- src/bases/local/laglocal.jl | 8 +++- src/bases/local/ndlcdlocal.jl | 2 +- src/bases/local/rtlocal.jl | 2 +- 9 files changed, 208 insertions(+), 8 deletions(-) create mode 100644 examples/divgwp_expansion.jl diff --git a/examples/divgwp_expansion.jl b/examples/divgwp_expansion.jl new file mode 100644 index 00000000..c6ed125d --- /dev/null +++ b/examples/divgwp_expansion.jl @@ -0,0 +1,66 @@ +using BEAST +using CompScienceMeshes +const CSM = CompScienceMeshes +using StaticArrays + +T = Float64 +D = 4 +NF = binomial(2+D, 2) +gwp = BEAST.GWPDivRefSpace{T,D}() +lgx = BEAST.LagrangeRefSpace{T,D,3,10}() + +function fields(p) + map(gwp(p)) do x + x.divergence + end +end + +ch = CSM.simplex( + point(1,0,0), + point(0,1,0), + point(0,0,0)) + +# p = CSM.center(ch) +# v = fields(p) + +coeffs = BEAST.interpolate(fields, lgx, ch) +Q = rationalize.(coeffs; tol=sqrt(eps(T))) + +using LinearAlgebra +norm(Q - coeffs) + +Q3 = Rational{Int64}[89//3 223//81 -7//81 -2 223//81 -7//81 -2 -7//81 -2 -2; -59//2 236//27 -13//54 13 -83//9 -4//27 8 -1//18 3 -2; 13 -13//54 236//27 -59//2 8 -4//27 -83//9 3 -1//18 -2; -2 -7//81 223//81 89//3 -2 -7//81 223//81 -2 -7//81 -2; 89//3 223//81 -7//81 -2 223//81 -7//81 -2 -7//81 -2 -2; -59//2 -83//9 -1//18 -2 236//27 -4//27 3 -13//54 8 13; 13 8 3 -2 -13//54 -4//27 -1//18 236//27 -83//9 -59//2; -2 -2 -2 -2 -7//81 -7//81 -7//81 223//81 223//81 89//3; -2 -7//81 223//81 89//3 -2 -7//81 223//81 -2 -7//81 -2; -2 -1//18 -83//9 -59//2 3 -4//27 236//27 8 -13//54 13; -2 3 8 13 -1//18 -4//27 -13//54 -83//9 236//27 -59//2; -2 -2 -2 -2 -7//81 -7//81 -7//81 223//81 223//81 89//3; -50 890//81 55//81 35//3 -565//81 125//162 20//3 70//81 5//3 -10//3; 50 565//81 -70//81 10//3 -890//81 -125//162 -5//3 -55//81 -20//3 -35//3; 30 -355//27 355//27 -30 80//9 0 -80//9 -10//9 10//9 0; -40 440//27 85//27 -10 5//2 -125//27 205//18 35//3 95//9 -25//2; -35//3 -55//81 -890//81 50 -20//3 -125//162 565//81 -5//3 -70//81 10//3; 15 -5//27 485//27 0 5 0 -485//27 -5 5//27 -15; 40 -5//2 -35//3 25//2 -440//27 125//27 -95//9 -85//27 -205//18 10; -30 -80//9 10//9 0 355//27 0 -10//9 -355//27 80//9 30; -25//2 35//3 5//2 -40 95//9 -125//27 440//27 205//18 85//27 -10; 25//2 -95//9 -205//18 10 -35//3 125//27 -85//27 -5//2 -440//27 40; -15 -5 5 15 5//27 0 -5//27 -485//27 485//27 0; 35//3 20//3 5//3 -10//3 55//81 125//162 70//81 890//81 -565//81 -50] +function BEAST.divergence(localspace::BEAST.GWPDivRefSpace, sh, ch) + BEAST.divergence(localspace, sh, ch, BEAST.dimtype(localspace, domain(ch))) +end + +function BEAST.divergence(localspace::BEAST.GWPDivRefSpace{T,D}, cellid, ch, ::Type{Val{N}}) where {N,D} + function fields(p) + map(localspace(p)) do x + x.divergence + end + end + T = coordtype(ch) + Dim = 2 + NFout = div((Dim+1)*(Dim+2), 2) + lag = BEAST.LagrangeRefSpace{T,D,Dim+1,NFout}() + coeffs = BEAST.interpolate(fields, lag, ch) + S = BEAST.Shape{T} + A = Vector{Vector{S}}(undef, size(coeffs,1)) + for r in axes(coeffs,1) + A[r] = collect(S(cellid, c, coeffs[r,c]) for c in axes(coeffs,2)) + end + return SVector{N}(A) +end + +divfns = BEAST.divergence(gwp, 1, ch) + +p = neighborhood(ch, (0.2, 0.6)) +gwp_vals = gwp(p) +val1 = gwp_vals[1].divergence + +lgx_vals = lgx(p) +val2 = zero(T) +for sh in divfns[1] + val2 += sh.coeff * lgx_vals[sh.refid].value +end \ No newline at end of file diff --git a/src/bases/divergence.jl b/src/bases/divergence.jl index 12943376..a4b32add 100644 --- a/src/bases/divergence.jl +++ b/src/bases/divergence.jl @@ -17,10 +17,11 @@ function divergence(x::Space) fns = x.fns dvs = similar(fns) for (i,fn) in enumerate(fns) - dvs[i] = similar(fns[i]) + dvs[i] = similar(fns[i], 0) for (j,sh) in enumerate(fn) el = els[sh.cellid] - dvs[i][j] = divergence(ref, sh, el) + append!(dvs[i], divergence(ref, sh, el)) + # dvs[i][j] = divergence(ref, sh, el) end end divergence(x, geo, dvs) diff --git a/src/bases/global/gwpdivglobal.jl b/src/bases/global/gwpdivglobal.jl index 1b97f43d..f707b805 100644 --- a/src/bases/global/gwpdivglobal.jl +++ b/src/bases/global/gwpdivglobal.jl @@ -36,4 +36,62 @@ end nf = order * (order+1) Nt = length(edges)*ne + length(mesh)*nf @test numfunctions(gwp) == Nt +end + + +function divergence(X::GWPDivSpace, geo, fns) + ch = chart(geo, first(geo)) + dim = dimension(ch) + dom = domain(ch) + # dim = dimension(dom) + + P = X.degree + Cont = -1 + NF = binomial(dim+P, dim) + + LagrangeBasis{P,Cont,NF}(geo, fns, deepcopy(positions(X))) +end + + +@testitem "divergence - global" begin + using CompScienceMeshes + const CSM = CompScienceMeshes + + T = Float64 + + m = CSM.meshrectangle(1.0, 1.0, 0.5, 3) + X = BEAST.gwpdiv(m; order=4) + divX = BEAST.divergence(X) + + x = BEAST.refspace(X) + divx = BEAST.refspace(divX) + + err = zero(T) + for i in eachindex(X.fns) + fn = X.fns[i] + for j in eachindex(fn) + cellid = X.fns[i][j].cellid + ch = chart(m, cellid) + + u = (0.2341, 0.4312) + p = neighborhood(ch, u) + + r1 = zero(T) + ϕp = x(p) + for sh in X.fns[i] + sh.cellid == cellid || continue + r1 += sh.coeff * ϕp[sh.refid].divergence + end + + r2 = zero(T) + ϕp = divx(p) + for sh in divX.fns[i] + sh.cellid == cellid || continue + r2 += sh.coeff * ϕp[sh.refid].value + end + global err = max(err, abs(r1-r2)) + end + end + + @test err < sqrt(eps(T)) end \ No newline at end of file diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 77297b5d..cbc3a617 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -11,6 +11,7 @@ function lagdimension end # M: mesh type # T: field type # NF: number of local shape functions +# P: point type mutable struct LagrangeBasis{D,C,M,T,NF,P} <: Space{T} geo::M fns::Vector{Vector{Shape{T}}} diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index 1eb173a4..1c83f5f7 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -19,7 +19,7 @@ function (f::BDMRefSpace)(p) (value=v*tv/j, divergence=d),] end -divergence(ref::BDMRefSpace, sh, el) = Shape(sh.cellid, 1, sh.coeff/(2*volume(el))) +divergence(ref::BDMRefSpace, sh, el) = [Shape(sh.cellid, 1, sh.coeff/(2*volume(el)))] const _vert_perms_bdm = [ (1,2,3), diff --git a/src/bases/local/gwpdivlocal.jl b/src/bases/local/gwpdivlocal.jl index 8c21b9c4..342c531a 100644 --- a/src/bases/local/gwpdivlocal.jl +++ b/src/bases/local/gwpdivlocal.jl @@ -2,6 +2,8 @@ struct GWPDivRefSpace{T,Degree} <: RefSpace{T} end function numfunctions(x::GWPDivRefSpace{<:Any,D}, dom::CompScienceMeshes.ReferenceSimplex{2}) where{D} (D+1)*(D+3) end +function dimtype(x::GWPDivRefSpace{<:Any,D}, + dom::CompScienceMeshes.ReferenceSimplex{2}) where{D} Val{(D+1)*(D+3)} end function (ϕ::GWPDivRefSpace{T,Degree})(p) where {T,Degree} ψ = GWPCurlRefSpace{T,Degree}() @@ -26,7 +28,7 @@ function interpolate(fields, interpolant::GWPDivRefSpace{T,Degree}, chart) where return interpolate(nxfields, GWPCurlRefSpace{T,Degree}(), chart) end -@testitem "divergence" begin +@testitem "divergence - pointwise" begin using CompScienceMeshes, LinearAlgebra T = Float64 @@ -66,4 +68,72 @@ end # @show curl_num, curl_ana, abs(curl_num - curl_ana) @test curl_num ≈ curl_ana atol=sqrt(eps(T))*100 end +end + + +function divergence(localspace::GWPDivRefSpace, sh, ch) + fns = divergence(localspace, sh.cellid, ch) + α = sh.coeff + S = typeof(sh) + return S[S(s.cellid, s.refid, α*s.coeff) for s in fns[sh.refid]] +end + + +function divergence(localspace::GWPDivRefSpace, cellid::Int, ch) + divergence(localspace, cellid, ch, dimtype(localspace, domain(ch))) +end + + +function divergence(localspace::GWPDivRefSpace{T,D}, cellid::Int, ch, ::Type{Val{N}}) where {N,D,T} + function fields(p) + map(localspace(p)) do x + x.divergence + end + end + atol = sqrt(eps(T)) + Dim = 2 + NFout = div((Dim+1)*(Dim+2), 2) + lag = LagrangeRefSpace{T,D,Dim+1,NFout}() + coeffs = interpolate(fields, lag, ch) + S = BEAST.Shape{T} + A = Vector{Vector{S}}(undef, size(coeffs,1)) + for r in axes(coeffs,1) + A[r] = collect(S(cellid, c, coeffs[r,c]) for c in axes(coeffs,2) if abs(c) > atol) + end + return SVector{N}(A) +end + + +@testitem "divergence - chartwise" begin + using CompScienceMeshes + const CSM = CompScienceMeshes + + T = Float64 + D = 4 + NF = binomial(2+D, 2) + gwp = BEAST.GWPDivRefSpace{T,D}() + lgx = BEAST.LagrangeRefSpace{T,D,3,10}() + + ch = CSM.simplex( + point(1,0,0), + point(0,1,0), + point(0,0,0)) + + divfns = BEAST.divergence(gwp, 1, ch) + + p = neighborhood(ch, (0.2, 0.6)) + gwp_vals = gwp(p) + lgx_vals = lgx(p) + + err = similar(Vector{T}, axes(gwp_vals)) + for i in eachindex(gwp_vals) + val1 = gwp_vals[i].divergence + val2 = zero(T) + for sh in divfns[i] + val2 += sh.coeff * lgx_vals[sh.refid].value + end + err[i] = abs(val1 - val2) + end + atol = sqrt(eps(T)) + @test all(err .< atol) end \ No newline at end of file diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index cd90f2c3..21355e0f 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -386,7 +386,7 @@ function interpolate(fields, interpolant::LagrangeRefSpace{T,Degree,3}, chart) w s = range(0,1,length=Degree+1) Is = zip(I,s) idx = 1 - vals = [] + vals = Vector{Vector{T}}() for (i,ui) in Is for (j,vj) in Is for (k,wk) in Is @@ -397,6 +397,10 @@ function interpolate(fields, interpolant::LagrangeRefSpace{T,Degree,3}, chart) w idx += 1 end end end - Q = hcat(vals...) + # Q = hcat(vals...) + Q = Matrix{T}(undef, length(vals[1]), length(vals)) + for i in eachindex(vals) + Q[:,i] .= vals[i] + end return Q end \ No newline at end of file diff --git a/src/bases/local/ndlcdlocal.jl b/src/bases/local/ndlcdlocal.jl index 1ecc2ad6..8b924607 100644 --- a/src/bases/local/ndlcdlocal.jl +++ b/src/bases/local/ndlcdlocal.jl @@ -78,7 +78,7 @@ function ttrace(x::NDLCDRefSpace, el, q, fc) return t end -divergence(ref::NDLCDRefSpace, sh, el) = Shape(sh.cellid, 1, sh.coeff/volume(el)) +divergence(ref::NDLCDRefSpace, sh, el) = [Shape(sh.cellid, 1, sh.coeff/volume(el))] function restrict(ϕ::NDLCDRefSpace{T}, dom1, dom2) where {T} diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index 7c16d6f8..c9350014 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -28,7 +28,7 @@ end numfunctions(x::RTRefSpace, dom::CompScienceMeshes.ReferenceSimplex{2}) = 3 -divergence(ref::RTRefSpace, sh, el) = Shape(sh.cellid, 1, sh.coeff/volume(el)) +divergence(ref::RTRefSpace, sh, el) = [Shape(sh.cellid, 1, sh.coeff/volume(el))] """ ntrace(refspace, element, localindex, face) From bac6a5ccddef1bf61a51a6ffc456fef04139d0a2 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 11 Oct 2024 17:37:11 +0200 Subject: [PATCH 410/528] adhere to 1.6 parsing rules --- src/bases/local/gwplocal.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/bases/local/gwplocal.jl b/src/bases/local/gwplocal.jl index 6f0f76e7..a13bfc23 100644 --- a/src/bases/local/gwplocal.jl +++ b/src/bases/local/gwplocal.jl @@ -1,9 +1,13 @@ struct GWPCurlRefSpace{T,Degree} <: RefSpace{T} end function numfunctions(x::GWPCurlRefSpace{<:Any,D}, - dom::CompScienceMeshes.ReferenceSimplex{2}) where{D} (D+1)*(D+3) end + dom::CompScienceMeshes.ReferenceSimplex{2}) where {D} + (D+1)*(D+3) +end function dimtype(x::GWPCurlRefSpace{<:Any,D}, - dom::CompScienceMeshes.ReferenceSimplex{2}) where{D} Val{(D+1)*(D+3)} end + dom::CompScienceMeshes.ReferenceSimplex{2}) where {D} + Val{(D+1)*(D+3)} +end function (ϕ::GWPCurlRefSpace{T,Degree})(p) where {T,Degree} dom = domain(chart(p)) From 1f8fc185de29ebfc1eee3de6c457db46713e568e Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 11 Oct 2024 17:42:21 +0200 Subject: [PATCH 411/528] more parsing issues on 1.6 --- src/bases/local/gwpdivlocal.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/bases/local/gwpdivlocal.jl b/src/bases/local/gwpdivlocal.jl index 342c531a..43317a30 100644 --- a/src/bases/local/gwpdivlocal.jl +++ b/src/bases/local/gwpdivlocal.jl @@ -1,9 +1,13 @@ struct GWPDivRefSpace{T,Degree} <: RefSpace{T} end function numfunctions(x::GWPDivRefSpace{<:Any,D}, - dom::CompScienceMeshes.ReferenceSimplex{2}) where{D} (D+1)*(D+3) end + dom::CompScienceMeshes.ReferenceSimplex{2}) where {D} + (D+1)*(D+3) +end function dimtype(x::GWPDivRefSpace{<:Any,D}, - dom::CompScienceMeshes.ReferenceSimplex{2}) where{D} Val{(D+1)*(D+3)} end + dom::CompScienceMeshes.ReferenceSimplex{2}) where {D} + Val{(D+1)*(D+3)} +end function (ϕ::GWPDivRefSpace{T,Degree})(p) where {T,Degree} ψ = GWPCurlRefSpace{T,Degree}() From 1a5769d8c082b82d2f3176a5a5d5703b246e3bf8 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 17 Oct 2024 13:34:42 +0200 Subject: [PATCH 412/528] Use MVector for GWP local value storage --- examples/Manifest.toml | 1151 +++++++++++++++++++++++------------ examples/efie_gwp2.jl | 2 +- src/bases/local/gwplocal.jl | 5 +- 3 files changed, 776 insertions(+), 382 deletions(-) diff --git a/examples/Manifest.toml b/examples/Manifest.toml index e3ef7d18..fb7d1bbf 100644 --- a/examples/Manifest.toml +++ b/examples/Manifest.toml @@ -1,28 +1,41 @@ # This file is machine-generated - editing it directly is not advised [[AbstractFFTs]] -deps = ["ChainRulesCore", "LinearAlgebra"] -git-tree-sha1 = "69f7020bd72f069c219b5e8c236c1fa90d2cb409" +deps = ["LinearAlgebra"] +git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" -version = "1.2.1" +version = "1.5.0" -[[Adapt]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "af92965fb30777147966f58acb05da51c5616b5f" -uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "3.3.3" + [AbstractFFTs.extensions] + AbstractFFTsChainRulesCoreExt = "ChainRulesCore" + AbstractFFTsTestExt = "Test" + + [AbstractFFTs.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[AbstractTrees]] +git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" +uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +version = "0.4.5" [[ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.2" [[ArrayLayouts]] -deps = ["FillArrays", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "ebe4bbfc4de38ef88323f67d60a4e848fb550f0e" +deps = ["FillArrays", "LinearAlgebra"] +git-tree-sha1 = "0dd7edaff278e346eb0ca07a7e75c9438408a3ce" uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "0.8.9" +version = "1.10.3" +weakdeps = ["SparseArrays"] + + [ArrayLayouts.extensions] + ArrayLayoutsSparseArraysExt = "SparseArrays" [[Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" +version = "1.11.0" [[AssetRegistry]] deps = ["Distributed", "JSON", "Pidfile", "SHA", "Test"] @@ -31,13 +44,14 @@ uuid = "bf4720bc-e11a-5d0c-854e-bdca1663c893" version = "0.1.0" [[BEAST]] -deps = ["BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "Distributed", "FFTW", "FastGaussQuadrature", "FillArrays", "InteractiveUtils", "IterativeSolvers", "LiftedMaps", "LinearAlgebra", "LinearMaps", "SauterSchwab3D", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SparseMatrixDicts", "SpecialFunctions", "StaticArrays", "WiltonInts84"] -git-tree-sha1 = "979d068d25c0e4775828c2b5e329216a9aedaf77" +deps = ["AbstractTrees", "BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "ConvolutionOperators", "Distributed", "ExtendableSparse", "FFTW", "FastGaussQuadrature", "FillArrays", "Infiltrator", "InteractiveUtils", "IterativeSolvers", "LiftedMaps", "LinearAlgebra", "LinearMaps", "NestedUnitRanges", "Requires", "SauterSchwab3D", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SpecialFunctions", "StaticArrays", "TestItems", "WiltonInts84"] +git-tree-sha1 = "757efc563022d39e95f751b7e5a29d020a57e049" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "1.5.0" +version = "2.5.0" [[Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" +version = "1.11.0" [[BinDeps]] deps = ["Libdl", "Pkg", "SHA", "URIParser", "Unicode"] @@ -53,33 +67,21 @@ version = "0.12.5" [[BlockArrays]] deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] -git-tree-sha1 = "43b09ac794ed8347592dd90539756d1c3416e5f2" +git-tree-sha1 = "9a9610fbe5779636f75229e423e367124034af41" uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" -version = "0.16.19" +version = "0.16.43" [[Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" +git-tree-sha1 = "9e2a6b69137e6969bab0152632dcb3bc108c8bdd" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+0" +version = "1.0.8+1" [[Cairo_jll]] -deps = ["Artifacts", "Bzip2_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2" +deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "009060c9a6168704143100f36ab08f06c2af4642" uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.16.1+1" - -[[ChainRulesCore]] -deps = ["Compat", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "80ca332f6dcb2508adba68f22f551adb2d00a624" -uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.15.3" - -[[ChangesOfVariables]] -deps = ["ChainRulesCore", "LinearAlgebra", "Test"] -git-tree-sha1 = "38f7a08f19d8810338d4f5085211c7dfa5d5bdd8" -uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" -version = "0.1.4" +version = "1.18.2+1" [[ClusterTrees]] deps = ["DelimitedFiles", "LinearAlgebra", "Pkg"] @@ -89,33 +91,37 @@ version = "0.2.1" [[CollisionDetection]] deps = ["Compat", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "a936b212eea4c2552a1fdff8f07aa5767fdad4ba" +git-tree-sha1 = "88a33f2fba4ef1065edd06164c84d8b03ee4d049" uuid = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" -version = "0.1.4" +version = "0.1.6" [[ColorSchemes]] -deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "Random"] -git-tree-sha1 = "1fd869cc3875b57347f7027521f561cf46d1fcd8" +deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] +git-tree-sha1 = "b5278586822443594ff615963b0c09755771b3e0" uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.19.0" +version = "3.26.0" [[ColorTypes]] deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4" +git-tree-sha1 = "b10d0b65641d57b8b4d5e234446582de5047050d" uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.11.4" +version = "0.11.5" [[ColorVectorSpace]] -deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"] -git-tree-sha1 = "d08c20eef1f2cbc6e60fd3612ac4340b89fea322" +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] +git-tree-sha1 = "a1f44953f2382ebb937d60dafbe2deea4bd23249" uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" -version = "0.9.9" +version = "0.10.0" +weakdeps = ["SpecialFunctions"] + + [ColorVectorSpace.extensions] + SpecialFunctionsExt = "SpecialFunctions" [[Colors]] deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] -git-tree-sha1 = "417b0ed7b8b838aa6ca0a87aadf1bb9eb111ce40" +git-tree-sha1 = "362a287c3aa50601b0bc359053d5c2468f0e7ce0" uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.12.8" +version = "0.12.11" [[Combinatorics]] git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" @@ -123,36 +129,47 @@ uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" version = "1.0.2" [[CompScienceMeshes]] -deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays"] -git-tree-sha1 = "f40ebed348c8bb7c3085e00f3936f90564fd0f23" +deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Permutations", "Requires", "SparseArrays", "StaticArrays", "TestItems"] +git-tree-sha1 = "6bb7cd3831707872fe404e01d156e1b149fcfa3a" uuid = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" -version = "0.3.3" +version = "0.8.3" [[Compat]] -deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "9be8be1d8a6f44b96482c8af52238ea7987da3e3" +deps = ["TOML", "UUIDs"] +git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.45.0" +version = "4.16.0" +weakdeps = ["Dates", "LinearAlgebra"] + + [Compat.extensions] + CompatLinearAlgebraExt = "LinearAlgebra" [[CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "1.1.1+0" [[Contour]] -git-tree-sha1 = "d05d9e7b7aedff4e5b51a029dced05cfb6125781" +git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8" uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" -version = "0.6.2" +version = "0.6.3" + +[[ConvolutionOperators]] +deps = ["LinearAlgebra", "LinearMaps"] +git-tree-sha1 = "ae44e38013c05c7ec59f428b4ea7ad7d34926b63" +uuid = "15927181-a1bb-497c-b745-8dbf505c019d" +version = "0.4.1" [[DataAPI]] -git-tree-sha1 = "fb5f5316dd3fd4c5e7c30a24d50643b73e37cd40" +git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.10.0" +version = "1.16.0" [[DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" +git-tree-sha1 = "1d0a14036acb104d9e89698bd408f63ab58cdc82" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.13" +version = "0.18.20" [[DataValueInterfaces]] git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" @@ -162,60 +179,89 @@ version = "1.0.0" [[Dates]] deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" +version = "1.11.0" + +[[Dbus_jll]] +deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "fc173b380865f70627d7dd1190dc2fce6cc105af" +uuid = "ee1fde0b-3d02-5ea6-8484-8dfef6360eab" +version = "1.14.10+0" [[DelimitedFiles]] deps = ["Mmap"] +git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" +version = "1.9.1" [[Distributed]] deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" +version = "1.11.0" [[DocStringExtensions]] deps = ["LibGit2"] -git-tree-sha1 = "b19534d1895d702889b219c382a6e18010797f0b" +git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.8.6" +version = "0.9.3" [[Downloads]] deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.6.0" -[[EarCut_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "3f3a2501fa7236e9b911e0f7a588c657e822bb6d" -uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" -version = "2.2.3+0" +[[EpollShim_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8e9441ee83492030ace98f9789a654a6d0b1f643" +uuid = "2702e6a9-849d-5ed8-8c21-79e8b8f9ee43" +version = "0.0.20230411+0" [[Expat_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "bad72f730e9e91c08d9427d5e8db95478a3c323d" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7" uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.4.8+0" +version = "2.6.2+0" + +[[ExtendableSparse]] +deps = ["DocStringExtensions", "ILUZero", "LinearAlgebra", "Printf", "SparseArrays", "Sparspak", "StaticArrays", "SuiteSparse", "Test"] +git-tree-sha1 = "3dc0ead7baa71735e03be4379d812dd8167e904a" +uuid = "95c220a8-a1cf-11e9-0c77-dbfce5f500b3" +version = "1.5.2" + + [ExtendableSparse.extensions] + ExtendableSparseAMGCLWrapExt = "AMGCLWrap" + ExtendableSparseAlgebraicMultigridExt = "AlgebraicMultigrid" + ExtendableSparseIncompleteLUExt = "IncompleteLU" + ExtendableSparsePardisoExt = "Pardiso" + + [ExtendableSparse.weakdeps] + AMGCLWrap = "4f76b812-4ba5-496d-b042-d70715554288" + AlgebraicMultigrid = "2169fc97-5a83-5252-b627-83903c6c433c" + IncompleteLU = "40713840-3770-5561-ab4c-a76e7d0d7895" + Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" [[FFMPEG]] deps = ["FFMPEG_jll"] -git-tree-sha1 = "b57e3acbe22f8484b4b5ff66a7499717fe1a9cc8" +git-tree-sha1 = "53ebe7511fa11d33bec688a9178fac4e49eeee00" uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" -version = "0.4.1" +version = "0.4.2" [[FFMPEG_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "Pkg", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] -git-tree-sha1 = "ccd479984c7838684b3ac204b716c89955c76623" +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] +git-tree-sha1 = "466d45dc38e15794ec7d5d63ec03d776a9aff36e" uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" -version = "4.4.2+0" +version = "4.4.4+1" [[FFTW]] deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] -git-tree-sha1 = "90630efff0894f8142308e334473eba54c433549" +git-tree-sha1 = "4820348781ae578893311153d69049a93d05f39d" uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" -version = "1.5.0" +version = "1.8.0" [[FFTW_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea" +git-tree-sha1 = "4d81ed14783ec49ce9f2e168208a12ce1815aa25" uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" -version = "3.3.10+0" +version = "3.3.10+1" [[FLTK_jll]] deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] @@ -225,48 +271,58 @@ version = "1.3.8+0" [[FastGaussQuadrature]] deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "58d83dd5a78a36205bdfddb82b1bb67682e64487" +git-tree-sha1 = "fd923962364b645f3719855c88f7074413a6ad92" uuid = "442a2c76-b920-505d-bb47-c5924d526838" -version = "0.4.9" +version = "1.0.2" [[FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" +version = "1.11.0" [[FillArrays]] -deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"] -git-tree-sha1 = "246621d23d1f43e3b9c368bf3b72b2331a27c286" +deps = ["LinearAlgebra"] +git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "0.13.2" +version = "1.13.0" + + [FillArrays.extensions] + FillArraysPDMatsExt = "PDMats" + FillArraysSparseArraysExt = "SparseArrays" + FillArraysStatisticsExt = "Statistics" + + [FillArrays.weakdeps] + PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [[FixedPointNumbers]] deps = ["Statistics"] -git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" +git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.8.4" +version = "0.8.5" [[Fontconfig_jll]] -deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" +deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] +git-tree-sha1 = "db16beca600632c95fc8aca29890d83788dd8b23" uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.93+0" +version = "2.13.96+0" -[[Formatting]] -deps = ["Printf"] -git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8" -uuid = "59287772-0a20-5a39-b81b-1366585eb4c0" -version = "0.4.2" +[[Format]] +git-tree-sha1 = "9c68794ef81b08086aeb32eeaf33531668d5f5fc" +uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8" +version = "1.3.7" [[FreeType2_jll]] -deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "87eb71354d8ec1a96d4a7636bd57a7347dde3ef9" +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.10.4+0" +version = "2.13.2+0" [[FriBidi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1ed150b39aebcc805c26b93a8d0122c940f64ce2" uuid = "559328eb-81f9-559d-9380-de523a88c83c" -version = "1.0.10+0" +version = "1.0.14+0" [[FunctionalCollections]] deps = ["Test"] @@ -275,10 +331,10 @@ uuid = "de31a74c-ac4f-5751-b3fd-e18cd04993ca" version = "0.5.0" [[GLFW_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll"] -git-tree-sha1 = "51d2dfe8e590fbd74e7a842cf6d13d8a2f45dc01" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll", "libdecor_jll", "xkbcommon_jll"] +git-tree-sha1 = "532f9126ad901533af1d4f5c198867227a7bb077" uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" -version = "3.3.6+0" +version = "3.4.0+1" [[GLU_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] @@ -289,24 +345,19 @@ version = "9.0.1+0" [[GMP_jll]] deps = ["Artifacts", "Libdl"] uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" +version = "6.3.0+0" [[GR]] -deps = ["Base64", "DelimitedFiles", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Printf", "Random", "RelocatableFolders", "Serialization", "Sockets", "Test", "UUIDs"] -git-tree-sha1 = "037a1ca47e8a5989cc07d19729567bb71bfabd0c" +deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Preferences", "Printf", "Qt6Wayland_jll", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "p7zip_jll"] +git-tree-sha1 = "ee28ddcd5517d54e417182fec3886e7412d3926f" uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" -version = "0.66.0" +version = "0.73.8" [[GR_jll]] -deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Pkg", "Qt5Base_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "c8ab731c9127cd931c93221f65d6a1008dad7256" +deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "FreeType2_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Qt6Base_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "f31929b9e67066bee48eec8b03c0df47d31a74b3" uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" -version = "0.66.0+0" - -[[GeometryBasics]] -deps = ["EarCut_jll", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] -git-tree-sha1 = "83ea630384a13fc4f002b77690bc0afeb4255ac9" -uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" -version = "0.4.2" +version = "0.73.8+0" [[Gettext_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] @@ -315,10 +366,10 @@ uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" version = "0.21.0+0" [[Glib_jll]] -deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "a32d672ac2c967f3deb8a81d828afc739c838a06" +deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] +git-tree-sha1 = "674ff0db93fffcd11a3573986e550d66cd4fd71f" uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.68.3+2" +version = "2.80.5+0" [[GmshTools]] deps = ["Libdl", "gmsh_jll"] @@ -344,10 +395,10 @@ uuid = "36aa67b7-9d79-4e90-bbc0-05abd90a007e" version = "0.1.2" [[HDF5_jll]] -deps = ["Artifacts", "JLLWrappers", "LibCURL_jll", "Libdl", "OpenSSL_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "bab67c0d1c4662d2c4be8c6007751b0b6111de5c" +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "LibCURL_jll", "Libdl", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "OpenSSL_jll", "TOML", "Zlib_jll", "libaec_jll"] +git-tree-sha1 = "82a471768b513dc39e471540fdadc84ff80ff997" uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" -version = "1.12.1+0" +version = "1.14.3+3" [[HTTP]] deps = ["Base64", "Dates", "IniFile", "Logging", "MbedTLS", "NetworkOptions", "Sockets", "URIs"] @@ -356,10 +407,10 @@ uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" version = "0.9.17" [[HarfBuzz_jll]] -deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] -git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3" +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"] +git-tree-sha1 = "401e4f3f30f43af2c8478fc008da50096ea5240f" uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" -version = "2.8.1+1" +version = "8.3.1+0" [[Hiccup]] deps = ["MacroTools", "Test"] @@ -367,53 +418,67 @@ git-tree-sha1 = "6187bb2d5fcbb2007c39e7ac53308b0d371124bd" uuid = "9fb69e20-1954-56bb-a84f-559cc56a8ff7" version = "0.2.2" +[[Hwloc_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "dd3b49277ec2bb2c6b94eb1604d4d0616016f7a6" +uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" +version = "2.11.2+0" + +[[ILUZero]] +deps = ["LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "b007cfc7f9bee9a958992d2301e9c5b63f332a90" +uuid = "88f59080-6952-5380-9ea5-54057fb9a43f" +version = "0.2.0" + +[[Infiltrator]] +deps = ["InteractiveUtils", "Markdown", "REPL", "UUIDs"] +git-tree-sha1 = "38298a8eabe09e49e6f60927c9e1ca3481688ba0" +uuid = "5903a43b-9cc3-4c30-8d17-598619ec4e9b" +version = "1.8.3" + [[IniFile]] git-tree-sha1 = "f550e6e32074c939295eb5ea6de31849ac2c9625" uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f" version = "0.5.1" [[IntelOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "d979e54b71da82f3a65b62553da4fc3d18c9004c" +deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] +git-tree-sha1 = "10bd689145d2c3b2a9844005d01087cc1194e79e" uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2018.0.3+2" +version = "2024.2.1+0" [[InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" - -[[InverseFunctions]] -deps = ["Test"] -git-tree-sha1 = "b3364212fb5d870f724876ffcd34dd8ec6d98918" -uuid = "3587e190-3f89-42d0-90ee-14403ec27112" -version = "0.1.7" +version = "1.11.0" [[IrrationalConstants]] -git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151" +git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.1.1" - -[[IterTools]] -git-tree-sha1 = "fa6287a4469f5e048d763df38279ee729fbd44e5" -uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" -version = "1.4.0" +version = "0.2.2" [[IterativeSolvers]] deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] -git-tree-sha1 = "1169632f425f79429f245113b775a0e3d121457c" +git-tree-sha1 = "59545b0a2b27208b0650df0a46b8e3019f85055b" uuid = "42fd0dbc-a981-5370-80f2-aaf504508153" -version = "0.9.2" +version = "0.9.4" [[IteratorInterfaceExtensions]] git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" uuid = "82899510-4779-5014-852e-03e436cf321d" version = "1.0.0" +[[JLFzf]] +deps = ["Pipe", "REPL", "Random", "fzf_jll"] +git-tree-sha1 = "39d64b09147620f5ffbf6b2d3255be3c901bec63" +uuid = "1019f520-868f-41f5-a6de-eb00f4b6a39c" +version = "0.1.8" + [[JLLWrappers]] -deps = ["Preferences"] -git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" +deps = ["Artifacts", "Preferences"] +git-tree-sha1 = "be3dc50a92e5a386872a493a10050136d4703f9b" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.4.1" +version = "1.6.1" [[JSExpr]] deps = ["JSON", "MacroTools", "Observables", "WebIO"] @@ -423,15 +488,15 @@ version = "0.5.4" [[JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" +git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.3" +version = "0.21.4" [[JpegTurbo_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "b53380851c6e6664204efb2e62cd24fa5c47e4ba" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "2.1.2+0" +version = "3.0.4+0" [[Kaleido_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -440,39 +505,49 @@ uuid = "f7e6163d-2fa5-5f23-b69c-1db539e41963" version = "0.2.1+0" [[LAME_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "170b660facf5df5de098d866564877e119141cbd" uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" -version = "3.100.1+0" +version = "3.100.2+0" [[LERC_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "bf36f528eec6634efc60d7ec062008f171071434" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "36bdbc52f13a7d1dcb0f3cd694e01677a515655b" uuid = "88015f11-f218-50d7-93a8-a6af411a945d" -version = "3.0.0+1" +version = "4.0.0+0" [[LLVMOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "ad927676766e6529a2d5152f12040620447c0c9b" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "78211fb6cbc872f77cad3fc0b6cf647d923f4929" uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" -version = "14.0.4+0" +version = "18.1.7+0" [[LZO_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "854a9c268c43b77b0a27f22d7fab8d33cdb3a731" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.1+0" +version = "2.10.2+1" [[LaTeXStrings]] -git-tree-sha1 = "f2355693d6778a178ade15952b7ac47a4ff97996" +git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec" uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.3.0" +version = "1.3.1" [[Latexify]] -deps = ["Formatting", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "Printf", "Requires"] -git-tree-sha1 = "1a43be956d433b5d0321197150c2f94e16c0aaa0" +deps = ["Format", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Requires"] +git-tree-sha1 = "ce5f5621cac23a86011836badfedf664a612cee4" uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" -version = "0.15.16" +version = "0.16.5" + + [Latexify.extensions] + DataFramesExt = "DataFrames" + SparseArraysExt = "SparseArrays" + SymEngineExt = "SymEngine" + + [Latexify.weakdeps] + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SymEngine = "123dc426-2d89-5057-bbad-38513e3affd8" [[Lazy]] deps = ["MacroTools"] @@ -483,25 +558,36 @@ version = "0.15.1" [[LazyArtifacts]] deps = ["Artifacts", "Pkg"] uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" +version = "1.11.0" [[LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.4" [[LibCURL_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "8.6.0+0" [[LibGit2]] -deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +version = "1.11.0" + +[[LibGit2_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.7.2+0" [[LibSSH2_jll]] deps = ["Artifacts", "Libdl", "MbedTLS_jll"] uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.11.0+1" [[Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +version = "1.11.0" [[Libffi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -510,56 +596,57 @@ uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" version = "3.2.2+1" [[Libgcrypt_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] -git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] +git-tree-sha1 = "9fd170c4bbfd8b935fdc5f8b7aa33532c991a673" uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.7+0" +version = "1.8.11+0" [[Libglvnd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] -git-tree-sha1 = "7739f837d6447403596a75d19ed01fd08d6f56bf" +git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" -version = "1.3.0+3" +version = "1.6.0+0" [[Libgpg_error_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "fbb1f2bef882392312feb1ede3615ddc1e9b99ed" uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.42.0+0" +version = "1.49.0+0" [[Libiconv_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "42b62845d70a619f063a7da093d995ec8e15e778" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.16.1+1" +version = "1.17.0+0" [[Libmount_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "0c4f9c4f1a50d8f35048fa0532dabbadf702f81e" uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.35.0+0" +version = "2.40.1+0" [[Libtiff_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "3eb79b0ca5764d4799c06699573fd8f533259713" +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] +git-tree-sha1 = "b404131d06f7886402758c9ce2214b636eb4d54a" uuid = "89763e89-9b03-5906-acba-b20f662cd828" -version = "4.4.0+0" +version = "4.7.0+0" [[Libuuid_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807" uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.36.0+0" +version = "2.40.1+0" [[LiftedMaps]] deps = ["LinearAlgebra", "LinearMaps"] -git-tree-sha1 = "9e417fe8b11edb183ee990c31722757cf7def62c" +git-tree-sha1 = "68c65fe9d32407ddb13eb26ef96fa803d45369cd" uuid = "d22a30c1-52ac-4762-a8c9-5838452405e0" -version = "0.4.1" +version = "0.5.1" [[LinearAlgebra]] -deps = ["Libdl", "libblastrampoline_jll"] +deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +version = "1.11.0" [[LinearElasticity_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -568,31 +655,52 @@ uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" version = "5.0.0+0" [[LinearMaps]] -deps = ["LinearAlgebra", "SparseArrays", "Statistics"] -git-tree-sha1 = "d1b46faefb7c2f48fdec69e6f3cc34857769bc15" +deps = ["LinearAlgebra"] +git-tree-sha1 = "ee79c3208e55786de58f8dcccca098ced79f743f" uuid = "7a12625a-238d-50fd-b39a-03d52299707e" -version = "3.8.0" +version = "3.11.3" + + [LinearMaps.extensions] + LinearMapsChainRulesCoreExt = "ChainRulesCore" + LinearMapsSparseArraysExt = "SparseArrays" + LinearMapsStatisticsExt = "Statistics" + + [LinearMaps.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [[LogExpFunctions]] -deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "7c88f63f9f0eb5929f15695af9a4d7d3ed278a91" +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.16" +version = "0.3.28" + + [LogExpFunctions.extensions] + LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" + LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" + LogExpFunctionsInverseFunctionsExt = "InverseFunctions" + + [LogExpFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" [[Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" +version = "1.11.0" [[METIS_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "1d31872bb9c5e7ec1f618e8c4a56c8b0d9bddc7e" +git-tree-sha1 = "1fd0a97409e418b78c53fac671cf4622efdf0f21" uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" -version = "5.1.1+0" +version = "5.1.2+0" [[MKL_jll]] -deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"] -git-tree-sha1 = "e595b205efd49508358f7dc670a940c790204629" +deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] +git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2022.0.0+0" +version = "2024.2.0+0" [[MMG_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] @@ -600,48 +708,76 @@ git-tree-sha1 = "70a59df96945782bb0d43b56d0fbfdf1ce2e4729" uuid = "86086c02-e288-5929-a127-40944b0018b7" version = "5.6.0+0" +[[MPICH_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "7715e65c47ba3941c502bffb7f266a41a7f54423" +uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" +version = "4.2.3+0" + +[[MPIPreferences]] +deps = ["Libdl", "Preferences"] +git-tree-sha1 = "c105fe467859e7f6e9a852cb15cb4301126fac07" +uuid = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267" +version = "0.1.11" + +[[MPItrampoline_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "70e830dab5d0775183c99fc75e4c24c614ed7142" +uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" +version = "5.5.1+0" + [[MacroTools]] deps = ["Markdown", "Random"] -git-tree-sha1 = "3d3e902b31198a27340d0bf00d6ac452866021cf" +git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.9" +version = "0.5.13" [[Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" +version = "1.11.0" [[MbedTLS]] -deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "Random", "Sockets"] -git-tree-sha1 = "9f4f5a42de3300439cb8300236925670f844a555" +deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] +git-tree-sha1 = "c067a280ddc25f196b5e7df3877c6b226d390aaf" uuid = "739be429-bea8-5141-9913-cc70e7f3736d" -version = "1.1.1" +version = "1.1.9" [[MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.6+0" [[Measures]] -git-tree-sha1 = "e498ddeee6f9fdb4551ce855a46f54dbd900245f" +git-tree-sha1 = "c13304c81eec1ed3af7fc20e75fb6b26092a1102" uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e" -version = "0.3.1" +version = "0.3.2" + +[[MicrosoftMPI_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "f12a29c4400ba812841c6ace3f4efbb6dbb3ba01" +uuid = "9237b28f-5490-5468-be7b-bb81f5f5e6cf" +version = "10.1.4+2" [[Missings]] deps = ["DataAPI"] -git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f" +git-tree-sha1 = "ec4f7fbeab05d7747bdf98eb74d130a2a2ed298d" uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "1.0.2" +version = "1.2.0" [[Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" +version = "1.11.0" [[MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2023.12.12" [[Mustache]] deps = ["Printf", "Tables"] -git-tree-sha1 = "1e566ae913a57d0062ff1af54d2697b9344b99cd" +git-tree-sha1 = "3b2db451a872b20519ebb0cec759d3d81a1c6bcb" uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" -version = "1.0.14" +version = "1.0.20" [[Mux]] deps = ["AssetRegistry", "Base64", "HTTP", "Hiccup", "Pkg", "Sockets", "WebSockets"] @@ -651,23 +787,41 @@ version = "0.7.6" [[NaNMath]] deps = ["OpenLibm_jll"] -git-tree-sha1 = "a7c3d1da1189a1c2fe843a3bfa04d18d20eb3211" +git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4" uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" -version = "1.0.1" +version = "1.0.2" + +[[NestedUnitRanges]] +deps = ["AbstractTrees", "ArrayLayouts", "BlockArrays"] +git-tree-sha1 = "1cbdce42da2370fee5ef906ef24179f8c070e3b9" +uuid = "032820ab-dc03-4b49-91f4-7d58d4da98b3" +version = "0.2.1" [[NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" [[OCCT_jll]] -deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "acc8099ae8ed10226dc8424fb256ec9fe367a1f0" +deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "bef34b68c20cc34475c5cb464ab48555e74f4c61" uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" -version = "7.6.2+2" +version = "7.7.2+0" [[Observables]] -git-tree-sha1 = "dfd8d34871bc3ad08cd16026c1828e271d554db9" +git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225" uuid = "510215fc-4207-5dde-b226-833fc4488ee2" -version = "0.5.1" +version = "0.5.5" + +[[OffsetArrays]] +git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e" +uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" +version = "1.14.1" + + [OffsetArrays.extensions] + OffsetArraysAdaptExt = "Adapt" + + [OffsetArrays.weakdeps] + Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" [[Ogg_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -678,16 +832,24 @@ version = "1.3.5+1" [[OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.27+1" [[OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" +version = "0.8.1+2" + +[[OpenMPI_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "e25c1778a98e34219a00455d6e4384e017ea9762" +uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" +version = "4.1.6+0" [[OpenSSL_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e60321e3f2616584ff98f0a4f18d98ae6f89bbb3" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "1.1.17+0" +version = "3.0.15+1" [[OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -696,21 +858,26 @@ uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" version = "0.5.5+0" [[Opus_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "51a08fb14ec28da2ec7a927c4337e4332c2a4720" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6703a85cb3781bd5909d48730a67205f3f31a575" uuid = "91d4177d-7536-5919-b921-800302f37372" -version = "1.3.2+0" +version = "1.3.3+0" [[OrderedCollections]] -git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" +git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.4.1" +version = "1.6.3" -[[PCRE_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "b2a7af664e098055a7529ad1a900ded962bca488" -uuid = "2f80f16e-611a-54ab-bc61-aa92de5b98fc" -version = "8.44.0+0" +[[PCRE2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" +version = "10.42.0+1" + +[[Pango_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e127b609fb9ecba6f201ba7ab753d5a605d53801" +uuid = "36c8627f-9965-5494-a995-c6b170f724f3" +version = "1.54.1+0" [[Parameters]] deps = ["OrderedCollections", "UnPack"] @@ -719,10 +886,16 @@ uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" version = "0.12.3" [[Parsers]] -deps = ["Dates"] -git-tree-sha1 = "0044b23da09b5608b4ecacb4e5e6c6332f833a7e" +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.3.2" +version = "2.8.1" + +[[Permutations]] +deps = ["Combinatorics", "LinearAlgebra", "Random"] +git-tree-sha1 = "f92b0a7b722b1ecfd5c0d77a7eda24b4eea5c56a" +uuid = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" +version = "0.4.22" [[Pidfile]] deps = ["FileWatching", "Test"] @@ -730,27 +903,37 @@ git-tree-sha1 = "2d8aaf8ee10df53d0dfb9b8ee44ae7c04ced2b03" uuid = "fa939f87-e72e-5be4-a000-7fc836dbe307" version = "1.3.0" +[[Pipe]] +git-tree-sha1 = "6842804e7867b115ca9de748a0cf6b364523c16d" +uuid = "b98c9c47-44ae-5843-9183-064241ee97a0" +version = "1.3.0" + [[Pixman_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29" +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] +git-tree-sha1 = "35621f10a7531bc8fa58f74610b1bfb70a3cfc6b" uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.40.1+0" +version = "0.43.4+0" [[Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.11.0" +weakdeps = ["REPL"] + + [Pkg.extensions] + REPLExt = "REPL" [[PlotThemes]] deps = ["PlotUtils", "Statistics"] -git-tree-sha1 = "8162b2f8547bc23876edd0c5181b27702ae58dce" +git-tree-sha1 = "6e55c6841ce3411ccb3457ee52fc48cb698d6fb0" uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" -version = "3.0.0" +version = "3.2.0" [[PlotUtils]] -deps = ["ColorSchemes", "Colors", "Dates", "Printf", "Random", "Reexport", "Statistics"] -git-tree-sha1 = "9888e59493658e476d3073f1ce24348bdc086660" +deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "Statistics"] +git-tree-sha1 = "7b1a9df27f072ac4c9c7cbe5efb198489258d1f5" uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" -version = "1.3.0" +version = "1.4.1" [[Plotly]] deps = ["Base64", "DelimitedFiles", "HTTP", "JSON", "PlotlyJS", "Reexport"] @@ -760,56 +943,116 @@ version = "0.4.1" [[PlotlyBase]] deps = ["ColorSchemes", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "LaTeXStrings", "Logging", "Parameters", "Pkg", "REPL", "Requires", "Statistics", "UUIDs"] -git-tree-sha1 = "180d744848ba316a3d0fdf4dbd34b77c7242963a" +git-tree-sha1 = "56baf69781fc5e61607c3e46227ab17f7040ffa2" uuid = "a03496cd-edff-5a9b-9e67-9cda94a718b5" -version = "0.8.18" +version = "0.8.19" [[PlotlyJS]] -deps = ["Base64", "Blink", "DelimitedFiles", "JSExpr", "JSON", "Kaleido_jll", "Markdown", "Pkg", "PlotlyBase", "REPL", "Reexport", "Requires", "WebIO"] -git-tree-sha1 = "53d6325e14d3bdb85fd387a085075f36082f35a3" +deps = ["Base64", "Blink", "DelimitedFiles", "JSExpr", "JSON", "Kaleido_jll", "Markdown", "Pkg", "PlotlyBase", "PlotlyKaleido", "REPL", "Reexport", "Requires", "WebIO"] +git-tree-sha1 = "f198c8a80c08987a2915156e6e6131e5d73b97f4" uuid = "f0f68f2c-4968-5e81-91da-67840de0976a" -version = "0.18.8" +version = "0.18.14" + + [PlotlyJS.extensions] + CSVExt = "CSV" + DataFramesExt = ["DataFrames", "CSV"] + IJuliaExt = "IJulia" + JSON3Ext = "JSON3" + + [PlotlyJS.weakdeps] + CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" + +[[PlotlyKaleido]] +deps = ["Base64", "JSON", "Kaleido_jll"] +git-tree-sha1 = "3210de4d88af7ca5de9e26305758a59aabc48aac" +uuid = "f2990250-8cf9-495f-b13a-cce12b45703c" +version = "2.2.5" [[Plots]] -deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "GeometryBasics", "JSON", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs", "UnicodeFun", "Unzip"] -git-tree-sha1 = "0a0da27969e8b6b2ee67c112dcf7001a659049a0" +deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "JLFzf", "JSON", "LaTeXStrings", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "PrecompileTools", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "RelocatableFolders", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "TOML", "UUIDs", "UnicodeFun", "UnitfulLatexify", "Unzip"] +git-tree-sha1 = "45470145863035bb124ca51b320ed35d071cc6c2" uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -version = "1.31.4" +version = "1.40.8" + + [Plots.extensions] + FileIOExt = "FileIO" + GeometryBasicsExt = "GeometryBasics" + IJuliaExt = "IJulia" + ImageInTerminalExt = "ImageInTerminal" + UnitfulExt = "Unitful" + + [Plots.weakdeps] + FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" + GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[[PrecompileTools]] +deps = ["Preferences"] +git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" +uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +version = "1.2.1" [[Preferences]] deps = ["TOML"] -git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d" +git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6" uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.3.0" +version = "1.4.3" [[Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" - -[[Qt5Base_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"] -git-tree-sha1 = "c6c0f690d0cc7caddb74cef7aa847b824a16b256" -uuid = "ea2cea3b-5b76-57ae-a6ef-0a8af62496e1" -version = "5.15.3+1" +version = "1.11.0" + +[[Qt6Base_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Vulkan_Loader_jll", "Xorg_libSM_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_cursor_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "libinput_jll", "xkbcommon_jll"] +git-tree-sha1 = "492601870742dcd38f233b23c3ec629628c1d724" +uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56" +version = "6.7.1+1" + +[[Qt6Declarative_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6ShaderTools_jll"] +git-tree-sha1 = "e5dd466bf2569fe08c91a2cc29c1003f4797ac3b" +uuid = "629bc702-f1f5-5709-abd5-49b8460ea067" +version = "6.7.1+2" + +[[Qt6ShaderTools_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll"] +git-tree-sha1 = "1a180aeced866700d4bebc3120ea1451201f16bc" +uuid = "ce943373-25bb-56aa-8eca-768745ed7b5a" +version = "6.7.1+1" + +[[Qt6Wayland_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6Declarative_jll"] +git-tree-sha1 = "729927532d48cf79f49070341e1d918a65aba6b0" +uuid = "e99dba38-086e-5de3-a5b1-6e4c66e897c3" +version = "6.7.1+1" [[REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +deps = ["InteractiveUtils", "Markdown", "Sockets", "StyledStrings", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" +version = "1.11.0" [[Random]] -deps = ["SHA", "Serialization"] +deps = ["SHA"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +version = "1.11.0" [[RecipesBase]] -git-tree-sha1 = "6bf3f380ff52ce0832ddd3a2a7b9538ed1bcca7d" +deps = ["PrecompileTools"] +git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "1.2.1" +version = "1.3.4" [[RecipesPipeline]] -deps = ["Dates", "NaNMath", "PlotUtils", "RecipesBase"] -git-tree-sha1 = "2690681814016887462cf5ac37102b51cd9ec781" +deps = ["Dates", "NaNMath", "PlotUtils", "PrecompileTools", "RecipesBase"] +git-tree-sha1 = "45cf9fd0ca5839d06ef333c8201714e888486342" uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c" -version = "0.6.2" +version = "0.6.12" [[Reexport]] git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" @@ -818,9 +1061,9 @@ version = "1.2.2" [[RelocatableFolders]] deps = ["SHA", "Scratch"] -git-tree-sha1 = "22c5201127d7b243b9ee1de3b43c408879dff60f" +git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864" uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" -version = "0.3.0" +version = "1.0.1" [[Requires]] deps = ["UUIDs"] @@ -836,31 +1079,34 @@ version = "6.1.3+0" [[SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" [[SauterSchwab3D]] -deps = ["CompScienceMeshes", "FastGaussQuadrature", "GrundmannMoeller", "LinearAlgebra", "ShunnHamQuadrature", "StaticArrays"] -git-tree-sha1 = "e5c1dce465dd31be2e21761922ceab26ec28db7c" +deps = ["FastGaussQuadrature", "GrundmannMoeller", "LinearAlgebra", "ShunnHamQuadrature", "StaticArrays"] +git-tree-sha1 = "87242fb25711b1f9eaa45506d8b5e6e0b50f086a" uuid = "0a13313b-1c00-422e-8263-562364ed9544" -version = "0.1.1" +version = "0.1.4" [[SauterSchwabQuadrature]] -deps = ["CompScienceMeshes", "FastGaussQuadrature", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "8d7eed829815a48c042589dd11d8526a0d81bf1c" +deps = ["FastGaussQuadrature", "LinearAlgebra", "StaticArrays", "TestItems"] +git-tree-sha1 = "b98948c567cbe250d774d01a07833b7a329ec511" uuid = "535c7bfe-2023-5c1d-b712-654ef9d93a38" -version = "2.1.3" +version = "2.4.0" [[Scratch]] deps = ["Dates"] -git-tree-sha1 = "f94f779c94e58bf9ea243e77a37e16d9de9126bd" +git-tree-sha1 = "3bac05bc7e74a75fd9cba4295cde4045d9fe2386" uuid = "6c6a2e73-6563-6170-7368-637461726353" -version = "1.1.1" +version = "1.2.1" [[Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +version = "1.11.0" [[SharedArrays]] deps = ["Distributed", "Mmap", "Random", "Serialization"] uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" +version = "1.11.0" [[Showoff]] deps = ["Dates", "Grisu"] @@ -876,65 +1122,95 @@ version = "0.1.0" [[Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" +version = "1.11.0" [[SortingAlgorithms]] deps = ["DataStructures"] -git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508" +git-tree-sha1 = "66e0a8e672a0bdfca2c3f5937efb8538b9ddc085" uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.0.1" +version = "1.2.1" [[SparseArrays]] -deps = ["LinearAlgebra", "Random"] +deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +version = "1.11.0" -[[SparseMatrixDicts]] -deps = ["LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "6ad782435088b00f7abdd4b6ae79fa522cc18758" -uuid = "5cb6c4b0-9b79-11e8-24c9-f9621d252589" -version = "0.2.4" +[[Sparspak]] +deps = ["Libdl", "LinearAlgebra", "Logging", "OffsetArrays", "Printf", "SparseArrays", "Test"] +git-tree-sha1 = "342cf4b449c299d8d1ceaf00b7a49f4fbc7940e7" +uuid = "e56a9233-b9d6-4f03-8d0f-1825330902ac" +version = "0.3.9" [[SpecialFunctions]] -deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "d75bda01f8c31ebb72df80a46c88b25d1c79c56d" +deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.1.7" +version = "2.4.0" + + [SpecialFunctions.extensions] + SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" + + [SpecialFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" [[StaticArrays]] -deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] -git-tree-sha1 = "23368a3313d12a2326ad0035f0db0c0966f438ef" +deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] +git-tree-sha1 = "eeafab08ae20c62c44c8399ccb9354a04b80db50" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.5.2" +version = "1.9.7" + + [StaticArrays.extensions] + StaticArraysChainRulesCoreExt = "ChainRulesCore" + StaticArraysStatisticsExt = "Statistics" + + [StaticArrays.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [[StaticArraysCore]] -git-tree-sha1 = "66fe9eb253f910fe8cf161953880cfdaef01cdf0" +git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682" uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.0.1" +version = "1.4.3" [[Statistics]] -deps = ["LinearAlgebra", "SparseArrays"] +deps = ["LinearAlgebra"] +git-tree-sha1 = "ae3bb1eb3bba077cd276bc5cfc337cc65c3075c0" uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +version = "1.11.1" +weakdeps = ["SparseArrays"] + + [Statistics.extensions] + SparseArraysExt = ["SparseArrays"] [[StatsAPI]] deps = ["LinearAlgebra"] -git-tree-sha1 = "2c11d7290036fe7aac9038ff312d3b3a2a5bf89e" +git-tree-sha1 = "1ff449ad350c9c4cbc756624d6f8a8c3ef56d3ed" uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" -version = "1.4.0" +version = "1.7.0" [[StatsBase]] deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "472d044a1c8df2b062b23f222573ad6837a615ba" +git-tree-sha1 = "5cf7606d6cef84b543b483848d4ae08ad9832b21" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.33.19" +version = "0.34.3" + +[[StyledStrings]] +uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" +version = "1.11.0" + +[[SuiteSparse]] +deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] +uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" -[[StructArrays]] -deps = ["Adapt", "DataAPI", "StaticArrays", "Tables"] -git-tree-sha1 = "ec47fb6069c57f1cee2f67541bf8f23415146de7" -uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" -version = "0.6.11" +[[SuiteSparse_jll]] +deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "7.7.0+0" [[TOML]] deps = ["Dates"] uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.3" [[TableTraits]] deps = ["IteratorInterfaceExtensions"] @@ -943,14 +1219,15 @@ uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" version = "1.0.1" [[Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits", "Test"] -git-tree-sha1 = "5ce79ce186cc678bbb5c5681ca3379d1ddae11a1" +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] +git-tree-sha1 = "598cd7c1f68d1e205689b1c2fe65a9f85846f297" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.7.0" +version = "1.12.0" [[Tar]] deps = ["ArgTools", "SHA"] uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" [[TensorCore]] deps = ["LinearAlgebra"] @@ -961,6 +1238,12 @@ version = "0.1.1" [[Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +version = "1.11.0" + +[[TestItems]] +git-tree-sha1 = "8621ba2637b49748e2dc43ba3d84340be2938022" +uuid = "1c621080-faea-4a02-84b6-bbd5e436b8fe" +version = "0.1.1" [[URIParser]] deps = ["Unicode"] @@ -969,13 +1252,14 @@ uuid = "30578b45-9adc-5946-b283-645ec420af67" version = "0.4.1" [[URIs]] -git-tree-sha1 = "e59ecc5a41b000fa94423a578d29290c7266fc10" +git-tree-sha1 = "67db6cc7b3821e19ebe75791a9dd19c9b1188f2b" uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.4.0" +version = "1.5.1" [[UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" +version = "1.11.0" [[UnPack]] git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" @@ -984,6 +1268,7 @@ version = "1.0.2" [[Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" +version = "1.11.0" [[UnicodeFun]] deps = ["REPL"] @@ -991,28 +1276,54 @@ git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" version = "0.4.1" +[[Unitful]] +deps = ["Dates", "LinearAlgebra", "Random"] +git-tree-sha1 = "d95fe458f26209c66a187b1114df96fd70839efd" +uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" +version = "1.21.0" + + [Unitful.extensions] + ConstructionBaseUnitfulExt = "ConstructionBase" + InverseFunctionsUnitfulExt = "InverseFunctions" + + [Unitful.weakdeps] + ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" + +[[UnitfulLatexify]] +deps = ["LaTeXStrings", "Latexify", "Unitful"] +git-tree-sha1 = "975c354fcd5f7e1ddcc1f1a23e6e091d99e99bc8" +uuid = "45397f5d-5981-4c77-b2b3-fc36d6e9b728" +version = "1.6.4" + [[Unzip]] -git-tree-sha1 = "34db80951901073501137bdbc3d5a8e7bbd06670" +git-tree-sha1 = "ca0969166a028236229f63514992fc073799bb78" uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d" -version = "0.1.2" +version = "0.2.0" + +[[Vulkan_Loader_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Wayland_jll", "Xorg_libX11_jll", "Xorg_libXrandr_jll", "xkbcommon_jll"] +git-tree-sha1 = "2f0486047a07670caad3a81a075d2e518acc5c59" +uuid = "a44049a8-05dd-5a78-86c9-5fde0876e88c" +version = "1.3.243+0" [[Wayland_jll]] -deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "3e61f0b86f90dacb0bc0e73a0c5a83f6a8636e23" +deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "7558e29847e99bc3f04d6569e82d0f5c54460703" uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" -version = "1.19.0+0" +version = "1.21.0+1" [[Wayland_protocols_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4528479aa01ee1b3b4cd0e6faef0e04cf16466da" +git-tree-sha1 = "93f43ab61b16ddfb2fd3bb13b3ce241cafb0e6c9" uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91" -version = "1.25.0+0" +version = "1.31.0+0" [[WebIO]] deps = ["AssetRegistry", "Base64", "Distributed", "FunctionalCollections", "JSON", "Logging", "Observables", "Pkg", "Random", "Requires", "Sockets", "UUIDs", "WebSockets", "Widgets"] -git-tree-sha1 = "a8bbcd0b08061bba794c56fb78426e96e114ae7f" +git-tree-sha1 = "0eef0765186f7452e52236fa42ca8c9b3c11c6e3" uuid = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" -version = "0.8.18" +version = "0.8.21" [[WebSockets]] deps = ["Base64", "Dates", "HTTP", "Logging", "Sockets"] @@ -1028,33 +1339,51 @@ version = "0.6.6" [[WiltonInts84]] deps = ["LinearAlgebra"] -git-tree-sha1 = "d412771d65e9760b6a7fdb86754c237ee2a92dfd" +git-tree-sha1 = "9d61cac63c100e936194e5db8613e33572fd2bb8" uuid = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" -version = "0.2.3" +version = "0.2.5" [[XML2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "58443b63fb7e465a8a7210828c91c08b92132dff" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] +git-tree-sha1 = "1165b0443d0eca63ac1e32b8c0eb69ed2f4f8127" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.9.14+0" +version = "2.13.3+0" [[XSLT_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] -git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "a54ee957f4c86b526460a720dbc882fa5edcbefc" uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.34+0" +version = "1.1.41+0" + +[[XZ_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "ac88fb95ae6447c8dda6a5503f3bafd496ae8632" +uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" +version = "5.4.6+0" + +[[Xorg_libICE_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "326b4fea307b0b39892b3e85fa451692eda8d46c" +uuid = "f67eecfb-183a-506d-b269-f58e52b52d7c" +version = "1.1.1+0" + +[[Xorg_libSM_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libICE_jll"] +git-tree-sha1 = "3796722887072218eabafb494a13c963209754ce" +uuid = "c834827a-8449-5923-a945-d239c165b7dd" +version = "1.2.4+0" [[Xorg_libX11_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] -git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.6.9+4" +version = "1.8.6+0" [[Xorg_libXau_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.9+4" +version = "1.0.11+0" [[Xorg_libXcursor_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] @@ -1063,16 +1392,16 @@ uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" version = "1.2.0+4" [[Xorg_libXdmcp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.3+4" +version = "1.1.4+0" [[Xorg_libXext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "d2d1a5c49fae4ba39983f63de6afcbea47194e85" uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.4+4" +version = "1.3.6+0" [[Xorg_libXfixes_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] @@ -1105,28 +1434,34 @@ uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484" version = "1.5.2+4" [[Xorg_libXrender_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "47e45cd78224c53109495b3e324df0c37bb61fbe" uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.10+4" +version = "0.9.11+0" [[Xorg_libpthread_stubs_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9" uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" -version = "0.1.0+3" +version = "0.1.1+0" [[Xorg_libxcb_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6" +deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] +git-tree-sha1 = "bcd466676fef0878338c61e655629fa7bbc69d8e" uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.13.0+3" +version = "1.17.0+0" [[Xorg_libxkbfile_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "926af861744212db0eb001d9e40b5d16292080b2" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "730eeca102434283c50ccf7d1ecdadf521a765a4" uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a" -version = "1.1.0+4" +version = "1.1.2+0" + +[[Xorg_xcb_util_cursor_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_jll", "Xorg_xcb_util_renderutil_jll"] +git-tree-sha1 = "04341cb870f29dcd5e39055f895c39d016e18ccd" +uuid = "e920d4aa-a673-5f3a-b3d7-f755a4d47c43" +version = "0.1.4+0" [[Xorg_xcb_util_image_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] @@ -1159,80 +1494,138 @@ uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361" version = "0.4.1+1" [[Xorg_xkbcomp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxkbfile_jll"] -git-tree-sha1 = "4bcbf660f6c2e714f87e960a171b119d06ee163b" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxkbfile_jll"] +git-tree-sha1 = "330f955bc41bb8f5270a369c473fc4a5a4e4d3cb" uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4" -version = "1.4.2+4" +version = "1.4.6+0" [[Xorg_xkeyboard_config_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xkbcomp_jll"] -git-tree-sha1 = "5c8424f8a67c3f2209646d4425f3d415fee5931d" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xkbcomp_jll"] +git-tree-sha1 = "691634e5453ad362044e2ad653e79f3ee3bb98c3" uuid = "33bec58e-1273-512f-9401-5d533626f822" -version = "2.27.0+4" +version = "2.39.0+0" [[Xorg_xtrans_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77" uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.4.0+3" +version = "1.5.0+0" [[Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.2.13+1" [[Zstd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e45044cd873ded54b6a5bac0eb5c971392cf1927" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "555d1076590a6cc2fdee2ef1469451f872d8b41b" uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" -version = "1.5.2+0" +version = "1.5.6+1" + +[[eudev_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "gperf_jll"] +git-tree-sha1 = "431b678a28ebb559d224c0b6b6d01afce87c51ba" +uuid = "35ca27e7-8b34-5b7f-bca9-bdc33f59eb06" +version = "3.2.9+0" + +[[fzf_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "936081b536ae4aa65415d869287d43ef3cb576b2" +uuid = "214eeab7-80f7-51ab-84ad-2988db7cef09" +version = "0.53.0+0" [[gmsh_jll]] -deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "9774ebf68348b3b56c74a78b829051310163fd76" +deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "1e7fe5c8dbe0e911931a18cdfbd2c7a1e01b68ef" uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" -version = "4.10.2+0" +version = "4.13.1+0" -[[libaom_jll]] +[[gperf_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "3a2ea60308f0996d26f1e5354e10c24e9ef905d4" +git-tree-sha1 = "3516a5630f741c9eecb3720b1ec9d8edc3ecc033" +uuid = "1a1c6b14-54f6-533d-8383-74cd7377aa70" +version = "3.1.1+0" + +[[libaec_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "46bf7be2917b59b761247be3f317ddf75e50e997" +uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" +version = "1.1.2+0" + +[[libaom_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1827acba325fdcdf1d2647fc8d5301dd9ba43a9d" uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" -version = "3.4.0+0" +version = "3.9.0+0" [[libass_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "5982a94fcba20f02f42ace44b9894ee2b140fe47" +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "e17c115d55c5fbb7e52ebedb427a0dca79d4484e" uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" -version = "0.15.1+0" +version = "0.15.2+0" [[libblastrampoline_jll]] -deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] +deps = ["Artifacts", "Libdl"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.11.0+0" -[[libfdk_aac_jll]] +[[libdecor_jll]] +deps = ["Artifacts", "Dbus_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pango_jll", "Wayland_jll", "xkbcommon_jll"] +git-tree-sha1 = "9bf7903af251d2050b467f76bdbe57ce541f7f4f" +uuid = "1183f4f0-6f2a-5f1a-908b-139f9cdfea6f" +version = "0.2.2+0" + +[[libevdev_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "daacc84a041563f965be61859a36e17c4e4fcd55" +git-tree-sha1 = "141fe65dc3efabb0b1d5ba74e91f6ad26f84cc22" +uuid = "2db6ffa8-e38f-5e21-84af-90c45d0032cc" +version = "1.11.0+0" + +[[libfdk_aac_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8a22cf860a7d27e4f3498a0fe0811a7957badb38" uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" -version = "2.0.2+0" +version = "2.0.3+0" + +[[libinput_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "eudev_jll", "libevdev_jll", "mtdev_jll"] +git-tree-sha1 = "ad50e5b90f222cfe78aa3d5183a20a12de1322ce" +uuid = "36db933b-70db-51c0-b978-0f229ee0e533" +version = "1.18.0+0" [[libpng_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "b70c870239dc3d7bc094eb2d6be9b73d27bef280" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.38+0" +version = "1.6.44+0" [[libvorbis_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] -git-tree-sha1 = "b910cb81ef3fe6e78bf6acee440bda86fd6ae00c" +git-tree-sha1 = "490376214c4721cdaca654041f635213c6165cb3" uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" -version = "1.3.7+1" +version = "1.3.7+2" + +[[mtdev_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "814e154bdb7be91d78b6802843f76b6ece642f11" +uuid = "009596ad-96f7-51b1-9f1b-5ce2d5e8a71e" +version = "1.1.6+0" [[nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.59.0+0" + +[[oneTBB_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "7d0ea0f4895ef2f5cb83645fa689e52cb55cf493" +uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" +version = "2021.12.0+0" [[p7zip_jll]] deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.4.0+2" [[x264_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -1248,6 +1641,6 @@ version = "3.5.0+0" [[xkbcommon_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] -git-tree-sha1 = "ece2350174195bb31de1a63bea3a41ae1aa593b6" +git-tree-sha1 = "9c304562909ab2bab0262639bd4f444d7bc2be37" uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" -version = "0.9.1+5" +version = "1.4.1+1" diff --git a/examples/efie_gwp2.jl b/examples/efie_gwp2.jl index 53f91e8b..ccc911d6 100644 --- a/examples/efie_gwp2.jl +++ b/examples/efie_gwp2.jl @@ -2,7 +2,7 @@ using CompScienceMeshes using BEAST # Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -Γ = meshsphere(radius=1.0, h=0.4) +Γ = meshsphere(radius=1.0, h=0.2) @show length(Γ) # X = raviartthomas(Γ) X = BEAST.gwpdiv(Γ; order=2) diff --git a/src/bases/local/gwplocal.jl b/src/bases/local/gwplocal.jl index a13bfc23..8f8fe2e8 100644 --- a/src/bases/local/gwplocal.jl +++ b/src/bases/local/gwplocal.jl @@ -37,7 +37,8 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di P = SVector{2,T} NT = @NamedTuple{value::P, curl::T} - nts = Vector{NT}(undef, NF) + # nts = Vector{NT}(undef, NF) + nts = MVector{NF,NT}(undef) idx = 1 i = 0 @@ -135,7 +136,7 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di idx += 1 end end - return SVector{NF}(nts) + return SVector(nts) end From d1682a486173525f585d38ea130c5dc903cbdf08 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 17 Oct 2024 14:41:09 +0200 Subject: [PATCH 413/528] add help to numfunctions to make docbuilding work --- docs/Manifest.toml | 484 +++++++++++++++++++++++++++++++++++++-------- docs/src/index.md | 2 +- src/bases/basis.jl | 6 + 3 files changed, 411 insertions(+), 81 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 9a126a0c..2f4406a9 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -26,13 +26,13 @@ version = "0.4.5" [[ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" -version = "1.1.1" +version = "1.1.2" [[ArrayLayouts]] deps = ["FillArrays", "LinearAlgebra"] -git-tree-sha1 = "33207a8be6267bc389d0701e97a9bce6a4de68eb" +git-tree-sha1 = "0dd7edaff278e346eb0ca07a7e75c9438408a3ce" uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "1.9.2" +version = "1.10.3" weakdeps = ["SparseArrays"] [ArrayLayouts.extensions] @@ -40,15 +40,17 @@ weakdeps = ["SparseArrays"] [[Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" +version = "1.11.0" [[BEAST]] -deps = ["AbstractTrees", "BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "ConvolutionOperators", "Distributed", "ExtendableSparse", "FFTW", "FastGaussQuadrature", "FillArrays", "InteractiveUtils", "IterativeSolvers", "LiftedMaps", "LinearAlgebra", "LinearMaps", "NestedUnitRanges", "Requires", "SauterSchwab3D", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SpecialFunctions", "StaticArrays", "WiltonInts84"] +deps = ["AbstractTrees", "BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "ConvolutionOperators", "Distributed", "ExtendableSparse", "FFTW", "FastGaussQuadrature", "FillArrays", "Infiltrator", "InteractiveUtils", "IterativeSolvers", "LiftedMaps", "LinearAlgebra", "LinearMaps", "NestedUnitRanges", "Requires", "SauterSchwab3D", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SpecialFunctions", "StaticArrays", "TestItems", "WiltonInts84"] path = ".." uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "2.2.1" +version = "2.5.0" [[Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" +version = "1.11.0" [[BlockArrays]] deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] @@ -56,6 +58,18 @@ git-tree-sha1 = "9a9610fbe5779636f75229e423e367124034af41" uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" version = "0.16.43" +[[Bzip2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "9e2a6b69137e6969bab0152632dcb3bc108c8bdd" +uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" +version = "1.0.8+1" + +[[Cairo_jll]] +deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "009060c9a6168704143100f36ab08f06c2af4642" +uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" +version = "1.18.2+1" + [[ClusterTrees]] deps = ["DelimitedFiles", "LinearAlgebra", "Pkg"] git-tree-sha1 = "4114d60c95974edf9272d88571d14abeed598942" @@ -64,15 +78,15 @@ version = "0.2.1" [[CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "59939d8a997469ee05c4b4944560a820f9ba0d73" +git-tree-sha1 = "bce6804e5e6044c6daab27bb533d1295e4a2e759" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.4" +version = "0.7.6" [[CollisionDetection]] deps = ["Compat", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "8d86c864d69f72e23adcd7c2014d205bf32c90a1" +git-tree-sha1 = "88a33f2fba4ef1065edd06164c84d8b03ee4d049" uuid = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" -version = "0.1.5" +version = "0.1.6" [[Combinatorics]] git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" @@ -80,16 +94,16 @@ uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" version = "1.0.2" [[CompScienceMeshes]] -deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays"] -git-tree-sha1 = "2c45ee4534fcc94741cd7446067cfd13728fa29b" +deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Permutations", "Requires", "SparseArrays", "StaticArrays", "TestItems"] +git-tree-sha1 = "6bb7cd3831707872fe404e01d156e1b149fcfa3a" uuid = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" -version = "0.6.1" +version = "0.8.3" [[Compat]] deps = ["TOML", "UUIDs"] -git-tree-sha1 = "c955881e3c981181362ae4088b35995446298b80" +git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.14.0" +version = "4.16.0" weakdeps = ["Dates", "LinearAlgebra"] [Compat.extensions] @@ -98,23 +112,24 @@ weakdeps = ["Dates", "LinearAlgebra"] [[CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.1.0+0" +version = "1.1.1+0" [[ConvolutionOperators]] deps = ["LinearAlgebra", "LinearMaps"] -git-tree-sha1 = "967b02ba48c8de3546668275b0cf9c46aecde6a5" +git-tree-sha1 = "ae44e38013c05c7ec59f428b4ea7ad7d34926b63" uuid = "15927181-a1bb-497c-b745-8dbf505c019d" -version = "0.4.0" +version = "0.4.1" [[DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "0f4b5d62a88d8f59003e43c25a8a90de9eb76317" +git-tree-sha1 = "1d0a14036acb104d9e89698bd408f63ab58cdc82" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.18" +version = "0.18.20" [[Dates]] deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" +version = "1.11.0" [[DelimitedFiles]] deps = ["Mmap"] @@ -125,6 +140,7 @@ version = "1.9.1" [[Distributed]] deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" +version = "1.11.0" [[DocStringExtensions]] deps = ["LibGit2"] @@ -134,9 +150,9 @@ version = "0.9.3" [[Documenter]] deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] -git-tree-sha1 = "4a40af50e8b24333b9ec6892546d9ca5724228eb" +git-tree-sha1 = "5a1ee886566f2fa9318df1273d8b778b9d42712d" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "1.3.0" +version = "1.7.0" [[Downloads]] deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] @@ -145,15 +161,15 @@ version = "1.6.0" [[Expat_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "4558ab818dcceaab612d1bb8c19cee87eda2b83c" +git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7" uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.5.0+0" +version = "2.6.2+0" [[ExtendableSparse]] -deps = ["DocStringExtensions", "ILUZero", "LinearAlgebra", "Printf", "Requires", "SparseArrays", "Sparspak", "StaticArrays", "SuiteSparse", "Test"] -git-tree-sha1 = "05585b2f21ed4c51878247cb5ce0beb87a71b631" +deps = ["DocStringExtensions", "ILUZero", "LinearAlgebra", "Printf", "SparseArrays", "Sparspak", "StaticArrays", "SuiteSparse", "Test"] +git-tree-sha1 = "3dc0ead7baa71735e03be4379d812dd8167e904a" uuid = "95c220a8-a1cf-11e9-0c77-dbfce5f500b3" -version = "1.4.0" +version = "1.5.2" [ExtendableSparse.extensions] ExtendableSparseAMGCLWrapExt = "AMGCLWrap" @@ -175,9 +191,15 @@ version = "1.8.0" [[FFTW_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea" +git-tree-sha1 = "4d81ed14783ec49ce9f2e168208a12ce1815aa25" uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" -version = "3.3.10+0" +version = "3.3.10+1" + +[[FLTK_jll]] +deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "72a4842f93e734f378cf381dae2ca4542f019d23" +uuid = "4fce6fc7-ba6a-5f4c-898f-77e99806d6f8" +version = "1.3.8+0" [[FastGaussQuadrature]] deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] @@ -187,12 +209,13 @@ version = "1.0.2" [[FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" +version = "1.11.0" [[FillArrays]] deps = ["LinearAlgebra"] -git-tree-sha1 = "bfe82a708416cf00b73a3198db0859c82f741558" +git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.10.0" +version = "1.13.0" [FillArrays.extensions] FillArraysPDMatsExt = "PDMats" @@ -204,6 +227,35 @@ version = "1.10.0" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +[[Fontconfig_jll]] +deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] +git-tree-sha1 = "db16beca600632c95fc8aca29890d83788dd8b23" +uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" +version = "2.13.96+0" + +[[FreeType2_jll]] +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc" +uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" +version = "2.13.2+0" + +[[GLU_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] +git-tree-sha1 = "65af046f4221e27fb79b28b6ca89dd1d12bc5ec7" +uuid = "bd17208b-e95e-5925-bf81-e2f59b3e5c61" +version = "9.0.1+0" + +[[GMP_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" +version = "6.3.0+0" + +[[Gettext_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" +uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" +version = "0.21.0+0" + [[Git]] deps = ["Git_jll"] git-tree-sha1 = "04eff47b1354d702c3a85e8ab23d539bb7d5957e" @@ -212,9 +264,15 @@ version = "1.3.1" [[Git_jll]] deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "d18fb8a1f3609361ebda9bf029b60fd0f120c809" +git-tree-sha1 = "ea372033d09e4552a04fd38361cd019f9003f4f4" uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" -version = "2.44.0+2" +version = "2.46.2+0" + +[[Glib_jll]] +deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] +git-tree-sha1 = "674ff0db93fffcd11a3573986e550d66cd4fd71f" +uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" +version = "2.80.5+0" [[GmshTools]] deps = ["Libdl", "gmsh_jll"] @@ -228,6 +286,18 @@ git-tree-sha1 = "e264cf5f081091e4af712a911d3b620567c565e3" uuid = "36aa67b7-9d79-4e90-bbc0-05abd90a007e" version = "0.1.2" +[[HDF5_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "LibCURL_jll", "Libdl", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "OpenSSL_jll", "TOML", "Zlib_jll", "libaec_jll"] +git-tree-sha1 = "82a471768b513dc39e471540fdadc84ff80ff997" +uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" +version = "1.14.3+3" + +[[Hwloc_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "dd3b49277ec2bb2c6b94eb1604d4d0616016f7a6" +uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" +version = "2.11.2+0" + [[ILUZero]] deps = ["LinearAlgebra", "SparseArrays"] git-tree-sha1 = "b007cfc7f9bee9a958992d2301e9c5b63f332a90" @@ -236,19 +306,26 @@ version = "0.2.0" [[IOCapture]] deps = ["Logging", "Random"] -git-tree-sha1 = "8b72179abc660bfab5e28472e019392b97d0985c" +git-tree-sha1 = "b6d6bfdd7ce25b0f9b2f6b3dd56b2673a66c8770" uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" -version = "0.2.4" +version = "0.2.5" + +[[Infiltrator]] +deps = ["InteractiveUtils", "Markdown", "REPL", "UUIDs"] +git-tree-sha1 = "38298a8eabe09e49e6f60927c9e1ca3481688ba0" +uuid = "5903a43b-9cc3-4c30-8d17-598619ec4e9b" +version = "1.8.3" [[IntelOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "5fdf2fe6724d8caabf43b557b84ce53f3b7e2f6b" +deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] +git-tree-sha1 = "10bd689145d2c3b2a9844005d01087cc1194e79e" uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2024.0.2+0" +version = "2024.2.1+0" [[InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +version = "1.11.0" [[IrrationalConstants]] git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" @@ -263,9 +340,9 @@ version = "0.9.4" [[JLLWrappers]] deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca" +git-tree-sha1 = "be3dc50a92e5a386872a493a10050136d4703f9b" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.5.0" +version = "1.6.1" [[JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] @@ -273,6 +350,24 @@ git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" version = "0.21.4" +[[JpegTurbo_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c" +uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" +version = "3.0.4+0" + +[[LLVMOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "78211fb6cbc872f77cad3fc0b6cf647d923f4929" +uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" +version = "18.1.7+0" + +[[LZO_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "854a9c268c43b77b0a27f22d7fab8d33cdb3a731" +uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" +version = "2.10.2+1" + [[LazilyInitializedFields]] git-tree-sha1 = "8f7f3cabab0fd1800699663533b6d5cb3fc0e612" uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf" @@ -281,6 +376,7 @@ version = "1.2.2" [[LazyArtifacts]] deps = ["Artifacts", "Pkg"] uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" +version = "1.11.0" [[LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] @@ -290,16 +386,17 @@ version = "0.6.4" [[LibCURL_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "8.4.0+0" +version = "8.6.0+0" [[LibGit2]] deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +version = "1.11.0" [[LibGit2_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" -version = "1.6.4+0" +version = "1.7.2+0" [[LibSSH2_jll]] deps = ["Artifacts", "Libdl", "MbedTLS_jll"] @@ -308,6 +405,31 @@ version = "1.11.0+1" [[Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +version = "1.11.0" + +[[Libffi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" +uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" +version = "3.2.2+1" + +[[Libgcrypt_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] +git-tree-sha1 = "9fd170c4bbfd8b935fdc5f8b7aa33532c991a673" +uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" +version = "1.8.11+0" + +[[Libglvnd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] +git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" +uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" +version = "1.6.0+0" + +[[Libgpg_error_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "fbb1f2bef882392312feb1ede3615ddc1e9b99ed" +uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" +version = "1.49.0+0" [[Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -315,6 +437,18 @@ git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" version = "1.17.0+0" +[[Libmount_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "0c4f9c4f1a50d8f35048fa0532dabbadf702f81e" +uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" +version = "2.40.1+0" + +[[Libuuid_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807" +uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" +version = "2.40.1+0" + [[LiftedMaps]] deps = ["LinearAlgebra", "LinearMaps"] git-tree-sha1 = "68c65fe9d32407ddb13eb26ef96fa803d45369cd" @@ -324,12 +458,19 @@ version = "0.5.1" [[LinearAlgebra]] deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +version = "1.11.0" + +[[LinearElasticity_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "71e8ee0f9fe0e86a8f8c7f28361e5118eab2f93f" +uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" +version = "5.0.0+0" [[LinearMaps]] deps = ["LinearAlgebra"] -git-tree-sha1 = "9948d6f8208acfebc3e8cf4681362b2124339e7e" +git-tree-sha1 = "ee79c3208e55786de58f8dcccca098ced79f743f" uuid = "7a12625a-238d-50fd-b39a-03d52299707e" -version = "3.11.2" +version = "3.11.3" [LinearMaps.extensions] LinearMapsChainRulesCoreExt = "ChainRulesCore" @@ -343,9 +484,9 @@ version = "3.11.2" [[LogExpFunctions]] deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "18144f3e9cbe9b15b070288eef858f71b291ce37" +git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.27" +version = "0.3.28" [LogExpFunctions.extensions] LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" @@ -359,16 +500,48 @@ version = "0.3.27" [[Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" +version = "1.11.0" + +[[METIS_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "1fd0a97409e418b78c53fac671cf4622efdf0f21" +uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" +version = "5.1.2+0" [[MKL_jll]] -deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl"] -git-tree-sha1 = "72dc3cf284559eb8f53aa593fe62cb33f83ed0c0" +deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] +git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2024.0.0+0" +version = "2024.2.0+0" + +[[MMG_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] +git-tree-sha1 = "70a59df96945782bb0d43b56d0fbfdf1ce2e4729" +uuid = "86086c02-e288-5929-a127-40944b0018b7" +version = "5.6.0+0" + +[[MPICH_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "7715e65c47ba3941c502bffb7f266a41a7f54423" +uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" +version = "4.2.3+0" + +[[MPIPreferences]] +deps = ["Libdl", "Preferences"] +git-tree-sha1 = "c105fe467859e7f6e9a852cb15cb4301126fac07" +uuid = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267" +version = "0.1.11" + +[[MPItrampoline_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "70e830dab5d0775183c99fc75e4c24c614ed7142" +uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" +version = "5.5.1+0" [[Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" +version = "1.11.0" [[MarkdownAST]] deps = ["AbstractTrees", "Markdown"] @@ -379,14 +552,21 @@ version = "0.1.2" [[MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.2+1" +version = "2.28.6+0" + +[[MicrosoftMPI_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "f12a29c4400ba812841c6ace3f4efbb6dbb3ba01" +uuid = "9237b28f-5490-5468-be7b-bb81f5f5e6cf" +version = "10.1.4+2" [[Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" +version = "1.11.0" [[MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2023.1.10" +version = "2023.12.12" [[NestedUnitRanges]] deps = ["AbstractTrees", "ArrayLayouts", "BlockArrays"] @@ -398,10 +578,16 @@ version = "0.2.1" uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" +[[OCCT_jll]] +deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "bef34b68c20cc34475c5cb464ab48555e74f4c61" +uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" +version = "7.7.2+0" + [[OffsetArrays]] -git-tree-sha1 = "6a731f2b5c03157418a20c12195eb4b74c8f8621" +git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.13.0" +version = "1.14.1" [OffsetArrays.extensions] OffsetArraysAdaptExt = "Adapt" @@ -412,18 +598,24 @@ version = "1.13.0" [[OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.23+4" +version = "0.3.27+1" [[OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" version = "0.8.1+2" +[[OpenMPI_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "e25c1778a98e34219a00455d6e4384e017ea9762" +uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" +version = "4.1.6+0" + [[OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3da7367955dcc5c54c1ba4d402ccdc09a1a3e046" +git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.13+1" +version = "3.0.15+1" [[OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -447,10 +639,26 @@ git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" version = "2.8.1" +[[Permutations]] +deps = ["Combinatorics", "LinearAlgebra", "Random"] +git-tree-sha1 = "f92b0a7b722b1ecfd5c0d77a7eda24b4eea5c56a" +uuid = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" +version = "0.4.22" + +[[Pixman_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] +git-tree-sha1 = "35621f10a7531bc8fa58f74610b1bfb70a3cfc6b" +uuid = "30392449-352a-5448-841d-b1acce4e97dc" +version = "0.43.4+0" + [[Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.10.0" +version = "1.11.0" +weakdeps = ["REPL"] + + [Pkg.extensions] + REPLExt = "REPL" [[PrecompileTools]] deps = ["Preferences"] @@ -467,14 +675,17 @@ version = "1.4.3" [[Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" +version = "1.11.0" [[REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +deps = ["InteractiveUtils", "Markdown", "Sockets", "StyledStrings", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" +version = "1.11.0" [[Random]] deps = ["SHA"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +version = "1.11.0" [[RecipesBase]] deps = ["PrecompileTools"] @@ -499,6 +710,12 @@ git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" version = "1.3.0" +[[SCOTCH_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "7110b749766853054ce8a2afaa73325d72d32129" +uuid = "a8d0f55d-b80e-548d-aff6-1a04c175f0f9" +version = "6.1.3+0" + [[SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" version = "0.7.0" @@ -510,17 +727,19 @@ uuid = "0a13313b-1c00-422e-8263-562364ed9544" version = "0.1.4" [[SauterSchwabQuadrature]] -deps = ["FastGaussQuadrature", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "13e353c9768ef99e21f5f618795c589a9c6aa4e7" +deps = ["FastGaussQuadrature", "LinearAlgebra", "StaticArrays", "TestItems"] +git-tree-sha1 = "b98948c567cbe250d774d01a07833b7a329ec511" uuid = "535c7bfe-2023-5c1d-b712-654ef9d93a38" -version = "2.3.1" +version = "2.4.0" [[Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +version = "1.11.0" [[SharedArrays]] deps = ["Distributed", "Mmap", "Random", "Serialization"] uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" +version = "1.11.0" [[ShunnHamQuadrature]] deps = ["LinearAlgebra", "StaticArrays"] @@ -530,11 +749,12 @@ version = "0.1.0" [[Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" +version = "1.11.0" [[SparseArrays]] deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -version = "1.10.0" +version = "1.11.0" [[Sparspak]] deps = ["Libdl", "LinearAlgebra", "Logging", "OffsetArrays", "Printf", "SparseArrays", "Test"] @@ -544,9 +764,9 @@ version = "0.3.9" [[SpecialFunctions]] deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "e2cfc4012a19088254b3950b85c3c1d8882d864d" +git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.3.1" +version = "2.4.0" [SpecialFunctions.extensions] SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" @@ -556,9 +776,9 @@ version = "2.3.1" [[StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "bf074c045d3d5ffd956fa0a461da38a44685d6b2" +git-tree-sha1 = "eeafab08ae20c62c44c8399ccb9354a04b80db50" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.3" +version = "1.9.7" [StaticArrays.extensions] StaticArraysChainRulesCoreExt = "ChainRulesCore" @@ -569,9 +789,13 @@ version = "1.9.3" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [[StaticArraysCore]] -git-tree-sha1 = "36b3d696ce6366023a0ea192b4cd442268995a0d" +git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682" uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.2" +version = "1.4.3" + +[[StyledStrings]] +uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" +version = "1.11.0" [[SuiteSparse]] deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] @@ -580,7 +804,7 @@ uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" [[SuiteSparse_jll]] deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.2.1+1" +version = "7.7.0+0" [[TOML]] deps = ["Dates"] @@ -595,22 +819,26 @@ version = "1.10.0" [[Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +version = "1.11.0" + +[[TestItems]] +git-tree-sha1 = "8621ba2637b49748e2dc43ba3d84340be2938022" +uuid = "1c621080-faea-4a02-84b6-bbd5e436b8fe" +version = "0.1.1" [[TranscodingStreams]] -git-tree-sha1 = "71509f04d045ec714c4748c785a59045c3736349" +git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.10.7" -weakdeps = ["Random", "Test"] - - [TranscodingStreams.extensions] - TestExt = ["Test", "Random"] +version = "0.11.3" [[UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" +version = "1.11.0" [[Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" +version = "1.11.0" [[WiltonInts84]] deps = ["LinearAlgebra"] @@ -618,26 +846,122 @@ git-tree-sha1 = "9d61cac63c100e936194e5db8613e33572fd2bb8" uuid = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" version = "0.2.5" +[[XML2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] +git-tree-sha1 = "1165b0443d0eca63ac1e32b8c0eb69ed2f4f8127" +uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" +version = "2.13.3+0" + +[[XSLT_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "a54ee957f4c86b526460a720dbc882fa5edcbefc" +uuid = "aed1982a-8fda-507f-9586-7b0439959a61" +version = "1.1.41+0" + +[[Xorg_libX11_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" +uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" +version = "1.8.6+0" + +[[Xorg_libXau_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" +uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" +version = "1.0.11+0" + +[[Xorg_libXdmcp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" +uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" +version = "1.1.4+0" + +[[Xorg_libXext_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "d2d1a5c49fae4ba39983f63de6afcbea47194e85" +uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" +version = "1.3.6+0" + +[[Xorg_libXfixes_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" +uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" +version = "5.0.3+4" + +[[Xorg_libXft_jll]] +deps = ["Fontconfig_jll", "Libdl", "Pkg", "Xorg_libXrender_jll"] +git-tree-sha1 = "754b542cdc1057e0a2f1888ec5414ee17a4ca2a1" +uuid = "2c808117-e144-5220-80d1-69d4eaa9352c" +version = "2.3.3+1" + +[[Xorg_libXinerama_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] +git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" +uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" +version = "1.1.4+4" + +[[Xorg_libXrender_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "47e45cd78224c53109495b3e324df0c37bb61fbe" +uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" +version = "0.9.11+0" + +[[Xorg_libpthread_stubs_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9" +uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" +version = "0.1.1+0" + +[[Xorg_libxcb_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] +git-tree-sha1 = "bcd466676fef0878338c61e655629fa7bbc69d8e" +uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" +version = "1.17.0+0" + +[[Xorg_xtrans_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77" +uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" +version = "1.5.0+0" + [[Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" version = "1.2.13+1" [[gmsh_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9554bb1cad1926e7d3afb68b0ab117d0b9bb73ee" +deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "1e7fe5c8dbe0e911931a18cdfbd2c7a1e01b68ef" uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" -version = "4.9.3+0" +version = "4.13.1+0" + +[[libaec_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "46bf7be2917b59b761247be3f317ddf75e50e997" +uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" +version = "1.1.2+0" [[libblastrampoline_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.8.0+1" +version = "5.11.0+0" + +[[libpng_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "b70c870239dc3d7bc094eb2d6be9b73d27bef280" +uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" +version = "1.6.44+0" [[nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.52.0+1" +version = "1.59.0+0" + +[[oneTBB_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "7d0ea0f4895ef2f5cb83645fa689e52cb55cf493" +uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" +version = "2021.12.0+0" [[p7zip_jll]] deps = ["Artifacts", "Libdl"] diff --git a/docs/src/index.md b/docs/src/index.md index 04ee0b30..489fb146 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -33,7 +33,7 @@ The *reference space* concept defines an API for working with spaces of local sh The functions that depend on the type and value of arguments modeling *reference space* are: -- [`numfunctions(refspace)`](@ref): returns the number of shape functions on each element. +- [`numfunctions(refspace, domain)`](@ref): returns the number of shape functions on each element. ## Kernel diff --git a/src/bases/basis.jl b/src/bases/basis.jl index e75a0733..0fab6ae3 100644 --- a/src/bases/basis.jl +++ b/src/bases/basis.jl @@ -62,6 +62,12 @@ Returns a vector of the shape functions defining the `i`th function of the `basis`. """ basisfunction(s::Space, i) = s.fns[i] + +""" + numfunctions(basis) + +Number of functions in the basis. +""" numfunctions(space::Space) = length(space.fns) struct DirectProductSpace{T,S<:AbstractSpace} <: AbstractSpace From e2e66182b69872b1deb90f93ea1579873e49a472 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 17 Oct 2024 15:02:22 +0200 Subject: [PATCH 414/528] clean is true in docs/make.jl --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index 91931468..640dbb5c 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,7 +1,7 @@ using Documenter, BEAST makedocs( - clean=false, + # clean=false, sitename="BEAST Documentation") deploydocs( From 4ff2c9e6d6dae50853ef8092ad042f79c0911895 Mon Sep 17 00:00:00 2001 From: krcools Date: Thu, 17 Oct 2024 15:08:03 +0200 Subject: [PATCH 415/528] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ad22f8ab..79cbc5e2 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Boundary Element Analysis and Simulation Toolkit [![CI](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml) [![codecov.io](http://codecov.io/github/krcools/BEAST.jl/coverage.svg?branch=master)](http://codecov.io/github/krcools/BEAST.jl?branch=master) -[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://krcools.github.io/BEAST.jl/latest/) +[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://krcools.github.io/BEAST.jl/dev/) [![DOI](https://zenodo.org/badge/87720391.svg)](https://zenodo.org/badge/latestdoi/87720391) ## Introduction From 677a354463f97db718a77f2772966a901f3723ce Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 22 Oct 2024 13:48:49 +0200 Subject: [PATCH 416/528] include reusable workspace in gwprefspace to avoid allocs --- src/bases/local/gwpdivlocal.jl | 11 +++++++++-- src/bases/local/gwplocal.jl | 14 +++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/bases/local/gwpdivlocal.jl b/src/bases/local/gwpdivlocal.jl index 43317a30..ee1d5f8f 100644 --- a/src/bases/local/gwpdivlocal.jl +++ b/src/bases/local/gwpdivlocal.jl @@ -1,4 +1,6 @@ -struct GWPDivRefSpace{T,Degree} <: RefSpace{T} end +struct GWPDivRefSpace{T,Degree} <: RefSpace{T} + storage::Vector{@NamedTuple{value::SVector{2,T}, curl::T}} +end function numfunctions(x::GWPDivRefSpace{<:Any,D}, dom::CompScienceMeshes.ReferenceSimplex{2}) where {D} @@ -9,8 +11,13 @@ function dimtype(x::GWPDivRefSpace{<:Any,D}, Val{(D+1)*(D+3)} end +function GWPDivRefSpace{T,Degree}() where {T,Degree} + NT = @NamedTuple{value::T, curl::SVector{2,T}} + GWPDivRefSpace{T,Degree}(Vector{NT}()) +end + function (ϕ::GWPDivRefSpace{T,Degree})(p) where {T,Degree} - ψ = GWPCurlRefSpace{T,Degree}() + ψ = GWPCurlRefSpace{T,Degree}(ϕ.storage) dom = domain(chart(p)) u = parametric(p) vals = ψ(dom, u) diff --git a/src/bases/local/gwplocal.jl b/src/bases/local/gwplocal.jl index 8f8fe2e8..3ecc0073 100644 --- a/src/bases/local/gwplocal.jl +++ b/src/bases/local/gwplocal.jl @@ -1,4 +1,6 @@ -struct GWPCurlRefSpace{T,Degree} <: RefSpace{T} end +struct GWPCurlRefSpace{T,Degree} <: RefSpace{T} + storage::Vector{@NamedTuple{value::SVector{2,T}, curl::T}} +end function numfunctions(x::GWPCurlRefSpace{<:Any,D}, dom::CompScienceMeshes.ReferenceSimplex{2}) where {D} @@ -9,6 +11,11 @@ function dimtype(x::GWPCurlRefSpace{<:Any,D}, Val{(D+1)*(D+3)} end +function GWPCurlRefSpace{T,Degree}() where {T,Degree} + NT = @NamedTuple{value::T, curl::SVector{2,T}} + GWPCurlRefSpace{T,Degree}(Vector{NT}()) +end + function (ϕ::GWPCurlRefSpace{T,Degree})(p) where {T,Degree} dom = domain(chart(p)) u = parametric(p) @@ -38,7 +45,8 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di P = SVector{2,T} NT = @NamedTuple{value::P, curl::T} # nts = Vector{NT}(undef, NF) - nts = MVector{NF,NT}(undef) + # nts = MVector{NF,NT}(undef) + nts = resize!(ϕ.storage, NF) idx = 1 i = 0 @@ -136,7 +144,7 @@ function (ϕ::GWPCurlRefSpace{T,Deg})(dom::CompScienceMeshes.ReferenceSimplex{Di idx += 1 end end - return SVector(nts) + return SVector{NF}(nts) end From 3d3f09550ec20de17a3e2e0cce833268c04b5ed8 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 23 Oct 2024 13:09:07 +0200 Subject: [PATCH 417/528] efie convergence examples --- examples/efie_convergence.jl | 55 +++++++++++++++++++++++++ examples/efie_convergence_cuboid.jl | 63 +++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 examples/efie_convergence.jl create mode 100644 examples/efie_convergence_cuboid.jl diff --git a/examples/efie_convergence.jl b/examples/efie_convergence.jl new file mode 100644 index 00000000..ff22ece0 --- /dev/null +++ b/examples/efie_convergence.jl @@ -0,0 +1,55 @@ +using CompScienceMeshes +using BEAST + +using BakerStreet +using DataFrames + +function payload(;h) + # Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) + @show h + Γ = meshsphere(radius=1.0, h=h) + # @show length(Γ) + X = raviartthomas(Γ) + + κ, η = 1.0, 1.0 + t = Maxwell3D.singlelayer(wavenumber=κ) + s = -Maxwell3D.singlelayer(gamma=κ) + E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) + e = (n × E) × n + + @hilbertspace j + @hilbertspace k + + A = assemble(t[k,j], X, X) + S = assemble(s[k,j], X, X) + b = assemble(e[k], X) + + Ai = BEAST.GMRESSolver(A; restart=1500, abstol=1e-5, maxiter=10_000) + u = Ai * b + u_Snorm = real(sqrt(dot(u, S*u))) + return (;u, X, u_Snorm) +end + +α = 0.8 +h = collect(0.4 * α.^(0:10)) +# h = collect(logrange(0.4, 0.05, length=8)) +@runsims payload h + +df = @collect_results + +using LinearAlgebra +using Plots +plot(df.h, real.(df.u_Snorm), marker=:.) + +# α = df.h[1] / df.h[2] +W1 = reverse(df.u_Snorm) +W2 = Iterators.drop(W1,1) +W3 = Iterators.drop(W1,2) +p = [log((w2-w1)/(w3-w2)) / log(1/α) for (w1,w2,w3) in zip(W1,W2,W3)] + +# efie = @discretise t[k,j]==e[k] j∈X k∈X +# u, ch = BEAST.gmres_ch(efie; restart=1500) + +# include("utils/postproc.jl") +# include("utils/plotresults.jl") + diff --git a/examples/efie_convergence_cuboid.jl b/examples/efie_convergence_cuboid.jl new file mode 100644 index 00000000..8f4fa5b7 --- /dev/null +++ b/examples/efie_convergence_cuboid.jl @@ -0,0 +1,63 @@ +using CompScienceMeshes +using BEAST + +using BakerStreet +using DataFrames + +function payload(;h) + # Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) + # @show h + d = 1.0 + Γ = meshcuboid(d, d, d/2, h) + d = 1.2 + Γ′ = meshcuboid(d, d, d/2, h) + # @show length(Γ) + X = raviartthomas(Γ) + X' + + κ, η = 1.0, 1.0 + t = Maxwell3D.singlelayer(wavenumber=κ) + s = -Maxwell3D.singlelayer(gamma=κ) + E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) + e = (n × E) × n + + @hilbertspace j + @hilbertspace k + + A = assemble(t[k,j], X, X) + S = assemble(s[k,j], X, X) + b = assemble(e[k], X) + + Ai = BEAST.GMRESSolver(A; restart=1500, abstol=1e-5, maxiter=10_000) + u = Ai * b + u_Snorm = real(sqrt(dot(u, S*u))) + return (;u, X, u_Snorm) +end + +α = 0.8 +h = collect(0.4 * α.^(0:13)) +# h = collect(logrange(0.4, 0.05, length=8)) +@runsims payload h + +df = @collect_results + +using LinearAlgebra +using Plots +plot(df.h, real.(df.u_Snorm), marker=:.) + +# α = df.h[1] / df.h[2] +W1 = reverse(df.u_Snorm) +# W1 = reverse((df.h).^2.231) +W2 = Iterators.drop(W1,1) +W3 = Iterators.drop(W1,2) +p = [log((w2-w1)/(w3-w2)) / log(1/α) for (w1,w2,w3) in zip(W1,W2,W3)] +plot(collect(Iterators.drop(reverse(df.h),2)), p, marker=:.) + +refsol = df.u_Snorm[1] +plot(log.(df.h), log.(abs.(df.u_Snorm .- refsol)), marker=:.) +plot!(log.(df.h), 1.75 * log.(df.h)) + +fcr, geo = facecurrents(df.u[1], df.X[1]) +import Plotly +Plotly.plot(patch(geo, log10.(norm.(fcr)))) + From 031669fb73cb533a74773606c10c9f76dc34a0d5 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 24 Oct 2024 15:48:47 +0200 Subject: [PATCH 418/528] nonconf quadrules use correct signature momintegrals --- examples/efie_convergence_cuboid.jl | 91 +++++++++++++-------- src/quadrature/nonconformingoverlapqrule.jl | 8 +- src/quadrature/nonconformingtouchqrule.jl | 8 +- 3 files changed, 65 insertions(+), 42 deletions(-) diff --git a/examples/efie_convergence_cuboid.jl b/examples/efie_convergence_cuboid.jl index 8f4fa5b7..6c4c27ce 100644 --- a/examples/efie_convergence_cuboid.jl +++ b/examples/efie_convergence_cuboid.jl @@ -1,45 +1,43 @@ using CompScienceMeshes using BEAST +using Makeitso using BakerStreet using DataFrames -function payload(;h) - # Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) - # @show h - d = 1.0 - Γ = meshcuboid(d, d, d/2, h) - d = 1.2 - Γ′ = meshcuboid(d, d, d/2, h) - # @show length(Γ) - X = raviartthomas(Γ) - X' - - κ, η = 1.0, 1.0 - t = Maxwell3D.singlelayer(wavenumber=κ) - s = -Maxwell3D.singlelayer(gamma=κ) - E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) - e = (n × E) × n - - @hilbertspace j - @hilbertspace k - - A = assemble(t[k,j], X, X) - S = assemble(s[k,j], X, X) - b = assemble(e[k], X) - - Ai = BEAST.GMRESSolver(A; restart=1500, abstol=1e-5, maxiter=10_000) - u = Ai * b - u_Snorm = real(sqrt(dot(u, S*u))) - return (;u, X, u_Snorm) -end +@target solutions () -> begin + function payload(;h) + d = 1.0 + Γ = meshcuboid(d, d, d/2, h) + X = raviartthomas(Γ) + + κ, η = 1.0, 1.0 + t = Maxwell3D.singlelayer(wavenumber=κ) + s = -Maxwell3D.singlelayer(gamma=κ) + E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) + e = (n × E) × n + + @hilbertspace j + @hilbertspace k -α = 0.8 -h = collect(0.4 * α.^(0:13)) -# h = collect(logrange(0.4, 0.05, length=8)) -@runsims payload h + A = assemble(t[k,j], X, X) + S = assemble(s[k,j], X, X) + b = assemble(e[k], X) -df = @collect_results + Ai = BEAST.GMRESSolver(A; restart=1500, abstol=1e-8, maxiter=10_000) + u = Ai * b + u_Snorm = real(sqrt(dot(u, S*u))) + return (;u, X, u_Snorm) + end + + α = 0.8 + h = collect(0.4 * α.^(0:15)) + runsims(payload, "solutions"; h) +end + +@make solutions +df = loadsims("solutions") +error() using LinearAlgebra using Plots @@ -50,14 +48,35 @@ W1 = reverse(df.u_Snorm) # W1 = reverse((df.h).^2.231) W2 = Iterators.drop(W1,1) W3 = Iterators.drop(W1,2) -p = [log((w2-w1)/(w3-w2)) / log(1/α) for (w1,w2,w3) in zip(W1,W2,W3)] +p = [log(abs((w2-w1)/(w3-w2))) / log(1/α) for (w1,w2,w3) in zip(W1,W2,W3)] plot(collect(Iterators.drop(reverse(df.h),2)), p, marker=:.) refsol = df.u_Snorm[1] plot(log.(df.h), log.(abs.(df.u_Snorm .- refsol)), marker=:.) -plot!(log.(df.h), 1.75 * log.(df.h)) +plot!(log.(df.h), 1.5 * log.(df.h)) fcr, geo = facecurrents(df.u[1], df.X[1]) import Plotly Plotly.plot(patch(geo, log10.(norm.(fcr)))) +uref = df.u[1] +Xref = df.X[1] +Γref = geometry(Xref) +errs = zeros(length(df.h)) +s = -Maxwell3D.singlelayer(gamma=1.0) +@hilbertspace k +@hilbertspace j +S11 = assemble(s, Xref, Xref) +for i in reverse(2:length(df.h)) + @show i, df.h[i] + u = df.u[i] + X = df.X[i] + Γ = geometry(X) + + qstrat12 = BEAST.NonConformingIntegralOpQStrat(BEAST.DoubleNumSauterQstrat(3, 4, 6, 6, 6, 6)) + S12 = assemble(s, Xref, X; quadstrat=[qstrat12]) + S22 = assemble(s, X, X) + errs[i] = sqrt(real(dot(uref, S11*uref) - 2*real(dot(uref, S12*u)) + real(dot(u, S22*u)))) + @show errs[i] +end + diff --git a/src/quadrature/nonconformingoverlapqrule.jl b/src/quadrature/nonconformingoverlapqrule.jl index 9276b7ce..12c563b0 100644 --- a/src/quadrature/nonconformingoverlapqrule.jl +++ b/src/quadrature/nonconformingoverlapqrule.jl @@ -49,9 +49,11 @@ function momintegrals!(op, Q = restrict(basis_local_space, basis_chart, bchart) zlocal = zero(out) - momintegrals!(zlocal, op, - test_local_space, nothing, tchart, - basis_local_space, nothing, bchart, qrule) + # momintegrals!(zlocal, op, + # test_local_space, nothing, tchart, + # basis_local_space, nothing, bchart, qrule) + momintegrals!(op, test_local_space, basis_local_space, + tchart, bchart, zlocal, qrule) for i in axes(P,1) for j in axes(Q,1) diff --git a/src/quadrature/nonconformingtouchqrule.jl b/src/quadrature/nonconformingtouchqrule.jl index 3c31236d..d2da86ab 100644 --- a/src/quadrature/nonconformingtouchqrule.jl +++ b/src/quadrature/nonconformingtouchqrule.jl @@ -62,9 +62,11 @@ function momintegrals!(op, Q = restrict(bsis_locspace, σ, bchart) zlocal = zero(out) - momintegrals!(zlocal, op, - test_locspace, nothing, tchart, - bsis_locspace, nothing, bchart, qrule) + # momintegrals!(zlocal, op, + # test_locspace, nothing, tchart, + # bsis_locspace, nothing, bchart, qrule) + momintegrals!(op, test_locspace, bsis_locspace, + tchart, bchart, zlocal, qrule) # out .+= P * zlocal * Q' for i in axes(P,1) From 95789fa4a7bdd232d7a50f4ecb53ffb5148a6cb9 Mon Sep 17 00:00:00 2001 From: bmergl Date: Thu, 24 Oct 2024 18:39:40 +0200 Subject: [PATCH 419/528] VIE bugfix Fix for VIE momintegrals! and grideval. (This was needed due to changes in momintegrals! and numfunctions.) Activates the VIE unit test. --- src/bases/local/laglocal.jl | 5 +++-- src/postproc/segcurrents.jl | 12 ++++++++---- src/volumeintegral/sauterschwab_ints.jl | 19 +++++++++++-------- test/runtests.jl | 2 ++ test/test_hh_lsvie.jl | 18 +++++++++--------- 5 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 21355e0f..0adea811 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -9,8 +9,9 @@ numfunctions(s::LagrangeRefSpace{T,1,3}, ch::CompScienceMeshes.ReferenceSimplex{ numfunctions(s::LagrangeRefSpace{T,2,3}, ch::CompScienceMeshes.ReferenceSimplex{2}) where {T} = 6 numfunctions(s::LagrangeRefSpace{T,Dg}, ch::CompScienceMeshes.ReferenceSimplex{D}) where {T,Dg,D} = binomial(D+Dg,Dg) -valuetype(ref::LagrangeRefSpace{T}, charttype) where {T} = - SVector{numfunctions(ref), Tuple{T,T}} +# valuetype(ref::LagrangeRefSpace{T}, charttype) where {T} = +# SVector{numfunctions(ref), Tuple{T,T}} +valuetype(ref::LagrangeRefSpace{T}, charttype) where {T} = T # Evaluate constant lagrange elements on anything (ϕ::LagrangeRefSpace{T,0})(tp) where {T} = SVector(((value=one(T), derivative=zero(T)),)) diff --git a/src/postproc/segcurrents.jl b/src/postproc/segcurrents.jl index a02e37c9..844f2eec 100644 --- a/src/postproc/segcurrents.jl +++ b/src/postproc/segcurrents.jl @@ -29,21 +29,25 @@ function grideval(points, coeffs, basis; type=nothing) V = valuetype(refs, eltype(charts)) T = promote_type(eltype(coeffs), eltype(V)) - P = similar_type(V, T) + if !(V <: SVector) + P = T + else + P = similar_type(V, T) + end - type != nothing && (P = type) + type !== nothing && (P = type) values = zeros(P, size(points)) chart_tree = BEAST.octree(charts) for (j,point) in enumerate(points) i = CompScienceMeshes.findchart(charts, chart_tree, point) - if i != nothing + if i !== nothing # @show i chart = charts[i] u = carttobary(chart, point) vals = refs(neighborhood(chart,u)) - for r in 1 : numfunctions(refs) + for r in 1 : numfunctions(refs, domain(chart)) for (m,w) in ad[i, r] values[j] += w * coeffs[m] * vals[r][1] end diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index 68385e7a..6b8fc46d 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -66,7 +66,7 @@ function reorder_dof(space::RTRefSpace,I) return SVector(K),SVector{3,Int64}(1,1,1) end -function reorder_dof(space::LagrangeRefSpace{Float64,0,3,1},I) +function reorder_dof(space::LagrangeRefSpace{T,0,3,1},I) where T return SVector{1,Int64}(1),SVector{1,Int64}(1) end @@ -102,16 +102,19 @@ function reorder_dof(space::LagrangeRefSpace{T,1,4,4},I) where T end function momintegrals!(out, op::VIEOperator, - test_local_space::RefSpace, test_ptr, test_tetrahedron_element, - trial_local_space::RefSpace, trial_ptr, trial_tetrahedron_element, + test_functions::Space, test_ptr, test_tetrahedron_element, + trial_functions::Space, trial_ptr, trial_tetrahedron_element, strat::SauterSchwab3DStrategy) + local_test_space = refspace(test_functions) + local_trial_space = refspace(trial_functions) + #Find permutation of vertices to match location of singularity to SauterSchwab J, I= SauterSchwab3D.reorder(strat.sing) #Get permutation and rel. orientatio of DoFs - K,O1 = reorder_dof(test_local_space, I) - L,O2 = reorder_dof(trial_local_space, J) + K,O1 = reorder_dof(local_test_space, I) + L,O2 = reorder_dof(local_trial_space, J) #Apply permuation to elements if length(I) == 4 @@ -146,7 +149,7 @@ function momintegrals!(out, op::VIEOperator, #Define integral (returns a function that only needs barycentric coordinates) igd = VIEIntegrand(test_tetrahedron_element, trial_tetrahedron_element, - op, test_local_space, trial_local_space) + op, local_test_space, local_trial_space) #Evaluate integral Q = SauterSchwab3D.sauterschwab_parameterized(igd, strat) @@ -161,8 +164,8 @@ function momintegrals!(out, op::VIEOperator, end function momintegrals!(z, biop::VIEOperator, - tshs, tptr, tcell, - bshs, bptr, bcell, + test_functions::Space, tptr, tcell, + trial_functions::Space, bptr, bcell, strat::DoubleQuadRule) # memory allocation here is a result from the type instability on strat diff --git a/test/runtests.jl b/test/runtests.jl index bbcee67f..f99b04bf 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -79,6 +79,8 @@ include("test_variational.jl") include("test_handlers.jl") include("test_gridfunction.jl") +include("test_hh_lsvie.jl") + using TestItemRunner @run_package_tests diff --git a/test/test_hh_lsvie.jl b/test/test_hh_lsvie.jl index 899c55b1..cc16cb35 100644 --- a/test/test_hh_lsvie.jl +++ b/test/test_hh_lsvie.jl @@ -11,12 +11,12 @@ using Test @testset "Lippmann Schwinger Volume Integral Equation" begin # Environment - ε1 = 1.0*ε0 - μ1 = μ0 + ε1 = 1.0*SphericalScattering.ε0 + μ1 = SphericalScattering.μ0 # Dielectic Sphere - ε2 = 5.0*ε0 - μ2 = μ0 + ε2 = 5.0*SphericalScattering.ε0 + μ2 = SphericalScattering.μ0 r = 1.0 sp = DielectricSphere(; radius = r, filling = Medium(ε2, μ2)) @@ -56,7 +56,7 @@ using Test # Assembly - b = assemble(Φ_inc, X) + b = real.(assemble(Φ_inc, X)) Z_I = assemble(I, X, X) @@ -69,8 +69,8 @@ using Test Z_version2 = Z_I + Z_Y # MoM solution - u_version1 = Z_version1 \ Vector(b) - u_version2 = Z_version2 \ Vector(b) + u_version1 = Z_version1 \ b + u_version2 = Z_version2 \ b # Observation points range_ = range(-1.0*r,stop=1.0*r,length=14) @@ -84,8 +84,8 @@ using Test Φ = field(sp, ex, ScalarPotential(points_sp)) # MoM solution inside the dielectric sphere - Φ_MoM_version1 = BEAST.grideval(points_sp, u_version1, X, type=Float64) - Φ_MoM_version2 = BEAST.grideval(points_sp, u_version2, X, type=Float64) + Φ_MoM_version1 = BEAST.grideval(points_sp, u_version1, X) + Φ_MoM_version2 = BEAST.grideval(points_sp, u_version2, X) From 3029def8a3531c72a585a901c8d02279abc10475 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Wed, 30 Oct 2024 11:48:11 +0100 Subject: [PATCH 420/528] Added scalartype for TD-Helmholtz. Added single thread dispatch for TDoperator assembly. --- examples/disabled/tdhelmholtz3d.jl | 2 +- src/helmholtz3d/timedomain/tdhh3dexc.jl | 1 + src/helmholtz3d/timedomain/tdhh3dops.jl | 29 +++++++++++++++++-- src/timedomain/tdintegralop.jl | 38 +++++++++++++++---------- 4 files changed, 51 insertions(+), 19 deletions(-) diff --git a/examples/disabled/tdhelmholtz3d.jl b/examples/disabled/tdhelmholtz3d.jl index 3f3b5362..de418316 100644 --- a/examples/disabled/tdhelmholtz3d.jl +++ b/examples/disabled/tdhelmholtz3d.jl @@ -12,7 +12,7 @@ e = BEAST.planewave(point(0,0,1), c, gaussian) X = lagrangecxd0(G) -Δt, Nt = 0.08, 300 +Δt, Nt = 1.0, 300 #T = timebasisshiftedlagrange(Δt, Nt, 0) #T = timebasiscxd0(Δt, Nt) T = timebasisc0d1(Δt, Nt) diff --git a/src/helmholtz3d/timedomain/tdhh3dexc.jl b/src/helmholtz3d/timedomain/tdhh3dexc.jl index 4fc28ffa..34e688d3 100644 --- a/src/helmholtz3d/timedomain/tdhh3dexc.jl +++ b/src/helmholtz3d/timedomain/tdhh3dexc.jl @@ -41,6 +41,7 @@ struct DotTraceHH{T,F} <: TDFunctional{T} field::F end +scalartype(d::DotTraceHH{T}) where {T} = T dot(::NormalVector, field::TDFunctional) = DotTraceHH{scalartype(field), typeof(field)}(field) diff --git a/src/helmholtz3d/timedomain/tdhh3dops.jl b/src/helmholtz3d/timedomain/tdhh3dops.jl index afec459a..99caa3fe 100644 --- a/src/helmholtz3d/timedomain/tdhh3dops.jl +++ b/src/helmholtz3d/timedomain/tdhh3dops.jl @@ -3,11 +3,22 @@ abstract type HH3DTDBIO{T} <: RetardedPotential{T} end struct HH3DSingleLayerTDBIO{T} <: HH3DTDBIO{T} "speed of light" speed_of_light::T + "operator coefficient" + weight::T "number of temporal differentiations" num_diffs::Int end -HH3DSingleLayerTDBIO(c) = HH3DSingleLayerTDBIO(c, 0) +HH3DSingleLayerTDBIO(c) = HH3DSingleLayerTDBIO(c, 1.0, 0) + + +function Base.:*(a::Number, op::HH3DSingleLayerTDBIO) + #@info "scalar product a * op (SL)" + HH3DSingleLayerTDBIO( + op.speed_of_light, + a * op.weight, + op.num_diffs) +end struct HH3DHyperSingularTDBIO{T} <: HH3DTDBIO{T} speed_of_light::T @@ -22,6 +33,18 @@ function HH3DHyperSingularTDBIO(;speed_of_light, numdiffs) HH3DHyperSingularTDBIO(speed_of_light, id, id, numdiffs+1, numdiffs-1) end + +function Base.:*(a::Number, op::HH3DHyperSingularTDBIO) + #@info "scalar product a * op (HS)" + HH3DHyperSingularTDBIO( + op.speed_of_light, + a * op.weight_of_weakly_singular_term, + a * op.weight_of_hyper_singular_term, + op.num_diffs_on_weakly_singular_term, + op.num_diffs_on_hyper_singular_term) +end + + struct HH3DDoubleLayerTDBIO{T} <: HH3DTDBIO{T} speed_of_light::T weight::T @@ -44,7 +67,7 @@ function quaddata(operator::HH3DTDBIO, V = eltype(test_element[1].vertices) ws = WiltonInts84.workspace(V) order = 4 - @show order + #@show order quadpoints(test_local_space, test_element, (order,)), bn, ws end @@ -89,7 +112,7 @@ function innerintegrals!(zlocal, operator::HH3DSingleLayerTDBIO, trial_element[3], x, r, R, Val{2}, quad_rule.workspace) - a = dx / (4*pi) + a = dx / (4*pi) * operator.weight D = operator.num_diffs @assert D == 0 @assert numfunctions(test_local_space) == 1 diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index bd9820f6..6f3c67da 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -53,9 +53,9 @@ function allocatestorage(op::RetardedPotential, testST, basisST, end aux = EmptyRP(op.speed_of_light) - print("Allocating memory for convolution operator: ") + #print("Allocating memory for convolution operator: ") assemble!(aux, testST, basisST, store) - println("\nAllocated memory for convolution operator.") + #println("\nAllocated memory for convolution operator.") kmax = maximum(K1); T = scalartype(op, testST, basisST) @@ -103,7 +103,7 @@ function allocatestorage(op::RetardedPotential, testST, basisST, ::Type{Val{:bandedstorage}}, ::Type{LongDelays{:compress}}) - @info "Allocating mem for RP op compressing the static tail..." + #@info "Allocating mem for RP op compressing the static tail..." T = scalartype(op, testST, basisST) @@ -130,9 +130,9 @@ function allocatestorage(op::RetardedPotential, testST, basisST, op_alloc = EmptyRP(op.speed_of_light) tbf_trunc = truncatetail(tbf) δ = timebasisdelta(Δt, Nt) - print("Allocating memory for convolution operator: ") + #print("Allocating memory for convolution operator: ") assemble!(op_alloc, tfs⊗δ, bfs⊗tbf_trunc, store_alloc) - println("\nAllocated memory for convolution operator.") + #println("\nAllocated memory for convolution operator.") bandwidth = maximum(K1 .- K0 .+ 1) data = zeros(T, bandwidth, M, N) @@ -167,10 +167,17 @@ function assemble!(op::LinearCombinationOfOperators, tfs::SpaceTimeBasis, bfs::S end end + +function assemble!(op::RetardedPotential, testST::Space, trialST::Space, store, + threading::Type{Threading{:single}}=Threading{:single}; quadstrat=defaultquadstrat(op, testST, trialST)) + + assemble_chunk!(op, testST, trialST, store; quadstrat=quadstrat) +end + function assemble!(op::RetardedPotential, testST::Space, trialST::Space, store, threading::Type{Threading{:multi}}=Threading{:multi}; quadstrat=defaultquadstrat(op, testST, trialST)) - @show quadstrat + #@show quadstrat Y, S = spatialbasis(testST), temporalbasis(testST) X, R = spatialbasis(trialST), temporalbasis(trialST) @@ -201,7 +208,7 @@ function assemble!(op::RetardedPotential, testST::Space, trialST::Space, store, store1 = (v,m,n,k) -> store(v,rlo+m-1,clo+n-1,k) assemble_chunk!(op, Y_p ⊗ S, X_q ⊗ R, store1) end - println("") + #println("") # P = Threads.nthreads() # splits = [round(Int,s) for s in range(0, stop=numfunctions(Y), length=P+1)] @@ -258,8 +265,8 @@ function assemble_chunk!(op::RetardedPotential, testST, trialST, store; # @show length(testels) length(trialels) - myid == 1 && print("dots out of 10: ") - todo, done, pctg = length(testels), 0, 0 + #myid == 1 && print("dots out of 10: ") + #todo, done, pctg = length(testels), 0, 0 for p in eachindex(testels) τ = testels[p] for q in eachindex(trialels) @@ -296,12 +303,13 @@ function assemble_chunk!(op::RetardedPotential, testST, trialST, store; end # next r end # next q - done += 1 - new_pctg = round(Int, done / todo * 100) - if myid == 1 && new_pctg > pctg + 9 - print(".") - pctg = new_pctg - end + #done += 1 + + #new_pctg = round(Int, done / todo * 100) + #if myid == 1 && new_pctg > pctg + 9 + # print(".") + # pctg = new_pctg + #end end # next p # println("") From 33e441191b67625ed9f9c07af84c918d0f71f294 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 7 Nov 2024 12:36:35 +0100 Subject: [PATCH 421/528] adjoint of GMRESSolver fixed --- src/solvers/itsolver.jl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index ea49e8d5..0f23e4be 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -91,7 +91,13 @@ function LinearAlgebra.mul!(y::AbstractVecOrMat, solver::GMRESSolver, x::Abstrac return y end -LinearAlgebra.adjoint(A::GMRESSolver) = GMRESSolver(adjoint(A.linear_operator), A.maxiter, A.restart, A.abstol, A.reltol, A.verbose) +LinearAlgebra.adjoint(A::GMRESSolver) = GMRESSolver(adjoint(A.linear_operator); + maxiter=A.maxiter, + restart=A.restart, + abstol=A.abstol, + reltol=A.reltol, + left_preconditioner=A.left_preconditioner, + verbose=A.verbose) LinearAlgebra.transpose(A::GMRESSolver) = GMRESSolver(transpose(A.linear_operator), A.maxiter, A.restart, A.abstol, A.reltol, A.verbose) From a5048169aae12a0c92b2a5d5ba028026efc5cadf Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 7 Nov 2024 12:44:32 +0100 Subject: [PATCH 422/528] add convergence study to examples --- .gitignore | 1 + examples/efie_convergence_cuboid.jl | 99 ++++++++++++--------- examples/efie_convergence_screen.jl | 104 ++++++++++++++++++++++ examples/efie_convergence_sphere.jl | 133 ++++++++++++++++++++++++++++ 4 files changed, 296 insertions(+), 41 deletions(-) create mode 100644 examples/efie_convergence_screen.jl create mode 100644 examples/efie_convergence_sphere.jl diff --git a/.gitignore b/.gitignore index b879feb5..a3268a4b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ dev/ profile/ /Manifest.toml /envs/ +/data/ \ No newline at end of file diff --git a/examples/efie_convergence_cuboid.jl b/examples/efie_convergence_cuboid.jl index 6c4c27ce..530489e5 100644 --- a/examples/efie_convergence_cuboid.jl +++ b/examples/efie_convergence_cuboid.jl @@ -33,50 +33,67 @@ using DataFrames α = 0.8 h = collect(0.4 * α.^(0:15)) runsims(payload, "solutions"; h) + return loadsims("solutions") end -@make solutions -df = loadsims("solutions") -error() - -using LinearAlgebra -using Plots -plot(df.h, real.(df.u_Snorm), marker=:.) - -# α = df.h[1] / df.h[2] -W1 = reverse(df.u_Snorm) -# W1 = reverse((df.h).^2.231) -W2 = Iterators.drop(W1,1) -W3 = Iterators.drop(W1,2) -p = [log(abs((w2-w1)/(w3-w2))) / log(1/α) for (w1,w2,w3) in zip(W1,W2,W3)] -plot(collect(Iterators.drop(reverse(df.h),2)), p, marker=:.) - -refsol = df.u_Snorm[1] -plot(log.(df.h), log.(abs.(df.u_Snorm .- refsol)), marker=:.) -plot!(log.(df.h), 1.5 * log.(df.h)) - -fcr, geo = facecurrents(df.u[1], df.X[1]) -import Plotly -Plotly.plot(patch(geo, log10.(norm.(fcr)))) - -uref = df.u[1] -Xref = df.X[1] -Γref = geometry(Xref) -errs = zeros(length(df.h)) -s = -Maxwell3D.singlelayer(gamma=1.0) -@hilbertspace k -@hilbertspace j -S11 = assemble(s, Xref, Xref) -for i in reverse(2:length(df.h)) - @show i, df.h[i] - u = df.u[i] - X = df.X[i] - Γ = geometry(X) +@target compute_errors (solutions) -> begin + df = loadsims("solutions") + numrows = size(df,1) + uref = df.u[1] + Xref = df.X[1] + Γref = geometry(Xref) + + errs = zeros(numrows) + s = -Maxwell3D.singlelayer(gamma=1.0) qstrat12 = BEAST.NonConformingIntegralOpQStrat(BEAST.DoubleNumSauterQstrat(3, 4, 6, 6, 6, 6)) - S12 = assemble(s, Xref, X; quadstrat=[qstrat12]) - S22 = assemble(s, X, X) - errs[i] = sqrt(real(dot(uref, S11*uref) - 2*real(dot(uref, S12*u)) + real(dot(u, S22*u)))) - @show errs[i] + + S11 = assemble(s, Xref, Xref) + term11 = real(dot(uref, S11*uref)) + + for i in reverse(2:numrows) + r = df[i,:] + (;h, u, X) = r + @show r, length(u) + Γ = geometry(X) + + S12 = assemble(s, Xref, X; quadstrat=[qstrat12]) + S22 = assemble(s, X, X) + errs[i] = sqrt(term11 - 2*real(dot(uref, S12*u)) + real(dot(u, S22*u))) + @show errs[i] + end + + return errs end + +@target weak_errors (solutions) -> begin + df = loadsims("solutions") + + α = df.h[1] / df.h[2] + W1 = reverse(df.u_Snorm) + W2 = Iterators.drop(W1,1) + W3 = Iterators.drop(W1,2) + p = [log(abs((w2-w1)/(w3-w2))) / log(1/α) for (w1,w2,w3) in zip(W1,W2,W3)] +end + + +@make compute_errors +# df = loadsims("solutions") +# error() + +# using LinearAlgebra +# using Plots +# plot(df.h, real.(df.u_Snorm), marker=:.) +# plot(df.h[end:-1:2], p, marker=:.) + +# refsol = df.u_Snorm[1] +# plot(log.(df.h), log.(abs.(df.u_Snorm .- refsol)), marker=:.) +# plot!(log.(df.h), 1.5 * log.(df.h)) + +# fcr, geo = facecurrents(df.u[1], df.X[1]) +# import Plotly +# Plotly.plot(patch(geo, log10.(norm.(fcr)))) + + + diff --git a/examples/efie_convergence_screen.jl b/examples/efie_convergence_screen.jl new file mode 100644 index 00000000..bd939708 --- /dev/null +++ b/examples/efie_convergence_screen.jl @@ -0,0 +1,104 @@ +using CompScienceMeshes +using BEAST + +using Makeitso +using BakerStreet +using DataFrames + +fn = splitext(splitdir(@__FILE__)[end])[1] + +@target solutions () -> begin + function payload(;h) + d = 1.0 + # Γ = meshsphere(radius=d, h=h) + Γ = meshrectangle(d, d, h, 3) + # Γ = meshcuboid(d, d, d/2, h) + X = raviartthomas(Γ) + + κ, η = 1.0, 1.0 + t = Maxwell3D.singlelayer(wavenumber=κ) + s = -Maxwell3D.singlelayer(gamma=κ) + E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) + e = (n × E) × n + + @hilbertspace j + @hilbertspace k + + A = assemble(t[k,j], X, X) + S = assemble(s[k,j], X, X) + b = assemble(e[k], X) + + Ai = BEAST.GMRESSolver(A; restart=1500, abstol=1e-8, maxiter=10_000) + u = Ai * b + u_Snorm = real(sqrt(dot(u, S*u))) + return (;u, X, u_Snorm) + end + + α = 0.8 + h = collect(0.4 * α.^(0:15)) + runsims(payload, "$(fn)/solutions"; h) + return true + # return loadsims("$(fn)/solutions") +end + +@target errors (solutions) -> begin + df = loadsims("$(fn)/solutions") + numrows = size(df,1) + + uref = df.u[1] + Xref = df.X[1] + Γref = geometry(Xref) + + errs = zeros(numrows) + s = -Maxwell3D.singlelayer(gamma=1.0) + qstrat12 = BEAST.NonConformingIntegralOpQStrat(BEAST.DoubleNumSauterQstrat(3, 4, 6, 6, 6, 6)) + + S11 = assemble(s, Xref, Xref) + term11 = real(dot(uref, S11*uref)) + + function payload(;h) + r = getrow(df; h=h) + (;h, u, X) = r + Γ = geometry(X) + + S12 = assemble(s, Xref, X; quadstrat=[qstrat12]) + S22 = assemble(s, X, X) + err = sqrt(term11 - 2*real(dot(uref, S12*u)) + real(dot(u, S22*u))) + return (;err) + end + + runsims(payload, "$(fn)/errors", h=df.h[end:-1:2]) + return true +end + + +@target weak_errors (solutions) -> begin + df = loadsims("sphere/solutions") + + α = df.h[1] / df.h[2] + W1 = reverse(df.u_Snorm) + W2 = Iterators.drop(W1,1) + W3 = Iterators.drop(W1,2) + p = [log(abs((w2-w1)/(w3-w2))) / log(1/α) for (w1,w2,w3) in zip(W1,W2,W3)] +end + + +@make errors + +df = loadsims("$fn/solutions") +hmin, i = findmin(df.h) +(;u, X) = df[i,:] + +using LinearAlgebra +import Plotly +using Plots +fcr, geo = facecurrents(u, X) +Plotly.plot(patch(geo, log10.(norm.(fcr)))) + +df2 = loadsims("$fn/errors") +plot(log10.(df2.h), log10.(df2.err), marker=:.) +plot!(log10.(df2.h), -0.25 .+ 0.5*log10.(df2.h), label="p = 0.5") +plot!(log10.(df2.h), 0.1 .+ 0.7*log10.(df2.h), label="p = 0.625") +plot!(log10.(df2.h), 0.2 .+ 0.75*log10.(df2.h), label="p = 0.75") +plot!(log10.(df2.h), 0.625 .+ 1.0*log10.(df2.h), label="p = 1") +plot!(log10.(df2.h), 1.5 .+ 1.5*log10.(df2.h), label="p = 1.5") \ No newline at end of file diff --git a/examples/efie_convergence_sphere.jl b/examples/efie_convergence_sphere.jl new file mode 100644 index 00000000..6c4a4f05 --- /dev/null +++ b/examples/efie_convergence_sphere.jl @@ -0,0 +1,133 @@ +using CompScienceMeshes +using BEAST + +using Makeitso +using BakerStreet +using DataFrames + +fn = splitext(splitdir(@__FILE__)[end])[1] + +@target solutions () -> begin + function payload(;h) + d = 1.0 + Γ = meshsphere(radius=d, h=h) + # Γ = meshcuboid(d, d, d/2, h) + X = raviartthomas(Γ) + + κ, η = 1.0, 1.0 + t = Maxwell3D.singlelayer(wavenumber=κ) + s = -Maxwell3D.singlelayer(gamma=κ) + E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) + e = (n × E) × n + + @hilbertspace j + @hilbertspace k + + A = assemble(t[k,j], X, X) + S = assemble(s[k,j], X, X) + b = assemble(e[k], X) + + Ai = BEAST.GMRESSolver(A; restart=1500, abstol=1e-8, maxiter=10_000) + u = Ai * b + u_Snorm = real(sqrt(dot(u, S*u))) + return (;u, X, u_Snorm) + end + + α = 0.8 + h = collect(0.4 * α.^(0:15)) + runsims(payload, "$fn/solutions"; h) + return loadsims("$fn/solutions") +end + + +@target errors (solutions) -> begin + df = loadsims("$(fn)/solutions") + numrows = size(df,1) + + uref = df.u[1] + Xref = df.X[1] + Γref = geometry(Xref) + + errs = zeros(numrows) + s = -Maxwell3D.singlelayer(gamma=1.0) + qstrat12 = BEAST.NonConformingIntegralOpQStrat(BEAST.DoubleNumSauterQstrat(3, 4, 6, 6, 6, 6)) + + S11 = assemble(s, Xref, Xref) + term11 = real(dot(uref, S11*uref)) + + function payload(;h) + r = getrow(df; h=h) + (;h, u, X) = r + Γ = geometry(X) + + S12 = assemble(s, Xref, X; quadstrat=[qstrat12]) + S22 = assemble(s, X, X) + err = sqrt(term11 - 2*real(dot(uref, S12*u)) + real(dot(u, S22*u))) + return (;err) + end + + runsims(payload, "$(fn)/errors", h=df.h[end:-1:2]) + return loadsims("$(fn)/errors") +end + +# @target compute_errors (solutions) -> begin +# df = loadsims("sphere/solutions") +# numrows = size(df,1) + +# uref = df.u[1] +# Xref = df.X[1] +# Γref = geometry(Xref) + +# errs = zeros(numrows) +# s = -Maxwell3D.singlelayer(gamma=1.0) +# qstrat12 = BEAST.NonConformingIntegralOpQStrat(BEAST.DoubleNumSauterQstrat(3, 4, 6, 6, 6, 6)) + +# S11 = assemble(s, Xref, Xref) +# term11 = real(dot(uref, S11*uref)) + +# for i in reverse(2:numrows) +# r = df[i,:] +# (;h, u, X) = r +# @show r, length(u) +# Γ = geometry(X) + +# S12 = assemble(s, Xref, X; quadstrat=[qstrat12]) +# S22 = assemble(s, X, X) +# errs[i] = sqrt(term11 - 2*real(dot(uref, S12*u)) + real(dot(u, S22*u))) +# @show errs[i] +# end + +# return errs +# end + + +@target weak_errors (solutions) -> begin + df = loadsims("sphere/solutions") + + α = df.h[1] / df.h[2] + W1 = reverse(df.u_Snorm) + W2 = Iterators.drop(W1,1) + W3 = Iterators.drop(W1,2) + p = [log(abs((w2-w1)/(w3-w2))) / log(1/α) for (w1,w2,w3) in zip(W1,W2,W3)] +end + + +@make errors +# df = loadsims("solutions") +# error() + +# using LinearAlgebra +# using Plots +# plot(df.h, real.(df.u_Snorm), marker=:.) +# plot(df.h[end:-1:2], p, marker=:.) + +# refsol = df.u_Snorm[1] +# plot(log.(df.h), log.(abs.(df.u_Snorm .- refsol)), marker=:.) +# plot!(log.(df.h), 1.5 * log.(df.h)) + +# fcr, geo = facecurrents(df.u[1], df.X[1]) +# import Plotly +# Plotly.plot(patch(geo, log10.(norm.(fcr)))) + + + From d3747cac8cf11f29255199f706975b07028af473 Mon Sep 17 00:00:00 2001 From: Surya K S Date: Mon, 11 Nov 2024 13:52:40 +0100 Subject: [PATCH 423/528] Type Stable Re-Parameterization of SingleNumQStrat Changed the parameter type from Int to R. This allows access to other quadratures such as the Dunavant Quadrature. --- src/quadrature/quadstrats.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index dba1fc33..581caf98 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -57,8 +57,8 @@ macro defaultquadstrat(dop, body) error("@defaultquadstrat expects a first argument of the for (op,tfs,bfs) or (linform,tfs)") end -struct SingleNumQStrat - quad_rule::Int +struct SingleNumQStrat{R} + quad_rule::R end function quadinfo(op, tfs, bfs; quadstrat=defaultquadstrat(op, tfs, bfs)) From 4d485d145644e8de9acfa1ca244ad00294d510b1 Mon Sep 17 00:00:00 2001 From: azuccott Date: Tue, 12 Nov 2024 15:04:36 +0100 Subject: [PATCH 424/528] Update builddual.jl --- examples/builddual.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/builddual.jl b/examples/builddual.jl index 84308adc..cba47e7c 100644 --- a/examples/builddual.jl +++ b/examples/builddual.jl @@ -3,7 +3,7 @@ using BEAST Faces = meshsphere(1.0, 0.35) Edges = skeleton(Faces,1) - +#ciao faces = barycentric_refinement(Faces) edges = skeleton(faces,1) # verts = skeleton(faces,0) From d234b221cf423a3edab29effd692615bac8769ad Mon Sep 17 00:00:00 2001 From: azuccott Date: Tue, 12 Nov 2024 15:06:23 +0100 Subject: [PATCH 425/528] Update builddual.jl --- examples/builddual.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/builddual.jl b/examples/builddual.jl index cba47e7c..84308adc 100644 --- a/examples/builddual.jl +++ b/examples/builddual.jl @@ -3,7 +3,7 @@ using BEAST Faces = meshsphere(1.0, 0.35) Edges = skeleton(Faces,1) -#ciao + faces = barycentric_refinement(Faces) edges = skeleton(faces,1) # verts = skeleton(faces,0) From f387b8a7f73f7819e905e76aca2fbae0268d2112 Mon Sep 17 00:00:00 2001 From: azuccott Date: Tue, 12 Nov 2024 15:25:49 +0100 Subject: [PATCH 426/528] Update Project.toml --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index a898b184..1b31d623 100644 --- a/Project.toml +++ b/Project.toml @@ -47,11 +47,11 @@ FillArrays = "0.11, 0.12, 0.13, 1" Infiltrator = "1.8.2" IterativeSolvers = "0.9" LiftedMaps = "0.5.1" -LinearMaps = "3.7 - 3.9, 3" +LinearMaps = "3.7 - 3.9, 3.11.2" NestedUnitRanges = "0.2" Requires = "1" SauterSchwab3D = "0.1" -SauterSchwabQuadrature = "2.3.0" +SauterSchwabQuadrature = "2.4.0" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" TestItems = "0.1.1, 1" From 4fa214fea3a5d521ed5d92a294026dd5b56e0d73 Mon Sep 17 00:00:00 2001 From: azuccott Date: Wed, 13 Nov 2024 16:55:58 +0100 Subject: [PATCH 427/528] Update ncrossbdmlocal.jl --- src/bases/local/ncrossbdmlocal.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bases/local/ncrossbdmlocal.jl b/src/bases/local/ncrossbdmlocal.jl index 72bd24e5..8ec3dced 100644 --- a/src/bases/local/ncrossbdmlocal.jl +++ b/src/bases/local/ncrossbdmlocal.jl @@ -19,6 +19,8 @@ function (f::NCrossBDMRefSpace{T})(p) where T (value= n × (v*tv)/j, curl=d),] end +numfunctions(x::NCrossBDMRefSpace, dom::CompScienceMeshes.ReferenceSimplex{2}) = 6 +#= const _vert_perms_ncrossbdm = [ (1,2,3), (2,3,1), @@ -35,7 +37,7 @@ const _dof_perms_ncrossbdm = [ (2,1,6,5,4,3), (6,5,4,3,2,1), ] - +=# function dof_permutation(::NCrossBDMRefSpace, vert_permutation) i = findfirst(==(tuple(vert_permutation...)), _vert_perms_ncrossbdm) return _dof_perms_ncrossbdm[i] From 809e480beecc158035d58a8d5fc8ada5444a6264 Mon Sep 17 00:00:00 2001 From: azuccott Date: Wed, 13 Nov 2024 17:36:20 +0100 Subject: [PATCH 428/528] perms of curl conf spaces not needed --- src/bases/local/bdmlocal.jl | 2 ++ src/bases/local/ncrossbdmlocal.jl | 22 ---------------------- src/bases/local/ndlocal.jl | 23 ----------------------- 3 files changed, 2 insertions(+), 45 deletions(-) diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index 1c83f5f7..605963d9 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -21,6 +21,8 @@ end divergence(ref::BDMRefSpace, sh, el) = [Shape(sh.cellid, 1, sh.coeff/(2*volume(el)))] +numfunctions(x::BDMRefSpace, dom::CompScienceMeshes.ReferenceSimplex{2}) = 6 + const _vert_perms_bdm = [ (1,2,3), (2,3,1), diff --git a/src/bases/local/ncrossbdmlocal.jl b/src/bases/local/ncrossbdmlocal.jl index 8ec3dced..cb332499 100644 --- a/src/bases/local/ncrossbdmlocal.jl +++ b/src/bases/local/ncrossbdmlocal.jl @@ -20,25 +20,3 @@ function (f::NCrossBDMRefSpace{T})(p) where T end numfunctions(x::NCrossBDMRefSpace, dom::CompScienceMeshes.ReferenceSimplex{2}) = 6 -#= -const _vert_perms_ncrossbdm = [ - (1,2,3), - (2,3,1), - (3,1,2), - (2,1,3), - (1,3,2), - (3,2,1), -] -const _dof_perms_ncrossbdm = [ - (1,2,3,4,5,6), - (5,6,1,2,3,4), - (3,4,5,6,1,2), - (4,3,2,1,6,5), - (2,1,6,5,4,3), - (6,5,4,3,2,1), -] -=# -function dof_permutation(::NCrossBDMRefSpace, vert_permutation) - i = findfirst(==(tuple(vert_permutation...)), _vert_perms_ncrossbdm) - return _dof_perms_ncrossbdm[i] -end \ No newline at end of file diff --git a/src/bases/local/ndlocal.jl b/src/bases/local/ndlocal.jl index 93a31471..a4b3bd17 100644 --- a/src/bases/local/ndlocal.jl +++ b/src/bases/local/ndlocal.jl @@ -68,26 +68,3 @@ function restrict(ϕ::NDRefSpace{T}, dom1, dom2) where T return Q end - -const _vert_perms_nd = [ - (1,2,3), - (2,3,1), - (3,1,2), - (2,1,3), - (1,3,2), - (3,2,1), -] -const _dof_perms_nd = [ - (1,2,3), - (3,1,2), - (2,3,1), - (2,1,3), - (1,3,2), - (3,2,1), -] - -function dof_permutation(::NDRefSpace, vert_permutation) - i = findfirst(==(tuple(vert_permutation...)), _vert_perms_nd) - @assert i != nothing - return _dof_perms_nd[i] -end \ No newline at end of file From 105923207aab9140260b9a6188d50e40356932b0 Mon Sep 17 00:00:00 2001 From: azuccott Date: Wed, 13 Nov 2024 17:51:30 +0100 Subject: [PATCH 429/528] space correction --- src/maxwell/mwops.jl | 2 -- src/timedomain/tdintegralop.jl | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 3e8429b4..dbac76e5 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -82,8 +82,6 @@ singularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DSng(op.alpha, op.gamma) - - # function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::BDMRefSpace, i, τ, j, σ, qd, # qs::DoubleNumWiltonSauterQStrat) diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index a0c1bee9..73bcb201 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -328,5 +328,3 @@ function momintegrals!(z, op, g, f, T, τ, σ, ι, qr::WiltonInts84Strat) end end - - From 87e777353bfc4a38c6f67069ac1c28839c8851dc Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 2 Dec 2024 10:44:00 +0100 Subject: [PATCH 430/528] nonconformingtouchrule no longer checks on conformity of local mesh refinement --- Project.toml | 2 +- src/quadrature/nonconformingtouchqrule.jl | 43 ++++++++--------------- test/test_nonconf_quadrules.jl | 42 ++++++++++++++++++++++ test/test_overlapping_edge_remeshing.jl | 32 +++++++++++++++++ test/test_tdmwdbl.jl | 4 +-- 5 files changed, 92 insertions(+), 31 deletions(-) create mode 100644 test/test_nonconf_quadrules.jl create mode 100644 test/test_overlapping_edge_remeshing.jl diff --git a/Project.toml b/Project.toml index 1b31d623..73e322f4 100644 --- a/Project.toml +++ b/Project.toml @@ -55,5 +55,5 @@ SauterSchwabQuadrature = "2.4.0" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" TestItems = "0.1.1, 1" -WiltonInts84 = "0.2.5" +WiltonInts84 = "0.2.8" julia = "1.6" diff --git a/src/quadrature/nonconformingtouchqrule.jl b/src/quadrature/nonconformingtouchqrule.jl index d2da86ab..cbe08aa3 100644 --- a/src/quadrature/nonconformingtouchqrule.jl +++ b/src/quadrature/nonconformingtouchqrule.jl @@ -26,13 +26,13 @@ function momintegrals!(op, isempty(σs) && return # test conformity - for a in τs - for b in σs - if !_test_conformity(a, b) - @infiltrate - end - end - end + # for a in τs + # for b in σs + # if !_test_conformity(a, b) + # @infiltrate + # end + # end + # end # volume(σ) ≈ sum(volume.(σs)) || @infiltrate @@ -144,13 +144,8 @@ function _conforming_refinement_touching_triangles(τ,σ,i,j) μ = faces(σ)[j] ρ = CompScienceMeshes.intersection(λ,μ)[1] - # τ_verts = [τ[mod1(i+2,3)], τ[mod1(i,3)], τ[mod1(i+1,3)]] - # σ_verts = [σ[mod1(j+2,3)], σ[mod1(j,3)], σ[mod1(j+1,3)]] - # τ_verts = [v for v in τ.vertices] - # σ_verts = [v for v in σ.vertices] τ_verts = Array(τ.vertices) σ_verts = Array(σ.vertices) - # @show typeof(τ_verts) T = coordtype(τ) P = eltype(τ.vertices) @@ -187,28 +182,20 @@ function _conforming_refinement_touching_triangles(τ,σ,i,j) insert!(σ_verts, p, V[2]) insert!(σ_verts, p, V[1]) - # println(τ_verts) - # println(σ_verts) - τ_charts = [ simplex(τ_verts[mod1(new_i,5)], τ_verts[mod1(new_i+s,5)], τ_verts[mod1(new_i+s+1,5)]) for s in 1:3 ] σ_charts = [ simplex(σ_verts[mod1(new_j,5)], σ_verts[mod1(new_j+s,5)], σ_verts[mod1(new_j+s+1,5)]) for s in 1:3 ] - # σ_charts = [ simplex(σ_verts[1], σ_verts[i], σ_verts[i+1]) for i in 2:length(σ_verts)-1 ] - - h = volume(τ) - τ_charts = [ch for ch in τ_charts if volume(ch) .> 1e6 * eps(T) * h] - σ_charts = [ch for ch in σ_charts if volume(ch) .> 1e6 * eps(T) * h] + τ_charts = [ch for ch in τ_charts if volume(ch) .> 1e6 * eps(T) * volume(τ)] + σ_charts = [ch for ch in σ_charts if volume(ch) .> 1e6 * eps(T) * volume(σ)] τ_charts = [ch for ch in τ_charts if volume(ch) .> 1e6 * eps(T)] σ_charts = [ch for ch in σ_charts if volume(ch) .> 1e6 * eps(T)] - signs = Int.(sign.(dot.(normal.(τ_charts),Ref(normal(τ))))) - τ_charts = flip_normal.(τ_charts,signs) - signs = Int.(sign.(dot.(normal.(σ_charts),Ref(normal(σ))))) - σ_charts = flip_normal.(σ_charts,signs) - - - # τ_charts = τ_charts[volume.(τ_charts) .> 1e3 * eps(T) * h] - # σ_charts = τ_charts[volume.(σ_charts) .> 1e3 * eps(T) * h] + # signs = Int.(sign.(dot.(normal.(τ_charts),Ref(normal(τ))))) + # @assert all(signs .== 1) + # τ_charts = flip_normal.(τ_charts,signs) + # signs = Int.(sign.(dot.(normal.(σ_charts),Ref(normal(σ))))) + # @assert all(signs .== 1) + # σ_charts = flip_normal.(σ_charts,signs) return τ_charts, σ_charts end diff --git a/test/test_nonconf_quadrules.jl b/test/test_nonconf_quadrules.jl new file mode 100644 index 00000000..01776b2a --- /dev/null +++ b/test/test_nonconf_quadrules.jl @@ -0,0 +1,42 @@ +@testitem "norm of constant field" begin + using CompScienceMeshes + using LinearAlgebra + + h1 = 0.2 + h2 = 0.176 + m1 = meshcuboid(1.0, 1.0, 1.0, h1) + m2 = meshcuboid(1.0, 1.0, 1.0, h2) + + E = Maxwell3D.planewave(;direction=point(0,0,1), polarization=point(1,0,0), wavenumber=0.0) + e = n × E + + cstrat = BEAST.DoubleNumWiltonSauterQStrat{Int64, Int64}(2, 3, 6, 7, 5, 5, 4, 3) + # cstrat = BEAST.DoubleNumWiltonSauterQStrat{Int64, Int64}(12, 13, 12, 13, 7, 7, 7, 7) + nstrat = BEAST.NonConformingIntegralOpQStrat(cstrat) + + Id = BEAST.Identity() + S = Maxwell3D.singlelayer(alpha=1.0, beta=1.0, gamma=1.0) + + X1 = raviartthomas(m1) + X2 = raviartthomas(m2) + + G11 = assemble(Id, X1, X1) + G22 = assemble(Id, X2, X2) + + u1 = G11 \ assemble(e, X1) + u2 = G22 \ assemble(e, X2) + + S11 = assemble(S, X1, X1; quadstrat=cstrat) + S12 = assemble(S, X1, X2; quadstrat=nstrat) + S22 = assemble(S, X2, X2; quadstrat=cstrat) + + term1 = real(u1' * S11 * u1) + term2 = real(u1' * S12 * u2) + term3 = real(u2' * S22 * u2) + + norm1 = sqrt(term1) + norm3 = sqrt(term2) + testval = norm(term1 - 2*term2 + term3) + @show testval / norm1 + @test testval / norm1 < 3e-2 +end \ No newline at end of file diff --git a/test/test_overlapping_edge_remeshing.jl b/test/test_overlapping_edge_remeshing.jl new file mode 100644 index 00000000..8384ac7f --- /dev/null +++ b/test/test_overlapping_edge_remeshing.jl @@ -0,0 +1,32 @@ +@testitem "conformity" begin + +using CompScienceMeshes + τ = simplex( + point(0.0624999999994547, 0.6856034446626162, 0.0), + point(0.06944444444542179, 0.6735753140519203, 0.0), + point(0.05555730739992443, 0.6735783483387338, 0.0)) + + σ = simplex( + point(0.07017543859225508, 0.6988976942759024, 0.0), + point(0.06249997795123046, 0.6856034064739717, 0.0), + point(0.06140343344926745, 0.6837043913625904, 0.0)) + + for (k,λ) in pairs(faces(τ)) + for (l,μ) in pairs(faces(σ)) + if CompScienceMeshes.overlap(λ, μ) + global i = k + global j = l + end + end end + + @test i == 2 + @test j == 3 + + τs, σs = BEAST._conforming_refinement_touching_triangles(τ,σ,i,j) + + # Ideally both should be true but the round-off causes CompScienceMeshes.overlap to + # incorrectly report. + @test BEAST._test_conformity(τs[1], σs[1])==true + @test BEAST._test_conformity(τs[2], σs[1])==false + +end \ No newline at end of file diff --git a/test/test_tdmwdbl.jl b/test/test_tdmwdbl.jl index 81fb700f..60d6c453 100644 --- a/test/test_tdmwdbl.jl +++ b/test/test_tdmwdbl.jl @@ -66,7 +66,7 @@ qrl = BEAST.quadrule(K, refspace(X), refspace(Y), refspace(T2), 1, test_els[1], 1, trial_els[1], 1, time_els[1], qdt, qs) z = zeros(Float64, 3, 3, 10) BEAST.innerintegrals!(z, K, x, refspace(X), refspace(Y), refspace(T2), test_els[1], trial_els[1], (0.0, 20.0), qrl, w) -@test_broken !any(isnan.(z)) +@test !any(isnan.(z)) verts = trial_els[1].vertices import WiltonInts84 @@ -85,4 +85,4 @@ h = 0.0 using StaticArrays m = SVector(-0.7071067811865474, 0.7071067811865478, -0.0) iG, vG = WiltonInts84.segintsg(a, b, p, h, m, Val{2}) -@test_broken !any(isnan.(iG)) +@test !any(isnan.(iG)) From 0f03d18ce354b73da84c9f2a99a1a88a78d27cee Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 4 Dec 2024 11:51:55 +0100 Subject: [PATCH 431/528] add discontinuous pairing example --- examples/discontinuous_pairing.jl | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 examples/discontinuous_pairing.jl diff --git a/examples/discontinuous_pairing.jl b/examples/discontinuous_pairing.jl new file mode 100644 index 00000000..f27e3883 --- /dev/null +++ b/examples/discontinuous_pairing.jl @@ -0,0 +1,41 @@ +# compute the continuity and infsup constants for various continuous and discontinuous pairings + +using CompScienceMeshes +using BEAST + +using Makeitso +using BakerStreet + +h = 0.1 +Γ = meshcuboid(1.0, 1.0, 1.0, h) + +id = BEAST.Identity() +nx = BEAST.NCross() +s = Maxwell3D.singlelayer(gamma=1.0) +t = Maxwell3D.singlelayer(wavenumber=1.0) + +X = raviartthomas(Γ) +Y = buffachristiansen(Γ) + +A = assemble(id, Y, X) +B = assemble(nx, Y, X) +C = assemble(t, X, X) + +SXX = Symmetric(-assemble(s, X, X)) +SYY = Symmetric(-assemble(s, Y, Y)) + +using LinearAlgebra +HX = sqrt(SXX) +HY = sqrt(SYY) + +QX = inv(HX) +QY = inv(HY) + +svdA = svd(QY*A*QX) +svdB = svd(QY*B*QX) +svdC = svd(QX*C*QX) + +using Plots +plot(svdA.S) +plot!(svdB.S) +plot!(svdC.S) From f7bfc758a2e38fad548f532e108dbdeb0981fc01 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Fri, 6 Dec 2024 12:10:30 +0100 Subject: [PATCH 432/528] Adding some higher order functionality and single thread support for timedomain --- src/bases/global/gwpglobal.jl | 2 +- src/bases/local/laglocal.jl | 5 +++- src/helmholtz3d/timedomain/tdhh3dexc.jl | 5 +++- src/identityop.jl | 32 ++++++++++++++++++++++++- src/solvers/itsolver.jl | 2 +- src/timedomain/tdintegralop.jl | 2 +- 6 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/bases/global/gwpglobal.jl b/src/bases/global/gwpglobal.jl index 3270e4da..f302632f 100644 --- a/src/bases/global/gwpglobal.jl +++ b/src/bases/global/gwpglobal.jl @@ -147,7 +147,7 @@ function gwpcurl(mesh, edges=nothing; order) _addshapes!(fns, cell, gids, lids, inv(α')) end - order < 2 && continue + order < 1 && continue face_ch = chart(mesh, cell) gids = Ne + (cell-1)*nf .+ (1:nf) lids = localindices(localspace, refdom, Val{2}, 1) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index d48b8865..31988ad3 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -9,8 +9,11 @@ numfunctions(s::LagrangeRefSpace{T,1,3}, ch::CompScienceMeshes.ReferenceSimplex{ numfunctions(s::LagrangeRefSpace{T,2,3}, ch::CompScienceMeshes.ReferenceSimplex{2}) where {T} = 6 numfunctions(s::LagrangeRefSpace{T,Dg}, ch::CompScienceMeshes.ReferenceSimplex{D}) where {T,Dg,D} = binomial(D+Dg,Dg) +#valuetype(ref::LagrangeRefSpace{T}, charttype) where {T} = +# SVector{numfunctions(ref), Tuple{T,T}} + valuetype(ref::LagrangeRefSpace{T}, charttype) where {T} = - SVector{numfunctions(ref), Tuple{T,T}} + SVector{1, T} # Evaluate constant lagrange elements on anything diff --git a/src/helmholtz3d/timedomain/tdhh3dexc.jl b/src/helmholtz3d/timedomain/tdhh3dexc.jl index 671de0f0..c3413610 100644 --- a/src/helmholtz3d/timedomain/tdhh3dexc.jl +++ b/src/helmholtz3d/timedomain/tdhh3dexc.jl @@ -16,13 +16,16 @@ scalartype(p::PlaneWaveHH3DTD) = eltype(p.amplitude) function(f::PlaneWaveHH3DTD)(r,t) + r = cartesian(r) + t = cartesian(t)[1] + k = f.direction u = sum(k[i]*r[i] for i in 1:length(k)) #u = dot(f.direction, r) a = f.amplitude c = f.speed_of_light h = f.signature - a * h(c*cartesian(t)[1] - u) + a * h(c*t - u) end diff --git a/src/identityop.jl b/src/identityop.jl index c55d1dce..df8e0f49 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -12,6 +12,36 @@ kernelvals(op::NCross, mp) = nothing integrand(op::NCross, kernel, x, g, f) = dot(g[1], normal(x) × f[1]) scalartype(op::NCross) = Union{} +struct Curl <: LocalOperator +end + +kernelvals(biop::Curl, x) = nothing +function integrand(op::Curl, kernel, x, g, f) + dot(f.curl, g.value) + end +scalartype(op::Curl) = Union{} + + +defaultquadstrat(::LocalOperator, ::GWPDivRefSpace{<:Real,D1},::LagrangeRefSpace{T,D2,D3}) where {T,D1,D2,D3} = SingleNumQStrat(7) +function quaddata(op::LocalOperator, g::GWPDivRefSpace, f::LagrangeRefSpace{T,D2,D1} where {T,D1,D2} , tels, bels, qs::SingleNumQStrat) + + u, w = trgauss(qs.quad_rule) + qd = [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] + A = _alloc_workspace(qd, g, f, tels, bels) + + return qd, A +end + +defaultquadstrat(::LocalOperator, ::RTRefSpace, ::LagrangeRefSpace{T,D2,D1}) where {T,D1,D2} = SingleNumQStrat(6) +function quaddata(op::LocalOperator, g::RTRefSpace, f::LagrangeRefSpace{T,D2,D1} where {T,D1,D2} , tels, bels, qs::SingleNumQStrat) + + u, w = trgauss(qs.quad_rule) + qd = [(w[i],SVector(u[1,i],u[2,i])) for i in 1:length(w)] + A = _alloc_workspace(qd, g, f, tels, bels) + + return qd, A +end + function _alloc_workspace(qd, g, f, tels, bels) q = qd[1] τ = tels[1] @@ -64,7 +94,7 @@ function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,2} where {T,Deg}, return qd, A end -defaultquadstrat(::LocalOperator, ::LagrangeRefSpace{T,D1,3}, ::LagrangeRefSpace{T,D2,3}) where {T,D1,D2} = SingleNumQStrat(6) +defaultquadstrat(::LocalOperator, ::LagrangeRefSpace{T,D1,3}, ::LagrangeRefSpace{T,D2,3}) where {T,D1,D2} = SingleNumQStrat(9) function quaddata(op::LocalOperator, g::LagrangeRefSpace{T,Deg,3} where {T,Deg}, f::LagrangeRefSpace, tels::Vector, bels::Vector, qs::SingleNumQStrat) diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index ea49e8d5..1334b58b 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -91,7 +91,7 @@ function LinearAlgebra.mul!(y::AbstractVecOrMat, solver::GMRESSolver, x::Abstrac return y end -LinearAlgebra.adjoint(A::GMRESSolver) = GMRESSolver(adjoint(A.linear_operator), A.maxiter, A.restart, A.abstol, A.reltol, A.verbose) +LinearAlgebra.adjoint(A::GMRESSolver) = GMRESSolver(adjoint(A.linear_operator); maxiter=A.maxiter, restart=A.restart, abstol=A.abstol, reltol=A.reltol, verbose=A.verbose, left_preconditioner=A.left_preconditioner) LinearAlgebra.transpose(A::GMRESSolver) = GMRESSolver(transpose(A.linear_operator), A.maxiter, A.restart, A.abstol, A.reltol, A.verbose) diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index e1e6a978..d4ba8b93 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -169,7 +169,7 @@ end function assemble!(op::RetardedPotential, testST::Space, trialST::Space, store, - threading::Type{Threading{:single}}=Threading{:single}; quadstrat=defaultquadstrat(op, testST, trialST)) + threading::Type{Threading{:single}}; quadstrat=defaultquadstrat(op, testST, trialST)) assemble_chunk!(op, testST, trialST, store; quadstrat=quadstrat) end From 38e0207c402131ed1cd6ba6dfbbc1bc813258c4e Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 20 Dec 2024 10:41:27 +0100 Subject: [PATCH 433/528] discontinuous_pairing example --- examples/discontinuous_pairing.jl | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/examples/discontinuous_pairing.jl b/examples/discontinuous_pairing.jl index f27e3883..fb7be053 100644 --- a/examples/discontinuous_pairing.jl +++ b/examples/discontinuous_pairing.jl @@ -39,3 +39,43 @@ using Plots plot(svdA.S) plot!(svdB.S) plot!(svdC.S) + +E = Maxwell3D.planewave( + direction=point(0,0,1), + polarization=point(1,0,0), + wavenumber=1.0) + +e = (n × E) × n + +struct SingField{T} <: BEAST.Functional + a::T + b::T +end + +function (f::SingField)(p) + x = cartesian(p) + x[3] ≈ 1.0 || return point(0,0,0) + return sqrt(f.b - x[2]) * sqrt(x[2] - f.a) / sqrt(f.b-x[1]) / sqrt(x[1]-f.a) * point(0,1,0) +end + +function BEAST.integrand(f::SingField, gx, ϕx) + return dot(gx[1], ϕx) +end + +e = SingField(0.0, 1.0) + +b = assemble(e, X) +G = assemble(id, X, X) +u = G \ b + +import Plotly +fcr, geo = facecurrents(u, X) +Plotly.plot( + [ + # patch(geo, norm.(fcr), opacity=0.5), + CompScienceMeshes.cones(geo, real.(fcr), sizeref=10.0), + ]) + +q = adjoint(svdA.U) * HY * b +plot(log10.(abs.(q))) +plot!(log10.(abs.(svdA.S))) \ No newline at end of file From 263926d826fdff3475248042411ba791907a611e Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 20 Dec 2024 11:07:43 +0100 Subject: [PATCH 434/528] some tests got unborken by update in WiltonInts --- test/test_tdmwdbl.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_tdmwdbl.jl b/test/test_tdmwdbl.jl index 81fb700f..60d6c453 100644 --- a/test/test_tdmwdbl.jl +++ b/test/test_tdmwdbl.jl @@ -66,7 +66,7 @@ qrl = BEAST.quadrule(K, refspace(X), refspace(Y), refspace(T2), 1, test_els[1], 1, trial_els[1], 1, time_els[1], qdt, qs) z = zeros(Float64, 3, 3, 10) BEAST.innerintegrals!(z, K, x, refspace(X), refspace(Y), refspace(T2), test_els[1], trial_els[1], (0.0, 20.0), qrl, w) -@test_broken !any(isnan.(z)) +@test !any(isnan.(z)) verts = trial_els[1].vertices import WiltonInts84 @@ -85,4 +85,4 @@ h = 0.0 using StaticArrays m = SVector(-0.7071067811865474, 0.7071067811865478, -0.0) iG, vG = WiltonInts84.segintsg(a, b, p, h, m, Val{2}) -@test_broken !any(isnan.(iG)) +@test !any(isnan.(iG)) From 0f4fa419ac787ba3e1bd03a98c7bdf60d8fd8ea0 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 20 Dec 2024 11:40:23 +0100 Subject: [PATCH 435/528] unbreak tests in tdmwdbl --- test/test_tdmwdbl.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_tdmwdbl.jl b/test/test_tdmwdbl.jl index 81fb700f..60d6c453 100644 --- a/test/test_tdmwdbl.jl +++ b/test/test_tdmwdbl.jl @@ -66,7 +66,7 @@ qrl = BEAST.quadrule(K, refspace(X), refspace(Y), refspace(T2), 1, test_els[1], 1, trial_els[1], 1, time_els[1], qdt, qs) z = zeros(Float64, 3, 3, 10) BEAST.innerintegrals!(z, K, x, refspace(X), refspace(Y), refspace(T2), test_els[1], trial_els[1], (0.0, 20.0), qrl, w) -@test_broken !any(isnan.(z)) +@test !any(isnan.(z)) verts = trial_els[1].vertices import WiltonInts84 @@ -85,4 +85,4 @@ h = 0.0 using StaticArrays m = SVector(-0.7071067811865474, 0.7071067811865478, -0.0) iG, vG = WiltonInts84.segintsg(a, b, p, h, m, Val{2}) -@test_broken !any(isnan.(iG)) +@test !any(isnan.(iG)) From d3969923c74990c2abb504fe995c19489da9ae8d Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 15 Jan 2025 15:01:54 +0100 Subject: [PATCH 436/528] using a given Matrix as discretisation now works for any AbstractMatrix --- examples/calderon.jl | 41 ++++++++++++++++++++++++++--------- src/bases/global/gwpglobal.jl | 2 +- src/operator.jl | 18 +++++++-------- src/utils/variational.jl | 5 +++++ 4 files changed, 46 insertions(+), 20 deletions(-) diff --git a/examples/calderon.jl b/examples/calderon.jl index 4e2d6084..5e2631c2 100644 --- a/examples/calderon.jl +++ b/examples/calderon.jl @@ -1,24 +1,45 @@ -# If you want a scatter plot of the spectra, define `plotresults = true` -# prior to running this script. - using CompScienceMeshes, BEAST using LinearAlgebra Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -println("Mesh with $(numvertices(Γ)) vertices and $(numcells(Γ)) cells.") X = raviartthomas(Γ) -Y = BEAST.buffachristiansen2(Γ) +Y = BEAST.buffachristiansen(Γ) -κ = ω = 1.0; γ = κ*im -T = MWSingleLayer3D(γ) +κ = 1.0; +T = Maxwell3D.singlelayer(wavenumber=κ) N = NCross() -Txx = assemble(T,X,X); println("primal discretisation assembled.") +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +e = (n × E) × n + +b = assemble(e, X) + +A = assemble(T,X,X); println("primal discretisation assembled.") + Tyy = assemble(T,Y,Y); println("dual discretisation assembled.") Nxy = Matrix(assemble(N,X,Y)); println("duality form assembled.") - iNxy = inv(Nxy); println("duality form inverted.") -A = iNxy' * Tyy * iNxy * Txx +P = iNxy' * Tyy * iNxy + +iT1 = BEAST.GMRESSolver(A; restart=1_500, abstol=1e-8, reltol=1e-8, maxiter=1_500) +iT2 = BEAST.GMRESSolver(A; restart=1_500, abstol=1e-8, reltol=1e-8, maxiter=1_500, left_preconditioner=P) + +u1, ch1 = BEAST.solve(iT1, bx) +u2, ch2 = BEAST.solve(iT2, bx) + +using Plots +Plots.plot(title="log10 residual error vs iteration count") +Plots.plot!(log10.(ch1[:resnorm]), label="A=b", l=2) +Plots.plot!(log10.(ch2[:resnorm]), label="P*A=P*b", l=2) + +ss1 = svdvals(Txx) +ss2 = svdvals(P*Txx) + +Plots.plot(title="singular value spectrum") +Plots.plot!(ss1, label="A", l=2) +Plots.plot!(ss2, label="P*A", l=2) + +@show norm(u1-u2) / norm(u1) @show cond(Txx) @show cond(Tyy) diff --git a/src/bases/global/gwpglobal.jl b/src/bases/global/gwpglobal.jl index 3270e4da..f302632f 100644 --- a/src/bases/global/gwpglobal.jl +++ b/src/bases/global/gwpglobal.jl @@ -147,7 +147,7 @@ function gwpcurl(mesh, edges=nothing; order) _addshapes!(fns, cell, gids, lids, inv(α')) end - order < 2 && continue + order < 1 && continue face_ch = chart(mesh, cell) gids = Ne + (cell-1)*nf .+ (1:nf) lids = localindices(localspace, refdom, Val{2}, 1) diff --git a/src/operator.jl b/src/operator.jl index b54b2a74..22dbb102 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -89,7 +89,7 @@ function assemble(operator::AbstractOperator, test_functions, trial_functions; end -function assemble(A::Matrix, testfns, trialfns) +function assemble(A::AbstractMatrix, testfns, trialfns) @assert numfunctions(testfns) == size(A,1) @assert numfunctions(trialfns) == size(A,2) return A @@ -137,15 +137,15 @@ function allocatestorage(operator::AbstractOperator, test_functions, trial_funct end -function allocatestorage(operator::LinearCombinationOfOperators, - test_functions::SpaceTimeBasis, trial_functions::SpaceTimeBasis, - storage_policy::Type{Val{:bandedstorage}}, - long_delays_policy::Type{LongDelays{:ignore}}) +# function allocatestorage(operator::LinearCombinationOfOperators, +# test_functions::SpaceTimeBasis, trial_functions::SpaceTimeBasis, +# storage_policy::Type{Val{:bandedstorage}}, +# long_delays_policy::Type{LongDelays{:ignore}}) - # This works when are terms in the LC can share storage - return allocatestorage(operator.ops[end], test_functions, trial_functions, - storage_policy, long_delays_policy) -end +# # This works when are terms in the LC can share storage +# return allocatestorage(operator.ops[end], test_functions, trial_functions, +# storage_policy, long_delays_policy) +# end function allocatestorage(operator::LinearCombinationOfOperators, diff --git a/src/utils/variational.jl b/src/utils/variational.jl index aa0a5158..7bb9930a 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -286,6 +286,11 @@ function getindex(A, v::HilbertVector, u::HilbertVector) BilForm(v.space, u.space, terms) end +function getindex(A::AbstractMatrix, v::HilbertVector, u::HilbertVector) + terms = [ BilTerm(v.idx, u.idx, v.opstack, u.opstack, 1, A) ] + BilForm(v.space, u.space, terms) +end + function getindex(A::BEAST.BlockDiagonalOperator, V::Vector{HilbertVector}, U::Vector{HilbertVector}) op = A.op From ce20e02361678773a0e6959bc14be9f763ae6f5b Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 16 Jan 2025 14:43:37 +0100 Subject: [PATCH 437/528] Lowest supported julia is current LTS 1.10 --- .github/workflows/CI.yml | 2 +- Project.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 0331687a..f3787468 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: version: - - '1.6' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'. + - '1.10' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'. - '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia. os: - ubuntu-latest diff --git a/Project.toml b/Project.toml index 73e322f4..cf1fbc32 100644 --- a/Project.toml +++ b/Project.toml @@ -56,4 +56,4 @@ SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" TestItems = "0.1.1, 1" WiltonInts84 = "0.2.8" -julia = "1.6" +julia = "1.10" From 84d7e91ad413cafe009b4e5ff4e81e3c904e34f0 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Thu, 6 Feb 2025 17:45:59 +0100 Subject: [PATCH 438/528] Fix consistency in 2nd order Lagrange basis --- src/bases/lagrange.jl | 17 +++++++++++++++++ src/bases/local/laglocal.jl | 38 +++++++++++++++++++------------------ 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index 7fea3c7d..c0bef513 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -1085,6 +1085,22 @@ function localindices(dof::_LagrangeGlobalFaceDoFs, chart::CompScienceMeshes.Sim return [] end +function localindices(dof::_LagrangeGlobalNodesDoFs, chart::CompScienceMeshes.Simplex, + localspace::LagrangeRefSpace{<:Real,1}, i) + return [i] +end + +function localindices(dof::_LagrangeGlobalEdgeDoFs, chart::CompScienceMeshes.Simplex, + localspace::LagrangeRefSpace{<:Real,1}, i) + return [] +end + +function localindices(dof::_LagrangeGlobalFaceDoFs, chart::CompScienceMeshes.Simplex, + localspace::LagrangeRefSpace{<:Real,1}, i) + return [] +end + + function lagrangec0(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}; order) @@ -1135,6 +1151,7 @@ function lagrangec0(mesh::CompScienceMeshes.AbstractMesh{<:Any,3}; order) end end + order < 2 && continue E = R12[nzrange(C12,cell)] I = V12[nzrange(C12,cell)] for (i,e) in zip(I, E) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 31988ad3..b8216152 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -201,14 +201,15 @@ function (f::LagrangeRefSpace{T,2,3})(t) where T j = jacobian(t) p = t.patch + σ = sign(dot(normal(t), cross(p[1]-p[3],p[2]-p[3]))) SVector( (value=u*(2*u-1), curl=σ*(p[3]-p[2])*(4u-1)/j), + (value=4*u*v, curl=4*σ*(u*(p[1]-p[3])+v*(p[3]-p[2]))/j), (value=v*(2*v-1), curl=σ*(p[1]-p[3])*(4v-1)/j), - (value=w*(2*w-1), curl=σ*(p[2]-p[1])*(4w-1)/j), - (value=4*v*w, curl=4*σ*(w*(p[1]-p[3])+v*(p[2]-p[1]))/j), (value=4*w*u, curl=4*σ*(w*(p[3]-p[2])+u*(p[2]-p[1]))/j), - (value=4*u*v, curl=4*σ*(u*(p[1]-p[3])+v*(p[3]-p[2]))/j), + (value=4*v*w, curl=4*σ*(w*(p[1]-p[3])+v*(p[2]-p[1]))/j), + (value=w*(2*w-1), curl=σ*(p[2]-p[1])*(4w-1)/j), ) end @@ -451,22 +452,23 @@ end function interpolate(fields, interpolant::LagrangeRefSpace{T,Degree,3}, chart) where {T,Degree} dim = binomial(2+Degree, Degree) - - I = 0:Degree - s = range(0,1,length=Degree+1) - Is = zip(I,s) - idx = 1 vals = Vector{Vector{T}}() - for (i,ui) in Is - for (j,vj) in Is - for (k,wk) in Is - i + j + k == Degree || continue - @assert ui + vj + wk ≈ 1 - p = neighborhood(chart, (ui,vj)) - push!(vals, fields(p)) - idx += 1 - end end end - + if Degree > 0 + I = 0:Degree + s = range(0,1,length=Degree+1) + Is = zip(I,s) + for (i,ui) in Is + for (j,vj) in Is + for (k,wk) in Is + i + j + k == Degree || continue + @assert ui + vj + wk ≈ 1 + p = neighborhood(chart, (ui,vj)) + push!(vals, fields(p)) + end end end + else + p = center(chart) + push!(vals, fields(p)) + end # Q = hcat(vals...) Q = Matrix{T}(undef, length(vals[1]), length(vals)) for i in eachindex(vals) From 218855ab1822b840c0c6271eefd0e2336928c910 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Thu, 6 Feb 2025 17:47:38 +0100 Subject: [PATCH 439/528] Add specialized integral operator for doublelayer loop-loop --- src/maxwell/mwops.jl | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index dbac76e5..e228e4fd 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -81,6 +81,16 @@ regularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DReg(op.alpha, op.gamma) singularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DSng(op.alpha, op.gamma) +struct MWDoubleLayer3DLoop{T,K} <: MaxwellOperator3DReg{T,K} + alpha::T + gamma::K +end + + +MWDoubleLayer3DLoop(gamma) = MWDoubleLayer3DLoop(1.0, gamma) # For legacy purposes + + + # function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::BDMRefSpace, i, τ, j, σ, qd, # qs::DoubleNumWiltonSauterQStrat) @@ -227,6 +237,23 @@ function (igd::Integrand{<:MWDoubleLayer3DReg})(x,y,f,g) return _krondot(fvalue, G) end + +function (igd::Integrand{<:MWDoubleLayer3DLoop})(x,y,f,g) + + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + γR = γ*R + iR = 1/R + gradgreen = -im*exp(-γR)*( im*γR + im*expm1(γR)) * iR^3/(4pi) * r + + fvalue = getvalue(f) + gvalue = getvalue(g) + G = cross.(Ref(gradgreen), gvalue) + return _krondot(fvalue, G) +end + ################################################################################ # # Handling of operator parameters (Helmholtz and Maxwell) From 58638d96346409b7a3fee1e4bef6893831eb1171 Mon Sep 17 00:00:00 2001 From: PaulOlyslager Date: Fri, 7 Feb 2025 09:57:37 +0100 Subject: [PATCH 440/528] fixing interpolation for first order --- src/bases/local/gwplocal.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bases/local/gwplocal.jl b/src/bases/local/gwplocal.jl index 3ecc0073..7ac06a14 100644 --- a/src/bases/local/gwplocal.jl +++ b/src/bases/local/gwplocal.jl @@ -195,7 +195,7 @@ function interpolate(fields, interpolant::GWPCurlRefSpace{T,Degree}, chart) wher end Q = hcat(Q1,Q2,Q3) - if d > 1 + if d >= 1 S = ((i,j,d+2-i-j) for i in 1:d+1 for j in 1:d+1 if d+2-i-j > 0) for (i,j,k) in S p_chart = neighborhood(chart, (s[i+1],s[j+1])) From 615fba7da16b47ebba7573d6f38bdb4faa6fdec3 Mon Sep 17 00:00:00 2001 From: Bernd Hofmann Date: Thu, 7 Nov 2024 17:31:33 +0100 Subject: [PATCH 441/528] new structure for docs --- .github/workflows/CI.yml | 24 +- .github/workflows/Documentation.yml | 29 ++ .gitignore | 4 +- README.md | 12 +- docs/Manifest.toml | 482 +++++++++++++--------- docs/Project.toml | 3 + docs/make.jl | 55 ++- docs/mkdocs.yml | 18 - docs/src/apiref.md | 8 + docs/src/assemble.md | 152 ------- docs/src/assets/logo-dark.svg | 474 ++++++++++++++++++++++ docs/src/assets/logo.svg | 474 ++++++++++++++++++++++ docs/src/assets/logo_README.svg | 486 +++++++++++++++++++++++ docs/src/assets/logo_README_white.svg | 486 +++++++++++++++++++++++ docs/src/assets/postproc.md | 25 -- docs/src/bases/buffachristiansen.md | 4 + docs/src/bases/raviartthomas.md | 6 + docs/src/contributing.md | 60 +++ docs/src/index.md | 104 ++--- docs/src/manual/examples.md | 20 + docs/src/manual/usage.md | 76 ++++ docs/src/operators/helmholtz.md | 0 docs/src/operators/identity.md | 0 docs/src/operators/maxwelldoublelayer.md | 0 docs/src/operators/maxwellsinglelayer.md | 26 ++ docs/src/operators/overview.md | 9 + docs/src/operators/planewave.md | 4 + docs/src/quadstrat.md | 81 ---- docs/src/references.md | 5 + docs/src/refs.bib | 21 + docs/src/tdefie.md | 96 ----- docs/src/tutorial.md | 74 ---- src/maxwell/maxwell.jl | 4 +- src/operator.jl | 14 +- 34 files changed, 2608 insertions(+), 728 deletions(-) create mode 100644 .github/workflows/Documentation.yml delete mode 100644 docs/mkdocs.yml create mode 100644 docs/src/apiref.md delete mode 100644 docs/src/assemble.md create mode 100644 docs/src/assets/logo-dark.svg create mode 100644 docs/src/assets/logo.svg create mode 100644 docs/src/assets/logo_README.svg create mode 100644 docs/src/assets/logo_README_white.svg delete mode 100644 docs/src/assets/postproc.md create mode 100644 docs/src/bases/buffachristiansen.md create mode 100644 docs/src/bases/raviartthomas.md create mode 100644 docs/src/contributing.md create mode 100644 docs/src/manual/examples.md create mode 100644 docs/src/manual/usage.md create mode 100644 docs/src/operators/helmholtz.md create mode 100644 docs/src/operators/identity.md create mode 100644 docs/src/operators/maxwelldoublelayer.md create mode 100644 docs/src/operators/maxwellsinglelayer.md create mode 100644 docs/src/operators/overview.md create mode 100644 docs/src/operators/planewave.md delete mode 100644 docs/src/quadstrat.md create mode 100644 docs/src/references.md create mode 100644 docs/src/refs.bib delete mode 100644 docs/src/tdefie.md delete mode 100644 docs/src/tutorial.md diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index f3787468..3de4984d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -42,26 +42,4 @@ jobs: - uses: julia-actions/julia-processcoverage@v1 - uses: codecov/codecov-action@v1 with: - file: lcov.info - docs: - name: Documentation - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: julia-actions/setup-julia@v1 - with: - version: '1' - - run: | - julia --project=docs -e ' - using Pkg - Pkg.develop(PackageSpec(path=pwd())) - Pkg.instantiate()' - - run: | - julia --project=docs -e ' - using Documenter: doctest - using BEAST - doctest(BEAST)' # change MYPACKAGE to the name of your package - - run: julia --project=docs docs/make.jl - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} \ No newline at end of file + file: lcov.info \ No newline at end of file diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml new file mode 100644 index 00000000..fda05bab --- /dev/null +++ b/.github/workflows/Documentation.yml @@ -0,0 +1,29 @@ +name: Documentation + +on: + push: + branches: + - master # update to match your development branch (master, main, dev, trunk, ...) + tags: '*' + pull_request: + branches: + - master + +jobs: + build: + permissions: + contents: write + statuses: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v1 + with: + version: '1' + - name: Install dependencies + run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' + - name: Build and deploy + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # If authenticating with GitHub Actions token + DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # If authenticating with SSH deploy key + run: julia --project=docs/ docs/make.jl \ No newline at end of file diff --git a/.gitignore b/.gitignore index a3268a4b..8a679b85 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ docs/site/ temp/ dev/ profile/ -/Manifest.toml /envs/ +/Manifest.toml +/test/Manifest.toml +/docs/Manifest.toml /data/ \ No newline at end of file diff --git a/README.md b/README.md index 79cbc5e2..7c5f2910 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,22 @@ -# BEAST -Boundary Element Analysis and Simulation Toolkit + + + + + + + [![CI](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml) [![codecov.io](http://codecov.io/github/krcools/BEAST.jl/coverage.svg?branch=master)](http://codecov.io/github/krcools/BEAST.jl?branch=master) [![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://krcools.github.io/BEAST.jl/dev/) [![DOI](https://zenodo.org/badge/87720391.svg)](https://zenodo.org/badge/latestdoi/87720391) + ## Introduction +Boundary Element Analysis and Simulation Toolkit + This package contains common basis functions and assembly routines for the implementation of boundary element methods. Examples are included for the 2D and 3D Helmholtz equations and for the 3D Maxwell equations. diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 2f4406a9..83f451f8 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -1,969 +1,1055 @@ # This file is machine-generated - editing it directly is not advised -[[ANSIColoredPrinters]] +julia_version = "1.11.1" +manifest_format = "2.0" +project_hash = "c93b20c7adfdce7dcd32bda66638d598d39160a5" + +[[deps.ANSIColoredPrinters]] git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" version = "0.0.1" -[[AbstractFFTs]] +[[deps.AbstractFFTs]] deps = ["LinearAlgebra"] git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" version = "1.5.0" - [AbstractFFTs.extensions] + [deps.AbstractFFTs.extensions] AbstractFFTsChainRulesCoreExt = "ChainRulesCore" AbstractFFTsTestExt = "Test" - [AbstractFFTs.weakdeps] + [deps.AbstractFFTs.weakdeps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -[[AbstractTrees]] +[[deps.AbstractTrees]] git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" version = "0.4.5" -[[ArgTools]] +[[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" version = "1.1.2" -[[ArrayLayouts]] +[[deps.ArrayLayouts]] deps = ["FillArrays", "LinearAlgebra"] -git-tree-sha1 = "0dd7edaff278e346eb0ca07a7e75c9438408a3ce" +git-tree-sha1 = "492681bc44fac86804706ddb37da10880a2bd528" uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "1.10.3" +version = "1.10.4" weakdeps = ["SparseArrays"] - [ArrayLayouts.extensions] + [deps.ArrayLayouts.extensions] ArrayLayoutsSparseArraysExt = "SparseArrays" -[[Artifacts]] +[[deps.Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" version = "1.11.0" -[[BEAST]] +[[deps.BEAST]] deps = ["AbstractTrees", "BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "ConvolutionOperators", "Distributed", "ExtendableSparse", "FFTW", "FastGaussQuadrature", "FillArrays", "Infiltrator", "InteractiveUtils", "IterativeSolvers", "LiftedMaps", "LinearAlgebra", "LinearMaps", "NestedUnitRanges", "Requires", "SauterSchwab3D", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SpecialFunctions", "StaticArrays", "TestItems", "WiltonInts84"] -path = ".." +git-tree-sha1 = "757efc563022d39e95f751b7e5a29d020a57e049" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" version = "2.5.0" -[[Base64]] +[[deps.Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" version = "1.11.0" -[[BlockArrays]] +[[deps.BibInternal]] +git-tree-sha1 = "78aa378482bf6f338eef8f2440fb62a75ab1aaa3" +uuid = "2027ae74-3657-4b95-ae00-e2f7d55c3e64" +version = "0.3.6" + +[[deps.BibParser]] +deps = ["BibInternal", "DataStructures", "Dates", "JSONSchema", "YAML"] +git-tree-sha1 = "f24884311dceb5f8eafe11809b6f1d867b489a46" +uuid = "13533e5b-e1c2-4e57-8cef-cac5e52f6474" +version = "0.2.1" + +[[deps.Bibliography]] +deps = ["BibInternal", "BibParser", "DataStructures", "Dates", "FileIO", "YAML"] +git-tree-sha1 = "520c679daed011ce835d9efa7778863aad6687ed" +uuid = "f1be7e48-bf82-45af-a471-ae754a193061" +version = "0.2.20" + +[[deps.BlockArrays]] deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] git-tree-sha1 = "9a9610fbe5779636f75229e423e367124034af41" uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" version = "0.16.43" -[[Bzip2_jll]] +[[deps.Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9e2a6b69137e6969bab0152632dcb3bc108c8bdd" +git-tree-sha1 = "8873e196c2eb87962a2048b3b8e08946535864a1" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+1" +version = "1.0.8+2" -[[Cairo_jll]] +[[deps.Cairo_jll]] deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] git-tree-sha1 = "009060c9a6168704143100f36ab08f06c2af4642" uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" version = "1.18.2+1" -[[ClusterTrees]] +[[deps.ClusterTrees]] deps = ["DelimitedFiles", "LinearAlgebra", "Pkg"] git-tree-sha1 = "4114d60c95974edf9272d88571d14abeed598942" uuid = "5100927d-02aa-593a-b4f9-7235df19f0db" version = "0.2.1" -[[CodecZlib]] +[[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] git-tree-sha1 = "bce6804e5e6044c6daab27bb533d1295e4a2e759" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" version = "0.7.6" -[[CollisionDetection]] +[[deps.CollisionDetection]] deps = ["Compat", "LinearAlgebra", "StaticArrays"] git-tree-sha1 = "88a33f2fba4ef1065edd06164c84d8b03ee4d049" uuid = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" version = "0.1.6" -[[Combinatorics]] +[[deps.Combinatorics]] git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" version = "1.0.2" -[[CompScienceMeshes]] +[[deps.CompScienceMeshes]] deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Permutations", "Requires", "SparseArrays", "StaticArrays", "TestItems"] git-tree-sha1 = "6bb7cd3831707872fe404e01d156e1b149fcfa3a" uuid = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" version = "0.8.3" -[[Compat]] +[[deps.Compat]] deps = ["TOML", "UUIDs"] git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" version = "4.16.0" weakdeps = ["Dates", "LinearAlgebra"] - [Compat.extensions] + [deps.Compat.extensions] CompatLinearAlgebraExt = "LinearAlgebra" -[[CompilerSupportLibraries_jll]] +[[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" version = "1.1.1+0" -[[ConvolutionOperators]] +[[deps.ConvolutionOperators]] deps = ["LinearAlgebra", "LinearMaps"] git-tree-sha1 = "ae44e38013c05c7ec59f428b4ea7ad7d34926b63" uuid = "15927181-a1bb-497c-b745-8dbf505c019d" version = "0.4.1" -[[DataStructures]] +[[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] git-tree-sha1 = "1d0a14036acb104d9e89698bd408f63ab58cdc82" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" version = "0.18.20" -[[Dates]] +[[deps.Dates]] deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" version = "1.11.0" -[[DelimitedFiles]] +[[deps.DelimitedFiles]] deps = ["Mmap"] git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" version = "1.9.1" -[[Distributed]] +[[deps.Distributed]] deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" version = "1.11.0" -[[DocStringExtensions]] +[[deps.DocStringExtensions]] deps = ["LibGit2"] git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" version = "0.9.3" -[[Documenter]] +[[deps.Documenter]] deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] git-tree-sha1 = "5a1ee886566f2fa9318df1273d8b778b9d42712d" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" version = "1.7.0" -[[Downloads]] +[[deps.DocumenterCitations]] +deps = ["AbstractTrees", "Bibliography", "Dates", "Documenter", "Logging", "Markdown", "MarkdownAST", "OrderedCollections", "Unicode"] +git-tree-sha1 = "ca601b812efd1155a9bdf9c80e7e0428da598a08" +uuid = "daee34ce-89f3-4625-b898-19384cb65244" +version = "1.3.4" + +[[deps.Downloads]] deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" version = "1.6.0" -[[Expat_jll]] +[[deps.Expat_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7" uuid = "2e619515-83b5-522b-bb60-26c02a35a201" version = "2.6.2+0" -[[ExtendableSparse]] +[[deps.ExtendableSparse]] deps = ["DocStringExtensions", "ILUZero", "LinearAlgebra", "Printf", "SparseArrays", "Sparspak", "StaticArrays", "SuiteSparse", "Test"] -git-tree-sha1 = "3dc0ead7baa71735e03be4379d812dd8167e904a" +git-tree-sha1 = "426576ad51731a375042a0887a0c9761d95ed00b" uuid = "95c220a8-a1cf-11e9-0c77-dbfce5f500b3" -version = "1.5.2" +version = "1.5.3" - [ExtendableSparse.extensions] + [deps.ExtendableSparse.extensions] ExtendableSparseAMGCLWrapExt = "AMGCLWrap" ExtendableSparseAlgebraicMultigridExt = "AlgebraicMultigrid" ExtendableSparseIncompleteLUExt = "IncompleteLU" ExtendableSparsePardisoExt = "Pardiso" - [ExtendableSparse.weakdeps] + [deps.ExtendableSparse.weakdeps] AMGCLWrap = "4f76b812-4ba5-496d-b042-d70715554288" AlgebraicMultigrid = "2169fc97-5a83-5252-b627-83903c6c433c" IncompleteLU = "40713840-3770-5561-ab4c-a76e7d0d7895" Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" -[[FFTW]] +[[deps.FFTW]] deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] git-tree-sha1 = "4820348781ae578893311153d69049a93d05f39d" uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" version = "1.8.0" -[[FFTW_jll]] +[[deps.FFTW_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "4d81ed14783ec49ce9f2e168208a12ce1815aa25" uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" version = "3.3.10+1" -[[FLTK_jll]] +[[deps.FLTK_jll]] deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] git-tree-sha1 = "72a4842f93e734f378cf381dae2ca4542f019d23" uuid = "4fce6fc7-ba6a-5f4c-898f-77e99806d6f8" version = "1.3.8+0" -[[FastGaussQuadrature]] +[[deps.FastGaussQuadrature]] deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] git-tree-sha1 = "fd923962364b645f3719855c88f7074413a6ad92" uuid = "442a2c76-b920-505d-bb47-c5924d526838" version = "1.0.2" -[[FileWatching]] +[[deps.FileIO]] +deps = ["Pkg", "Requires", "UUIDs"] +git-tree-sha1 = "62ca0547a14c57e98154423419d8a342dca75ca9" +uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +version = "1.16.4" + +[[deps.FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" version = "1.11.0" -[[FillArrays]] +[[deps.FillArrays]] deps = ["LinearAlgebra"] git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" version = "1.13.0" - [FillArrays.extensions] + [deps.FillArrays.extensions] FillArraysPDMatsExt = "PDMats" FillArraysSparseArraysExt = "SparseArrays" FillArraysStatisticsExt = "Statistics" - [FillArrays.weakdeps] + [deps.FillArrays.weakdeps] PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -[[Fontconfig_jll]] +[[deps.Fontconfig_jll]] deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] git-tree-sha1 = "db16beca600632c95fc8aca29890d83788dd8b23" uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" version = "2.13.96+0" -[[FreeType2_jll]] +[[deps.FreeType2_jll]] deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" version = "2.13.2+0" -[[GLU_jll]] +[[deps.GLU_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] git-tree-sha1 = "65af046f4221e27fb79b28b6ca89dd1d12bc5ec7" uuid = "bd17208b-e95e-5925-bf81-e2f59b3e5c61" version = "9.0.1+0" -[[GMP_jll]] +[[deps.GMP_jll]] deps = ["Artifacts", "Libdl"] uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" version = "6.3.0+0" -[[Gettext_jll]] +[[deps.Gettext_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" version = "0.21.0+0" -[[Git]] +[[deps.Git]] deps = ["Git_jll"] git-tree-sha1 = "04eff47b1354d702c3a85e8ab23d539bb7d5957e" uuid = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2" version = "1.3.1" -[[Git_jll]] +[[deps.Git_jll]] deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] git-tree-sha1 = "ea372033d09e4552a04fd38361cd019f9003f4f4" uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" version = "2.46.2+0" -[[Glib_jll]] +[[deps.Glib_jll]] deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] git-tree-sha1 = "674ff0db93fffcd11a3573986e550d66cd4fd71f" uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" version = "2.80.5+0" -[[GmshTools]] +[[deps.GmshTools]] deps = ["Libdl", "gmsh_jll"] git-tree-sha1 = "299aa66053646db77f8aa7fafcebe0f9e5c0d1dc" uuid = "82e2f556-b1bd-5f1a-9576-f93c0da5f0ee" version = "0.5.2" -[[GrundmannMoeller]] +[[deps.GrundmannMoeller]] deps = ["LinearAlgebra", "StaticArrays", "Test"] git-tree-sha1 = "e264cf5f081091e4af712a911d3b620567c565e3" uuid = "36aa67b7-9d79-4e90-bbc0-05abd90a007e" version = "0.1.2" -[[HDF5_jll]] +[[deps.HDF5_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "LibCURL_jll", "Libdl", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "OpenSSL_jll", "TOML", "Zlib_jll", "libaec_jll"] git-tree-sha1 = "82a471768b513dc39e471540fdadc84ff80ff997" uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" version = "1.14.3+3" -[[Hwloc_jll]] +[[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "dd3b49277ec2bb2c6b94eb1604d4d0616016f7a6" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" version = "2.11.2+0" -[[ILUZero]] +[[deps.ILUZero]] deps = ["LinearAlgebra", "SparseArrays"] git-tree-sha1 = "b007cfc7f9bee9a958992d2301e9c5b63f332a90" uuid = "88f59080-6952-5380-9ea5-54057fb9a43f" version = "0.2.0" -[[IOCapture]] +[[deps.IOCapture]] deps = ["Logging", "Random"] git-tree-sha1 = "b6d6bfdd7ce25b0f9b2f6b3dd56b2673a66c8770" uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" version = "0.2.5" -[[Infiltrator]] +[[deps.Infiltrator]] deps = ["InteractiveUtils", "Markdown", "REPL", "UUIDs"] git-tree-sha1 = "38298a8eabe09e49e6f60927c9e1ca3481688ba0" uuid = "5903a43b-9cc3-4c30-8d17-598619ec4e9b" version = "1.8.3" -[[IntelOpenMP_jll]] +[[deps.IntelOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] git-tree-sha1 = "10bd689145d2c3b2a9844005d01087cc1194e79e" uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" version = "2024.2.1+0" -[[InteractiveUtils]] +[[deps.InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" version = "1.11.0" -[[IrrationalConstants]] +[[deps.IrrationalConstants]] git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" version = "0.2.2" -[[IterativeSolvers]] +[[deps.IterativeSolvers]] deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] git-tree-sha1 = "59545b0a2b27208b0650df0a46b8e3019f85055b" uuid = "42fd0dbc-a981-5370-80f2-aaf504508153" version = "0.9.4" -[[JLLWrappers]] +[[deps.JLLWrappers]] deps = ["Artifacts", "Preferences"] git-tree-sha1 = "be3dc50a92e5a386872a493a10050136d4703f9b" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" version = "1.6.1" -[[JSON]] +[[deps.JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" version = "0.21.4" -[[JpegTurbo_jll]] +[[deps.JSON3]] +deps = ["Dates", "Mmap", "Parsers", "PrecompileTools", "StructTypes", "UUIDs"] +git-tree-sha1 = "1d322381ef7b087548321d3f878cb4c9bd8f8f9b" +uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" +version = "1.14.1" + + [deps.JSON3.extensions] + JSON3ArrowExt = ["ArrowTypes"] + + [deps.JSON3.weakdeps] + ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" + +[[deps.JSONSchema]] +deps = ["Downloads", "JSON", "JSON3", "URIs"] +git-tree-sha1 = "243f1cdb476835d7c249deb9f29ad6b7827da7d3" +uuid = "7d188eb4-7ad8-530c-ae41-71a32a6d4692" +version = "1.4.1" + +[[deps.JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" version = "3.0.4+0" -[[LLVMOpenMP_jll]] +[[deps.Krylov]] +deps = ["LinearAlgebra", "Printf", "SparseArrays"] +git-tree-sha1 = "4f20a2df85a9e5d55c9e84634bbf808ed038cabd" +uuid = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" +version = "0.9.8" + +[[deps.LLVMOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "78211fb6cbc872f77cad3fc0b6cf647d923f4929" uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" version = "18.1.7+0" -[[LZO_jll]] +[[deps.LZO_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "854a9c268c43b77b0a27f22d7fab8d33cdb3a731" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" version = "2.10.2+1" -[[LazilyInitializedFields]] -git-tree-sha1 = "8f7f3cabab0fd1800699663533b6d5cb3fc0e612" +[[deps.LazilyInitializedFields]] +git-tree-sha1 = "0f2da712350b020bc3957f269c9caad516383ee0" uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf" -version = "1.2.2" +version = "1.3.0" -[[LazyArtifacts]] +[[deps.LazyArtifacts]] deps = ["Artifacts", "Pkg"] uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" version = "1.11.0" -[[LibCURL]] +[[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" version = "0.6.4" -[[LibCURL_jll]] +[[deps.LibCURL_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" version = "8.6.0+0" -[[LibGit2]] +[[deps.LibGit2]] deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" version = "1.11.0" -[[LibGit2_jll]] +[[deps.LibGit2_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" version = "1.7.2+0" -[[LibSSH2_jll]] +[[deps.LibSSH2_jll]] deps = ["Artifacts", "Libdl", "MbedTLS_jll"] uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" version = "1.11.0+1" -[[Libdl]] +[[deps.Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" version = "1.11.0" -[[Libffi_jll]] +[[deps.Libffi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" version = "3.2.2+1" -[[Libgcrypt_jll]] +[[deps.Libgcrypt_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] -git-tree-sha1 = "9fd170c4bbfd8b935fdc5f8b7aa33532c991a673" +git-tree-sha1 = "8be878062e0ffa2c3f67bb58a595375eda5de80b" uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.11+0" +version = "1.11.0+0" -[[Libglvnd_jll]] +[[deps.Libglvnd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" version = "1.6.0+0" -[[Libgpg_error_jll]] +[[deps.Libgpg_error_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "fbb1f2bef882392312feb1ede3615ddc1e9b99ed" +git-tree-sha1 = "c6ce1e19f3aec9b59186bdf06cdf3c4fc5f5f3e6" uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.49.0+0" +version = "1.50.0+0" -[[Libiconv_jll]] +[[deps.Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" +git-tree-sha1 = "61dfdba58e585066d8bce214c5a51eaa0539f269" uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.17.0+0" +version = "1.17.0+1" -[[Libmount_jll]] +[[deps.Libmount_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "0c4f9c4f1a50d8f35048fa0532dabbadf702f81e" uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" version = "2.40.1+0" -[[Libuuid_jll]] +[[deps.Libuuid_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807" uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" version = "2.40.1+0" -[[LiftedMaps]] +[[deps.LiftedMaps]] deps = ["LinearAlgebra", "LinearMaps"] git-tree-sha1 = "68c65fe9d32407ddb13eb26ef96fa803d45369cd" uuid = "d22a30c1-52ac-4762-a8c9-5838452405e0" version = "0.5.1" -[[LinearAlgebra]] +[[deps.LinearAlgebra]] deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" version = "1.11.0" -[[LinearElasticity_jll]] +[[deps.LinearElasticity_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "71e8ee0f9fe0e86a8f8c7f28361e5118eab2f93f" uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" version = "5.0.0+0" -[[LinearMaps]] +[[deps.LinearMaps]] deps = ["LinearAlgebra"] git-tree-sha1 = "ee79c3208e55786de58f8dcccca098ced79f743f" uuid = "7a12625a-238d-50fd-b39a-03d52299707e" version = "3.11.3" - [LinearMaps.extensions] + [deps.LinearMaps.extensions] LinearMapsChainRulesCoreExt = "ChainRulesCore" LinearMapsSparseArraysExt = "SparseArrays" LinearMapsStatisticsExt = "Statistics" - [LinearMaps.weakdeps] + [deps.LinearMaps.weakdeps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -[[LogExpFunctions]] +[[deps.LogExpFunctions]] deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" version = "0.3.28" - [LogExpFunctions.extensions] + [deps.LogExpFunctions.extensions] LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" LogExpFunctionsInverseFunctionsExt = "InverseFunctions" - [LogExpFunctions.weakdeps] + [deps.LogExpFunctions.weakdeps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" -[[Logging]] +[[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" version = "1.11.0" -[[METIS_jll]] +[[deps.METIS_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "1fd0a97409e418b78c53fac671cf4622efdf0f21" uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" version = "5.1.2+0" -[[MKL_jll]] +[[deps.MKL_jll]] deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" version = "2024.2.0+0" -[[MMG_jll]] +[[deps.MMG_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] git-tree-sha1 = "70a59df96945782bb0d43b56d0fbfdf1ce2e4729" uuid = "86086c02-e288-5929-a127-40944b0018b7" version = "5.6.0+0" -[[MPICH_jll]] +[[deps.MPICH_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] git-tree-sha1 = "7715e65c47ba3941c502bffb7f266a41a7f54423" uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" version = "4.2.3+0" -[[MPIPreferences]] +[[deps.MPIPreferences]] deps = ["Libdl", "Preferences"] git-tree-sha1 = "c105fe467859e7f6e9a852cb15cb4301126fac07" uuid = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267" version = "0.1.11" -[[MPItrampoline_jll]] +[[deps.MPItrampoline_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] git-tree-sha1 = "70e830dab5d0775183c99fc75e4c24c614ed7142" uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" version = "5.5.1+0" -[[Markdown]] +[[deps.Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" version = "1.11.0" -[[MarkdownAST]] +[[deps.MarkdownAST]] deps = ["AbstractTrees", "Markdown"] git-tree-sha1 = "465a70f0fc7d443a00dcdc3267a497397b8a3899" uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" version = "0.1.2" -[[MbedTLS_jll]] +[[deps.MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" version = "2.28.6+0" -[[MicrosoftMPI_jll]] +[[deps.MicrosoftMPI_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "f12a29c4400ba812841c6ace3f4efbb6dbb3ba01" uuid = "9237b28f-5490-5468-be7b-bb81f5f5e6cf" version = "10.1.4+2" -[[Mmap]] +[[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" version = "1.11.0" -[[MozillaCACerts_jll]] +[[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" version = "2023.12.12" -[[NestedUnitRanges]] +[[deps.NestedUnitRanges]] deps = ["AbstractTrees", "ArrayLayouts", "BlockArrays"] git-tree-sha1 = "1cbdce42da2370fee5ef906ef24179f8c070e3b9" uuid = "032820ab-dc03-4b49-91f4-7d58d4da98b3" version = "0.2.1" -[[NetworkOptions]] +[[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" -[[OCCT_jll]] +[[deps.OCCT_jll]] deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] git-tree-sha1 = "bef34b68c20cc34475c5cb464ab48555e74f4c61" uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" version = "7.7.2+0" -[[OffsetArrays]] +[[deps.OffsetArrays]] git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" version = "1.14.1" - [OffsetArrays.extensions] + [deps.OffsetArrays.extensions] OffsetArraysAdaptExt = "Adapt" - [OffsetArrays.weakdeps] + [deps.OffsetArrays.weakdeps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -[[OpenBLAS_jll]] +[[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" version = "0.3.27+1" -[[OpenLibm_jll]] +[[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" version = "0.8.1+2" -[[OpenMPI_jll]] +[[deps.OpenMPI_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] git-tree-sha1 = "e25c1778a98e34219a00455d6e4384e017ea9762" uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" version = "4.1.6+0" -[[OpenSSL_jll]] +[[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" version = "3.0.15+1" -[[OpenSpecFun_jll]] +[[deps.OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" version = "0.5.5+0" -[[OrderedCollections]] +[[deps.OrderedCollections]] git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" version = "1.6.3" -[[PCRE2_jll]] +[[deps.PCRE2_jll]] deps = ["Artifacts", "Libdl"] uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" version = "10.42.0+1" -[[Parsers]] +[[deps.Parsers]] deps = ["Dates", "PrecompileTools", "UUIDs"] git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" version = "2.8.1" -[[Permutations]] +[[deps.Permutations]] deps = ["Combinatorics", "LinearAlgebra", "Random"] git-tree-sha1 = "f92b0a7b722b1ecfd5c0d77a7eda24b4eea5c56a" uuid = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" version = "0.4.22" -[[Pixman_jll]] +[[deps.Pixman_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] git-tree-sha1 = "35621f10a7531bc8fa58f74610b1bfb70a3cfc6b" uuid = "30392449-352a-5448-841d-b1acce4e97dc" version = "0.43.4+0" -[[Pkg]] +[[deps.Pkg]] deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" version = "1.11.0" weakdeps = ["REPL"] - [Pkg.extensions] + [deps.Pkg.extensions] REPLExt = "REPL" -[[PrecompileTools]] +[[deps.PrecompileTools]] deps = ["Preferences"] git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" version = "1.2.1" -[[Preferences]] +[[deps.Preferences]] deps = ["TOML"] git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6" uuid = "21216c6a-2e73-6563-6e65-726566657250" version = "1.4.3" -[[Printf]] +[[deps.Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" version = "1.11.0" -[[REPL]] +[[deps.REPL]] deps = ["InteractiveUtils", "Markdown", "Sockets", "StyledStrings", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" version = "1.11.0" -[[Random]] +[[deps.Random]] deps = ["SHA"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" version = "1.11.0" -[[RecipesBase]] +[[deps.RecipesBase]] deps = ["PrecompileTools"] git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" version = "1.3.4" -[[Reexport]] +[[deps.Reexport]] git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" version = "1.2.2" -[[RegistryInstances]] +[[deps.RegistryInstances]] deps = ["LazilyInitializedFields", "Pkg", "TOML", "Tar"] git-tree-sha1 = "ffd19052caf598b8653b99404058fce14828be51" uuid = "2792f1a3-b283-48e8-9a74-f99dce5104f3" version = "0.1.0" -[[Requires]] +[[deps.Requires]] deps = ["UUIDs"] git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" version = "1.3.0" -[[SCOTCH_jll]] +[[deps.SCOTCH_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] git-tree-sha1 = "7110b749766853054ce8a2afaa73325d72d32129" uuid = "a8d0f55d-b80e-548d-aff6-1a04c175f0f9" version = "6.1.3+0" -[[SHA]] +[[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" version = "0.7.0" -[[SauterSchwab3D]] +[[deps.SauterSchwab3D]] deps = ["FastGaussQuadrature", "GrundmannMoeller", "LinearAlgebra", "ShunnHamQuadrature", "StaticArrays"] git-tree-sha1 = "87242fb25711b1f9eaa45506d8b5e6e0b50f086a" uuid = "0a13313b-1c00-422e-8263-562364ed9544" version = "0.1.4" -[[SauterSchwabQuadrature]] +[[deps.SauterSchwabQuadrature]] deps = ["FastGaussQuadrature", "LinearAlgebra", "StaticArrays", "TestItems"] git-tree-sha1 = "b98948c567cbe250d774d01a07833b7a329ec511" uuid = "535c7bfe-2023-5c1d-b712-654ef9d93a38" version = "2.4.0" -[[Serialization]] +[[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" version = "1.11.0" -[[SharedArrays]] +[[deps.SharedArrays]] deps = ["Distributed", "Mmap", "Random", "Serialization"] uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" version = "1.11.0" -[[ShunnHamQuadrature]] +[[deps.ShunnHamQuadrature]] deps = ["LinearAlgebra", "StaticArrays"] git-tree-sha1 = "dfa53166b13cd6f352d54c99a24124321ef95282" uuid = "164309f2-5039-4884-b6c7-6da8aa5c66ad" version = "0.1.0" -[[Sockets]] +[[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" version = "1.11.0" -[[SparseArrays]] +[[deps.SparseArrays]] deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" version = "1.11.0" -[[Sparspak]] +[[deps.Sparspak]] deps = ["Libdl", "LinearAlgebra", "Logging", "OffsetArrays", "Printf", "SparseArrays", "Test"] git-tree-sha1 = "342cf4b449c299d8d1ceaf00b7a49f4fbc7940e7" uuid = "e56a9233-b9d6-4f03-8d0f-1825330902ac" version = "0.3.9" -[[SpecialFunctions]] +[[deps.SpecialFunctions]] deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" version = "2.4.0" - [SpecialFunctions.extensions] + [deps.SpecialFunctions.extensions] SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" - [SpecialFunctions.weakdeps] + [deps.SpecialFunctions.weakdeps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -[[StaticArrays]] +[[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "eeafab08ae20c62c44c8399ccb9354a04b80db50" +git-tree-sha1 = "777657803913ffc7e8cc20f0fd04b634f871af8f" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.7" +version = "1.9.8" - [StaticArrays.extensions] + [deps.StaticArrays.extensions] StaticArraysChainRulesCoreExt = "ChainRulesCore" StaticArraysStatisticsExt = "Statistics" - [StaticArrays.weakdeps] + [deps.StaticArrays.weakdeps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -[[StaticArraysCore]] +[[deps.StaticArraysCore]] git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682" uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" version = "1.4.3" -[[StyledStrings]] +[[deps.StringEncodings]] +deps = ["Libiconv_jll"] +git-tree-sha1 = "b765e46ba27ecf6b44faf70df40c57aa3a547dcb" +uuid = "69024149-9ee7-55f6-a4c4-859efe599b68" +version = "0.3.7" + +[[deps.StructTypes]] +deps = ["Dates", "UUIDs"] +git-tree-sha1 = "159331b30e94d7b11379037feeb9b690950cace8" +uuid = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" +version = "1.11.0" + +[[deps.StyledStrings]] uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" version = "1.11.0" -[[SuiteSparse]] +[[deps.SuiteSparse]] deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" -[[SuiteSparse_jll]] +[[deps.SuiteSparse_jll]] deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" version = "7.7.0+0" -[[TOML]] +[[deps.TOML]] deps = ["Dates"] uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" version = "1.0.3" -[[Tar]] +[[deps.Tar]] deps = ["ArgTools", "SHA"] uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" version = "1.10.0" -[[Test]] +[[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" version = "1.11.0" -[[TestItems]] +[[deps.TestItems]] git-tree-sha1 = "8621ba2637b49748e2dc43ba3d84340be2938022" uuid = "1c621080-faea-4a02-84b6-bbd5e436b8fe" version = "0.1.1" -[[TranscodingStreams]] +[[deps.TranscodingStreams]] git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" version = "0.11.3" -[[UUIDs]] +[[deps.TypeTree]] +deps = ["InteractiveUtils"] +git-tree-sha1 = "c1adbb6b9d9374babe8975a456f383a37a8e02ec" +uuid = "04da0e3b-1cad-4b2c-a963-fc1602baf1af" +version = "0.3.0" + +[[deps.URIs]] +git-tree-sha1 = "67db6cc7b3821e19ebe75791a9dd19c9b1188f2b" +uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" +version = "1.5.1" + +[[deps.UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" version = "1.11.0" -[[Unicode]] +[[deps.Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" version = "1.11.0" -[[WiltonInts84]] +[[deps.WiltonInts84]] deps = ["LinearAlgebra"] git-tree-sha1 = "9d61cac63c100e936194e5db8613e33572fd2bb8" uuid = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" version = "0.2.5" -[[XML2_jll]] +[[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "1165b0443d0eca63ac1e32b8c0eb69ed2f4f8127" +git-tree-sha1 = "6a451c6f33a176150f315726eba8b92fbfdb9ae7" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.13.3+0" +version = "2.13.4+0" -[[XSLT_jll]] +[[deps.XSLT_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"] git-tree-sha1 = "a54ee957f4c86b526460a720dbc882fa5edcbefc" uuid = "aed1982a-8fda-507f-9586-7b0439959a61" version = "1.1.41+0" -[[Xorg_libX11_jll]] +[[deps.Xorg_libX11_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" version = "1.8.6+0" -[[Xorg_libXau_jll]] +[[deps.Xorg_libXau_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" version = "1.0.11+0" -[[Xorg_libXdmcp_jll]] +[[deps.Xorg_libXdmcp_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" version = "1.1.4+0" -[[Xorg_libXext_jll]] +[[deps.Xorg_libXext_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] git-tree-sha1 = "d2d1a5c49fae4ba39983f63de6afcbea47194e85" uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" version = "1.3.6+0" -[[Xorg_libXfixes_jll]] +[[deps.Xorg_libXfixes_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" version = "5.0.3+4" -[[Xorg_libXft_jll]] +[[deps.Xorg_libXft_jll]] deps = ["Fontconfig_jll", "Libdl", "Pkg", "Xorg_libXrender_jll"] git-tree-sha1 = "754b542cdc1057e0a2f1888ec5414ee17a4ca2a1" uuid = "2c808117-e144-5220-80d1-69d4eaa9352c" version = "2.3.3+1" -[[Xorg_libXinerama_jll]] +[[deps.Xorg_libXinerama_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" version = "1.1.4+4" -[[Xorg_libXrender_jll]] +[[deps.Xorg_libXrender_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] git-tree-sha1 = "47e45cd78224c53109495b3e324df0c37bb61fbe" uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" version = "0.9.11+0" -[[Xorg_libpthread_stubs_jll]] +[[deps.Xorg_libpthread_stubs_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9" uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" version = "0.1.1+0" -[[Xorg_libxcb_jll]] +[[deps.Xorg_libxcb_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] git-tree-sha1 = "bcd466676fef0878338c61e655629fa7bbc69d8e" uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" version = "1.17.0+0" -[[Xorg_xtrans_jll]] +[[deps.Xorg_xtrans_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77" uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" version = "1.5.0+0" -[[Zlib_jll]] +[[deps.YAML]] +deps = ["Base64", "Dates", "Printf", "StringEncodings"] +git-tree-sha1 = "dea63ff72079443240fbd013ba006bcbc8a9ac00" +uuid = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" +version = "0.4.12" + +[[deps.Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" version = "1.2.13+1" -[[gmsh_jll]] +[[deps.gmsh_jll]] deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] git-tree-sha1 = "1e7fe5c8dbe0e911931a18cdfbd2c7a1e01b68ef" uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" version = "4.13.1+0" -[[libaec_jll]] +[[deps.libaec_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "46bf7be2917b59b761247be3f317ddf75e50e997" uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" version = "1.1.2+0" -[[libblastrampoline_jll]] +[[deps.libblastrampoline_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" version = "5.11.0+0" -[[libpng_jll]] +[[deps.libpng_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] git-tree-sha1 = "b70c870239dc3d7bc094eb2d6be9b73d27bef280" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" version = "1.6.44+0" -[[nghttp2_jll]] +[[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" version = "1.59.0+0" -[[oneTBB_jll]] +[[deps.oneTBB_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "7d0ea0f4895ef2f5cb83645fa689e52cb55cf493" uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" version = "2021.12.0+0" -[[p7zip_jll]] +[[deps.p7zip_jll]] deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" version = "17.4.0+2" diff --git a/docs/Project.toml b/docs/Project.toml index ad968743..48345484 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -2,3 +2,6 @@ BEAST = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244" +Krylov = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" +TypeTree = "04da0e3b-1cad-4b2c-a963-fc1602baf1af" diff --git a/docs/make.jl b/docs/make.jl index 640dbb5c..ebc40742 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,11 +1,50 @@ -using Documenter, BEAST +using Documenter +using DocumenterCitations +using BEAST -makedocs( - # clean=false, - sitename="BEAST Documentation") +bib = CitationBibliography(joinpath(@__DIR__, "src", "refs.bib"); style=:alpha) -deploydocs( - # deps = Deps.pip("mkdocs", "python-markdown-math"), - repo = "github.com/krcools/BEAST.jl.git", - # julia = "1.0", +makedocs(; + modules=[BEAST], + authors="Kristof Cools and contributors", + sitename="BEAST.jl", + format=Documenter.HTML(; + prettyurls=get(ENV, "CI", "false") == "true", + canonical="https://krcools.github.io/BEAST.jl", + edit_link="master", + assets=String[], + collapselevel=1, + sidebar_sitename=true, + ), + plugins=[bib], + pages=[ + "Introduction" => "index.md", + "Manual" => Any["General Usage" => "manual/usage.md", "Application Examples" => "manual/examples.md"], + "Operators & Excitations" => Any[ + "Overview" => "operators/overview.md", + "Local Operators" => Any["Identiy" => "operators/identity.md",], + "Integral Operators" => Any[ + "Helmholtz" => "operators/helmholtz.md", + "Maxwell Single Layer" => "operators/maxwellsinglelayer.md", + "Maxwell Double Layer" => "operators/maxwelldoublelayer.md", + ], + "Excitations" => Any[ + "Plane Wave" => "operators/planewave.md", + ], + ], + "Bases" => Any[ + "Spatial" => Any["Raviart Thomas" => "bases/raviartthomas.md", "Buffa Christiansen" => "bases/buffachristiansen.md"], + "Temporal" => Any[], + ], + "Geometry Representations" => Any["Flat" => Any[], "Curvilinear" => Any[]], + "____________________________________" => Any[], + "Internals" => Any[ + "Multithreading" => Any[], + ], + "Contributing" => "contributing.md", + "References" => "references.md", + "API Reference" => "apiref.md", + ], ) + +deploydocs(; repo="github.com/krcools/BEAST.jl.git", target="build", push_preview=true, forcepush=true) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml deleted file mode 100644 index 9fae846d..00000000 --- a/docs/mkdocs.yml +++ /dev/null @@ -1,18 +0,0 @@ -site_name: BEAST.jl -site_author: krcools -site_description: Documentation for BEAST.jl -repo_url: https://github.com/krcools/BEAST.jl - -markdown_extensions: - - mdx_math - -extra_javascript: - - https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML - - assets/mathjaxhelper.js - -docs_dir: build -pages: - - Home: index.md - - Tutorial: tutorial.md - - Assembly: assemble.md - - Quadrature Strategies: quadstrat.md diff --git a/docs/src/apiref.md b/docs/src/apiref.md new file mode 100644 index 00000000..9b37de9f --- /dev/null +++ b/docs/src/apiref.md @@ -0,0 +1,8 @@ + +# API Reference + + + +```@autodocs +Modules = [BEAST,VIE,Maxwell3D,TDMaxwell3D,BEAST.Variational] +``` \ No newline at end of file diff --git a/docs/src/assemble.md b/docs/src/assemble.md deleted file mode 100644 index 00824548..00000000 --- a/docs/src/assemble.md +++ /dev/null @@ -1,152 +0,0 @@ -# The Matrix Assemble Routine - -A lot of the design of this package derives from the need to express boundary element and finite element matrix assembly in a concise but general manner that is compatible with a wide range of linear and bilinear forms as encountered in the solution of variational problems. - -In this section the matrix assembly routine at the center of this package will be discussed. As a case study we will go over the steps required to extend support for new kernels and new finite element spaces. - -The matrix assemble routine is surprisingly short: - -```julia -function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store) - - test_elements, tad = assemblydata(tfs) - bsis_elements, bad = assemblydata(bfs) - - tshapes = refspace(tfs); num_tshapes = numfunctions(tshapes) - bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes) - - T = promote_type(scalartype(biop), scalartype(tfs), scalartype(bfs)) - zlocal = zeros(T, num_tshapes, num_bshapes) - - qd = quaddata(biop, tshapes, bshapes, test_elements, bsis_elements) - for (p,tcell) in enumerate(test_elements), (q,bcell) in enumerate(bsis_elements) - - fill!(zlocal, 0) - strat = quadrule(biop, tshapes, bshapes, p, tcell, q, bcell, qd) - momintegrals!(biop, tshapes, bshapes, tcell, bcell, zlocal, strat) - - for j in 1 : num_bshapes, i in 1 : num_tshapes - z = zlocal[i,j] - for (m,a) in tad[p,i], (n,b) in bad[q,j] - store(a*z*b, m, n) -end end end end -``` - -Support for direct product spaces, linear combinations of kernels, non-standard storage of matrix elements, and parallel execution is provided by layers on top of this assembly routine. In this section we will focus on discussing the design and implementation of this inner building block that lies at the basis of more general functionality. - -Finite element spaces are usually stored as a collection of functions that in turn each comprise contributions from a limited number of geometric elements that make up the support of the function. In FEM and BEM matrix assembly, however, we need the *tranposed* information: given a geometric cell, and a local shape function, we need the ability to efficiently retrieve the list of basis functions whose definition contains the given local shape function on the given cell and the weight (aka coefficient) by which it contributes. The data structure that contains this information is referred to as the assembly data `ad`. In particular, `ad[e,s]`, where `e` is the index of a geometric cell and `s` is the index of a local shape function, returns an iterable collection of pairs `(m,w)` where `m` is an index into the iterable collection of basis functions making up the finite element space and `w` is a weight, such that shape function `s` on geometric element `e` contributes with weight `w` to basis function `n`. - -```julia -test_elements, tad = assemblydata(tfs) -bsis_elements, bad = assemblydata(bfs) -``` - -One final note on the `assemblydata` function: as you can see from the above snippet, the function returns, in addition to the actual assembly data, an iterable collection of geometric elements. This collection is a subset of the collection of elements making up the geometry on which the finite element space is defined. The elements returned are those that actually appear in the domain of one or more of the functions that span the finite element space. The double for loop that iterates over pairs of trial and testing functions will only visit those used elements. Elements that are part of the geometry but do not appear as part of the support of a function are skipped. This behaviour is required to guarantee scalability when using multiple threads in assembling the matrix: each thread is assigned a subset of the basis functions; visiting unused elements in all threads is harmful for the overall efficiency. - -With this assembly data in hand, matrix assembly can be done by iterating over geometric cells, rather than over basis functions. Doing this avoids visiting a given geometric cell more than once. When computing matrices resulting from discretisation with e.g. Raviart-Thomas elements, this can speed up assembly time with a factor 9. - -The problem of matrix assembly is now reduced to the computation of interactions between local shape functions defined on all possible pairs of geometric cells. The space of local shape functions can be retrieved by calling - -```julia -tshapes = refspace(tfs); num_tshapes = numfunctions(tshapes) -bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes) -``` - -Here, `num_tshapes` and `num_bshapes` are the number of local shape functions. For example, when using Raviart-Thomas elements, the number of local shape functions equals three (one for every edge of the reference triangle). - -Based on this dimension, and based on the types used to represent numbers in the fields over which the spaces and the kernel are defined, the storage for local shape function interaction is pre-allocated: - -```julia -T = promote_type(scalartype(biop), scalartype(tfs), scalartype(bfs)) -zlocal = zeros(T, num_tshapes, num_bshapes) -``` - -Note that the computation of the storage type ensures that high precision or complex data types are only used when required. At all times the minimal storage type is selected. Not only does this keep memory use down, it also results in faster linear algebra computations such as matrix-vector multiplication. - -Before entering the double for loop that is responsible for the enumeration of all pairs of geometric cells (a trial cell pairs with a test cell), the implementer is given the opportunity to precompute data for use in the integration kernels. For example when using numerical quadrature rules to compute the double integral in the expression of the matrix entries, it is likely that a set of quadrature points for any given trial cells will be reused in interactions with a large number of test cells. To avoid computing these points and weights over and over, the client developer is given the opportunity to compute and store them by providing an appropriate method for `quaddata` . If memory use is more important the runtime, the client programmer is perfectly allowed to compute points and weights on the fly without storing them. - - -```julia -fill!(zlocal, 0) -strat = quadrule(biop, tshapes, bshapes, p, tcell, q, bcell, qd) -momintegrals!(biop, tshapes, bshapes, tcell, bcell, zlocal, strat) -``` - -For a given pair `(tcell,bcell)` of test cell and trial cell (with respective indices `p` and `q` in collections `test_elements` and `bsis_elements`), all possible interactions between local shape functions are computed. After resetting the buffer used to store these interactions, the quadrature strategy is determined. The quadrature strategy in general could depend on: - -- the kernel `biop` defining the integral operator, -- the local test and trial shape functions `tshapes` and `bshapes` (functions of high polynomial degree and functions that are highly oscillatory typically require bespoke integration methods), -- and the geometric test and trial cells `tcell` and `bcell` (cells that touch or are near to each other lead to quickly varying or even singular integrands requiring dedicated integration rules). - -The method returns an object `strat` that: (i) describes (by its type and its data fields) the integration strategy that is appropriate to compute the current set of local interactions, (ii) contains all data precomputed and stored in `qd` that is relevant to this particular integration (for example a set of quadrature points and weights). This explains why the indices `p` and `q` where passed too `quadrule`: they allow for the quick retrieval of relevant pre-stored data from `qd`. - -The routing that is responsible for the actual computation of the interactions between the local shape functions takes the quadrule object `strat` as one of its arguments. The idea is that `momintegrals!` has many methods, not only for different types of kernel and shape functions, but also for different types of `strat`. For example, there are implementations of `momintegrals!` for the computation of the Maxwellian single layer operator w.r.t. spaces of Raviart-Thomas elements that employ double numerical quadrature, singularity extraction, and even more advanced integration routines. - -*Note*: the type of `strat` depends on the orientation of the two interacting geometric cells. This information is only available at runtime. In other words, there will be a slight type instability at this point in the code. This is by design however, and not different from the use of virtual functions in an c++ implementation. Numerical experiments show that this form of runtime polymorphism results in negligible runtime overhead. - -When all possible interactions between local shape functions have been computed, they need to be stored in the global system matrix. This is done in the matrix assembly loop: - -```julia -for j in 1 : num_bshapes - for i in 1 : num_tshapes - z = zlocal[i,j] - for (m,a) in tad[p,i] - for (n,b) in bad[q,j] - store(a*z*b, m, n) - end - end - end -end -``` -For both the test and trial local shape functions, the global indices at which they appear in the finite element space (and the corresponding weights) are retrieved from the assembly data objects. The contributing value `v = a*z*b` is constructed and its storage is delegated to the `store` method, which we received as one of the arguments passed to `assemble_chunk!`. In the simplest case, `assemble_chunk!` can be used like this: - -```julia -Z = zeros(ComplexF64, numfunctions(tfs), numfunctions(bfs)) -store(v, m, n) = (Z[m,n] += v) -assemble_chunk!(kernel, tfs, bfs, store) -``` - -In other words `store` will simply add the computed value to the specified entry in the global system matrix. Allowing the caller to specify `store` as an argument allows for more flexibility than hardcoding this behaviour in the assembly routine. Indeed, when computing blocks of a larger system, or when e.g. the transposed or a multiple of a given operator is desired, a fairly simple redefinition of `store` can provide this functionality. This is also the reason why `assemble_chunk!` ends in an exclamation mark: even though strictly speaking none of the arguments are modified, the function clearly has an effect on variables defined outside of its scope! - -## Case Study: Implementation of the Nitsche Operator Assembly - -In the Nitsche method for the Maxwell system, penalty terms are added to the classic discretisation of the EFIE. When discretized using a non-conforming finite elements space (typically because the underlying geometric mesh is not conforming), the penalty term will force the solution to be divergence conforming in some weak sense. The penalty term derives from the following bilinear form: - -```math -p(v,u) = \int_{\gamma} v(x) \int_{Γ} \frac{e^{-ik|x-y|}}{4π|x-y|} u(y) dy dx -``` - -Note that ``u(x)`` is supported by a 2D surface ``Γ`` whereas ``v(y)`` is supported by a 1D curve ``γ``. The complete implementation of this operator could look like - -```julia -mutable struct SingleLayerTrace{T} <: MaxwellOperator3D - gamma::T -end - -function quaddata(operator::SingleLayerTrace, - localtestbasis::LagrangeRefSpace, localtrialbasis::LagrangeRefSpace, - testelements, trialelements) - - tqd = quadpoints(localtestbasis, testelements, (10,)) - bqd = quadpoints(localtrialbasis, trialelements, (8,)) - - return (tpoints=tqd, bpoints=bqd) -end - -function quadrule(op::SingleLayerTrace, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd) - DoubleQuadRule( - qd.tpoints[1,i], - qd.bpoints[1,j] - ) -end - -integrand(op::SingleLayerTrace, kernel, g, τ, f, σ) = f[1]*g[1]*kernel.green -``` - -Every kernel corresponds with a type. Kernels can potentially depend on a set of parameters; these appear as fields in the type. Here our Nitsche kernel depends on the wavenumber. In quaddata we precompute quadrature points for all geometric cells in the supports of test and trial elements. This is fairly sloppy: only one rule for test and trial integration is considered. A high accuracy implementation would typically compute points for both low quality and high quality quadrature rules. - -Also `quadrule` is sloppy: we always select a `DoubleQuadRule` to perform the computation of interactions between local shape functions. No singularity extraction or other advanced technique is considered for nearby interactions. Clearly amateurs at work here! - -`BEAST` provides a default implementation of an integration routine using double numerical quadrature. All that is required to tap into that implementation is a method overloading `integrand`. From the above formula it is clear what this method should look like. - -That's it! diff --git a/docs/src/assets/logo-dark.svg b/docs/src/assets/logo-dark.svg new file mode 100644 index 00000000..0933c2a5 --- /dev/null +++ b/docs/src/assets/logo-dark.svg @@ -0,0 +1,474 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/assets/logo.svg b/docs/src/assets/logo.svg new file mode 100644 index 00000000..92a297f0 --- /dev/null +++ b/docs/src/assets/logo.svg @@ -0,0 +1,474 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/assets/logo_README.svg b/docs/src/assets/logo_README.svg new file mode 100644 index 00000000..ba367792 --- /dev/null +++ b/docs/src/assets/logo_README.svg @@ -0,0 +1,486 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BEAST + + diff --git a/docs/src/assets/logo_README_white.svg b/docs/src/assets/logo_README_white.svg new file mode 100644 index 00000000..67821b66 --- /dev/null +++ b/docs/src/assets/logo_README_white.svg @@ -0,0 +1,486 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BEAST + + diff --git a/docs/src/assets/postproc.md b/docs/src/assets/postproc.md deleted file mode 100644 index ce87622a..00000000 --- a/docs/src/assets/postproc.md +++ /dev/null @@ -1,25 +0,0 @@ -# Post-processing and Visualisation - -## Field computation - -The main API for the computation of fields in a vector of points is the potential function. This function is capable of computing - -```math -F(x) = \int_{\Gamma} K(x,y) u(y) dy -``` - -with ``u(y) = \Sigma_{i=1}^N u_i f_i(y)`` and ``K(x,y)`` the integation kernel defining the type of potential. For example, the far field for a vector valued surface density is - -```math -F(x) = \int_{\Gamma} e^{ik \frac{x \cdot y}{|x|}} u(y) dy -``` - -For a far field potential such as this, the value only depends on the direction, not the magnitude, of ``x``, as can be read off from the normalisation in the exponent. - -The following script computes the far field along a semi-circle in the xz-plane. - -```julia -Θ, Φ = range(0.0,stop=2π,length=100), 0.0 -dirs = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] -farfield = potential(MWFarField3D(wavenumber=κ), dirs, u, X) -``` diff --git a/docs/src/bases/buffachristiansen.md b/docs/src/bases/buffachristiansen.md new file mode 100644 index 00000000..0304d621 --- /dev/null +++ b/docs/src/bases/buffachristiansen.md @@ -0,0 +1,4 @@ + +# [Buffa Christiansen Basis Functions](@id bcDef) + +Another citation [buffaDualFiniteElement2007](@cite) \ No newline at end of file diff --git a/docs/src/bases/raviartthomas.md b/docs/src/bases/raviartthomas.md new file mode 100644 index 00000000..db88b5c0 --- /dev/null +++ b/docs/src/bases/raviartthomas.md @@ -0,0 +1,6 @@ + +# [Raviart-Thomas Basis Functions](@id raviartthomasDef) + +Text + +A citation [raoElectromagneticScatteringSurfaces1982](@cite) \ No newline at end of file diff --git a/docs/src/contributing.md b/docs/src/contributing.md new file mode 100644 index 00000000..5453ae43 --- /dev/null +++ b/docs/src/contributing.md @@ -0,0 +1,60 @@ + +# Contributing + +In order to contribute to this package directly create a pull request against the `master` branch. Before doing so please: + +- Follow the style of the surrounding code. +- Supplement the documentation. +- Write tests and check that no errors occur. + + +--- +## Style + +For a consistent style the [JuliaFormatter.jl](https://github.com/domluna/JuliaFormatter.jl) package is used which enforces the style defined in the *.JuliaFormatter.toml* file. To follow this style simply run +```julia +using JuliaFormatter +format(pkgdir(BEAST)) +``` + +!!! note + That all files follow the JuliaFormatter style is tested during the unit tests. Hence, do not forget to execute the two lines above. Otherwise, the tests are likely to not pass. + + +--- +## Documentation + +Add documentation for any changes or new features following the style of the existing documentation. For more information you can have a look at the [Documenter.jl](https://documenter.juliadocs.org/stable/) documentation. + + +--- +## [Tests](@id tests) + +Write tests for your code changes and verify that no errors occur, e.g., by running +```julia +using Pkg +Pkg.test("BEAST") +``` + +For more detailed information on which parts are tested the coverage can be evaluated on your local machine, e.g., by +```julia +using Pkg +Pkg.test("BEAST"; coverage=true, julia_args=`--threads 4`) + +# determine coverage +using Coverage +src_folder = pkgdir(BEAST) * "/src" +coverage = process_folder(src_folder) +LCOV.writefile("path-to-folder-you-like" * "BEAST.lcov.info", coverage) + +clean_folder(src_folder) # delete .cov files + +# extract information about coverage +covered_lines, total_lines = get_summary(coverage) +@info "Current coverage:\n$covered_lines of $total_lines lines ($(round(Int, covered_lines / total_lines * 100)) %)" +``` + +In Visual Studio Code the [Coverage Gutters](https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-coverage-gutters) plugin can be used to visualize the tested lines of the code by inserting the path of the *BEAST.lcov.info* file in the settings. + +!!! note + From Julia 1.11 onwards the the coverage can be displayed in Visual Studio Code directly, since the [TestItemRunner.jl](https://www.julia-vscode.org/docs/stable/userguide/testitems/#Code-coverage) package is employed. \ No newline at end of file diff --git a/docs/src/index.md b/docs/src/index.md index 489fb146..86b8736c 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,73 +1,83 @@ -```@meta -CurrentModule = BEAST -``` -# BEAST.jl documentation +# BEAST.jl -BEAST provides a number of types modelling concepts and a number of algorithms for the efficient and simple implementation of boundary and finite element solvers. It provides full implementations of these concepts for the LU based solution of boundary integral equations for the Maxwell and Helmholtz systems. +This Julia package, the *boundary element analysis and simulation toolkit (BEAST)*, provides routines to convert integral and differential equations to linear systems of equations +via the boundary element method (BEM) and the finite element method (FEM). +To this end, the (Petrov-) **Galerkin method** is employed. -Because Julia only compiles code at execution time, users of this library can hook into the code provided in this package at any level. In the extreme case it suffices to provide overwrites of the `assemble` functions. In that case, only the LU solution will be performed by the code here. +Currently, the focus is on equations encountered in **classical electromagnetism**, where frequency and time domain equations are covered. +Several [operators](@ref operator), basis functions, and geometry representations are implemented. -At the other end it suffices that users only supply integration kernels that act on the element-element interaction level. This package will manage all required steps for matrix assembly. +!!! note + SI units and for time-harmonic simulations a time convention of ``\mathrm{e}^{\,\mathrm{j}\omega t}`` are used everywhere. -For the Helmholtz 2D and Maxwell 3D systems, complete implementations are supplied. These models will be discussed in detail to give a more concrete idea of the APIs provides and how to extend them. +!!! tip + The code is designed such that users can also easily hack into the code and implement new features. + Design goals are extendability and a performant execution. -Central to the solution of boundary integral equations is the assembly of the system matrix. The system matrix is fully determined by specifying a kernel G, a set of trial functions, and a set of test functions. -## Basis +--- +## Installation -Sets of both trial and testing functions are implemented by models following the basis concept. The term basis is somewhat misleading as it is nowhere required nor enforced that these functions are linearly independent. Models implementing the Basis concept need to comply to the following semantics. +Installing BEAST is done by entering the package manager (enter `]` at the julia REPL) and issuing: +``` +pkg> add BEAST +``` -- [`numfunctions(basis)`](@ref): number of functions in the Basis. -- [`coordtype(basis)`](@ref): type of (the components of) the values taken on by the functions in the Basis. -- [`scalartype(d)`](@ref): the scalar field underlying the vector space the basis functions take value in. -- [`refspace(basis)`](@ref): returns the ReferenceSpace of local shape functions on which the Basis is built. -- [`assemblydata(basis)`](@ref): `assemblydata` returns an iterable collection `elements` of geometric elements and a look table `ad` for use in assembly of interaction matrices. In particular, for an index `element_idx` into `elements` and an index `local_shape_idx` in basis of local shape functions `refspace(basis)`, `ad[element_idx, local_shape_idx]` returns the iterable collection of `(global_idx, weight)` tuples such that the local shape function at `local_shape_idx` defined on the element at `element_idx` contributes to the basis function at `global_idx` with a weight of `weight`. -- [`geometry(basis)`](@ref): returns an iterable collection of Elements. The order in which these Elements are encountered corresponds to the indices used in the assembly data structure. +--- +## Overview -## Reference Space +The following [operators](@ref operator), basis functions, and geometry representations are implemented. +To see details and all variations, have a look at the corresponding sections of this documentation. -The *reference space* concept defines an API for working with spaces of local shape functions. The main role of objects implementing this concept is to allow specialization of the functions that depend on the precise reference space used. +#### Operators -The functions that depend on the type and value of arguments modeling *reference space* are: +- **Integral operators** + + Maxwell (3D) + - Single Layer + - Double Layer + + Helmholtz (2D & 3D) + - Single Layer + - Double Layer -- [`numfunctions(refspace, domain)`](@ref): returns the number of shape functions on each element. -## Kernel +- **Local Operators** + + Identity (+ variations thereof) -A kernel is a fairly simple concept that mainly exists as part of the definition of a Discrete Operator. A kernel should obey the following semantics: +```@raw html +
+``` -In many function definitions the kernel object is referenced by `operator` or something similar. This is a misleading name as an operator definition should always be accompanied by the domain and range space. +#### Basis functions -## Discrete Operator +- **Spatial** + + Low Order + - Raviart-Thomas (Rao-Wilton-Glisson) + - Buffa-Christiansen + - Brezzi-Douglas-Merini + + High Order + - Graglia-Wilton-Peterson (GWP) + - B-Spline based -Informally speaking, a Discrete Operator is a concept that allows for the computation of an interaction matrix. It is a kernel together with a test and trial basis. A Discrete Operator can be passed to `assemble` and friends to compute its matrix representation. -A discrete operator is a triple `(kernel, test_basis, trial_basis)`, where `kernel` is a Kernel, and `test_basis` and `trial_basis` are Bases. In addition, the following expressions should be implemented and behave according to the correct semantics: +- **Temporal** + + Lagrange? + + ... -- [`quaddata(operator,test_refspace,trial_refspace,test_elements,trial_elements)`](@ref): create the data required for the computation of element-element interactions during assembly of discrete operator matrices. -- [`quadrule(operator,test_refspace,trial_refspace,p,test_element,q_trial_element,qd)`](@ref): returns an integration strategy object that will be passed to `momintegrals!` to select an integration strategy. This rule can depend on the test/trial reference spaces and interacting elements. The indices `p` and `q` refer to the position of the interacting elements in the enumeration defined by `geometry(basis)` and allow for fast retrieval of any element specific data stored in the quadrature data object `qd`. -- [`momintegrals!(operator,test_refspace,trial_refspace,test_element,trial_element,zlocal,qr)`](@ref): this function computes the local interaction matrix between the set of local test and trial shape functions and a specific pair of elements. The target matrix `zlocal` is provided as an argument to minimise memory allocations over subsequent calls. `qr` is an object returned by `quadrule` and contains all static and dynamic data defining the integration strategy used. +```@raw html +
+``` -In the context of fast methods such as the Fast Multipole Method other algorithms on Discrete Operators will typically be defined to compute matrix vector products. These algorithms do not explicitly compute and store the interaction matrix (this would lead to unacceptable computational and memory complexity). +#### Geometry Representation -```@docs -elements -``` +- **Low Order** ("flat") + + Triangular + + Quadrilaterals + + Tetrahedra -```@docs -numfunctions -coordtype -scalartype -assemblydata -geometry -refspace -``` -```@docs -quaddata -quadrule -momintegrals! -``` +- **High Order** (curvilinear) + + NURBS-surfaces + diff --git a/docs/src/manual/examples.md b/docs/src/manual/examples.md new file mode 100644 index 00000000..d98f8cbd --- /dev/null +++ b/docs/src/manual/examples.md @@ -0,0 +1,20 @@ + +# General Usage + +Text + +--- +## EFIE + +Text + + +--- +## MFIE + +Text + +--- +## Time Domain Something + +Text \ No newline at end of file diff --git a/docs/src/manual/usage.md b/docs/src/manual/usage.md new file mode 100644 index 00000000..1a5ad433 --- /dev/null +++ b/docs/src/manual/usage.md @@ -0,0 +1,76 @@ + +# General Usage + +!!! info + The fundamental approach, which applies in most cases is: + 1. Define trial and test functions. + 2. Define an operator and an excitation. + 3. Assemble the system matrix and the right-hand side + +The available basis functions and corresponding geometry representations, available [operators](@ref operator), and excitations are defined in the corresponding sections of this documentation. + + +--- +## Introductory Example: EFIE + +The fundamental procedure is exemplified for the electric field integral equation (EFIE); further common steps are discussed afterwards: + +```@example introductory +using CompScienceMeshes +using BEAST + +# --- 1. basis functions +Γ = meshsphere(1.0, 2.5) # triangulate sphere of radius one +RT = raviartthomas(Γ) # define basis functions + +# --- 2. operators & excitation +𝑇 = Maxwell3D.singlelayer(wavenumber=2.0) # integral operator +𝐸 = Maxwell3D.planewave(direction=x̂, polarization=ẑ, wavenumber=2.0) # excitation +𝑒 = (n × 𝐸) × n # tangential part + +# --- 3. compute the RHS and system matrix +e = assemble(𝑒, RT) # assemble RHS +T = assemble(𝑇, RT, RT) # assemble system matrix +typeof(T) #hide +``` + +--- +### Explanation + +The example follows the 3 steps. +Specifically, in the example trial and test functions are the same ([Raviart-Thomas](@ref raviartthomasDef)), the excitation is a [plane wave](@ref planewaveEx), and the operator is the [Maxwell single layer operator](@ref MWsinglelayerDef). + +!!! tip + The [`assemble`](@ref assemble) function is the key function of this package, it accepts either *excitation + test function* or *operator + test + trial function*. + + The operator can also be a linear combination of several operators. + +```@docs +assemble +``` + +--- +### Further Common Steps + +The linear system of equations can now, for example, be solved via the iterative GMRES solver of the [Krylov.jl](https://github.com/JuliaSmoothOptimizers/Krylov.jl) package. +However, other solver could be used. +Subsequently, different post-processing steps can be conducted, such as computing the scattered field from the determined expansion coefficients. +This is shown in the following: + + +```@example introductory +using Krylov + +# --- solve linear system iteratively +u, ch = Krylov.gmres(T, -e, rtol=1e-5) + +# --- post processing: compute scattered electric field at two Cartesian points +points = [[3.0, 4.0, 2.0], [3.0, 4.0, 3.0]] +EF = potential(MWSingleLayerField3D(gamma=im*2.0), points, u, RT) +``` + + +!!! warn + Add a plot here! + + diff --git a/docs/src/operators/helmholtz.md b/docs/src/operators/helmholtz.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/src/operators/identity.md b/docs/src/operators/identity.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/src/operators/maxwelldoublelayer.md b/docs/src/operators/maxwelldoublelayer.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/src/operators/maxwellsinglelayer.md b/docs/src/operators/maxwellsinglelayer.md new file mode 100644 index 00000000..760416a7 --- /dev/null +++ b/docs/src/operators/maxwellsinglelayer.md @@ -0,0 +1,26 @@ + +# [Maxwell Single Layer Operator](@id MWsinglelayerDef) + +## Definition + +```math +\bm{\mathcal{T}} +``` + + +When applied to +```math +a(\bm t, \bm b) = α ∬_{\Gamma \times \Gamma} \bm t(\bm x) ⋅ \bm b(\bm y) \, g_γ(\bm x,\bm y) \,\mathrm{d}\bm y \mathrm{d}\bm x + β ∬_{Γ×Γ} ∇_Γ⋅\bm t(\bm x) \, ∇_Γ⋅\bm b(\bm y) \, g_γ(\bm x,\bm y) \,\mathrm{d}\bm y \mathrm{d}\bm x +``` + +with the free-space Green's function + +```math +g_{γ}(\bm x,\bm y) = \dfrac{\mathrm{e}^{-γ|x-y|}}{4π|x-y|} +``` + +## API + +```@docs +Maxwell3D.singlelayer +``` \ No newline at end of file diff --git a/docs/src/operators/overview.md b/docs/src/operators/overview.md new file mode 100644 index 00000000..e5a06451 --- /dev/null +++ b/docs/src/operators/overview.md @@ -0,0 +1,9 @@ + +# [Operator Overview](@id operator) + +```@example introductory +using TypeTree +using BEAST + +print(join(tt(BEAST.Operator), "")) +``` \ No newline at end of file diff --git a/docs/src/operators/planewave.md b/docs/src/operators/planewave.md new file mode 100644 index 00000000..88b9875f --- /dev/null +++ b/docs/src/operators/planewave.md @@ -0,0 +1,4 @@ + +# [Plane Wave Excitation](@id planewaveEx) + +Text \ No newline at end of file diff --git a/docs/src/quadstrat.md b/docs/src/quadstrat.md deleted file mode 100644 index cc1a606d..00000000 --- a/docs/src/quadstrat.md +++ /dev/null @@ -1,81 +0,0 @@ -# Quadrature strategies - -## Introduction - -There are many ways to approximately compute the singular integrals that appear in boundary element discretisations of surface and volume integral euqations. - -BEAST.jl is configured to select reasonable defaults, but advanced users may want to select their own quadrature rules. This section provides information on how to do this. - -## quaddata and quadrule - -Numerical quadrature is governed by a pair of functions that need to be designed to work together: - -- `quaddata`: this function is executed before the assembly loop is entered. It's job is to compute all data needed for quadrature that the developer wants to be cached. Typically this is all geometric information such as the parametric and cartesian coordinates of all quadratures rules for all elemenents. It makes sense to cache this data as it will be used many times over in the double for loop that governs assembly. Typically, near singular interactions require more careful treatment than far interactions. This means that multiple quadrature rules per elements can be required. In such cases, the developer may want to opt to sture quadrature points and weights for all these rules. The function returns a quaddata object that holds all the cached data. -- `quadrule`: quadrule is executed inside the assembly hotloop. It receives a pair of elements and the quaddata object as its arguments. Based on this, the relevant cached data is extracted and stored in a quadrule object. The type of this object will determine the actual quadrature routined that will be called upon to do the numerical quadrature. - -## quadstrat - -The pair of quaddata and quadrule methods that is used is determined by the type of the operator and finite elements, and a `quadstrat` object. This object is passed to the assembly routine and passed on to `quaddata` and `quadstrat`, so it can be considered during dispatch. - -Parameters, such as those that determine the accuracy of the numerical quadrature, are part of the runtime payload of the quadstrat object. This is usefull when the user is interested on the impact of these parameters on the performance and the accuracy of the solver without having to supply a new pair of `quadstrat`/`quaddata` methods for each possible value of these parameters. - -Roughly this leads to the following (simplified) assembly routine: - -```julia -function assemble(op, tfs, bfs, store; quadstrat=QS) - - tad, tels = assemblydata(op, tfs) - bad, bels = assemblydata(op, bfs) - - qd = quadata(op,tels,bels,quadstrat) - for tel in tels - for bel in bels - qr = quadrule(op,tel,bel,qd,quadstrat) - zlocal = momintegrals(op,tel,bel,qr) - - for i in axes(zlocal,1) - for j in axes(zlocal,2) - m, a = tad[tel,i] - n, b = bad[bel,j] - store(a*zlocal[i,j]*b,m,n) -end end end end end -``` - -It is conceivable that the types and functions described above look like this: - -```julia -struct DoubleNumQS - test_precision - trial_precision -end - -function quaddata(op, tels, bels, quadstrat::DoubleNumQS) - tqps = [quadpoints(tel,precision=quadstrat.test_precision) for tel in tels] - bqps = [quadpoints(bel,precision=quadstrat.basis_precision) for bel in bels] - return (test_quadpoints=tqps, basis_quadpoints=bqps) -end - - -struct DoubleNumQR - test_quadpoints - trial_quadpoints -end - -struct HighPrecisionQR end - -function quadrule(op, tel, bel, qd, quadstrat::DoubleNumQs) - if wellseparated(tel, bel) - return DoubleNumQR(qd.test_quadpoints[tel], qd.basis_quadpoints[bel]) - else - return HighPrecisionQR(tel, bel) - end -end - -function momintegrals(op, tel, bel, qr::DoubleNumQR) - ... -end - -function momintegrals(op, tel, bel, qr::HighPrecisionQR) - ... -end -``` \ No newline at end of file diff --git a/docs/src/references.md b/docs/src/references.md new file mode 100644 index 00000000..a56db3de --- /dev/null +++ b/docs/src/references.md @@ -0,0 +1,5 @@ + +# References + +```@bibliography +``` \ No newline at end of file diff --git a/docs/src/refs.bib b/docs/src/refs.bib new file mode 100644 index 00000000..8b42808a --- /dev/null +++ b/docs/src/refs.bib @@ -0,0 +1,21 @@ + +@article{raoElectromagneticScatteringSurfaces1982, + title = {Electromagnetic Scattering by Surfaces of Arbitrary Shape}, + author = {Rao, S. and Wilton, D. and Glisson, A.}, + year = {1982}, + month = {may}, + journal = {IEEE Transactions on Antennas and Propagation}, + volume = {30}, + number = {3}, + pages = {409--418} +} + +@article{buffaDualFiniteElement2007, + title = {A Dual Finite Element Complex on the Barycentric Refinement}, + author = {Buffa, Annalisa and Christiansen, Snorre}, + year = {2007}, + journal = {Mathematics of Computation}, + volume = {76}, + number = {260}, + pages = {1743--1769} +} diff --git a/docs/src/tdefie.md b/docs/src/tdefie.md deleted file mode 100644 index d4e2af38..00000000 --- a/docs/src/tdefie.md +++ /dev/null @@ -1,96 +0,0 @@ -# Solving the Time Domain EFIE using Marching-on-in-Time - -If broadband information is required or if the system under study will be coupled to non-linear components, the scattering problem should be solved directly in the time domain, i.e. as a hyperbolic evolution problem. - -## Building the geometry - -Building the geometry and defining the spatial finite elements happens in completely the same manner as for frequency domain simulations: - -```julia -using CompScienceMeshes -using BEAST -D, dx = 1.0, 0.3 -Γ = meshsphere(1.0, dx) -X = raviartthomas(Γ) -nothing # hide -``` - -Time domain currents are approximated in the tensor product of a spatial and temporal finite element space: - -$j(x) \approx \sum_{i=1}^{N_T} \sum_{m=1}^{N_S} u_{i,m} T_i(t) f_m(x)$ - -This package only supports translation invariant temopral basis functions, i.e. - -$T_i(t) = T(t - i \Delta t),$ - -where $\Delta t$ is the time step used to solve the problem. This time step depends on the bandwidth of the incident field and the desired accuracy. Space-Time Galerkin solvers of the type used here are not subject to stability conditions linking spatial and temporal discretisation resolutions. - -We need a temporal trial space and test space. Common examples of temporal trial spaces are the shifted quadratic spline and shifted lagrange basis functions. In this example, a shifted quadratic spline `S` is used for the trial space, while a delta function `U` is used as the temporal test space to obtain a time-stepping solution. - -```julia -Δt, Nt = 0.25, 300 -S = BEAST.timebasisspline2(Δt, Nt) -U = BEAST.timebasisdelta(Δt, Nt) -nothing # hide -``` - -We want to solve the EFIE, i.e. we want to find the current $j$ such that - -$Tj = -e^i,$ - -where the incident electric field can be any Maxwell solution in the background medium. To describe this problem in Julia we create a retarded potential operator objects and a functional representing the incident field: - -```julia -x = point(1.0,0.0,0.0) -y = point(0.0,1.0,0.0) -z = point(0.0,0.0,1.0) -gaussian = BEAST.creategaussian(30Δt, 60Δt) -E = BEAST.planewave(x, z, BEAST.derive(gaussian), 1.0) -T = BEAST.MWSingleLayerTDIO(1.0,-1.0,-1.0,2,0) -nothing; # hide -``` - -Using the finite element spaces defined above this retarded potential equation can be discretized. - -```julia -V = X ⊗ S #Space and time trial basis -W = X ⊗ U #Space and time test basis - -B = assemble(E, W) -Z = assemble(T, W, V) -nothing # hide -``` - -The variable `Z` can efficiently store the matrices corresponding to different delays (`assemble` knows about the specific sparsity pattern of such matrices and returns a sparse array of rank three fit for purpose). - -The algorithm below solves the discrete convolution problem by marching on in time: - -```julia -Z0 = Z[:,:,1] -W0 = inv(Z0) -x = BEAST.marchonintime(W0,Z,-B,Nt) -nothing # hide -``` -Computing the values of the induced current is now possible in the same manner as for frequency domain simulations by first converting our MOT solution back to the frequency domain using the fourier transform, along with some adjustments. - -```julia -Xefie, Δω, ω0 = fouriertransform(x, Δt, 0.0, 2) -ω = collect(ω0 + (0:Nt-1)*Δω) -_, i1 = findmin(abs(ω-1.0)) -ω1 = ω[i1] - -ue = Xefie[:,i1] -ue /= fouriertransform(gaussian)(ω1) -fcr, geo = facecurrents(ue, X) -nothing # hide -``` - -For now the package still relies upon Matlab for some of its visualisation. This dependency will be removed in the near future: - -```julia -include(Pkg.dir("CompScienceMeshes","examples","matlab_patches.jl")) -mat"clf" -patch(geo, real.(norm.(fcr))) -mat"cd($(pwd()))" -mat"print('current.png', '-dpng')" -``` diff --git a/docs/src/tutorial.md b/docs/src/tutorial.md deleted file mode 100644 index dd380779..00000000 --- a/docs/src/tutorial.md +++ /dev/null @@ -1,74 +0,0 @@ -# Tutorial - -```math -\newcommand{\vt}[1]{\boldsymbol{#1}} -\newcommand{\uv}[1]{\hat{\boldsymbol{#1}}} -\newcommand{\arr}[1]{\mathsf{#1}} -\newcommand{\mat}[1]{\boldsymbol{\mathsf{#1}}} -``` - -In this tutorial we will go through the steps required for the formulation and the solution of the scattering of a time harmonic electromagnetic wave by a rectangular plate by means of the solution of the electric field integral equation. - -## Building the geometry - -The sibling package `CompScienceMeshes` provides data structures and algorithms for working with simplical meshes in computational science. We will use it to create the geometry: - -```@example 1 -using CompScienceMeshes, BEAST -o, x, y, z = euclidianbasis(3) - -h = 0.2 -Γ = meshrectangle(1.0, 1.0, h) -@show numvertices(Γ) -@show numcells(Γ) -nothing # hide -``` - -Next, we create the finite element space of Raviart-Thomas aka Rao-Wilton-Glisson functions subordinate to the triangulation `Γ` constructed above: - -```@example 1 -X = raviartthomas(Γ) -nothing # hide -``` - -The scattering problem is defined by specifying the single layer operator and the functional acting as excitation. Here, the plate is illuminated by a plane wave. The actual excitation is the tangential trace of this electric field. This trace be constructed easily by using the symbolic normal vector field `n` defined as part of the `BEAST` package. - -```@example 1 -κ = 1.0 -E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) -e = (n × E) × n -nothing # hide -``` - -The single layer potential is also predefined by the `BEAST` package: - -```@example 1 -t = Maxwell3D.singlelayer(wavenumber=κ) -nothing # hide -``` - -It corresponds to the bilinear form - -```math -t(\vt{k},\vt{j}) = \frac{1}{ik} \int_{\Gamma} \int_{\Gamma'} \nabla \cdot \vt{k}(x) \nabla \cdot \vt{j}(y) \frac{e^{-ik|x-y|}}{4\pi|x-y|} dy dx - ik \int_{\Gamma} \int_{\Gamma'} \vt{k}(x) \cdot \vt{j}(y) \frac{e^{-ik|x-y|}}{4\pi|x-y|} dy dx -``` - -Using the `LinearForms` package, which implements a simple form compiler for Julia (`@varform`), the EFIE can be defined and discretised by - -```@example 1 -@hilbertspace j -@hilbertspace k -efie = @discretise t[k,j]==e[k] j∈X k∈X -nothing # hide -``` -Solving and computing the values of the induced current in the centers of the triangles of the mesh is now straightforward: - -```@example 1 -u = solve(efie) -fcr, geo = facecurrents(u,X) -nothing # hide -``` - -The resulting current distribution can be visualised by e.g. Matlab, Paraview, Plotly,... - -![](assets/facecurrents.png) diff --git a/src/maxwell/maxwell.jl b/src/maxwell/maxwell.jl index ea515750..4943a4f5 100644 --- a/src/maxwell/maxwell.jl +++ b/src/maxwell/maxwell.jl @@ -10,10 +10,10 @@ module Maxwell3D Bilinear form given by: ```math - α ∬_{Γ×Γ} j(x)⋅k(y) G_{γ}(x,y) + β ∬_{Γ×Γ} div j(x) div k(y) G_{γ}(x,y) + a(t,b) = α ∬_{Γ×Γ} t(x)⋅b(y) g_{γ}(x,y) dx dy + β ∬_{Γ×Γ} ∇_Γ⋅ t(x) ∇_Γ⋅ b(y) g_{γ}(x,y) dx dy ``` - with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|``. + with ``g_{γ} = e^{-γ|x-y|} / 4π|x-y|``. """ function singlelayer(; gamma=nothing, diff --git a/src/operator.jl b/src/operator.jl index 22dbb102..3397d413 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -19,6 +19,11 @@ end scalartype(op::TransposedOperator) = scalartype(op.op) defaultquadstrat(op::TransposedOperator, tfs::Space, bfs::Space) = defaultquadstrat(op.op, tfs, bfs) +""" + LinearCombinationOfOperators{T} <: AbstractOperator + +A linear combination of operators. +""" mutable struct LinearCombinationOfOperators{T} <: AbstractOperator coeffs::Vector{T} ops::Vector @@ -76,9 +81,16 @@ transpose(op::Operator) = TransposedOperator(op) defaultquadstrat(lc::LinearCombinationOfOperators, tfs, bfs) = [defaultquadstrat(op,tfs,bfs) for op in lc.ops] +""" + assemble(operator, test_functions, trial_functions; + storage_policy = Val{:bandedstorage}, + threading = Threading{:multi}, + quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) + +Assemble the system matrix corresponding to the operator `operator` tested with the test functions `test_functions` and the trial functions `trial_functions`. +""" function assemble(operator::AbstractOperator, test_functions, trial_functions; storage_policy = Val{:bandedstorage}, - # long_delays_policy = LongDelays{:compress}, threading = Threading{:multi}, quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) From 7723c1a013cc8a53ea3a08bbfe2bf25123e5ce18 Mon Sep 17 00:00:00 2001 From: Bernd Hofmann Date: Thu, 7 Nov 2024 19:05:02 +0100 Subject: [PATCH 442/528] update README --- README.md | 83 ++++++++++---------------- docs/src/assets/currentREADME.png | Bin 0 -> 201421 bytes docs/src/assets/currentRealREADME.png | Bin 0 -> 158101 bytes docs/src/manual/usage.md | 21 +++++-- 4 files changed, 50 insertions(+), 54 deletions(-) create mode 100644 docs/src/assets/currentREADME.png create mode 100644 docs/src/assets/currentRealREADME.png diff --git a/README.md b/README.md index 7c5f2910..2826e005 100644 --- a/README.md +++ b/README.md @@ -6,75 +6,58 @@ - +[![Docs-stable](https://img.shields.io/badge/docs-latest-blue.svg)](https://krcools.github.io/BEAST.jl/stable/) +[![Docs-dev](https://img.shields.io/badge/docs-latest-blue.svg)](https://krcools.github.io/BEAST.jl/dev/) +[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/HoBeZwe/SphericalScattering.jl/blob/master/LICENSE) [![CI](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml) [![codecov.io](http://codecov.io/github/krcools/BEAST.jl/coverage.svg?branch=master)](http://codecov.io/github/krcools/BEAST.jl?branch=master) -[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://krcools.github.io/BEAST.jl/dev/) [![DOI](https://zenodo.org/badge/87720391.svg)](https://zenodo.org/badge/latestdoi/87720391) ## Introduction -Boundary Element Analysis and Simulation Toolkit - -This package contains common basis functions and assembly routines for the implementation of -boundary element methods. Examples are included for the 2D and 3D Helmholtz equations and for -the 3D Maxwell equations. +This Julia package, the *boundary element analysis and simulation toolkit (BEAST)*, provides routines to convert integral and differential equations to linear systems of equations +via the boundary element method (BEM) and the finite element method (FEM). +To this end, the (Petrov-) **Galerkin method** is employed. -Support for the space-time Galerkin based solution of time domain integral equations is in -place for the 3D Helmholtz and Maxwell equations. +Currently, the focus is on equations encountered in **classical electromagnetism**, where frequency and time domain equations are covered. +Several operators, basis functions, and geometry representations are implemented. -## Installation -Installing `BEAST` is done by entering the package manager (enter `]` at the julia REPL) and issuing: +## Documentation -``` -pkg>add BEAST -``` +- Documentation for the [latest stable version](https://hobezwe.github.io/SphericalScattering.jl/stable/). +- Documentation for the [development version](https://hobezwe.github.io/SphericalScattering.jl/dev/). -To run the examples, the following steps are required in addition: - -``` -pkg> add CompScienceMeshes # For the creation of scatterer geometries -pkg> add Plots # For visualising the results -pkg> add GR # Other Plots compatible back-ends can be chosen -``` - -Examples can be run by: - -``` -julia>using BEAST -julia>d = dirname(pathof(BEAST)) -julia>include(joinpath(d,"../examples/efie.jl")) -``` ## Hello World -To solve scattering of a time harmonic electromagnetic plane wave by a perfectly conducting -sphere: +To solve scattering of a time-harmonic electromagnetic plane wave by a perfectly conducting sphere: ```julia -using CompScienceMeshes, BEAST +using CompScienceMeshes +using BEAST -Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -X = raviartthomas(Γ) +# --- basis functions +Γ = meshsphere(1.0, 2.5) # triangulate sphere of radius one +RT = raviartthomas(Γ) # define basis functions -t = Maxwell3D.singlelayer(wavenumber=1.0) -E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=1.0) -e = (n × E) × n +# --- operators & excitation +𝑇 = Maxwell3D.singlelayer(wavenumber=2.0) # integral operator +𝐸 = Maxwell3D.planewave(direction=x̂, polarization=ẑ, wavenumber=2.0) # excitation +𝑒 = (n × 𝐸) × n # tangential part -@hilbertspace j -@hilbertspace k -efie = @discretise t[k,j]==e[k] j∈X k∈X -u = gmres(efie) -``` -![](output.png) +# --- compute the RHS and system matrix +e = assemble(𝑒, RT) # assemble RHS +T = assemble(𝑇, RT, RT) # assemble system matrix -## Features +# --- solve +u = T \ -e -- General framework allowing to easily add support for more kernels, finite element spaces, and excitations. -- Assembly routines that take in symbolic representations of the defining bilinear form. Support for block systems and finite element spaces defined in terms of direct products or tensor products of atomic spaces. -- LU and iterative solution of the resulting system. -- Computation of secondary quantities of interest such as the near field and the limiting far field. -- Support for space-time Galerkin and convolution quadrature approaches to the solution of time domain boundary integral equations. -- Implementation of Lagrange zeroth and first order space, Raviart-Thomas, Brezzi-Douglas-Marini, and Buffa-Christianssen vector elemenents. +# ... post processing ... +``` +

+ +                  + +

diff --git a/docs/src/assets/currentREADME.png b/docs/src/assets/currentREADME.png new file mode 100644 index 0000000000000000000000000000000000000000..659f50319743c34a11e893658c5c138b68c75185 GIT binary patch literal 201421 zcmeEtbyQqU(LU4!RK?Z`mTkzoS8VCdpwnLuhwX=K9 zp6{I9|K6Fob9=h0>vsRTs=BJXCsJ8a1|5YM1quoZT~1c=0~8cYJmiW%LV)C8Cu8+O zZj+{3a%OUJ^iW8U9}r4tI4EdHDjo)s{5u^1Ny9+HLcv1v!ywlyNcx55rv@{OphqCxjY0?_Vu?;UwWHiVfisVkse^EGHoW zaB#9Wv$QdVg7V1nO%#&t6(#LAR4ppN!xo6(Hz`$gJ4YSV38hk_s~4}pph@);OA344 z->>4p#Cr?Ya&>T=o9$M|f)f$VBFnkt zO_0W{w!(M=PY5ZGqBkuit!&~=60%2JwfF6<7s00X+Wr%1`@3UZyNh-11_qjk#kg4I@Jce`FKO9Ka7;L^+uII#s^v z6(JwKav;j^dGK@hncdjKPDmzh40JY2!y_9CoLivuSVL#LZL1^9ZEhe#y^Z&A@!U{rm9|wY9KFb5T6N^ zh%kzv2S0>?t*HwT;9+ZH=gjXRMD>R+KP3H<%|ZqEL*imBM5P6hBMEyaQvfG3Co?OP zl!v7oJC!gBK+ws=jQ@kA^xrHXzl5kPTwEOZSyK7J&@sKoi202+exLDfT0bY0ljqP1sgs7+><$!;*&(=Xf;h*g7 zoc~4vf(MHS(1C@GnU%%XmgV1TIJ-!>L74pAp#NhHXEjd;QKWn@|U~Xya@TV38?Ej#2u{8S^ zSpU(smy$o-`S*rEs{fPke^CFU?|+CPR0<0GlJ+3im*&Yy3Q@g0pWnnDWNE_xC&|WX z%w=i{#x6nsFO*^Oyj6IR1^5oSm}^&<b}Se@b44g&)!x2(Z8xIfXFzlMm?&zl4)1(8bntA@;s9%%^ zAPAv{47seOGvxW6f3p74C{pcynNhDX6$U--0U32+{T=y z|H<9i-ps`v=wvEp4#5$E6+}G$%uoQjKLhplKdId|S>ahk82zlTq(0!5iK0P5Du`TR(q& z=wp6%T4`Dx{93p6_4^c`26raJMXE}p4^&*Q%?YJ2dRgTag~*DX3tJOaHlI8Kq@nep z$^(jD<(?gfYPuGZ%v$6Hcx~UjPDuRUU)pDM7nG0!&w7)5Ef44JTJj-X8op5~>vR+O zq_36<^m|&|QLlgTk>8J_t~~xI@wmxq(GKATvVMqR+K(wnl@t2E0Sc!W?iF4mq3p%C*#5%Cbz~u zoKE98T40fZ%NYF?m{&o6*RA!))7!(|@GB|^*}Tr8JgA)0uYxfHUlcyIFdB|CR#uso z7!6C6YbzgcJ~MNPaZ|HhTybz5Gm{!IO%OYs8BtqQt=mB{Iy59JI5IRe#5p4Awb=1d zAIM(Uo6?J9TN!Yy7 zJcO=>yfiBxM5bsf6_^zCVsskgOqQ+-D#ks4eGoLREi0a~v5`*lG9ir?cefFgzAD&; zWQ-O3-k!d5!602^?6di?G;WD%)ZIj|M(i>=|1PcYHF~ zv$0Ct->+s(F#y)4?Z0Cl`@VHcpE1`}qp~uobyZa?Zd;^03pgp|FWwhJ6=#FZ=RE)- zNy4!{4zN?Ex;$$?$6`hCo@TYA~_^bX^M=*(jbRn;dmywdwJDWmpnM1={=Ag%O-;XubqZH-Eu>@qLlb0W^Vj933eOjhAinOXx~Q6@vI7*UMaSEYwFNhqhV zoBO7O>r3n4=ie|h%PkB1lf83+v3GO7yMNm=omNVf!jPnEpN;ZY^a6t8a$Z-h46s10Ed!=6t+d& zwz?~S{NwNisE}4s%)8Es?CN0qcDRy~%?p@s{Pfjg3Ndt;770k$WQR03csHJ(1;%X= z-Od|Gs`q|pXPUZxkd~I-QjwN6Rxvs!Ay7L1Il`EvJ@rtwfJC?CiR2z#p6aX|y|lwb zO<1;4jHW1M>DNmCxk!?u&Q$F;jaA?)ELa$oiuL2Gd+PCQn+NwjmP!gz-F_2l-l|1I zBr1KFB=8eW8iv?J{TBBwAx+G9uQ3AtOu?|X4fdW!pX=f@hzz}Fy#9W@(=#D>{&Zuo zYOSB-`bWa7+}}e@VadqMxEP<)+9$%CyV<(9Lyu3bO0(F^iOYxtXY>{ko+OIKLdR6H z#x%HCqmcDWZ%yGVJ}{C7avFW!+k&GKhOLGcm=+XhJY#6W(|{TjN<0{p&jbBW-fmE# zDEN4@&mt>maekud+5vYQ1=@?;@5kmfc0W9=vA>^zElaK}Ksli=I)lYVy2)EWaB~B< z({ioAPmxDpYHGd-)}I8I5XaTa?ZcC!ay>GP*1+*{plhb|4V`sM4)+q%qexE~H8mzJ zW1Q>^8oCE8U!NIC*p~?A{%r{#96*krI!6t6LaTF3T->mwRFq**Qh=x%Qe;iC+@+<5 z8Ku(w;#e4>Ln?<18LYWgYL&!Ns!}+X%HdBT0j7K`3IQ6wy=3A8Oby{$dSJ@dQF-Tz z6T%|R0ga54S~)z^$P!YOr__g(UXGz1&9p-mjvw6MWL&vJGrTQwNGUO$;+q6X!cEf{2sot}qP37%}_o=Qp{GT%H8oj9lKj zQfzOV@aedmQ#&vurTP^yHOgJg$YX*;S1wU8c$viQkT1%9&pdcUn3)~s`r?l1MPEf4 zE9njfXlT8fRT2tn-bF8B2!@zLUN`~33T?(DEZZc=SXW-cPwty%`h>XGljC9r-WDupt2ODm)obHnrx>0c|1gDqdpRYBwVf9`zhI)H zN9oEx2pzf!F{UJqM8Er3Ue#|5=1k~GFL>Lf$tk63R;=7@Y*--tYV`xFRGmfl<#W7y zNT2U1TDLN(S1(q2P$^3Ad|)y?^c1vI=P*;xcgLIDbg96hlY(l?VB&7DzN}R<{QU8c z1t_*ZUZnOT4xWsN10zr7phUrNtBA_t`~su= zCk>B9ZYV@)qchQC%&&>zW#;{J?`z*1*ZxFExP+DIO!$I0NG~bv1xE!ErbxZTCu~+w z^Q*)5u)~dujXK(voPP8?ii_GGAyN;Nbin%vCE{nqbLRhpYo) z-`q1O2~H#??WgONr(^LGOM`7cWd(lZ=;sFIG4Cm-qSEof;==2~S5V7#REJcjsF-p+ zdD@dA2;^0dkGvhrD9aW-iBso^o*TVH5%d{&H1|d{TF0{a5g0P~yv#zn3P{)gVzoB+ zMevbBMmGq=A?p>mO)NWrP=iB~AyEs0b6a7iVRNV1F11i%>?!i}@O;IbEJ`%lhnN-@ zaT~Dd_N@%%^3(6HFx2|Urwg6Y&p)n@JqJ_9F4|uIHK%XW-jMlqKYhbl-(6SC!qfa# zqM~u}`a!@6`_d3D=jpVXj1GCTuS1~&Trq?r{@u&Yy7QXnDOQ}Qo% zzjG$KTr8gkovtQPv5w!U2K1INjP1vML_(H4B`G&dwjqeKv#&ABsd9G5QOME0**0OW zG}7WeS2p!?m|-5nIYs?mC*3RGBgt6Q zPd|9_^6lD=fp|pcU_r{kTFQD`(1CqVX@qUHCtZ^n_VXEJ!o>r>g85OQ1NUP(@_B~2sRcTw#ZE@sU?%_5Jb)uq0c!^ zV2-PZi#YJsy$6b`0-Ib;QUP~yva}-Z2Y|Cr34a-N&-$G;@7(&%i{vw>^>(Gg(&XYL zJz**4PI2a3aTPV2Wh(Nj({(e*$Sy2v#!8vvPygI{y(6gj21gk{URIjVfF`6u7!y4L zWTdN#hwqnLdtbX*KmUfq0a$fst1bm#&xH z-8GEw^|0(Y&3^p(o2Xif9}>*^bU1#!#`g(9pDF^;<3{C(OQ+vB7RamneEu zbk|ewu>^LKLHOXHCCt$wIOKCoOkZKlw+iSOC8x7aGFTfmCb)&2i$ir*jqI-X7J8M; zr$2fZW_6!289#46WDQys!oc#!lqNtm*o0RBBoU7%_7~`hN1J(RHJHycp^V!l^+O|= zQtrGxVenCF+A&{k?y=gWoJ$4DG@g)PHp#`IdCi(^4>G_+!Sn^aiOy^6`^HDYV-qXEdo)(?@FmAFq+k56*D#OyioN}yV zqP~TMu9em3=CaZDmIK7tRg}w$8kJyScrVhipz~O)GRuZji~OZIq}bgyUa(tvu9pfE z5+N3&C@12k;6dDS#=K@Rjg|QhZv@Sgzo&*xrs;Y_sqE>Ap&@S-w&5dhAm-voz^%^F}G&atqc z7Fy`CZk_~9x1kW729Wq^*l130vmGqInUkOAf*y)Sb$CF@%EIosf|0{orF#WbNnTm0 z*NKg>Kc#m4Aq}-=E_2%zSm81Fvqo@Y_|F{oV6?kg$g*JgFdE+ctUHl^NM8(0B>g&x z&G$TCg*|kctNxI4N&a=-4axyDQW{L0-waY*rRenH1bFtl2K6y|f_C_W z$3--p?b#$3BYZEu?hL+RW{P}s;PMr(+icNMndO~e`cS>!%t$9q(N z$FzHL;V5z>qbnL^_jqN}NcGBJtr=rNHO^2CmuecGWx89#(e>%SEVVLVtXt{@h$6%X zm8}cI>Y|m=Bpk7wwi8BCv8t#bev{^F7qN-rS4O>)Y)$xR$LKOF2{zv+z}h2^KdFku z$l4tVWknki?x^!5^UunsWq$;Ctl*KUDcIoEU(}Zen+DiMaFyAPp4U_Qnc*Q5zqe_5 zXvD=MGj+rjXpI3965usrZ=@%R2qm zz2E}*M`rtst>E0&qTN?xZvM73$2L2 zChs2%Bgg|z8QqS%D2(wEZshnAsFESAeF2F}v%9C~HBT#0wm!yBsje1O50U zyc_wA1D?$sSd5h3cDq))IrMg@x~o#N*V?UW-Iuv|V@t^R*r&s6Jjyou5Jmf8q#I2? ze+0}sFKyileCs3l+fbKRF$gBXKv<2@Y%?GAXMJkFGcNLXik+genj+mUKUE$y*8tPP zwcDCoscBAIEW2(5ngtpYs(Z`nkRse8P;Lz@fdcn z{P_*D^!}WL34;S?O1IWq+f;xzaxFps#P6ygSc;JGxGMqHk3_dSK(OoH5zu4RZDDHt zcgO06R`e4-_FXh0`^kyUvTq)lC<13P0~$M6?RBP3abAdQSYs{L>hL>@G?$ugr--YJ z+6wYxQlAGlF#Oa$kB-jMWM$8niW+bQyZ?c5xYlm52y=1WNaT2~dno_yfx`T+fqXGf z;lHL`OOYF&N0s{-!w3F&QcvhQ3u8=o6vAuT@+mFMn5#*Pop;pMwB_Aen6Y*d@-b=w z#qg4y(&_|;sTGmYgk6L#?TE}p7hPQcgf(G0fp5X zYp90r>A_(9SO!faQ}Mj1q2~1gRMKpg^}vceXTm*}t}S>r@y6smdFmfOZjPWopo;EG zBlYWgeRGw!k?KZ!jbx$AHj@gDiQysi8n%6=Mbp#Y_q4)J%=IK3_c)0@B1nWplZVLu zH-oTLs~HvZu!)qZbK7liq0+x`H7~zWW)9^^LcaVMV-qSG!n{hg=#8Z~(f!PC;4hPc zeyDflq8zDgNUnr&@19{NnSsx5wAnz>M;n~u<8R9=U=p)|Q|@?uf{>o? zx6-2MdIL)Hq3nr?x1QG;;{|@-?>0l0h9E0L1L(0Vc{WGIP5yP;-%7gz6%Xix^f~I^ z`-tlGkQE$qwL;yQPLTsZXi#7|T^qPD{@Hwx zdF{IhU?Tlbgi^id^Xt3zJ!)=iIO3@vuCp~}e`Z7Xm9KIQ02_-oSKLxz??ik%8$QU` z#O3qyJ()#_bT1iAR61>%V01=xCwVQcXamw(aReX9(m3u%0L=!$v?Mn3j-%^4T7ZPG=2!W> zjSDAdNsVr7QA~+5;q%`p7BKOtzZ_`xWxT)Jpyl zrf(Ho@v(8sxxX((e*FxdVE^?*qU;`8Gg8RL)XL7z!7)P`8weouWUEyCp%)&jVd*GQ zI7Lbg=?LddPli&{Bjs=*gX#8 zr%p|jxDM<^X3|xx^lx3cB5-xW4kcw+j0zE@Wmt5E?}AV*Cy4?>Fa(i>Xt2W9VDk{( z6Oetn$V${Xe1-K(7Dh{|CfX=-%a~}(dlaeysvj@S7dK!NZ2xg=R>aSW4H6}ksDE!@^>K-aZF3S-c637SGB zwk3(OoXVZDsxi?cN=%^)NK=4mpk?GQ+)IK>G8gKdLQg19AE;%R?X5Axo>dTk!-$|x zq5vj2Whw1`>aMq7WlU3ymMb=tp}SZ4Ggqg_`os`$19wRlo>9E(D{!;O@4WBhot3o^aj4xR>nOXEd|VVpVy z9gKt8L)+Zo)*6-M!_$!>*y#8q^w_1qaK__wkI@KK$3OxmUOr9zP#dH{r%P6}Qv&MC z<6RdG(P`~ud2@ihdM}u#q2+7-LL&-~P2e#%iafXIYp6ly^Ozmb2asnWke#Gzk~w)kX_N1jo3jQ{N` zU@AbIy(2UGnn?T|xZLZ2=W0XiY7!4fIZvASqX$g>I$4(Bn(VoVk~BC}%JD4?G9ErM z-dpQP_+7VT3NHsHmWFV>zCPv3@>5xE9}-i^;ZulKCXLvY0QL)BDm^E zozl9_xW|ILqZNiWsbB&`w;^m)95Yf`v~8}+bK;gBG#V%iUlXstMGH&zuynshh5hzr zJLOkz%z%i;#anXH%-6W;LsIBS$>na~(|RKp2~d~mB4-_4aUVG2fQ}7GKeO*0lXD3v zE!mkb`XjfhZXlf-Yar3-0E#QnTC8fAyt2oz;t;Jn&fx5R2w&>Wbmd!bp-sZ+KETpA z7FN{x33{$snYBne>L6hjpLi*&QcaC)X~JtQJ%E;M!=?M_>Vp7R*FjL#m~rcx17isT z6;!iz-qoW(d0f3^Jxa5`=!WB%bAE#x!7mz%@}4{ROo9)-g7utTi!QWsy}iV{2IUF6 zpP6_wgJ+s;)l-O9r(reG)e)9?N6U)gqu(Y(e0o`)y`z-eJ}|wH@3`m0 zj8VjjpvTR!fTMd&iEE8ED4JdGhH5$02_`4S2{x|k?K9fFVG%J{rKr{OVC0rIsWUrL zrwePT2}b_XwS;Gy?B_qxa~!Cp+rt#+ZbDG4YlTz>@PW2#$R=w8c%Ox59fF02ChW? zjgOU%6uJMiNE>hT6{5^Ek`wo8|GAo>vpjc&xS5T|NXX$d>SXQa))y^Hnpau^H`_Mm6{joBo9Im?i@n8Yj7}XMW^uf`P($38*RM zFdAhN$;FZN1^LABrkr!I)SPz3Rq)$Z$smj>!A3WL0uE$$QeD=;V)!0PbXtWf#o}=J zGF5#aGhQlqUUo9zRFA9biqgW1J7skV&1XT8U^_rk2Te_^XO|y}XMF1}Yo6P~zeF2! z!}fGi+?cX{08GnvA87W?NwjHDT4zFBj)Z#D9>)F64^Fls!|XJMiZ`qo3ashXcUYz2 zz!~d#7j%%|iJJV-S<>|~I!m}sN2Q`>QMec&%e)(t zn7=4ydX@NRRZfJ}^I#+uD%ln?y7u~r7-(yUw^l#l+itf3V=**P%}hqG?sK2%dJLiEL{s^2A%u< z<>L#5ZO|n?<4g;$ULu@E#7i25s@RN*c0jGR{}961Z0vL?igtB#i@qd{9(j4t+no7 z6(^?d#t544j(+%T&aV#^uQ7W*CI8VpBT zB-?9^L`!^X)Kv1aFZ44=@m^>JBmgjM&uW67hMs+TjR&^QLB&2ntX%-(jlFzGxjCy9 zLSG9jQqTQ$bfxZ+*v_Yk`>hg`MSRk1N9z#-OkV!&xlgOcvyQQWWa_fjW3ztH=@uXI zn9>~BkQtuLO1JJz5Z!ZGwI|mD*c#CVW?|tm@g7X10qln6Z*sJh_JwkKszB+sgwUV@grh zQ>CR?ry+evlJ}Bz&UP(N<4n5pg@t9JVm_4~KAt9flB%w3y3fgY>(FGaaKu+1W@qHZ zjuzi_ep5gEBI=~ZncmssfLo=m3|^eUgk^h{wSkZO0dOjhy`TSr(roA8Ld8g0NIex; zd1UhB8RWwZwKz>053l;gLM`VWEJ*R$GCDV$bMn*3N@4|0OhvNXKx?nDVvn_3w9?yc zLqskr8YC7afE|$$Ns(M%v=M=zW-}u5`e>^g0a0Z7rL>I~Ri!$>8ao}yvs1s_FTqZG zq?aIP^H_3`=XTkkz1G$`5nPB}~Ep_?rI`DfzT6dekSme~lSpd6c z$T=SW^Bs|$a81&>$IXww)%Q5NH;LfKy8k9Jc@{r)RfJ1(AvT~BE+5WiPR)mX`|7z% z9GeC;&#I<-igA^1`8F>3p2uu&j{800$ZWrUK~EDx1)r9u6(lxez?Di9SjA27+q5VL zUIZ#Sb(5!nbhYDbOUw_Y!p!0eDr!oey-{U41G01w7r4vCJ6?If-6~&z7{eMJCWg*{ zZ_)P%ZD>=_%lzo@DK~%}lFN+UwQc;dn_Ah$bU!UWQ@Up?jQT6_tLiiTh*b{ytl*E! z?JBB7$luGIO*z$AKLZ~jrk>^Tuz}si1rpfQa$PpU9M!)EbIO=6_Zy^@_UQy)r3Ux>sWtVxYo06e$aOj8s-Ts#7P*Q zY8hJ?+ekybPC!Rbz=%(PBrsi!t!Am!VWgv&whiAX6AR)h1HI$vKKoQ}9Ka{4reBG4 zSXCrccAZNM$9q1lB7SXTwXzTB!$La=7WMFb&KL1P$jJ_zw+X@|4LY^BzyGGx-4krA zJl|hfBY5adfG3NhW+&e@LHhO3I^S!tl&F=QRMXH+CoObMVm81<+^Itevdu8I9Gd~= z!t)|!_ztJ$@u^4Sc(&Yc?tf-Zbok@bzS*B+5S@I2p50IB zg*U7z4Bc?810{p^cgemL+J(D!?8AG3`U^ZBuCcU;QKv{~U)z;mG32h4$w5H3@EcT4`js~JuB2pn}C-u_q`mtqNW=)z` zuE8L_#DD{Te%#?p&YuMY)3kgM5_MDwfL* zKr#8}OA*76x@dC4FrBpB@kd;7@_FIhK@OB7BI_)sVFc}-pVrJfNURU%HH|Lx;LLH_)^s-fmvO`HD z9x-VGV}-w8gMPrrRdgptVaL6#A>z_-70Ai6De)#X5@QT!z1(Cs`;*g(|U_6hOST- z*JQ}@1v!b7=LT+8PFNMvbxO{NXt*TdQ}1ym?DPmSCo#E92qQyHUe70lx+0DvWtcT@ zq%CYZGozE%gbcs_%>}dsgUvJ!$>*MCM)@K*+|>6K<4vZTm=E8Dr<5>6we_Asie;Wl zM|dBXLrpRW4K>#lGqipg+7+SPt>?Z{m^0XFMA2S0X~zf<Yl*V54&gS=9pnU&W|`LBO(!v6}vT?Y!s(K^?!V=rSaHIJ{lPgM`dj&xNEiJfQG`% z0*8~v0iY$X065$ZWHz}@Xn2mNKwJ#Z`oi;|N5sMSpBp>yHu@Fu15;8Szu6hT8##a) zp+4J6xWQEIY#LkHCQo6K#>Wq zhfmQT&&dYf{v5L=NL5romBer7sT-)>MpL4=h9C3_kcZo(t*$b7I}q77jQh`jw`N0FaCzZ83+lDfucVE9{qW@#dFhJ$)9x@`Uvd8%>cN)6MtsJtruVLBj(8o&D7pEDp z4!?-Ebc191hd-#ZE${4VV#e*_ayXm{C_odjet_UWlR{>H78!k2E5cl^SH1pvFGR5M zfFkm027B2X;ZenKt6Zugxtg8S0enoSHoE>gY`hX^!!s z7fX*8=&6!3ikf~PDB6b%w>$zM>(b*N@SnDTVEDknxEcY-VkX$S?kxt-b)=XN6yG3p z%2`OOAueh^EXBdn7_IQNT^QmHJ=aT+P#yGzTcreFDk&Z(7K%+@jhLd9?kC|iCAq;2 zpT6M`PS!Y1$A`}~e2RYFSzDsnIJU9`7AJ05PzE`Ych|%9Mwh6A>OaFa=kzCYvD8PB z3u_7@%nmH$wIC-w;sb1o(pyM5((}*R?JX5T603Q)jJOV)i z9%_OOFpca8M}1U+JZBhk>kzx}y+XVJRc1fD%u9Xfu2=cz6ju){AXA-87utl091>rU z(-1hJ(9ypS=1BtgLfjC{1XD>G@d7ICOs}zpU7MKhN+D$zPdikypIw$|@;H;6ZsWUm zJ+yw2JbKjgBQH8|hdmaoNpm9N@kB|UB^X`w_SB-S3GkcKfc^?oP2Dlbxl0Ao{gyOw zUB?_p4rOVe#-ZgDKQ^>NZehGdX zX=e-Xs;qg^JFJJ?ZHlngBZ$7-iA>l1jD3bX)GmN-0#(%Oy!I^GT%ZTwQLG{+yRD(y zLJ!fJZHSj4J>4wb!-+2`d?oO5*T6a~$t_I%u0c8KEbv<)xEb)MM)n9*OHSSnQ;VN8 zXvQi0{2?Mb(OMhBKcHYQ-*X*3VYi9PlE-QQ;Wjk0&wFjl)Ln8MZgYWGK0-**k$BtW2#V)S2IX~wZ;wo=TaP=gF>h540C|1Xd~ z68jTKD%=8zxJp>}vcfTq6ZSSL=;(AtA5;H$>69JqZHDS$q)I&q!MfYmY;c~iPPQ(iDY1E)p@<3KTi+z?i0noK`aBEig3I4$dl)!&!kUy9HZ$-o2<>f zUE-JGM8VK=%jEE}b)m+AwGWM>8Liu~64j;?<1mkIwxvN&V+YO!$RFk&qKnz%uBC^@ z8GEz%$#A$;s1KS2Tn0gWf4#R|>)H6rU$J)tK>CbJ!6q%I1!=tB$swMX3%8~Hapl)2 zs5VoclX7k9uV_O$k~G|rVqB?vn>hr~x9*X4!p~q#uw|2G&hkry?$^YgqPY0HITh|{ zVU{A>B75DD+6UJz^~> zfbT}n67e1j_K640{+mkcdK;4tK>gEloJ5231H1%9-YW7ZsiqsB1bG8JZF$my$*Y0i z370TKPR5i6Ojji<0(8Qx{y(K}in5QOYh#9zNmsmKXi^sH)OMUZ@5RXcxuo$YUJ9z|(i`0|`T2Sg-!4xWKv~*4Y zxtWItMT>zatYXm~A&GyEG21>#Tx{0@THLM{W%5p-g|F!EkU}RQ`EDQD(RQkG4w+0k z-dM1k67Kdj*}@c{FZk!0uQe_3GFT4N!3E}GGGa{hi(_b+8^&P6dKdn3cwwDasXP?P zQk$x`9{D>7sEc>emEFTZ1Lg*&)Y_1xO0kE+s;blr9v&_KH?8FRMPwXtMv@ z%JP@=dop3!Pq8*YJVqXp!Vwl`X&ART!jF&-(OI%>0!6M2QAtcZAUh8y7gLBDztIbO z*A;UBlXm{W`phf1nm+Co24W(9>;h-m*hM<;Q&5xskSwXs@A2DgLXp+apCsubjQa3!uD^gyc9lt6 zk&)}Gt^zr$iznKEEJF*hU$@Gqceux2SZA5!!NNnzl0(HckVSbtZr*RQZ7`5>8M+Z~ zAJDP%20>TKq&Q@-yD7kJ%WjCJ z(Z^=B4g2YktY~|E+iT9Q^lLcq4W_?)F(2t8)HL$2&sL>YI`c`VP+WHHh`kL=tk})d zY!oox1ZAPqh2a`nh9XKwmU-?lM)zAFdAKZ(NwkA$Elg~caf>VsEMJ0*LYU*kcON*oP# zD1vh|jWK>FEV>9gsi#4Ww$7wIg_RUa&oxMe!iGV9P{_(?6t8D~0Z*RqsCJm_S@O$F zKZT-QbCJ~e{ct;|@SJ!nF^!&pO3?4-Mu-OguJY~Ct`s+g_GcEXtz*MSb!qM)Gnq|>FsFc)5EN&I>Xw$!Q!H$h(U1D)A?7nq#zv4?FI z$jXxjDr=QB%8&S+J+sg}Lez^1c*&L<4?(aYb^`fR+cW_SpuV;69Tp0Tmj!QEzR z1&8@;G^$Vx$wxb&*#%5;Bc0(HB8I_{|KNl>gWjk=wD(ooI3R&|mj`%>-a60ED;c$! z8ZQBnb3jb4hwZ?rEX1d3{5lmQ^yJ^WX>GJn-pr)voZIvBj0*M)5s!3tZp5uDCA#w!Nek=YN6c8h{aa&{AYx>^k z;>#>Kpi-H(>{uiS?+8~ji!`K6%EJzZhD38W*#+A5YtVg*x@>^ttG)4gPvuvv1c6^X z*43S8uBTLO{D=(hTgZHll0@EAB>V}YQsE3+ezZ10TaB-!l_NQpVZHBv2`yuu3_fD!3MeFyoC^lE9~J0Yy+j1QT$((v zCT92@ce)7~r4Mc2PvjkfOX;|S3lIexU}ZZUY*2=-0zk9d$Sx@WBGH(+Ip|6kmG`L^ zu25!&XSzkv+Rasy&~=`nEFFaVKW#!(8H7`ZZ4idsHX*j8&@aK8#dA46-t1(e4c^aK zGig3tQ*Q&_Y2zKPoy$!hpzs{ASmYo_D{; z8ig+^5fvW~VbfBi0H|IVRC1w4Equh#YU(AeqhxW5c3MZ=vTyp+4Mw#jX;A6HexSzH zt3!+lB)F@0u+h+e`SU=44N2Vi;I97iW|>HDLo`-LN6TV!c2gHv{f{^VxR7bDGLu8U zIXk~mM`WzeID7x|n>C7+2ps1*KW}}3v(MY1sb;`nYS5S%QAE$2E?~2kyKdbtZft7~ zlRXhTS*H3NX~*2NVv=sQ<%-%G_3oNzLQvg8F-Wo)h|~JX5OxT% z)=kmHh&jS3-7?N;bo6_KItw64N=PI!}SPqIwtTJqPZQny}ZMBWO{t{kE%*q)b+ zpDRQ{zjGUE8UUEpha}NOt+9!Y=-M$j)6hiM#62IVRMfn3SsX@5s&;WG{Aqh(v!@YW zbmqxh6lj?=@KL`=D`Ba>3J-gD zQq12<9H=R7pWgw(%_Y=lhdp>Bn$O*8`|WN`Y9IHGqyrdvK;E{sZUSM)m%%>!%(o}H z4?ij>0VMJ$r;xxwmYk&re#3g7>YLs@HhcHe0M9$bO(%sUe|{DyZPie{8Hm_a*Z(h? zzA`Mz@9TQ#M!I7N=?3W-kQhO_8B#*JI|mRDsX+w^>28MZ5(%ZHJEgn(9e>aJ|323> zbMHB4pS9OoXWfWpqk{G)dO!e(hsF{Po6(Fn=`jTp^CQy?ur_VehbRbJ{zf!h-_hC4 z?~k>em)kTUt3;(#p5{ziL2^7O6HGL5)jbzTagHt}_J(|`A0%ipps_=Sw(Q}D>88I2 zQDR6Km!HYK?9m>dYBNJt#Ajo?wiOS`0#=oA18mSnM!Gg9u5lB#HqY(IzHFq}z+(8WY1HNv=gseV=sP$0Ko>$g2%uK=1;$-a5U#)xe;4zgEqdgZM$(Wle@eJ zZtUaNR^a#Wjx(;2HzNH`I>-qFvw9-afnU~kIeim(cX;!qDa>4Gya+?9#y9E85RPa zo&z#K2-H#vbQiJlR?V12z}3dk+)aSe_Cb#Cby(QTni(S>Biw#JktI2 zu5{m8I=N6sVE1O|(euvpRed(s>so^_-)^!6Ndov=e7-3Cs5!K+~jFZuEtxYsQpl!3&z_^#?ee zE^NLeWZ;p@6@)F~U`n+s8ThC$3PtEkHZCa#NBCMxEOYnqglVrda2-rCxBU_RIhfNb zV%s7#7d=9C-XB(lVT!zB6j2F>p10lNK&@s}U-q~6PQQ*Y8tVPFX;jY=RJ-4smIkg6 zF}82kVP$c&E52j!-G22Ba_t7^gyBNQ>@dyy-Xbu>Z|&luBZhqAwR4y7MrXVB!FFtz z^tAjn%0NEK!!Bdsp)piW7F5Z@i0Epu1FXEcLD#uEQR6#LNA#V0d7J<90`%PNKrCM8 zoKglK1-=4W)MJ-O24c5PesXhgqc*!e!?2e5?}Xc<3EH&2*AKZ=d7Jb%eN4HlxKdP3 zI;Xl03A@2pPzQ$${)cjs2$%#_GutK;kg9HaU8R_(FX|lE%^mb^0bv2bf0K`Ma&j9v zJP@~PKpu<0{lu`2d$Fs?LAo7=`+3`!h<7CA zR9{QbVg|M({pa&NHoR@FOResWmjUVBZtBR+2ZXk$n0+}#YP;6%Z-6z0*t_UT`{q@) zpDQsGt6VQI!Mv{JKXd2y!|GO^^v8{LpH}8k^YumVkgX(Khx&jS&}LZ@?yz8oPq~F2 z`vvM6LZ9}%V`nPFD=~oIl#`TJ)X@>jUqER5)wsb(^UIs1qNR`q=Z~Ixv>ApkK&G5} zntta!D?-nbdD!xbo}s^NK|sOW*Ns@u!I&4E=Jzz7%eb&X@g-aYAw-twZ`9qixS4R# z3v}tK&12hT^yV77xxU8hDLn1#{G#&umFo7%%ItHp|>f z9>4IPR--jVF6mgAlmkPeBizs@)26h z2n{{CZ+Q^B0SVmc>xd0TTist4%gDh_3(dA_S-cal z<_JVtWv3G%i78Z@oRGM#O?h+KzyBO)0x*Pl8S{d?Su7w0mL=q%>EGP#Cn^g7V#ofk zstx`8ZkK>m#|fEwLg4S)=k?CPvsrKKxf>(C>HPaD^_=oe!+H9jvBj4@R9Y6#e8p53 z!aLs~Typ&^m@{v=zq02dy!2mL6}xM8_o>XS%wJqDUn4LkVo1}6u(13+Yq7W<|Us|9n`{eeQoQs3QEK2s4104E~(Z-6^|< zP~1>(D#aVZz`Q8dl2T$#`V22BUQp>DI}-*!fzMYQC=)LcmOg0U1`oyV zS>v(OHX4xDjubZI)$Jl0kF~Jn=ZyYF1jHhR>wJbhP*mfx5uN1o`PkMtbTS$HB6y9N zf<@&%Z}XnI&CL6Qg}Plx93a*7gE_9hPxxO@hxLy)yH~1S|B$$!d5#*Wu zuS6x&hUY&@9le{I$f2ONzvg3n;lelzlgjl7m!F8@+K9yt&+v;B!8N!T+^f*MYtHU8 zehxDZL2mee4#83jLA!bg*x5aKhWa{HdjCEk`BnRV*545}=@2lu+%P9Y7S#2R=|EA^ z2hEeAK6iMrP4{Vfm8-Vuf>(5v=%J3Sdj{$A9Qlwf_^=rJ$}_D?=QcGgMmSKCwZT+i z;w8auJ+`5JjdzTh{899ekU5A)pvZ0ZYUCSM;1_AdpCTxmhSucQwlbPT&-+wIoSwm{ z%enTcSPu}2-ivi`v9AhRVpO@&f( z9aDsvL9R`iN|?0iuzsXFkd3a77hORPvS-*?K(SCt@|}a0F#gJt`slunon!DJv)9JC z_3%~T`j_K(z6SAq$q4l;@mFte+hcO;6ZN?u=g$26mk!b~gV{Xoa#)xW$|}KtIqZKG*(8341DgdvO!aRU zFqRKrRB`tpe1njPJX#GnqqrUybdDGy%UVPU>1bQbM3~l?2!D~ljm=(q3Z2W|Yx=vm zU5h>EJq1T%oN3?N`TVdCtVWp}xm?(;lHs|NzEgcI|IciP0pmfG2T&sc$PzQyp6%Rx zHXFE*bB=jlm_o7o%=4u6e4F-y^Ac&dT)L7@;<6Mr@^_yjG}A1m-@V5KIdO|)(-+AR zZ>yJdvNMq6m8HN~bLw!qh{v@Q{b%S`L`gIUZ*IyT3mcz=2OTpb@CbDv1p#ueIos_> ziKuWE97^NrT^9)mT_2w30*?9KjGUR?SB^CY_`5HsE@i0xYMtO>vUpjcH=#T))GFb|s=jS-`FQF* z>RDV`S$afje%D;tVgG7xcUSp1VCMK*a+F%HqMOj%8Oe00%Q?%I)Pl98(eU+5kA?54 zGn_lB98|3DSjP4P9VCv_Z)DCD#R^z(Az1j;d58{l;3!K+1GlagM@ZTdzWB+Lb>jn4 z5ZU#p*&eV_d*5B=an?fy$j6(4t9?|?dgr^Psq)dWWY0j1eY;x(Z364mxXukTS?V?(-889Ar=;}}NXMwHiA9RjQhunpOIkaG-@JEqVFge4m`I+;Z~j?@j~Sq+y7Wv`vW^sd7atjI6zEnkZ28BOYo>g_Beipc^6)hG8+sm{?8a4S(OUc%u`^rz$JRoGL<$}}geXN_~|WI>f6o`jLn)If19s%{)U2-VjO3^Xp51Z>|rsliJe-@5b03P26%>TE{% z6W75&_YFtQ1GVjn#QcQAAU2K2>_SQ0QvRL+brMH@;@%Dk(eU~q|Ew;1T|EWRL0732 zU`NjKd}wU|39Vzfk}8X~{KDb%hYHmS6HLJ66eMu-^7OnKwe^wX8wU16zmd%Iun+MB zInmA=N}?9umnsY2aTHp}fTI$qbk}$r!+HtIU{3eS_Ge!T>!rXq(;TD|T+eVaMoV-DJH&POIlLC+vf#1a zA0?i?bfcY8!yk(hj=(UNI;5I^wH^GJnri*nxN=QN_t1?r?TwyYi)4P~Uxwp7P5ALt{QpXr7e*_Zhy)yX2Q1~MaJww0~JjUMbirHX1M&v6LK9F!g z$Q|JLlWIq$D-|6k29YpL22S+({k{gdV@`YB%nJpYG)L!6sNG8#gn>El=fM$QM`-Qa z|0|K2t5F)l<_PR(s1YZk!N8?Bmp!En0jMKrAp3+Bw2+Ace&mV*Y|E9nkMA zHqzWdiJXtPj-f2MR`aflT{I0TpBCX{|5QI-uia%SW`qj!?uH9c&Cgb7(QaV(>ZDwE zQ}Sws5H(1tgGSDm$4t08JH4$V&TQX~0J#CX(`E4$0B}7@7!!zkWnp^5_fxA&<{N1oQZV zN5f`@svEZtNOx+S;F?~qnZu1~h>Jb>MBN&kR>qlMs+cdMA`k}#C@u5$a=Q)$S zDO(I0_?!@tzJsomG8|&(kV4d}Nl@-H* zgG?MdGXR{cny>+JUoI3Y6zW#t%vSJvfg|tpjeKJ2huF`ixfDAE6ja#+b8`G+F@KNc z^dG$dyAp7@(f9FlcM`X--t{DgCL83*Pi?je$W^Gpl#JM(#V~`&5?1-+<+$r9wvn?b zQu2t%>l-SmXX!WKZ@V#9=a;R^e|YlkeppHb8^7}oRtWi{O21*({uRp%y+5Z~gC?$v zKkI{b0;Vt5_G{;Ms-B>}Ra+}f34l^b$`9&v`9knF#=vGnyRj#=d3Km=aV+Gk!d=p< zKV^%gAYl#|oG=3^b#ow{EqKSd&gA?EG|u+RyI!+*!gWSA#n}I67n}&V@95ZCYH6rz ze1UB{G|P6gYZ_sqR2nQ)$mN>%jK!$cQW8|c@zt(rW5mgkv$ciNw;V+&4P!ug3U1#B z!GujH>95^Rv=g4?bY3#vmyLgAn1B$NQ>x(%;BJCQxg(^>g1%l~&=Bse`(shjHf`7d z>@X9kEh!qD4z{nO58bJxZ=kbOPf}9}fA@1K4qmP_e+isHUH*gC@@%T8MqL(NOMBT& zuGJO(6zY(}i!A-7D=$a+RH*ka*OeRRcl?j}>-aKS%pX5$Tk)J0&S#nbH$`qSw(O|;ZR;4W=MH!D^!)j;bj6NhV9E2u+NpR*EAPiFbb+9TZlvGVF1aTVLz(I;A+iehu;P z<>J%G`3eki)Kla5pi%~;goYj@kWmSOQ3Nwh!KVOXH-m8@kyDH=^bp2!=So;@5rF*f zVR*g+v~D20Jfh!SvJucIWv3qT_96rt_AH4MZx&p`4ne*X?O(EUVsA=BDz9PwR> z890FrY=^9U#dw!)>0r(raT(W%g!ym-=aVbnpr@Ro_63|k~MCd~)BTb6s zk4k4OkT{|QfP7ltI+Radfg*-h3+?H@l$Oif?3F&t*WJHl1>)07mQ%vs zwWWwNvISWw9bl*L<%tK2)9Kd(SmBAzBMm6SBpZQT&b5+*0`l{bN_=(j3Iur%s*GDQ zd<-v{iWF`TLJ;KGP!Vkg&cpqfB_R8%YwdXv{80vn++bGFNP)?F`caxKSib zxs#tcmH6?rWy!IJCoMRGyMz+QVo5#lrL#w)i3pzwU(XK zeTHy6wBOpnUFygqvPK?B40aeYb|>X|1lj{L77LZEd3B@uuj zWzv;5oyG?K8axpHk*}71y6@mQUYE`)$;(6po>B($+Cxmz4o(L~_w$&*-zNqrFy>mY z54{|s`|n9dEoZW2-K@B*{VHpyPt8Y+6t-xh%O|7H-e9jq8eR;$QfJmGLV7csW|{he!KniJuAcSc!k%$ARv*a1LgdgnmR#R;gPoEql= ziR=Aa4S2qe=s=OaYDr_ts2o8cWUDsKU?dHD?%dphWhDB)4w5Hb!0&5l6WuWXkyZdY>Q&77x{;kJ?BLEfT|0(Z2l76Q8j8Z4 zhfiOjf*b!GmvvYOU-iH#?t{$xt^jn`Q)u>WD0hL!7>WZ5!Lb(xEOOdw$x6FQj;b_) zn%pf8+s?{tM$YObC=A+9ZryZE@U{X`j0VMP0(=Ziv{xNx$HxvRR@8J0Zqr?eYmVrl z_ztc@TGWSOd3&Md;3w>XD@8d@-wUdy98``8PKPEWoNG+JVK41YFHrckE>lZp38c>OAR_DrB*KS$Khihi)#;zELcSv?X1+A*bN|;MsxU=a$g^=FYo;FmJ$;vDvHpXz zKjp>niW4f#W_70?oKuEq=t%jSbQHsgY(!8?eHk~>1aK5y&f;A`&-_(f7AO@_v`Uk| z*9HGcVCX!$f&HM+ zrmxWA3X9G_J;`^|%4a`8f|3C~W220V?skHt9BuNdqX}=-Fw!@UGt36b=B0HeU(D+- zjLMpo^4y_C4V}~_#Xnk&8lRdNKrMiN8-Tk5`m#waa<;U|n^9pIFq%`$ES0pmA=Ju= zF2BIb1P7KR+>jJx692?^=OiL`DrAf007Za`|EmNydcQ_werDtaHWyt3y3!CpD||?~ zeSQ{`<~3>77bCvMAf}H($Vx8ZEjWU$^THtIBPg6uR2*%<=Ft8}y2nmnp;Ab6?!kN8 zQ;CMD-sCRJF95@Z%zkbVVmd|~c>-*G5{R78ja-VygFYDu9*kUy+0j9}z61dG+XjfW zBXhFcdO82YcuWi3#Lmtu4zlM4+jP=9)Qau>DsGygADB%MO%{jk;DDx`Z zJMqqH_s!D4IyZ_HKT7Hp$Imf-(c}ObBYAF6^4hpVtwv*_$r1tZrBBxm!Vn0sD(iy( z=8f-1+_!Nd=-^)(JTGGy;o{BP669`QrV6wCLSWaZ(Wj;}H)~UxN3G!qNU(+m9*@|K z#IU3`>h&P!C8kJaYTptnzwRjTMVR#Zg9NWv&l*zG)-Habcu2z6nuXgauD-+$A9-Ud6|5b+9~vY<Y}9h;Wd0UEc{`*mlkN$~?sXHxztWhPQ(#HR&y5iV5 zgSVYWwLX00gK6QGHF8KoC`wZp%K$5?A<_C13W1YY?t{G+oPm{Y=zH4EgILG!vs1b# z?~Fi`)IMRI(Rv>?c*K_}kn)!#5t}1(g|z`9UcM62X44U~y}FX=f(L2|Q^FvaQ=Z~q zIb?7%P1&MufoZ+*?|dTq0-3D!5H#vy5_)ebQd1t+6OU~;+1uvKdVriiit>m)^S%sF zgBwqMFoO*(#~MApf~qrx+{0MQi&2hFI|o#O}2g`YsQ_*Qo9c5p~{S45L(~ zRxc>N&rkif6QvE0V2+hvop(E7=Ogj6UW1bXhY#$FvJt%sf(26z&BKE_>Uj;t-o8;q zjW%19U2o&NJyO+5y&2$Vj3U|$LUd=f#A~~Xb_VJ$1eRKwo%QB&(1tc>FldWeKo;;M z0ET7ada{NFo1g|FIz+EJW8nRD_|A1q;*o@hM#yh<1nVPi1x|4+mxvDgia^Vdt?T(6 zf`YN50>%L+a)R(Q4k8Xr#{(Zq(CF#?*t&L4WnM18XHNMKn_c4TGMT4JM1s%jHwbiB zH?M&Um>leht|ge*4j}J z1QHhFo*)o{oInQH&6iR))Qv^d(w-4)Gt?IDb))f}mR_vmwpBV#J3xTB#i*7MM(bLR z%PQQU{d@joT1b;Y|8uT6BE1tSKXuQ5a+qX{BGj@BNvpjAWp{w(k&K5%{@AnnKn*47 zOuzpd=DmBvp}qczEovfS(4WP*rF{pX3^_5O$9P|BC2>_F;3lZxlJ)pnAobmWhoBXs zxFEU8W`+OlRn*#@mbI57dc2Hzxm+RfDppT}{uIjQZ!v@4IZwmar2qkfleNoo*O{e| z`F|qUlV_jLi)W3HVYBDl6KD%(X6zvpvHIdQkrFiU;;F;cs6KmKbrY5!?kU@H^)LQ` zRXT62emlSL|6Isn#njrOWnCVZQw{BJ$&vsJoQ_O??~M78t@L5x&o5DDCMnKLVlh|| zZJ~QTE)6$uqEsp&sdSd@SE=O^e22r;U%k+ebb>)LEQq?bq0v%MvrI12nCSOc2|PjQ zK^&-S0W{~()6o!ynE&!Ud&B)9uO9cLlh&7IJBnJ@*rWoF5N_axB@;kW$(zkwB(0@MdgP9wgqt zC1fB9Uk$z7uDF+BjiF|`1CBx{RpEU(?PHCtcY+gT z)kawu+GC_zY2UIxzfLaY#;-zd^_QoHiKD;iS+f@HzU0 zH@I$W6QRYv2#_5KU2YNLhOA=t)W}r5P-H@`JG{t|%CGk4BQ8J98=lci8NMUL`qLr?Jx_{fpBK&{d=O;af8NFIsz$ED;1LJpODi^TNg0M zS21Bc9FiR*TW8A@vOg(ITSvQk&$(%;b=|1{YGph0J<=Y^v$!~_UeEdx{CPD{5#C75 zj3EeSaz#B+R`B*E7tN#x7D+@&UB+1CZ|(8EBz=`%K;mykpfk6G^oL{sZJcQ7W~Nn? zL1r{Q#@vEDR=pXzh~MSxpx1mcpP|;|_a!otz$>vvf_+jt zZ~;e_e`@d{xt+5+J4G$c&1FMMPG&x8-WF_J48C7^@=Iq!^bT#5c>Ul9r?*u@p&ZeP zvt}(De`2QJ4^W4VxVd@ga>UxK9U~7$Ruqx`T$$!%F;fR9@|Hy@62$!;r4g!ce8v14 zx;Td-t6u-><_w$02ztw$&)uXM-}pYPVFdvM7KnlE89?GDza{pfmZV&* zn)yL$-ljoUJbPm_r9R0=Oq%cXulS!UHUEaVeY`9af8&Cq@ljZRwPve-c2Bvm&JK7o^Z3=rNfmKp& zf5#CfCZio`QMYS(MZO1c-sQ8IkjGPD&=Y1Z_kG92>J7Y6pk17|3trBwhz_(wimw;G?Avk4 z757Kx^9?=Gxy;oN+*aQ0?*NSN<{fhItQOE(fe_6RF7VF*GqMaGz&VY~-aeQT!SmpDBMAkWmXhV1Q z?X5rB+lfj_9S7HA=&VLp*Y+N?)O0u^!VM~0aN*c9U^@F(LS|j9)E8Wz5>lt9;N)sI4=-uA0>BdnL}1{Au*d zYw<_d^R4OlKCc8J3Xj62jK;}V*@s9{s|VManNc!ropKz`Om?ft1@sTgfQan*L_ z;6mBeS)(pq5W5lJ*LExHnGD!Prwg~*Nz$YIoSXtHqDcBn`864LB|vCtu*gQ)lVInA zgEQFz*$2bQyaKuPe5zIj1l{~g5)6qDumI}JD?+6af#N<&SahoIPKCiTOkHl?1!7R# zTXQ~pGr#h*eQF)^RwO0Y9 zP(Q}c^%SsGJoqtC5oBc;@pdF!23tykntbx>%p8yCi9RW**|BKH)DN)VcA4j-o_^~; zYlvqmWq)S21|rRm9n^qKgW&%gV1v#Fay0|5A5 zUsg9;0{3+WGRtA4E?NV`*Ap4$t@ly+vHYcC%PUl#+ycK&r4S zg(T9fKgw#vA4i=`m3_*<=LcpfBN1gy`+sax2zHmI9&nO%RkPwWfBnzui?9A7{#4Op!;)m@gS~mS9U^kv zoyufTIn{US*(qnFGe_t~UvIHWTU%+?)PC!)%E~yh*_i+r@!8MEcJ`JcC<&x6TTG(4 z&L8@LpSlSaZ&@gC>#t^c7qKbBUv?}ZTXA5hxb0uxNKKdK6Cze59W_$E_S87uKXIlW zaC>yFppRALHhc&(^x&7C3iN8_u(IB#t3^<|!;}4S0hI{iQJY=_aOMD0F+#B#LHUzD zz*zDAWaX43sF{cPjg5IVh62yYR8@Pz+FGUX!uKs(eMqO)D(1V}uAO3NpONbYs`+So zb_x;H4ufXYPrKbd#Th|K%7}+}og5NP)Ak93~4?lE0 z=8xaV49tn^eF}RQac%C_>=E6)O7YeAunywvyPsvNT?EBWK`d2acal5d***}N>~E0k zl!&wp5yxF^LqDgR4Ey=pfoRD);V-<}mAz`;3As`Cj=IAL^pKZe#VI5Y9P<%BP+wFr zYzq-ZlbrgdxdfjRs7v40L!8?$i4Qpr8qRLMkpAI3ze@`7Bi7#cNMEJc3mef)Trt3i zmz_PYUB&6zjZ%2ZBNd&VpF}hh8dn`8F--D!IrYK6gREl>)#iJ9{YG%8r#71<>$^uP))CdoAC@RD$KTw1QXfx!Y%R^I*9zq# z|0RUn{*xl#so~V{6VBzh(^uU=xakg7XdwQg7*Y&vg;YpAwv}M1<Y{2e$KeswfxF;JHzY=N)32g_b=I6GHUT^ocejCx^{v=~0G)%AOPEv~|kLmU&>-xz+pygQ3&;1&vQ9 zA+iPBAgI{vqNUVipaDi!-L&zF&mHb-RErb8Pt;ZnQVs(Hu5k5bF1;KhZrCl+CWM9Sq($Z;CCP0u>ewIjTr{73oUQl*hZ^LUj2 zVqJSs)Jg|-b%}RZW!K+anaa!&ToN!Vg4Sod`gs`6^ij`l1N-6^VyexNqf_-cJ51tF zudVSwnIVb7B)~HZ$0+(zrZ#K^thjO{T zS`Ei_q`Y!vwW=<6BJY5;&u^Np3WjH1Z`WyrzQxa`zh+!oNDBnmaa+!!{5xrU4>Wyd zjF2+LP{>~h(*^Wpp{vKu@5qPhy!}q_c)B#k!j-L*<~fMZyeP|so2vTfw~a#AB` zQ8bc$CoS1Ao>CH#4Iw5bsiU^+#}sApx-eTU zkfp9kkmS*J!K;vrUj}G&_KA(oCJg0zkPT@x9vUJ@t3^@EdGk?x)KB zN&ENN7Q7#O_7ZELP{)G3lw+MY2l#MrJB-M zo#z^Q!0$Z}Xe8Tvz7s0}CXKu$CZMQPM$>KB@h)?4-ltP76~! zNHwWflGNXxIxro+dBbLD8CMKt@=Py--A+7^;_|*(860yq9MF8lv8MXDg(%kk_l19) zmI#7a$4>k0(1R(SCwmgGmS$3c0Y==J<9N}byv9`Its`mm8<}xN3S35)?JR}zVJdGjvi#P$8{|zKtyVEBbt`#!s^^H>DdW z9RUIkoA^9hv_cCJrR-a65-2je9B<@r^#3HsfboRs8RtyasS{@DOVC#(grNkTWgL0{ z^i_q%o8JFZ>5}t!8CT(JZ($#9`x%0bgs7<7C+UQeHxLn&RdnXWz}IxXOCRJXS-)m) zxXDE6g}r@wI4a51P2)mW%R^Cv0&^}cqyD+Z^}Q8y&yWL}-1@yeL^d47?>$GpwU<{6 zV4`BLse`xY*HlLNaV?L>aj@`k=BS<-(>}roY5LhY8e0r@r@L_M^##LlZULSI*!7G% zP~@<#yIA#V0GxGGTUbyyOTd%zurM(A^uh+3Fziee(y)NsH?ZVh^2q>?o!3-9quB_b zJy0Q+My}?~lqMo) z=Gkoz!2Io}q@6FwCyXTQ+aooAU9~DN@Lr)${5qS%l+i+_lxln5XKC?2#_Q(i#aVgq zPoHATy!jzcm;WP*nLTOQl zkncKyD_i?qb=iJ)5v<7a*6j)p?c5u zcG2V?(Q$|KmEM4V5Yk9u-udA6KwxQfn)UM*GF4Qi%SfLv!esUmVHGGpM9w$a8n3LU zt)LJ$LS#d{K3Sz!p0Qs3u|l)ZXSDohKEPi3L!c{b4Qq7BCZ@d=<;$k{1G`GzQbN$t zQ)Y2p19b&5xR|Hd&^YQkq`$>WeQB}i2*gv&X4ahYpqW0nXuQ)R%?UG?Wt*oS zxhQM#yTrb?F!GW_Z{YQ3A7bFKKiSsX&+ugrO;X-&Xjm?D(>8U;&_BA>4?&r15NfMv z-|5l^;(0W4MT|`3);yY2pw_KaxLyDKvf*Ho|O@g~8c6kM++l@nXT&#Lrp5NP`n; zf?HG%*@}s}wP4j2>7Nrs(Yr2#4Kf}1&GiewZl=Z?2WHcb2xWuCxzlC*eXZzll!N%Z zq1R%Q^e8gu?szf0Zc^5JAcn;y*lzBNELLTnV)Ciy@M<(qcBFn9^1$y7;3wtp9}pVO z(h?a7D=?V24fY|u(*-Xk(rs_XJeZO$^Fw}|+fDBH1hlN@S;83~R+L3TV22J{l0cR3 zvl7)3SC7V>(9dMZzNa``Mga-9MIO<;xL&#^R8$O@U;Gn>z_lWTg1-gtlIWW<3#?*| zXHtsk9LQ*7++@m`MU>_o3GuT+&(X7M5}hb%I5|u3SnYU?>ro{C78w~SXFqKPvH8$} zt~qQMT7DY&XuHi$Akk4j9ge%WTzGmS$JtuChAQTGppu!ndePaJl{w)hw-jSn93a^> zw%oark;SC*vaVugzZt_;YBO|24Vaqs8Or$K$g?<~UQ~#^P~0QFQbN?q*7E?l7rRW) zD>or~DY&O}-YVh0G+MYDB<>_lLB{O=UME=*o5aKw+}xP=r&>GOX=-8o)JuC0EtM0L zYWaE&(XzJRq!t&O5M zs8SvBfc;4z%}4@tWp#0o?&G&MPj0hLUqyc2JDEGrkKc?%s<7KFuzA%8Pd=O$2e&N~ zlyP#>_#dJ2wqGoHqj@6+v^qCQ*VW|LwXB1l@gG@Mo@zWb>d=({3W)g0=f|vf3zS4A zKEEwcVI`!Z#R8-Ftk6sx+vlAV&(B#)>ce*gO3DZL+?%p*tAPPMep6&EgKx zxT43MP7oj5((RmqfE-37*55@yZ5Fl}_zCB^QASW92lOKnGQ_Jr5CShY!cYlA&BJ~> z1IW*7kVFWk!r_wv438@Y48D@XcO97Tmlykoc=7n3lclG5cYzXPj9dhb6vr>Tz3uv*J?20gZ6d?Hv@!8>o+%gN~W~lph@V?G-m>NNC zLu@FF%Fz7oQ$$y~>ue_VQ#JN*H!Y&j8K-6jIWz+ze0k?04)cpfnO^x}?P`-JO4Wux z+yRYvG|-Y#kprK<_HH5Zwz4ddI;OrN(Kp8D(jvPvgPIwIs^dFa8Xnc7_stuuu>QqI z2uowxtwLr-Ncp^|Dx@V`^#H@6P!ZfuFT{lmbbA9S@PNbRUyMwFL_3Pfx(`p7{RtWC z%K^u2Tk#Uk2)D+U@njd1K9nfBF|+$^7kM0SSr_*GCfo%7X(}2A-TrSQU9>VJt2@_S zQ2z2agfe@h8S2v2n7pMhC%zuY?9^GU;DA{m_U)W1b(QM|U7baEWe4K*6-dN@dI$Z{ zWrOEh9;_?fSkF08nmdsnPcXew%REL(3Af=U4O$7^0AbJClO}$ortkR>yo9adI`{?} z{d3WqR&g$JtR&N5LnfdD>dVs=i-M1%`)ebd(h1_^MXQ$?Qvu%37~7|n3t7b(Bom(2 z(xLhii-@UQCwl2^&f7e&&03e$2B}S&CSBR;P(g%A>b@=6u+i)LM%MofCcD4kiwd>CeHp_Pm|${wdAn;V&dE9Z1P_@sFge zoGjEYWSiDAMQW?xXNKO$4bvF8B_?^$=ZHVUt;{&Zf|#E9Y0frI`)==M7SMzcsw83W z{{tC8=DyU@w0`vmU(M;*67=m9PePU8hy>AL*m{jMIIgAyG`@6Z^O13=)AG@A`_d36 zUT70+Lyq_(e45QvG z+r4Ra`BHQ$1p$Xjk)Tv0AZz$#Z37m=a^v%oMCEC3fq$C5fJeK2i(KFSv$9X#^&Bh*IPfJ{PBhmj z+%7!21<2Z5$Fl*DfzR(onXG?+ZZ$vxM51fF=RN<*O&Qo(uA!UQ?E=pGkMyZxbR*Vc^l001BWNkl|8|zA6)5N>EHdp;S|*hCm4m5cG5lIIM(4$pJS#^?8^-EUK+nQttabI)O*00BJ{c ztc;_H5VxJ!+O+C;HUaYO@Ax`f#-@oV@UC8n7`g4)5=5?nG!HD7z_I~AHiAbpXx!M> ze$cNM|9*5Wz|AiW*i#`Z;jtWZ>jEN;RLENOu?&!JAA;?b(tAbWj1fR89_Ze^X=zsi zvz5UoPfse@oI1c*#2th z8ngz-N}GCPBsI6I0k$m-vbr`)Atw@C^q_~-8@3w*WQ|>IWKr8z9nApJ$gZ{njQ~Di zzaq9~(EuQw+SNwDSP72hc>M$nMVh}(oWi3WfNTjk7R7|5XMv;uMhp_~B^9JFD5GZ= z8q4wb2jEx*+qD`Z&06$00Z6lk;!b)y+B!wP0gB#10-;`JxBMwj9(gkZa$3M*2;>&` zO#J2tD#n5q0A$obw*tEWX;*cu45vLH%~Z##a}!cXk2Sw~z?QLT-u{lSZ>o@10BIC! z0wkqbW1!5e)dOTBV62-Lt_MoLGnZ9)+cTr?0n*IIjs{?BkMZYJw(>DkA1eW}(!MSO z7cT~9a90>Z1 z!ErSOpZWoO<2lIGtOwc?sVcG)`|tprD~E~0S-ju{{h`ZBNx%mWL+YDvw= zU|3!)mFI^X$h~JD9049U1&NB&tfXcMOnnRQ@{+XXbrWCz|Kj5K_o z-2xh`2uBj!bkjHR`Zh24oDMH%V0Qp~c0SqiG(4wD%QZR8#qfvG?m3O zWxDCnAsvDFHCWG}=|y76B_g37Bs>(03Qrz*j>CuVZx_(CmB+IaQ%lqO)gQQl)A40d zAQLWCq2(2r9DwK7VP;KKkbUS4o0Vkci04Ebp4F7`XltN4ZeQB8zSq`f$>r*AoH_+E z8i1>IA|F{WwWzDchemd_(Exhc0NQh_pu4zytn6x=3Z#|lXr(Y3MJ+r6ggo%ZU1Ix6 zfuofIX$u_7wzGwrXlp>I5f+pYkRq+Sj~+5nm_GPmH9S^|q(@a#_ z*j9xyWd*V5A0Z#wMbSSh{@XWT`)|E3{yG3UBzTW1wyliEZii)qzU<8?kWm-HoRtFU zMs?g6H*MxW+EE=h#@r_C>u@B&FaF|J2uF;^WycDn*|>ff*1rY*o_cf4(F5(z*Z-`c zjcuenS}BlQd&InD79A@5#ukv`FlUh-;t2|P?A}@Z^OOdMgYdbBA(kyEkY2WrT!KgS zvAleoul)NTaP772HL%<8*y5{2D^53`5umW*>$9cXJ@`k{>|sP z>-cL_B;x=ejf!NP0;3f;HVcrgfX7OJGz7;Mfnptg+D>k6J)!tCL6uf6(HJy$1*_Pq`&(SJc0g&rofcz;@1mz9D&^UNp4A=Tqpc@FX4Q=d309jc`V^la} z1d#9Uhtx}uTeSwr%Ho+yfP6@YJ0IJ8tLkIx&|ZQ8KVIJ&ci;Ud3=g;M>oz>L3LKZF z^f!L!3Z7k03u(M>ClH3&egTig0!*$6)v@xxc85{l|5^%K5&d&NGzLd7ERv}NM@=iR zvND6FHSVL4({qmcZP-;EJ7Qi2=fZTveXJ+XaO~wDK-h==3;QALU@1YTs$&g6`i^~r z>pQ2I@GZIqM!WFn1|VAnj+Fo@hkz95iPnOm9dO+E9($Vq{D&G$Tvp0svk+;us*KY} zbwRQZ*1K|a99?DZMi0sD<7efZRDxq=Y?I)o7O0TrLK({kOosp&Z89CO79g8Z9qj_7 z6VhOuf@Y3d79S(FcHh2MEuY@Gc^B>>*2qPoy6=>{4 zJLj=MoD3hl<@?j6C&jLvkn4u&LCAPT?#4_U=3{kTb$ybb!KaJF9ywg6V98$8x((a~N9%jv!JzPp#R@fowH6KHCi= zM-j>J7Ws)SgGURdmwe)Nw!jhKi8=f5SeXy01qLXF%;}=hsxp?48iLhvnAs~ab*aRO zYbEB-msr^*k&DRmJ&`5)qRjdQA=j(kSjADan;IIzo$>3{ZntSGpHm(bYIgtJ) zR|qWUKo0c6@({duachpHSrc))xsHa%Mgt{VtXhG_ER6CTt%U$sD8TtHU_5azXeCik zl_6gc?YGvKA(apSso0$Pr8wy4nXsIy@%F(umvyFjbS+4tSaA<@9yHE5$aSoI<&qE@0Dq@gp{D#&y>MPH-iH=Zv!J4&<#TvcAB94Z zVo~PFC!gb`mmX;6KiUYneF)6sf#)C8zy4xHHu=d5U}_lhs%VHd8-p|Px=#9v?4i#$ zf+BkuRmV}yWSwZxP;K0}S8{2?JjacbC&>hZodg4&Ajv3-N-)%oEL)EZddN%hy6u+G zxa;L^b{*-VBfd!uCr>?5{e7lZ#mkl~CtFunn+;xvR+0Yf8KnR9GKBY$_4dA^*FZ84 z-#Q|;XEUDdu72%14#n^*vyW1vwjNE?nmlCFDhs=S4)4+dx*mNWU5~sECB0zyp8hB7 zFB+T+!O{*m@>+=l?-s8kua#Kb1u27tHarOp&w7C!pPI$9UU)^X%a{(g7sS?0%sc33 zF3Zpz6x&xKGCBC(L3mt-jw$f1x8`#@J$c@>rCNpdJ~GtQXDZ1eO0w7x9If&k&Ej+x zPB~n#Ab(1{DkD@O3?}1n@@28LJw7xL?yu@h^R<1`4EbE#10gTmcma4+NE-~Y9ex<= zhRe6XTld1{=R)c&kjn^gG@jvL?rG!o8+(1^Zhkd-=cZ$}9uxgv3k8Ko9y!iKKRB$@ zj<)jXHfC1O=x@LESBXnuwBY5iSn&v42FFaH&W=`O4@1!%cohEz;DwY9QpGqLnxX1w z1dagl_@Yqx%)l|!jUuZ^l0>m&YfF-h>hV*sP#}x1*@@m0Y^XF=f0qU3=IUo0X?g{A z_FFd=>yjKNYDteTB7OD<(w8QY&SYSq6MptW2#rGQ1ZX+e=hlwuSgSy;reQ~S>r}^Q zyWvb=^Hm!|C1BINAm`5BbuQUag7Y8+o%IfM0*(NOmta2Wau7?+;+y?0g^pcyDx_7F zb*c;2#$aZj#N^cyQ~M>Bw@akDq1fQNcbv!)Jt}5)C9Bc-T$0R?mt41}CFjM=vaXD5 z6AI+Z--d*P>(;IU*{t#yOT*-n#6n6!ei^Pdc)ze!*JlB>@?opVruU86c~e7} z+!@eKp6ay%te%4CX#INF!2-qI+ffeAB4rB{i+Oy$FskAq6zt&s?>x%WPk-x{Pk!>R z+6tuAn9x212H?0dr@!svZ{?t}pRC#q3n`e1ZGz)mTx{oKRV{ZE*~4JRb`;rzB3ECq zX)VQkc1j#{ML`m z*buCaL9!Q8P3FaKy?dRGWA(C7Vj{rQ&-am!s!e^q9eCWhe_d2!SN3*v5{+^o-R3;5 zCgDs>B9<_gHy!-e;(UT|Ch%| zz1*6@CbRrUCwY#hU}iJ_u`<`O?m?rJ`)DZsMMOTNRY8qW)wNkXx)$K(7e|=O(Kz=p z;Dw-9JeepmQn83`r%zf(9(vbc_F>4IxC{B5D4>~mP5nx5$2>@}Jf2TKh7`-7C?5Lz zwj;|bidw|yOLE_Rf5GTzZ<~?Y@Ms?#-|_LAcrNraBs7o6WjwMdl6ot_F_RbYXb6tR zcj0{YG-)k`(%Z>++WJw>s5Or)$@l{usLJN&8-b%4Ko%YT$|?v9#@mFl#p08P@K!~2+$pdOSZLstiR&AaU$Q{Nns_3 zl<*=d2bW-P+Y|0kT|SZ5AtW0g*=0sG!EI>e?*kvMN7!V1lLNzfyl2N@BavLTDL;8LanpZ&LVgi;+7v^GOGrw^u2?ebV90lYVA67+DNfcvj zlXhA^LFdvWldE-VV<6axB3BP?MT;uzS_@mhW&*80WNA+ueV5}W9yYblHzJmS;ja3A zUFXOUkPLxQ`uqvxTVFtWctKdxM$s2R|JYj~R0Ft*7do~(vap*0N2o8L=}T3^qLmHZ zEI2}IEo{j}{#v8LnTMKK`6OF}CuW=9ue}5C^Lz5_A5i(SH%7K&m_neVrDH_Lu`Hc0 zn(7uCaV!lV2#O5~YO;tXS^^;zI!9q`S~Lqc+A%E; z>(db3vT0#^bjT>^;SlnvRixE4#bOShFNC6aP*oo%CTFR2_f6f$Hoaq{oHm>rWn4VA{qu(Yddqt?EzQ5~&pX;WaTw5ttw&b|WKEIe)u zaDfPK*_Ohuw07NRD{yS30x7u^SFEh-hnk$&SJLq354#7(D@Mh(>UPEcpw-o@Xc@iPlkuv?R(nFqe)DyF6eLj^ha1@Li`|}6#%)I6bj@* zLOfy@G+2m(rq_X^mFie1=9ybLN->imn~f6+^%_=1SGMLcQycB5j#VdFR*6e-cC3#R z)uXnLiCK}1`QvdB>F5IN9spT~^QHfvz4s22%}_@1Ji@ zFg~yy-hBhSWEBim>B6VtgFGH9#S<^qvg*P!e^^JjEL(DBN)oZ6t;Vk+Vl{<;kDj!rWXR`J9h*CP1-Zjy;d3 z_0kP?1kw$4bVDB-QbLb8S1DjkQ@@x)FS$&St5acwywV8%23A&Lgj@wZh=z^@O10` zv4+&qY8U?<(i0sOj}LA6jP2iV9sMi9iPMC=@GI9r*xUa2b4wlDA&^1`gl=`a!UG+2 zC4TI2lR(;8mlwA7!cD`qgwf7EdFeV1ELA0=2|@5a&=!G=oXxZT+s)OEX@)?yD;^sW zNT+s$ov^&x8fCPz4gL1Csa*nTrH;#DGB5E*&knPY?+i-`{@cw63*BV;J?OIi78?wu z2?o=If@wnG6n%XQbaf>Ng;RvXDLkHnYXX`0Hz=HT|M}d|M<)vDlsHoya(<8Z?;qxo zF$IKmAiWN#A4bwWEwTBTl|{8KfsE>~(c%GeB9KMP{j?Iu5`6>^5;;77_Y_iIXCS&B zNm9^tl~j6;Z+`Q4xa+Q+Ki5k?7mtb(3RNu-2t=1Nsaq9~#h^fPSVC8TbF<5>6SuCu zgGiv?6?JTwBx5HoBn59If>3}=ru71Cwn;K|zp%6Wx*6f~^4J`Ml!Fr@QBb-#qB%_GC=Zs#{? zRY0l{n0lA_T0&Nb)b6>mOM!^V(4mhhb<0xL;Hk4~=;M?jcKAr=gV!W$ffqh+De3QR4IqH0B3;aDjkcOyv(iNq|5 z;%`eh>Ix)>C5pk)qfO=D+zS13OCOu1j6V66+t9e+EIlG0oHT*WS#vA6V-+T_3D!qF-qACO?R z4hO#o@j7G8>jSUX6o)<^gu)OAl>Y2D#bZ7DN0&?>+Ub?3xcLH*wW)jF|1!QOtHr!J|9|6Sk3wXZrEPc_nc)cME zLq|7KbVWRT@8OU9TtJrh{1^KTc=uQTCzpi)Xsb*&wQeBhb%aw3Xjb8v(@+eB{_yIi zg=5{*uv%bZ{se-UXfD(cY4>U}maVr+9ZT*EW*9b>2Gq5klV14u&oFI813q}sm03Fn` zus2dE1j_=^Di#e9bkTe*|F(zMQj`^k&Xsp!HDjWd-VNO|UQUX}QK+k$cVtehAnoNx{-`tIr0 z+}AgBApT4XIn&3csAE|urZIbI+awh0)1~|2qN|&rgXB_c>D_SajQQVBuWj#sJe_cn z5^AI&|J|lCv4i8Csgo2smu2(cOtxJMvSQv!K~l;9DNjEM&njkv+gbE&AnrBO<8yL^ zx2(UHZU1s**3zS=s_s=A0@C9W{2d>6@4k+|%-wtL>dvvNJIC>*=GC%4&M9vUaqKhEhKWE7A$LU^Mo9^HsYuWVk;(8sbvEK`8MUxV;|_X6?_8&ekX*jPX= z4cS++uoaLov2?O+K|qclgpIF+r~#Q3S9$RbyOF;1963FUPxK>-GCpq?2OdAeV~-s- z?z(H&i=IBdXm7w|e9^e&?>^3WatYKm2updyyg~2J5_IX^NQOC6QluDHCK*okq3E@% zl$APW3rXhV7n>4}#vk4fA#+km@dACH8+8{pF6(M>xZvMJ!n=x;r=OHEKtZbSwfKbg-?>LW zfRu*8?}C)>?AR;2tgEdR0lBWvKy>-f+{8cHCCSvYR@~RtSVvo^&_?`YjqnSkxcN2z ziZ@_R_#ILB-Kww|HzHEgVIf%&nblA@f?02KxVt1A-vZ?VC5^1ob-lc5ItoZT*Y)yz zsCHr7xvrNdS_aNM3dL-#>$)s5*W3+D-H=<^&&6b(;J=(?ptw?ELf4aoBMJ}y+|NW|zuMya4DnT2TrQrNZ(k0?l}T7lWvd19$4bfaxtotNFKDEPDCI^yX$|oU2kkZcFsIUVQU9fio#G*UQ|f zf5Q}8N*|ZHZs%D48#p@fW)2O#ohMiQ8cz?ulVkmF;B?PxxYTt!v%&4ey{pN}-DqOt z%%zWO4=#_9%mxTt1hLqe?=gA-PM&q3h%yv4ktb^f001BWNklpPGB~Wd?tX_oFB!qj-UY6zG@0C$^^bbSX!ks(;cV#&z|Osyl+jrF9MQkrR@i-Y@G=Alw ze=sLTW)L*!esm5oqvBU06~giI%ux!GN&9kInw*xdI)Rnr8(qdi^#n2g=ykAjReb@_ zZt4va_4IY?l{S+9=i|Wonkob4N*u4z_ z=_=l_1p(P}JlFEumWATj9+lH~6gl#m0;lgNGPXx0Ga#YOX!t&Sg4|N@ij0rf8nx8C zlLGSe1s8MHrGWIb=qSh)%^1jv^*I9b548Th%JGkl%=^5%7>jcJW2*wv8%VM1wjbI4 zzp5p$DG?d;+l$6}ML)8IDqKf`;7LGlZ)jIDyF|=+k zo{K5ur2<*0KysBxreDNp^u!vtt7{hBLW)(r+ZkIphGd9lsUY#n4GTy2_3ykIhP&zt z#@=sUX7ID4gbvQ*nX+GqvV9^;+boNN3e0FQBtr`U>+3K66W;tn)BmoX3%gzFcyI$u zbgx`iyXBeES{MZ}-nr*yG$;qusta4#N<(*7GcN4UPwXVCw-Z}da$$?TAgmlF-MFkZ z1#)47WS_zOdX1Sa8WTG;7S`${26b{_1GQ@35>$iGkvL*n#k+5k{5`8Mf=Wl{T-jw^ z^GrM7!mj7CuG#2axUjqY2A&olUv&@c*)nrc9i)OmA<{gz zsdiUa+u9He2O0=i8+W!WLQ!3beRSLR-Y}?WAxR!M3Je1d4gh=Cb;ZhQy0OLWfEU#R zedF~9gro}eOvp@cP+_=E0B2$bG!;BSNI0E%GY|xdl^OSiE{VP)=0QRfB_zp9EH=;c z&!6S=>HRNC_w|d4JX+n=Bz5|}H;t6h5Jib#uy0v7UYvfOVzE}7XhxBFbqYyTn9Gd0 zD#ha{99>xa{(iEdJn?lxW7=4@ndaiM#2x)S|N1-{8iLT;aBxE%>k&vN>ez@d+F1p4 zTvo^iJe6yjK(<01s|n=4pKfo=R#U};bzE>|;n-=bM;+U;2Gp@x0=Zc9HkT+2| zxwXj2U2Qcd*L5QjDQ$$FFd(=WjsR&p&qf^heCe_|ygIwCD^|{B0-5O3Nc1)KqIt3c zH-9^Tm{d)XXm^z@)DLPMqO49r-^H50zT7vDDySt05+vXs;J|^C?Av$bMM)oDG~#h= zVZr!?FaHIiA>cU?ruSh}I0DOkrpv-nRr3(A--HT2l}m5XDR`V~98^%pox?2&N7NKr z=22pATg33Ytasv2n#b05MB&5Np?`QM>aHPDAG;T$$Nz#oj|u^4Db3?Q8E;xJTB&2U z&w-k@ARwEGb#$_F4M;cCu^FN0#>O*>eJwPZQQb&DwjA&1sG29xwQsZf%p{3n zd$;rAYMo4%Vc&b#h50U4mJsQ*fP9PIxJZ6G0@97kx)}l4u6WE9;fdogvqvB}1#+(a zdst6Ex^-FCtba|!Kh|?sTkT}byqXfS8zJ&e@ckFF?{%}5cG_+n;vgHkuB!y3s3n=& z?EG)yi775!eufh#zt4%2-$(h-Gw#;-@Q`!)p4_}`jj{ap9xhKEHuVAE^K}slMR?!) z{^mvLzJAfTt$(=I^I-9t)5s4m5*{-Ui{`T~yQ?dNBiKi7cX{9gdok7oVRaA=+LwY6 zG{dBgzx@h$AM{k!bvtZSeZ_E9rSc zH~1B_wNaEOnhP>(mO56P+7%zFpd-9`H@+pEfiIk;=i6hBF6l-#cfAKSQ0E@0K+*sg zB{SS+Si7->f|s5B@RIXJjaYPYKR0Vm)UjRnb+gpbwfov8!`hA2b76mTb`zI#?Wccl z45YZNdyfCQVU*YO%=6{deZ-7@Teow&)^(uP4cRFd_RYq#yhr~>ck{Rf7j`o)Ydeux z?ZWoR5NYH5x|9i#D|*NlJS3kj@QNiEdKw0vgYGjBngTfsf(C|S7N~bE_GE)~p#qOh zuOwW^W4-vE+}zSZvXJ^I=1Mht`LrD3WB8>=hxtlMR=kIa#Cy5aYqzh zyUu*A*r`^cr?nmZXgm6mPR?C%;b)Ujf9)yxd+Ug?Tc$Ff}z zHuDbkzwwXj-i0jcFZlS^)`4{Cg(Z-UYCsCBT7UnWFWc9FY=u1bdGg#CS>&bN^K1{r z*%+{Kt4#vCPQvNUQ0!bhqIWFgJT*!La_O&MhBnZeqH;qW?FgjXI*`q}tnCP-wGL#n z)Nv`}T$fWXz`s5X_w6-9*yL#lPD9@@%Tnuud=#>MkX!?qLCDrgnyxM&ZdVk%sm-_K zzOL84(2cv=jy~>aqT7|8fXqYYfBTe`L>Qlo=TT0a@q3&_DWAtIwE6m`=Y7+c+G#&*@gHscisJ8Z<@S33kY~UVMNiK6_VsZ@dlZg zJGmkp?Zji32Yzjj`D!gD7R$pYpEqC2H#FqO_`?Rm(TO@*g`@0&VAV=C?|~;BHb|#} z;7+2^Mmz!ti&=sn--oc+nEtgQ9<_l0#m(KQt3$}=VsLzt)#1&^V(Ump^$Seg=|lCq ztt>3Stv>VBfjVv-{dDW%$=ge^`+AlJ?;$pDC*FmlL{I!JYqhhyb8nugud`#^ZLDLn z0@Ja zbc6ZgZ&^SdEWWIz&*ip|xKm;9X00qB2P1}4w2&*oSRqV(spX~ozP-(rj?MO(LNd9= z{O^3pE8ty%b9ngUlS8B@d-449Ep2~yT^1a>&r}x!cWZVHA6ZxLo}-J=%au7qA4DJ&AY9?*WL5C6aR7C zoQx&vCSBJ>oI7nevA9}J*HR|~+f!I#^O?^gY3_riir!x0{kJo{;cbj<|1G9A{VK7+ zyT}F21yW2+;63>jVp)ynrs0NRcc*%{w%TQ&d^7IrP9(`Vy0H5|=WX%zL`t;(gq^w5RF4DYBuL?(;`nV zA~cA5Yt@B)kA8qx8IQK~x!kz0+i_V}n@0xS5aO|3E~r_#j05A!ZtJrlol$h`#S8!c;-C$LW)Ra z2vJm!M1@2$#&ge|=k)0pNAd7$+HJl6XGe`U{NhJ!A*&>NnOQtnzeb}~I67I>3%|V! z;mc<*-u%+W!qJX8RtrY}#bM@O7q4|+TZLmI(&%RM=<$>O$UUwJq#Mdui-eQr1*m#~ ziP&jYN4GXjAYHqwtF4?sQ$--xU-&YfLd+FejQ8J0vio|9-ueZCgQH(v{_j{0KKV2F z&~0>tKsJ+M{X)C19T)dioqMmFFT-<+$XwAJV|Vw=aCh&lvkSV}H6*dVN|h90>)Eym zq?pwZv+fGmx8lOKtL@m-+-=~(Zr5d9Z9c`Is0k>N;hHYGwM-r!KGHmatk@`XW!JYs zQY-N4*FVGKFEiIFAuAy+C`hwju}mHplM@uRDwma&I#vru+4)vU*=d5_ucNKM6(iW!9Cgf2!1VWP zg`?uINhk&kTM}prMVL#PN6kj0u@QBwB9KlAG>xdEHV`2HlA#L9SYwASLwZHH>xR}6 zfozy0(*%KZN|JFRi`mddbh&~S)osaA}#;f|L*%D0BtrXh+?E4ez`BB`aac`MG=KN4- zE!@I8k;Qj8TbAhE!?pcx&i_h=YnL5oykZllgH&b z+ya4gvUJqIBWZLG$9XP21d4$XQPGC;6t|}-+!QB&=Mwps#whMcp^fA)f*MFTl1gj) z!tSAQHts#x)@N))AN2&Jz5<3r)P^iNhtS)R$f5?_E;N6pVE)bxXEgSA$;^pOtlf!Q zL6I<4g%PKc4G5%wK3Mud^$27x4tdMoTTLL9;EJFO$5ser#D{Qrj7&C0INXmc`w;}t z^&*!pO}!|RXRcZDxbLy&jR#(DPaQQ33iawdxgn0_Nnc3Neg13Y0srV{Nr(Rs!`IW8 zI;ttN9a46}X60*8>D8|x-7!b-DGyoAL-Duwnkz+zt=R+`b#;j1=5Dls2A|(T9V-{5 z0<+0Wtm@iwWvQbLf%NW7F*5zF6+$?0Crg9(xFU;gsiW0SFPPWX-WPsu{O?SlX;hxa zD1wmzJzWxsF))&#XUxAhY=A?gSLYW-mzWs)b^xHQr6@;qGu(_B7dq2qJ_&nt# z8LKVj=243d)X`2jI#5Tkkf7(t-;yt^_;n0l59yaU2*|Q{v~yE0|1O(E8N11?=6DT1 z;=5}*~Ex63r*H|K3pn36)7S|MVTWINn$&4n@t^ zu0Z4WKX~55`Pot;V+RCeIbp`mUbP#BTaW5tW*A2Iz|1O$t6n}keG_r5{o?e}tOqe` z5ZtG+YWEo;t78NQ6K%cc@9wD-kZy#dl`vK(&(vK*6Sc?)4be?Y)LXctzFU9s8EeVr3b$*2J0R&1XHFoI?1zNr(gfD63cR zMbol)JR%-%lJ9-*W7l-@%r)t@esW%X@Z3}90kJ|j>S2Z0TQ;DbnXWxjDY7$hv{}r= zLW1sNpGPXDu*SR#Y8GYoAjN@OK(1QcHKV?81p7pp>PbqLmuinQofYt=E)wgzsmUWl z6Ga6b5@_raU|iX35>D2yCY>#&8oIA*+7Py2ky(L(vl1gmWoGg*=I(hX*)_X6HVz|) z{wGp;s_x0X)dZT=lkI&rC-=3H>im2Z6f-t`b{51jD4aAe^)l!Srfb{C11(lD^6o6n zNfrFFkQ)S}O`nRR%i4AoZ7z|o!TY1T^27foTf zDhfMRn=a^^H^SB-80j@z1^Qg-=@SzQ*@CcqKoy1d_S|Z>wbiEM?YXb>ewbPd*&z5t zjXi-`j%5bOw>jvHphooJO=K?#Og%Tm_>&vC@aPU^kF6##)`PA}D1id9S9SfKSq*Nr zB&Ss;$kZKYHpw%M#O8des%2pvlW&A@0PY(u^8IOrl9jPTM5f6*+-Hp1d5994O)l$cZgK)H=j$$D}&#})V6%u4^HasD>i2uar zYN?}|t}h(HIZ?(U9Pid1L(y{-@;UtRvjh`Oq&}O*L+Da4jvSWQ^@zgAVVTIRIR&)E4B8hCK`hgeaj485 zLYY0(@aM`2G_fW+VRWhuVRZ+ZcBrGEI9~v{0;E&0l*4RH)+?bZ@mNV=SRdWl{S`3$~sTbdzX^QGU)E^NbJ z`ys_?hkf>En(4+aTht+zrrd!hYt!Xy)kX7l=Sqc#nVHj+hpnc`?B9R*nr>Zq&5_45 zM)bj%6EhXmQIB|7de=^jzS1eAHwfqEVWBiQqJDV)Ui%owD(YAkj$*;&{!W!P#Oxee zbQ^}hbYdyyVEmhng`?u^f*vsDxWjmofRJT2V=On52_(*v?70qIa&hDnnr~$y#!et# zdWhW6EzMEJ7O3O$!cr%JrdjG}C5&A&BHnc6Ms++`N*&D=;rTJRVWhbgvjytdDuL{D z|7t6NbVD7>6tVhhWD9Y#q=%-1M>1Vo9ocNRfLJwUIzb@+eDZcOdXr#Ht!Pspb_#(ZCa0QO27TiSVL9?wigDGK~aeCJ%Z88MKrE z15-RkgN^67;4}Mb1B7IgK%PoUjAuo6l(?_9HC$jq9_sV>gR_){3(YHegDBve)1F79D%&1{PFvrIAXm1{y)Gd zji0i3jJ<6e#dTo_Sr+uubMTqRp(g;pY3UqOhdO%V7l@pDXjwP{w&Zbn@$>bN-Sul| zVi(LjQeQZN*-=LJ+S10C=!Y0!C2{3M)nO-8T=+Lo(-fnd(e-{9 zTi<$#k_NGkwvhEsMwHlV|`w_)7D&Me0~D*3rq_#6Wgh zJeq?TI01sXA`m-ixdO~ATE>-N#=^BLAf;~bT~~*cwHO0AUkvcNQ*UcS4LQd^ddD(E z_sw8e*mxCat?ah>ZzJAC+O936L>@Wj{E%9SfvgwvXth>~h-ZTElh>NBR% ziC3l>W*f{!A-Blj=x3OW>~!%YJdVQg<@#gzSy@DqHizn9!gOD!Bb(d4mu1l?E*>@K zM_>Rw5CQULhUwXj%?n6}4C{uvrkEsKOfDzTIAvJNc?d3vL}mn6g3(bpg6X>c)Q?&g zkWLxajVzXfuN;BjxaEZtjt6c4e+G8XKw$C8%*}XpIFSL1>GlPr(xB8)*@fM#fIO5Q zZtn}%sRh|itfS*0HIXIq!2{&}Y&U;ZxSgb~FsFt}f-%^!-}4&It=!uX47{;30y3Vd zKL|8k??@7jtq^RX2BfHiZu4=v6r+z zi`|^~&Qv&ju zaa%tVR~}qAIEhxuXqF`}dc;Hgm$qPdCGb?vcK^aj))`+S6WxmH?{cJ$WjD2)nW_1a zdd(rCKKQ?O!F`(wtoEe1n2V6B7ff%HC}Sra0cJBSyR0h?NA=v-E~uk4dl1j5&!e3G zXN2tR3P>5c3ByyGfRYZ;1jM_V>#|W**ReSPxspJ$u*X9&Brem%{__&UqY?w>B_gx6 zF6l-#=W%c$w&=pPtLxYlg~`IE9_Y2vtnDVr9Jm244_96QFMSro07!9KPFkr)EaEoe z!YJKhpJI~8R?O?-OxB7E`*SldS?Ma%^2%!O!gkYD#!*Ch%vPL*ut26?Bt0yUTq9Bp z31mY$>faya%-*$JmU@_1gJg71N1x>%2v4w?m9}xB2Ryxy8Ex-5b z>uRCXtPA_pUi;;*)dU(Tr4W9+vU+|a_qA(#yP!B|COs(&O^~KK zwIA(7rEZw>zsIQ%M;_#t+W)Vz8{0OK#?EzZL7r9s7eVO07q+A8dimi4W5A#W*^BjW zfSQHyDnNi@9(2vTdL)Fi8CX~ubE=vr7&L!xFc{+e`HPH>UR$*vuZe`2N1r-k95{N4 ze8L>rUQZY;tg!Ts?dV}o4Rw_AF@hJq4M6Yw{<_4`8sF%aI!1l)p5i5kM)#8H2d-zW@Lr07*naR6hTWrYWQ6@F(y;@@71TKe0?3 zmluG*0D5qs>K7GZWc-R#$K{Er7Z^*8QV4)ltW0?6nii4st*H-N>R3%6zxvb*LLII4 zl_RwYG|f`S12@!Ob;p4O+6JJ*F6($5Dpe+sS_^_z>TBX>n@pmq=_=DMfh;A_xalgR zOHd35WP1b>BO*(iMdo)(%v>)qzf)pqvq)k@B-bOLmO3M;J^@1nV>|`_&loJIS7e>F zlE-BMzs9`Uv89x|ZRkHs5{;~aT4$cLl0Z{U9hdK~?8hs&9!JTlIr4+^%ojBN{MnUt zGP?~H-VEa(F<-OW+|$QK)Ujf(%GFPxG3t~Ia!jNd`LrtnSx%x6XB#A_Wej*y!#0Xf zw_KSeNH3VevHEwuV>Luq4r{qwibBEMyLDaR$3K3KpZw(cYbtr>njnwowCIERL-VL5 z94CeY#QyJ{E6E)e>L})8bRYf<-Nsx=aK*@m)}ng5E2v{V;aHD4mW5+EZmqmm_(YBC z!iyY9_SOsYo+ z9hVoLssx%w!qJH+y0JT-tXvI@2#AT!5J)HLSWO^R-K-JU6m{Ha(o)xlB$ouTsS-WS z5Xhx2Jktd{<5@J1`FT_yLyz4N8O?5Yu7TDbnLsd(e+6UR4D@0PtfE;rckk!dExdsIW>Lt)f zn?R_OFz!eo>(NImft*PpT$IXJ&6k;l&;W>41H>=F@MeUA zW481WG%jDB;`Hgqt{M7x)sx5lPaH9hKXim#s<2EQ6Ax@!5snt>DCT2C5B()VrjpeU zUiuEQMI=i3cwOPxh&Yyo<6_kTB`O!}31q3GOxaCsO^m65VJ=R_!{@(&QiwZ}M(?v9 z!~5*VYAB=aX+K=25%I`)!%QaOPiAvmG+fQL+WU?uRi6f zgij}^%_?s5+=sOEL0$YV1EvTj$Ib_nF2Q%J@^tooCLxJ3UH*ZVs3WT?_mXd%sQ*v0+hFc(=4D4CWfA(q&3%k= z{G(l{l@V+F?^`Y9WB3)++t*?Qlv=SEP|Rf6;V=wGpu6;?23}d4Rc$pvgYl=pu0!3H zHL$h6iawe@=kbI&aNy)MRl4V@CXZuNbH=?Nco21R#kFg!>mhTu#g9^=j%DE}=3_NE z5TcexDa1&1??O@&bf5T#y28=UWnC>Ct<+kJ` zI#vrvxABisAIJl(eXd3X@}py~XzQN#%Nbr4xy(EIj`04$XZXFjF3vBl-@#qcr z!qE&AuE73rY@&9N>L7vqU&6!n@2dv_o0=z(Q!$B&rRKJwM)a|nc*j-==8|V#dK{valhBDi)~x-}N*Za6kCY{INmOS_$M(7(oLu8o=oGLBwaeIuxmP!CCEru5O53bbil#8uAHUo;`YD9dE0aTsbdh+#;&QT3$MC6tLd-kwhQXK_ z_1$~^n{5-vhCeSC_&?OTheVR5Tfm6;SbEQHGeM?$vspTW zvUH}t*zzm>Jsal@=Pw6p&w? zhosh-hdEhi#VQNP|92Jiwzk~Yb6GnHNVg??+$PI73CJr|!UteF22(NnKG;$ddVG^_ ziA}yG&gHrt$)l@#TXJ77rd^a>SnphRWvnRhY44D4Kc3!Hg$ zrOQmk0}Dbf4bz8V?d>2}ZT?#a&Fi#Tp(T@9T8@46czpc&um1%T6Z6LE)qPiW>7J|B zZJo#tJUG|mrI5^H1U+QlaXos(TSFbie2lK6pGVM4%wSixugeSX->b7GtZ`Gn&Y@Yz zIgBrBqD(#Fn02u)yd|^5ISOdFo5T|?Y0ma2ymzOLF6Y?WljFHrGYO*^t0>Ro#;Ox& zoVv+4xuxsb=shoAmAkkeQvgAS1J5-*>l`<;AWDIUu5z39a$*7(+6oS{0>HbAh z{qG>t`$qDiThP2C7@|W!ed+|vJY*&S3NW7$*xC#I9V*+mcHG_S&`JVLxtmNY)+m9d z9hY^rRlBgQE^9mY^ILjP@bbt-w)z+8mYeH;_}>%1y7C9F2;2NHn(K@U``AQ-9lt#n zb|aT{&3$dch22cDjO}=_abdUXwyqG64t=mMZ8m#bHzOeJ+}Cbg))lnJt^4|pqsxgh zcIMW7?f8tBpzkE4ax2L*Zdc)gYWBbO%76;e^&m8Ht_jI7BKPvWb9=#Kz}h|->4D)c zQ#jg$7HJ-VH78{P8HI(uI&K^d7&gG8Ky(CR<7Stcnnk?`x?1bL7H2>of~|uH&y7K@ zh^DKA!+q$wiXccVEG%&B*kw+izPkHfUzKj_eUBeDe)`xE7NQcFSB{W=^$2>4IO5=!9H61vf71ZZCXrFMRMi7%FL! zE+4#Y*A=9WRTu)zq=bP>6Z!UqRRq9y1xi}@z zG%5_<3V|Fv0EcIXcw6LXUH5a#>s5Yl?O&D&WVRpE}MOu6FGIhkV3JZ``;ps_%n^zf4 zm$qq|I<`n4+o6ut0t()$4XCfo32A41v^uRcJ|b43#f{)7cQ? z!fsTTaV)`@h(93^NqSl8&Y?*TbIt(DpGQ)_ABClhwdBz%Aj88e<$Lr}Pd521#}6A%95_OLj!?&v4WAO|`^eLzls6L}`cQLJv60O`;`qN7v+zF- zw4OlI6m@KpKz4#US~r*K1e#{4qgH_IqM0~3`3#)@5uE%t^L6qWNR~p!lBLpYL;F?4 z%oSL(?8sxM2;^LoWw4qgkgbwOHw1Dn+1c&qg(Q$li{}e#0*#bXkW#Ik0v(HoyQGd~ zYc_cXK;Bma*&2&`8!B1U;Mg?i&)1?#Em?Wl(#PH`^1|PN=I(H_G-QqsPeEeGsXjbT zqJ6$f>V^hu6KH&v4O`8@<)5yU?~xC$eDAm43ef=R^h)nnN%9~`9`@}!%>MmXZ}QAl zM;?>vD9%Ij88CD}gPv_jEYfx4f7uF0YbZpQ7v8(adM%S#)j)_j z6$Ni#+muP}3p~e-!ZA+I7m(#S^6T$y?_oEfj@1ORQ3;=RspG0KKjDNzIAIQ?#o`?5 z^OLZ5O;arl8`|Z+t~|0=t!3>H$fl`d^=eV4gioiaqjkfvwlJ*Mj<8GS_7&*cK0WNgbC(aS<-W;1wI1r;f{F(j|Sg75o~^%s^xv$lgk_*ieWo8h?MG zh?qHq5x5D&`UTllFASYAC6A3}?<9xGL%6Rh4bM@0nx9y}$h+`t|ET&qDq=FiHzWVhkZWM_}p) zw!+bRI;s|qN}ao~tsCy{hg;XT9^Ysu93>l<$Gt&#C<&>mNw}D+rH#M3-4u^@Tg@ql zO>%3Gd7WOYGnvEgN#S~VmYcn2*`};y|4o#Lm@<19W%e+|f$Od)b+lT!gikQ3bc8yt z8gpKZ@>-UBE(JN@Ge3skxYykgyphG9b@gM5Su+OmcW-wuHd>~RRzX)P;UlE49CfrK zko{puj6tAOgEun=sTs({%r(0m>#~&o5wj5eF~sf#y#tSPIm^0{wUUKhFU#63fvl5- z-AtCXTLNj#!cOK|7mku>Ad5P(WZ+RWyt0lYnscF`M=7cT!WG!waz*&&;Ud2zIeWDV zUdVgQ*Mir~ATPMx<$kMpBgH>ZY^NK1aioZP}asLH->k~)^{@QlFW8Mtn+ z@mf+%9j*K8bc@3=tHR8`z}k0#+yu86@It>A=JHEOGoL^k{9QA)#Yy~^;vSXKxGNLL zliTNOGq7g?t2ewH|LBx~4J+}Ff!>;%qrd5T=Kt|Yf&+U@P3aAjRbtD9!|%Cg?^Tz1 zeN~aiggX4-g#f4l8PfY8W5H;W2iD$0=<>G^^t>Z=tQL+=)Ny$sScJV3W`{AgpN6f% zsFpaF_ac|;U_QGHh6X=AXVG=N4M0mnl3tOyuv)1QM!rw zqFK}9CF|QC@;<>nxep19I{iec*eYvll5t0Q-_4z zm4EIOfqc5KmCvQ`bN>&TC6JA>tlK1z&1P9QOCV29Drj}$ltsZnkqx|xM$o4c@)d~$ zigX2vgnTLiuZGW~p-4t$Y^>CWs$My7pdu_XpA=Z%@35(ACXEbaAe@9~40`8bF<`Odsv2 zV<+fiz5t;BWJV#h4a6q;Kb%OynG|5;ToK4}+~dl#av+dJ0Y<9EkW`g{Eqf~IV_86k zSAjggLLUL6H;8y~32#xsv* zfb5fDv?Rc$(oob*+W5ZR@QdqBce;~}HrPz-xvz_ZH`Gm(DZ8fc@|_?m)+*wOBs?=^ z-GnoE$DU|SKt9@9td(5?O+D&p6^o4oqm?=)i&b$pi)q-{Zx$(R)vSp!F74!;z|E^0 zyRSPz9j&W|HG!ttZZb7v(qnEuPFJ=73rRDfQtEJ&cG+cJZJiL1pGrMopD5D_0okg{ z+NFT(l*`&qKx#u!I0=(WG6l^*Hw0u!$LG-q`BcJwQy@nCMS@-xzemF(o0PF(2O@Dz zX9=XVqsQ^UkHABL=>hYJ)3(&RTSk=->%`lcyT zw~vwCJxgx$qA4r`s+pLmN(id?y!@UpY0oU&`CQB2sZsKb5C`vqq)GO)F4 zg*I9VW5uL!K^@Cxmwl}V22lHV+ET_>`498P;8X1KjM<7tJL=e$fV{W_=N4NYD`f#0 zj>$-xSVJA%(nVL)(Q0RB+lzl3|9jWeae2(SjDPGDb*vGP3$U0n7ynkpVI!N4m&*5O z?pqkTwZUzmIDm?F1!P5S$4(2#X0ohX5RmO;Sz9fw!Bj!uTvp^rTIBJ#$d|@tZk7zZ zMHQ{667icP(eKevO73T&)n_b3EMVZu2uMXTH<;N3jPy3F5@Tyc1&(YtSnrE-D%(e) zj!Vn%YG_?~ii2tLn-|IKnXRCexsfG$PH!R?n#72SsnULH8=;XS7mK6+l^-wP7!6od6^uKeB+@O5_K^m|qg zci93QY%v*qvakG@3%su9OlyZ^pGt8!MSh2Q(Dv-R1>fi1jPk?VkDX1+GEO?ISIGh-m_vari>kG9aJx&~yu?P~2U=Bd-? z;AzUtLAF-N=U4g3j;w6Fvg|>Yz3kg}gnj!CUsajcR|R=orjBbOP~LsBaoe zYymgrOj{w4jsEJ}QFO6F@b68|tI z^O*^mPh3{`)R@daOv!wGR^s31C4RCb@q9|;Y*u8dAP`ptiVj=ht5uEHsXD;`%r8N@ zlYQHTh|N0RIRaUxj>{stMfdfQZLm-}8${(iWBCYit+k9y@0o6Vr+Sd^QY>Z>V_~EV zYmkp@MLM|walQvZ6Tw$Bedcw{&VB89$m}TN)?~>YeXMQ-U|@Ehf#wM04`*Ph8UWTI zkkxUI)dF(aeQg)_xU8H?@sEoapy;sirDh$;$D9E|+3%R1uG`9IbtFsgRkjZxTM$}Ik^8#_ZB-4Mu55Jyvh zfG}EG3ol%`d%Lv<_V&r-*WSf8`SLPpTrbZy{azPbhzBt{PxxSihN#a?w)SPZ)39|K zZartdb|=7hD}}ASD&6 zE_aGRhWeXWD4QgZZIj2cy*64~1G1egYdiBuE7y@MTv)~XPk9*msgHM`QuxBO#P=5^ zo=u8e$_b>~)M0Ndt~|J#_k!Qoq30Q7kXA zT#5RHj34Rj2+EId#ru`}@qXoglpo)UbkVu0hPCrdGt{ve`e-+=GgX4qErHzSw7feI z$oQc1!ckEJvS9AT&L@cgY`f3?1gaLGkc5RFK~OP!Zimbp*~U7(imelbPFqw}j$>2TxaQBFF7nmCA=pY=WI(JCW zz%viUZrJEjZ#9Z{BwzX*Sr7t14uozH96S)^kv7^w)~R<9UYFNdKX8Iwo{6SNW3_NR z=kjDKrDrJi??Mj_wND+p)3A0CHqStB3jCJir_>7`vjTi!V;KLjCB%GtyI!KOh-$d3 z4%|!z_W0jJ(SbTT1-aHCkYfqBW5gYGtfq_nob1DZ(heLh4 z1kx!{rV|9xRc%Mtlhh{uv6U=qPbu5T?y}nEe%L;QCfx+u5px0Ru>Do8Sg{U|48!hCmtu44ik~*vknt zGlOZwkbn`Yqvs^Wtn@N}4s>nhS{AT5=z9r>l?#SP@e?|^qKZY)towL1(Z~PPis&mmuQN@yewJ>(ztzygPzePdF+rTvY!*cW)l%#(Cd)eyVVz0rbsg zvw2?|}E|vE%h7*~zXwnMr0dGj`%A-mIO?#(A=n?AS^+hvOr* z$B7SGmM=-vNmCaklFj=jo9w$A4K&U|)%@`SjY6Sr6i9l0&r?sco817a>aF+vyx-p? zSD=5ddGB=3w%AP4k##ku)N#-PoWqk#u>Kfqn1omc?optV&HrJKrNz=O{H1yPqZ`$+ zO<}YE-bfSLYQ0fr|JNI-^>!A3SE_l0>r>O>xC*MH-j2*joS2EUsg7>cM7Nfi701eR z5P{6bwmGu1jibZmuBnjq>GJIN8E+>SA^oEL?|ip(XRs4tqXuM;1Pgh03Zn6FYd6;w zDX9(>RhgEzaM4`TzhR07*naROhNoF!en!c&rL*La@w!$Xn;F)} zgv?i09XF<{ylGVI0DI%@*>0gFh+P162IOIoOyF{5!)@cL4mn*N1-Jro4OplvgbsgW z8t+&J?HF(YQ6hl4>bNpcL7N6?Rr6f*f|T1V>*NxQo`s+hcAesqAfh?3^&PDm3nkxDYdnw_Q?1!OE9ZZ_b zP#p%9kPuUqBu@fQx(RYx896|Rtp zYP@GdbBkw84v;DkKHG${nn{Vj6q_^ZHs1H?>2|pjL*Og+S$$ zDAQ0H*B=xAUb$Afpy)WT%g3C0c*f_zGiG2LRmV)xox*7P)SLSwYrRnxe}1E{&%0dp znV?`7|JbSODEC72Ht6|P$owbh|4ZokFqP09M2mmT$DfZfU+hlfg8oU1j}(vsIp?8n zWVYxn{;@xREZ_7C=JAg~#5xX2Ssn<#%zuo3nZN7#43DROhrjFj48I@w3hEbr4OXSP zGwE(M@w*1BK)%fe+^&`a*{%4;Vg()_caDF&yvl~%2Tqa>xm!dUv5sxepd2C|@9pSZ zDwPoDv#^|o(62!F4X)8 zB}X2ng|!Rr6h>2(W+%wz8Qryk7}4{2!VB)V(WXVgJ#7#yGO)eYbquYB z$PS1<2+8+D;vEp(4WTiR>p&r=Kr2uyJwZ`X*?&32J!@3YJ+Pg?alj_RKng)94i%qy z9d1>}m5r6h_(x+$8GmEfJpR#7mi0z?lDoo3_~q#1d@TMi{Ce#ByeaY;TS61`N#a8E zNqKUy5gtpt2OiuGKRyA=0r!KvZ<~3Mdnpd`8mPV0F*rWt(1OIb0x5gj5qOP%v~yXv zEDbK=A79)FB@2Fy$u&lz%N#DOa(u7eJzK0Vha9F>tDvg>R95NDFG6OS*)qJfuF^{} zkXD0yGstfM`K=)B0%;u(F;^$`>SNxecr8iR+3QvcWJ|Gd>=j6=<7m-M3`{j|lA@H! zFV8VMca|$xj*!buQ>~V$l`E9T;z+|`$k^agein-J=IWzT6h*@;a}XbG?yM!Ky>yCr zI7uWFLzY5BB5_o;$b}16uKS+X*JW?(Ctf+Az3t!q3AL@Gpq^Rz`KGkO#&i=XQc-ho zj2P};?L370TE`EEJ5(GkCbotG8M+aqs0+o>qbs0x1VVQ-Vc1M{OfA5`RIAF^PL>AO zc-?l@;3r{sPO7s3lxLBPvy7a7gsE*mi;@l?a5OY14jWti`Hc)ca*R;c-2li}131x! z0@>(+?W8(}RzqkVM7E2sq17H_gXdQMIbW3c{3{VY{>I=yW0i{yL!?I>RUe-IIUn6> z8X4FFJ@y`4FB#U(W4FBzwv+O>QJ!Rb=mM_`on(9H0vqK?*YEeu@cZjGAO(N_%kYt} z!gM~k6WVHXaYEa~Q9J4T<@H(CwxCv5AfMO=#o+UGS`^4asT}z0HsT*wS$NSG9K7or zVp%rCvYanltB0|Z&Qe=6nnF>0G+P*(766p!Kw=|b{aDC=1ODBdkmSw zZ5a3QkI!Cg?=2m#;W(ZT@L}Wb`DFAm;>=sMaaRu-_#6NbdI+`hvo1-xK zcVeam;eL+X4Dkmb@w1Tp1&G}X(OX(oMc2~ZS6aTEDM;KnsIsc3b5BTS7RFq5t@D$C z?NxR3sz45JaaY^Xs{)xHh_IjSsi+Jlj7En;ve-{*3QZx^kv|@+r)CoUpX$f3ZzrKqZvS&#Xl}& z;K&vi&sn%)t3IZGyn#^Ge!ldpJ~mXx=KKL~-AvQnrGK^z->;~XyFh*uNOwbV6sGsN zp9eGzG6{Pxf>D7Sh{EU2xO@F{6xJp~Oud$%IOlwQw6IKNRTR#ZVN*=>5N;bFF_;{$ zU-*#8$x4tIgjxZjgH4y^!~*K%0u`l5IzE6bg^*+!sB-Sy#C5GcUUxB%Pagh>_Qc`m zxi@o;fk=+E(Iqw{a(rmp9GOV1W$s)#iKTj_AjUqXPM*CqcaoJs*PJKNd&Nhah^VH-*v6s^g|V{``SR zAn}i*9)u@9@sD#I3TaL?AcIsM^$O(3lU1IY+sRvakv~lQ!%Cdue~f>bw?$s?q%fLp z)I#^Q|FEGz1`z{!^BNbEhX44-xA{m?Sv4q*Zd*5EAg#L3%=Eka`-b{xswiH_5WV0I zmgDh0WZ8p8q*EOm_UaV8>T~!?8A#m@8wLbZ1*Jaf6-Xrqr}MC-#{n2quwy`cjl>Ep z-M<|*8FBG^*T?R%3Qt=sdXc0!tgNDgf$Xcz?CZNWi}S7KDOQ`W9S)~>>Zw5T7VKhr3)VdlpI?6~Dh@5ODV@IrZHwwsygoxwR zH?>W5v;fCY(itEd@sDy;z^ir?TUWK@vi&m(gr}i@+F4~Z14qe0<_`mD4K7=3FU6s(7`~x3 z7&izjeGAlfS5;AwI*xzzqB^dOH4hosov4mRtZBoo7JV zDUfdC9oxo2eGjYpByL zm7sbUst1I(aH%Bz#LYG~osm-v#Kc4>OAwJ;M`$DGKp*F5FGm1ZgyLB9 zSXRcX;@Gl?RX{lk+Okj^wZnLhaWs8kmZ5eD)C*!9dN!Adc!Qca{HTs*Dr4L0`|ZL zZf+IGxd6lWDoT4v5U*L*N(Gh{VDda1dl_DO9G?27_Uea5Zw28RA1kxX?CV?BwWyDw`a~Itq=p@agT#1Y<$iHxF~R+w$F!ju+jHD zDsRcOwJ>zuiqH)#>J`9JeIP8AkpJl?!2)FY3CCaY3y{X5;C=wIXN~=0Q3yDBSYN^? z*)I+|HF4?!9DfB~*$dA-Dvkqt#m}ZLh~KfYwOi{8brD9NB2a)_NtCgb=sEyMH!kZA z0P<}ec6GU$02#Cy-Pa2qLoz6V1kO z0R4uw#||RB0A!>9tA7CRUJ}sQYH?o5>_7m8gF;8=s=EJSKhp6%?~sClTK5?}RCn-dJTO%NW-f0@NU8ndk3 zsElTI&SEhhWr|TeHVO}Q=8s(b`Hd~{j|Ih<_T9jutpy2?4yvOOAV2?#H{bQ>$<9Aw zBLmyaf>a$F)`)*BRzd1%!e0Xv&D6xn^N?E*s-s)$8xqILLK84RK4%v1Xa$ge^mgxW zu+?WEs7ru!n`M1H0HjfU^y{+Lo4)%@zmJFpedifBS=OfT?sE&I3%DA`ptvqA6 zkJ0vU5-b2R=W<${FNi~`!CR9s;Dc(^WQ5Yaqt?rd5fshhAI;((_3C5AaPu4L<1J0a zQc=4R$(d!x7IhmUQ|Z6;MlPH zw0-W~*P+e>*^CJ^6%*C5V_38RNa?$WI)5D(s-xb%`G*aP$C>ohpyl8X$wP7f;J5z_1S% z-4M&NF_fd{RA+_1?bJsn)v;kM>SN17WjN|qHJRG_E!Iw>3TdZ2Hh^*7utaQ4!EYt~ zT_0t256Wuu-R(-!jNYzP;j&>-PMfHY4S=jw#G$wBt57M!uI2(-sxDJjigkFre$~h8 z1|I+7$?tJn{4}Gz0tMG;kk;)y8tP-H0uhrsb8Rl`8fcTOwEJ@dkdX+1DUO9X@wH`r zpKt%!%8eMZ0LO;fsE0X&>%*=3D0PsO83*1_7VSabv&p;4*ak0i4v^s`{;>m>wGkla zKe!Gxtbr^C4vQ-;YvX-6H-~g?&i@O0P#yK=HUm2d)p2E;Ew9LrMIjx4U;=`*B%&0LY-g(Gnm7NR~03?_yDqyo)_G)|ei- z%4+tWmbrHvKJp#=!_3~_cbtLehdMeZOZCyE>ew*1>SNVnPi&<=nnC1p*%=sHmVy-A z6c&dcE8RP4>Ap4vNMrnCpT)+l0gy)ZQ7yv4i*S4E-z^rWbX+1bulMe~zSPI-BHMcJ z_<%2Q zg8$9HF)TNCXG#&mN%4Ly^RF=$BoE3tbG0#SvN6^QB7<78*$x;x%6J0eNhqF%dsJ9= zM`!nYA`dUSn5$f6V21`Fx~=m;Sj|Fq9If~Qg?HUS{DlRiLN(BFuGsiOrStP>H?9_O z8J?;18{XvS#-F*!!1j}2?Z_I24D8vQd$3CHGz5yz+LA4`9o?Fj3~aYq){ZyTJPX@zmbL9(vEGGdVk$(n=Ds>~O;xGYRI0Ta zWksP}RVY>~ESD=R)G9r(UP4lsP$)tyrZF?K$l=3JTsN85*9APD+mL#AaPE7I zWF%r?Sg%102pI;<&@9LngJA_8O~5f?r?0A@M!=)iAs||rzWk4-1rLxn-sVEZnt|o9);5N2 z?Ag$L>MSI|N8Cg~7c7WY0jDi9~vYciBhgs2?Bp* zDaT9}#sO>&sEzc$wl&XIQ@NDCJEPR9DTTg=@&=7*f^x#oJ2v zj7=lmG4A*S;+fib6tyyCTj2X@o~x zfDB70{U#?|)nHzMZE>M6&YTd7eA@sBLA3yB6Scg27G@-vuYn{XMWa+>DrtIKIh4BY+wOB@LAf6gG%)J~Rab1t@nZ=FymlQ&UCn z;lTjvY7e^Pu15UhmHVOQA^O3W%UW+vZ485OS+`9zeGvU+D1SZ>m-Xl0Y_o|!rI3B> z3d{NKdGlmRBArN(Ed;ZJDwg4?Lva5tNO%wpFYeOVx~9nX#S$l$TIU9(e+i|3iPE+U z;xiZGM6V1Jy|9|-m0{#U>+d(**NRec9qVYcnF9PjKY>59*xMKrWz4KU>0AsvSB63r zMlzyLw&N6^Z+4Ixkl~Xl_*5R|Ej$}Ldq2+M?5Er3s(J=?@_Xlz>mI1kvWH>ZFH?V>d$2oLhD8A!j?Hb=oGk4kT2 z866qBiFI5&2nz=we@eikoel4RRXdvZ=y&fS@q0f)n(b`ia8w;T0m$ljAkU#$4{X1& zj(SKe4?(q8e6JefoMIR2<2u$Ic(h8yDJ$ZLbvXNtZgl^+U)?6 zwh;|@pW+V+-P*cC8r!zi*ndPOXo0ggnb4_ZAtIp=)mkv|o5eD`d=l>6>0w$J5MSRu zmTQB@cJL`t+H#T7mW!=0d1-|3Y#-r;4AHR&wa7V7v5scyqgCJQs*4=JM(m>qU6Hw($7m(F588fBA>prK$ACMTT`-)QAF1L9~9~ z)OnD{OkS*BlBZ3Tl%2tG&0|7{{3Z(SiLuenk z^QwU33fR{(ux}n#U8#<4A<@dxFV&fS018E@D#2bxyYfGa1sF<+WEwwamnahiKt3A; zINAYZK;S4Xa=wDRR3UsmOZfC6;p8uJ=Cfbq^Ig%X_Xx5V~L$q^v!U{d^0CXu_+@p*h@9By5WV}`X~ z^|28PU95;B;V`$9P5L@?0+5Xi?C|9B3P7p}3kh^th41Cz4Pkras+ORzDE#Y2`JRuX|OyGF_*(2?AFau zZM{Zji89)ide@ud;F!>yv?yS%fOZ+AHSJ2L6*!*G`X4Wi1ezIXgt9b<8XgnDbM<5w zu6z&9{v#~B0wo{r=DYum$+<^R?psgz*^9mtXcWb}tR^iMVO>A;*~D{Nv?A;jAnzJd zSvw7JNz_i$LQw64;%dlkgQc5baW`alLVhz8$DrB|wWRo6Hx|1Hnf>)=N``a_a?79w zlo9RJ>af%JN2f#?E0Y!InQ)GQ{QslB#YK5Ta5vT%1NoL4q1Ragl`1vl#WJCp0^y4} zBIC0}Uz;ZO(j>9xCW!67O!VLkkz@0O&t{Pq#13A)t&b-k`fc8r`&L)hj=odSf4n{~ zhG4W`V{%bK326M>u%KiKjwSkqAn+}KdSP=Y)?n$7b?Wa z?7y4xPu8J6bt~HGF_;>FS_mm#fvCHk+~hLs9EMTdvrzgM_tTP7?4#auP^>~K2C~hX z8ts8zlgB0@!TJz9Ycds^#XYug!f0|O{?RmPre*EbU}g^bE*UgBnU>-TktCULI7+FMBbO^&7l||16duoQiak71KF+Er+!19ZpLQitrU29j zp+1{hD?%tG?uD!XHgS$t@TgZDjlW|Dkc}*Bw~C`3JT8|-K(L!$!*P$D2~g_EGrB~X zmIRmy7tq+4I9GubwSFI#+*QnY?y~zt89RX7&M8iW-c9Aw9=P8F{2|&6@l<5Q@ePvm@0LbK>8NX&98%Th0{wh=E~s?p1(H|omfvb3#zI>F$QzJXlKUIUfYItWRvKnsLGBo`I;_y z#wk(8YK=6@yjJVar~do00vVe!Mc>VU(7T}i^o znRZ}oc|aB+mJ%Ph1wIQfybcaGTgSzS5~(l1ic)5L{5aL>wNIS6W_2D99oeJ({=jz# z%QEpE6aRVLT@6i_^~%jupn3^H%l6=C<+65Dth;2VTKIHT8^N(*3&r5C?@IDauTTfo z=RkelI;PPUVw~b7XPw>NeiCJjwgGsD1n)s+)uqqD!mG}4jBag>em}?`h0v!!{zZyBz<=5&F_CcZg7#vui_Lh)mghAL8^*k6qME6WZi|lB zRG9Tx19C8c1ZBG-;(@xX4fcE4U*lc>^J~P*cSplhx7$;v~92>aLb0y-O9tgX&VR>2{b`j!+mWFk0wc? zZy)o&DUDrOMeK?o)Z@wHyJGmD1M|V>D!aJ#b;hm^Ix)bf>T3Buco<{Oae#zf{ z)~fEK*)!R2Ut5klhw?wa)jn96PSl>W3agy zRx&NkqN?IqjK|W1!*TZPdH$N$e7t7L;|({@K3vFOpi-@Id!$LRD=zE0yV|HaHt(jG zfbHtInyHRPaI}kwG%Apxq<3(%QXY+}h@JZAS#@lT&n1vf3wXq5$exrErFk6RYskFj z8vp+ zUw5K9HX+fZgpV)PaTQ40Kz;*AZvpusknaL%2S}?$|2_bzhf4V|^5t2cJ}+_qM$J>7 zSU;*`%QGu9on=s5UDK`ycXua&0Kp+xfB?ZgxJ$_3?ykX|1a}$S-Q9z`ySuxd{k*5X zDu$w_X85u8l3smZeYXfB{31GYIsjPUz}XMWhtTqMpaX)Q0`svTVMiwS=nHrl>SCc! z-!R(+Z4<^}Fwhr9?s!APOSvQsKNq^mnWdL$CFdU<<4(55r63ET?%XL@C10z=EzE~Q znfw-Hw%;z`&NqWP{*(3yeD2_aITpVE4L2-{&L~@8Ns|-xnn=dTzB;E;CX($Ut^-Zt zBl7U4Z07RTJo7hFhT^&>Iw%qD#nxykBnsNyDltDDyx_cF46JJLcfO~h*iS(9(jOii zIbpWtashMX8yo+2j@G3WgL`u#YAG7rFeN%>R!+`=BIR*i<=e{?x@d`L!SYB7pTAmi zA7a@R$xy~BAw?UUkE1_4gxa8s)Y|+YvouDFI}WgU0trK`+!%?p2KLC+kLatu*4jmL zIHt8q`^DSv8{ap$eH)-j1hQNVp@5tEkrSZ~{7$%~ zh*)2DPFiewaUM&s&i#9meX|i%jygZ2J(9PWg*zw^Y%JYiIT7-_a)Fw^0#}`ngJk$Q z$prGp%c|K$Hl%0dr7+qZ$m|r&U{;@C3CqR(d%u78hP>|!1*yXS={EOjP^Q2mu<)ev z%g+`%J9%w<#z={xil>*0pB%zhl{BHOTO$9p_oJbKk#P)h*UrI0Nn6A#LKORC)ng;Y zC9R2Oaqfq}^E=z$P)zYnaWZOgl-kCU>{X-H`VUGYE|%hAlNkTyU^B3HlWWmSI2jXL z9OS~quvpaQ+PoQ&cj;9eSYzrjIJnmPQ|BWbp(Vla^{H3HH0reIN3|nEgkXFGA}axzRiC(6@~c^0UdqPK!A$rVd>dvJJXpn{<+XeSkQ8_ zb&{Hba=OGZw8XI~(-o!KHq2{!>@XSlh5Rzp&uYKgS{3`#le;_gaA*Bcdq%_J{^P&a zGdG|DJ$9EB7-1QiSTaUSySR1Nh)^a)Js*?6?(cN1(7VbC4n&%UjH_TArQsRu(o^n= z*TT3Z5u`HElq&q45O&ZsYm8_LL!}u-T4-ARdul1P2vW>CA}u*iRYZQQyclSw@~j<~ zhO&-)&4=XWblU1wd%KEb*8GnI`Qurg;ef?ba)H#i0)hp zvf}EFHP_^^?wr~N;bkXD9cJOD^Onyn8yZSWtMksx$4W%_HLMMk-HZ`@b&{`V<|&9{ zQ69&JMwxWrCi_~GpMu_##DurZhXMpFf)Lg4$*Ry*5cTtsM2?EHmOw+lXIwe;^X$I@7($C1>^CyBiIhQhz7RDT!K& z;%s-zxs)lG&J94)nQ^`$@KqUS`V`z}z;nqrF)+pt!xys9d7%&E;ZJW^XC`%mtL8-@ zAo!_^K^++L4?j(JO3zBdC!Xw8AiTBgGLg$7Q3$8>sIZQ7^E^{@$VIBXJh`RE1o^^- zA~yN3awbn3$bo*pN)np>%)?cxD7s<_A@OfpnMF1v__@X3T8qK_9@y){^Mo!jU$aU# z*^`S!7j0r=rXQfJh}=<0#)&8U(_%N9ZD9Sd*XvZ=4LHQCbr-C4=KuxjvtNIyZ1iEP z!B+bi-;AGSVYIjE0DhA6%~LzeKR&V5T=C6|gzNLbjAerdZi83-t_d6AEqssLDyWe9 z`QdJ_GFMDDYvi6iTa4;l#-{!-(HRhf*pTkJKrA6+~we9V zUi5g`PFd#V?YlZ(n+|-C7_}ullc|z^(8mCBVa*x?)}Y)}jRASkluaolo(X?xo8r}2 z^Z3K%Aj3ts-okSskKO`Yh5xRWJcwB&EeUXKMCU=sgv`ogg8Gn>6_aFw7I zBfG{7auB;Yc6eV?mO178h!?HAjqv=Wc)s`xC4#9U6YJi6U%gqdwAV*6?olSDx__v}eu6j-7AMYBT(1;MoPAE|D zsgmXeeB)_`8gvaVk_uY3hGpYTi(c;|N97tdLcq_3p#Ocl)K?G+r4 zJxI?TKI0YYHZ8;9`($d2cJI87V89CVSB96Zxk4uV ztV>a-xc?Z3V8Q+f14%s%N-6SRA=@Gmom61^Lyp$>I>DOA`S@A9!MWH(h`pEa5ICj~*oso1%lvED{egTNMvOD&p_r5G zrxXcAKj}KY`|qlOD9cyGsLeiQ_Vq{+*8^D`^Paw7tQdL)4<&7*bq!)tpv^8&^d%E|hCDmHI;u%CncQf(&Ul5XUZ~ zy>;hSp$CPD&@-?}?758#1e#UJNcP0MH~`4^I!Az*$-&|}L#+Gk7tpivI1FW@oQOAP zbw)W`Md#;E_x_5Vc)wVZ@EnNq4|S;z=^LR$)OEg-v zg#5`YbH*ccd=oO^obxRF-&S7;E*_H15o^8MjvBK?kv~1HF~|%d%3Z;dlxhDomh&$& z*o_ItRHrzq?`zUG#;CvlU!sM{#;(M-VR^42d1MND<9V-qqqd;78&{b6xh zFd7IPZno<3k+nooUynTJ-QT&0V4 z_r1}AnJTUa|BRk@-LEh}_)lR3-kkQMQUBZ6O1L%R7y?P`NlQk*T*6E4s_5z@_;B=# zG^r?BPh}u}zccU2G>^ADL`>pEx97y~DJZ+(b+LA(^#5!LGY zt3MOb+=wC&Eue?8tL<^URqg+*+&71ntO#W{_BCx_l?plg5{R63jxB^<*6{B04IsMM z;gO;k!oh9uxyL{$XgwtMX`$%K6d~rj-}VsU^rs08BP|9_4SH#v6oN`uGXc?hN=3C^ zT_y(XrnCo_6H-)fDIvp`#@pwVqUe8+H{{r6MT#<&fuKiKiv1%o3hY03Q%=s~7Cm0N zy7ll9+LPG=D-vTqW0PLGhZgUkd{8o{qUp`q@l4jEuy#qe{c*YWGQX(lG<|w!E*8?j zAERWSw|xrzTc`cvuI;r%a`0zuQ)RMUUKVvHaQkMpa~bbY78;?kaw6yuHWDkEf87@B z;U~5{fBf-3SS`g!6QGBzd?|vHq|(clLQ9U@>mz(_l8mh2!$nwp5_WpO1V~>4KPp=y zrjx8o1I}GP`6nFM02(!7Q+|uW;?2Xb1!}POaO2zxh7t-te2sFlNipD$9yN#@B9Y6Zz9;Yw+_Jsyw(iU{5r6GT* z!u+USy#~!l^2Xwf{?vgEc`tY{&le}29e8+T^BGU_e>%wJ)1?Ej z`{jQHgdE@~p_rg{`)F93Rf(2oXi6eI|BE~(+1}@Sa4B_vgSUpfWi&Y2Y($)#Ojx6y zl0BdiBx$eVVgytr8bCyXt8Lej0S4RRlh0g;lC?UW!>3`2F(5P9I6IUWJ!q;Iy66po zH#W({IwB$X&1$X`0e~3VRh;HfkrPo=rT?GMT1waZQIVO2M*F8-5nU5G-)xFg^xeZ{ zK5#_BxF*tFvaFKM0+_GW!@fo(7fOC zubDChEz=&D_TH%sI`Qs%p3`QTC3@^KW{!%rHMXH<=lHC%u0EdY%5^URQTQDeQ$us< zvbE$ta?(+qIV~F;QaLb@NEUjSf6_yG*2;{yt;)P_0`_?k8p8e5s&^5^P#Qp1IXad?5|W%X0$3bP!=PMC};^o;S+hT&rP z$Z&g!iOHk;$z!`oT3Ww&6Tw`WCRGZQ`t*v0Dur~&oT}i{4c6)%AMP5RY7F_1bc^6Y zmk7kli{olNKXzcGn|H&G=j}=ah*c$6KuzrWK^xuBjD13y+ozuM#PXav={2%hic@pU zqximQ4WR?e7veEiI*>YS#<>r=TKEVGtzKD9sJ#J(?sdBJ>Uj;fI4oY#2&jPzDmb5H zE>m<#%P6d!gV_rEC{xhhUTQ_j@LOCt!HRUtz0RNbKqJ-As%Yjo={{qPD0DWxg3{=H zKcl1I98R5Y)EC*P?ZlTvRTE;4ga}hfXum!}S2fwG$Cw&5vBi4#`rFk;p9;G_^p!jb zabug#)G@Qu%x+MVfiW!SL)q=J2x`CoL>R!!5=4SJVPYe@8sF-H^ZvDOt3MMH%evrXdAAJ(-s^v1KMOHo_+3#TqnP-Ujw`xbr@RpTdr!mSJ0Ly)<1vH12mA>He3h2_uqfu&07U zcw28vTuqSeqI=z@PMdV5|7-R)h*C^3W*4zoYqw>6wcy*$#PO%{-+|4q@DOc!42y9x zJPbuIl9>>>VzDW<-Zcw>e2m}RZ`Tdmw!$E$u zZVOLe`?>=}6iPJZRL)cZE7D&3!{rLja$U_XCl(Ee*mjJw_Qz8AB}Pe+pS!ckqmEp6 z-FT9tJ;h&G*=euaCn@ag-T(CEdH&q?Ft{GirnWNf)Ag4e{}I-muBR%R8J?_C^dU1t zWSJdNF+9A)lcY9KDDzerPcQ9g8|{_2Xpy*THER`ViA1PHyD@VS6&0pvtiF7);;_G* zamYQX4)?}F&C5X>pH7ZnCYfhY0NS^H9VQfg3*y4k{XO*vQLy<%U~j1F@wUzs(a3^g zQ#o7`&n`Q>!$f4EUtfG{u*Rte(|&!wsxb+YoCDRNWb zNrKxxXCG5cXf%j#R*8ieR_AhM=Ge?zpsv+L-^}6kO3)yQF_;$1mL)lIV#E*s8jC1R z{oppvHa-sqU?21za}>J|9-wM2`4ojguW$1@=`8eklG3a$m{$a_LP4axVa3QSgu+UO zb&~FOf{*PjiXx-Ovp$r;I#uo5J$X>ZIN_C_p}uiBG{MVea$x*Q5dI~EVQumHIQiLq z&yZ|qKp_r0P&n%Hvf%B(2gs3LKKgtY6fEYl1{ZmIF(8EyQ(Asnn}jB{T1KPsBNtUe z%#m-ZM*2ALs?X^pSE)W;c4WHO?rg6~qAETR*mUs2@8pu$MK9V1HM#Z`U>W5{zq`Z# z)r5+cOO`O8MCz!8d#hLkoiE3T$ZCFjQ+yj2^lQRaA0|fP2up@063UUi?$Uv&LYr`4 zxLFwrP}`}{f983AoMTTubLIG)+N&VeQ|ZOQ12<&_Wd5NefnyEuy464CSeumhix6~! zvPyrJ5>8?GFq&vk9>Ae2Dv&9X@;U5$e^`=cy#J&g`J69)XW%|TlS~~(Fj3v{A5ot< zkUEq7h#kQ|jUSp6p?HpLSiU1CDqW6)(8(mlV~WX=1LGiyus67};2A=H?H`vfL8r4I zY4?k1$bLx4%Q2O18;B+x=$3o9d?AxIJYV|6t>_xiV(NMIIdie8aM(w7vaP`nN3XfG5A z)&&jV`+DB1bswh$Fzpsg;<)8rf8B}Ilwfsq%52aBZOrQ(?aH_q!XOMPKWlenY7SNB z`>libaTcKloa?m;f$=q?p3#`BZx|cD&0oSUAfpXT+&c zC@6y`^eQ8LO+|tr%hb3f!CRRQ>C?l`+Xhs%y*OzC1s}-wMwxkt@N6tOF<<5V>$;zUn(nNp$7%Y4^R+ZTPqCVXx7?cyj z^SK0vt%+p`2rRLeeK`%u-U-=%L!>+*lZGV`3?Pq7-}1EKQu z>ddSCg2qo`4BS2?q82X@wz(>-79L0PIa2=-)LvxDmeNF;)frxAY=cGLEk!*6C%6Pd$}vk+t{s^Qr?0*ZTC3$h#b)t*jRrk5aHB8nuh?x_ZQ4Zn_J8bb+s)De|QO}Wk;L?ywL4O)^jp6nv#oTmhP8;#cwk} zk%?^ouK?2DKihhR#Jek-=k?rV%l*6N8{R#0pq0t&+epi@81w!`o#(i_M;uvjLr7Pn zc==$9BXBDy#a7xv+3whUQ1kCxLHNN71Ja|OB4#5R6ugl%jOJQ~LUlr1MBsRO8UhW)xEy%kzez(MxDBKLiKLYB34>cx@eoc2EBzSXB-y zDq`#*tyxD^OO56J^MkYT-yrO(f^*;YyZ7v1gQ`*7;MO!%F#QxJ4i3q$g7cp!aSn z+uT$L?ai(1A442d{7yX*A0XSNf*nLmdEZaOCD*s56mKjXK0T~_c<|@*cWbL2POmR|jy_o+@Yqiyen z_UZSFUb(~#60qlt6Kj>^u zVybI8 z%WTj9F>wU1Q=!#KeX<@VQrHl4*?!Ne7))5}ROUlvai`u@C^Yn8LJj7%kv{M2pt@dx z0@$xZMFXb(uxPq`u;m2lpt8{3vP~mwN`m~wp`?Cyp{&h{@!7^PJ`@64xOK3P(ZFXI zZS+RNkQ|i>dR1Xsg2^*i%>K1aO+`m1EvLk~$^39zuwbdQ^WD=fl>+aMf#tITWet>} z+DmyD>5CeUDvm>u;qz;U6>+l??yK0`NyMx}hL)8*B1KE1Ce3ue2qZ;BU}Y8~fFn__JD|iD*_r z&VR@D`knY_d-qe)P)!=$v~n0$Xm89eE1*dV8-(@*x1>6)a=NZ?*?lWYg`RZUw=hqWUa61hjx6mnB!bj^Zr{yVOMj{i~ zHKG1J(RcbUM%ZaTqqq^2$4}h2!cA+V+v1@zQmm0fHZ_#yGgHhO1=)+hXIv^!w(+>4 z1gbVXzAOA=T_RCSg*L=wwh$}IDz1wM?`q9Z?`&(%XwN-V3>bplfoImg&*!4QeT))<(9rj*N?MaEYw*m05Z$%?l|ZKU*Wtt!M6w&Q36{1zxj3 zA5O_?NQ<9;@%xB~oXX&mzhM-EOE@AY zEC{-O{QV;%Y2YrFYc^vmK^Xm9$QoU#e7A;lDZ#59p-hlwYjs$6#5GMTZds=eR6Tlk z*1zAm^tMHMsC<7}LAmguT~EI{7`Lb(pXuf|8bdY@KldrbA^&OC@;;c@EN>}qnaa5M zZ+~jcC6VyfE&g0}VL_y@T8NL1#4evNmhDbu3ei!wK2j-mBdjo^-szC6A{f?UEcO=m z2Oh`>oa=7oL$k)XYjm-~sPI_;qQk)gDv%7F8|-vOV*}qiy)8nU8x+#T*Y_NX2fZKH z%yD!4oV61xDh9OUQ9OHTzymi3h41J>b{~d$%4QB>8Hh=h{9Z_9GlQm|HzzPQ`fNhB zCop=)jmLzm^xjI70!<}1?@0C^!c(tMor=oL-YVUCHq1GbL|MN8PXdUAV?X%}C#DZK z5`ad>~4o zcyy9~o2{^Yu?)K4%6ztorpQ*TAF#{N{Qa+BLl2MZzMG8dm({`yia!$6U$n^&q-u>X z6aIy5=8&ww)7dXfzDwcL{t8P=>ETua@iW z9HHh@Ixt*>`*D$=zd2mOI6@Zhf52~tS4oJ!V_JYE@6ZF3^c^>lR*l zV$J`m-D97>tqIokkNj=PsQ2!gL<2Is-;OMJmlm1G0>VdHC(;SSFD6LenY1# z0d#Ga_R9xK{YPmR4_y~H@@TK~XH)!3lIlHXDR_~Q9DH~q|F1$};gNYB>?iC&hgXPn z2BPOW80C4CGB-&@5vQOJQSlb^-`U4hxm!e&&GGQ7$2v`62jU${8$KQn@|W60!V=T^ z-lPIdLv|tUL?e_KOXYzvE#}jcUx|S63v})>03^!L_8K6I#-F9vDEUvvR+@)Dc)uy_ z$Cx%BSm8_b1m#?`q*gHVI2Q{v|0maaZ-q3B!EOskr*UDMrZM6l^5X&l)c)1!g=suB z4z3UYdNEj-2lvg?r3W`w!UjH>lI>`jh$TMV4~{RqO8m5H=OT zsZ)#fbt$6>H{Zyuwo-v|S)$tQgDs1*rn-kTvR}1oW0ikBP^7FvmhP))gICiWH{a=% z&fX}3pD22=l?}E%XneTO8UA=;7TaX{=!tMYH~|OV#|!WUI_&!UX%dbb361v<{2t>ndMH#UFPXn2o>FM zTkGVd3on!;z;R9j)(6(7Gu}R#7RH~C58BUE+-hM4OZ~`J0JW@TeSq*F;fPSnA#j4& zE|Y@u^H&Slf4PAcEe!+D9xaL#!RwUm<&j-liKq&$?`S}6T(}reIu%ZWuhRBZNSIOd zf*XgzI^~WP)~AzR^mnkj*QNd1a$aZNuV;Jsx!v;pXCqLZ&yQrI?x7@U^e;E{Wg8ywB$aQaiWagx0&+ z;G*rj{gZWru*jH5!!^0gz!Y zN7dH4Nz2<5ZO(C3{-WF24PfmF%r zUHRWfu0Ap zSxio8uk05Fm=jS?r)zKZ(oege zn?@gJ@A^#KAM`GxyLgTY&QWnGPgMpeK~p8eOQxB8mgnv0N=_WmJ7dkkJxO%i1emKD zVu$EL=xt-0?=T=q;8Lgezt5rw>G9A-$&b>%v6=1ySCt+jr~w-eb#|8{4>R z`4CD`fXP(S@+(5#GidQV$3l)4w0MeYaZ$k!02b=xXlD+@+Y~KuQSQDcKSFuZ+FN)X z7=XewmW_ixC#!s3Xwo#S;0lwksK@Z4OtP~*Jr?-_=xYXVEqXH&<7XCLSF$VpfLXLC zr^6Q#0Io+pe%jnqwCB!uyr=ED9w$2-YDC2)*Y|iiW{3qZ!^FT-7ZUE=;nl5wlO0C-eb1Xdq0C(gWE$7Dh`-epDLOYR(GY)2CESx$#cF-B=*xZh@b0M$^IZz4`06 zT6A;(8g5&&&QVuPwKjdv(G{qFY#RI++7cD=)tU{U>UEJ8kq(fV>hk1l z>#nfZ2`(U#nkD#onfut@j}g8f z0}d$Vb@oUX)IL!gn`6a5tG^MGQEFK#!Tn%WI=48K(oVZF-TYRB`#N%B&K?WtX8EsL z!ldN{q!@NS#%C75X+g&7JnYWzY6?_HNR#-#dcxQ}lvxe0IPP7qc%IZmF`HJ@Z&hlOkH6y)o^j z>v%v_gqyAPJ|Po3+Jo3H6`x#4m|9G1JnmBN;L2ZnudM|Zw)EeNILNQLq9k+?Wd3WY z29f}5>H?wfR99ejcCmc+_xru95F#1EYoJC{5}{+O>LDfR87TKon4JgeUMH7b;!&5; zP_FZ}C|Td2js$<#6Vwl?*5A!66A;qH9Sys5l6Su|=G_f%B0!>La53@#ERY3v4CPu$LF`LjmYM;)^QnW|<@a z^V|tpC8`M10z12rYyS0w$|hh?2^eN453~0seS4gK`!I!0*`k^;AP-POnv3LosS`y} z&SE`fl)$iPHI?21oAu2}`&$>10vRRU-=M9(pINc3PM7I-1G=$8sbfU`W9XSLLee^K z?!4CDc&kbCf*vL!T_q%D*+Pq2mKZhjOOa-=wDfCoySnICj5JsyE~C7=s=iY8elS_i zRwSu|?~f-j-?BF?jQ-oBrF*G~QBI5ABy911E-$3`w-k!~$WBl8c_&Bw1+3klz0O$< zTEewe^92vTent4hx1rA6sZSo<2Rgpe1PFcKIKj7%BW?_$2NF&?Rqij4tBzF*B6Uo_ zF&dTf-ID)bK?XfmRB?bZ=8%vhVz<}nk%7dyk?*vjENLL~tY__tC~n_ofklw|tfEav z3z7FZ;s|fVp*+ADgNQT-{XQyKhoOxA|n9e|= z6wp?cV3oczR9-W3Ed`kcImR!uI!^$Nn62}lTt#frSy$)(l`Oycc$$%oxlTBg$6{6= zUzL(FeqUd9(mLfmREZM(I7-k#*zrV2!r%UM12!ySI{B!w^r@s#HNcaEhq3YZ{AOVO zVcP(BcKasefty2;!Qy@8bWt)K( zZm%$cmN12g{KtA|o1}=>8Ok2(j9}qndygC8-KG>Yy!>k&{M)b$zG!=E|7s4a5i5&d z|CIIz+m!?h)j49s>UuNmxf7^g0AxnEIN-d@hotSaz|k)8na1o)MT$m=8rzKA=OmDu zmfxCbea7d}4+e(Mftp?1;ma)@HQefKMcqo*WUac^g%Eu8lx-&#>b=vc^I)2PuR(Jt ztMG+}LkG$cx#UU&0jaVX80EW|pA?z3^+kS_u&7Nb(fb0NWTq&Q7rBV$jbERihcS|B z2o7an=OlE@lZea5*S+wDJDMhtp~`F(O4zhbo`7?h_~pTdu;HhH6eW}Mm@xB-X4nH- z9AL@nfIgQq69GPjAa-P)FzB7i>e4b0k(8U|OW1yepyShLy|3w883RjInLVSNU`%Ds z$oNxskA7(psKISa^W--8}kuSa0q*{bIgp2FyyVu+c3#}P)f^Xw!C*6k$H z$8k@*nXgH1+>K&b$@nT>9INwG@0wZiGvdTwU%%J(4wJ)(4S1O$4C+9k59+#FMi-dc z%)~fIDg7h0us~sCE7CEI`jO3X)?Ja1ReFNIMPGHFdFOE60v zhi@%P%!K^Vrt}=@2Ajj_O@~~BEz@Mw6r}zvRzhO7mI_gkle}!m!+0)iM$@3=yPA2k zHWC}x85P2+E-;wH*!}ymG#irAfKxg%C2ckT<4%*txfNs02t%|doP(eR>lRsAF?i`i z{-j;Bc|q_1_C6hl=HZ1X4EYEg%m55@`F=C>y8iD=QpTN4*H54j#Fw(vXDv}T*?h`W z7d3mns=0{h&ZVfT@iPV5?@+#sy>SS?=Dbx>DOsQrf0u-p-;aFyeq@Mc&Cq?^vMDhC zMX?`+lA;;rPz`7-07`y%ktysr5GXDx!XEtt!x1h~B)`gl8m6ZfyO|pJ0A(+dMQ5kt zYZ;^>>kl#{9fD8@p^LeV_zN&iW|%`dZr!PQZmOS+-Rm;SS1??>Nv7D;{<6jooPD!t zFsofQ1g}`-pJATp4I0uJvac=-uwt57tGgDFM;|Su2D=}D6=o-EEA;JxCAep+mg}I$ z81^ca%x+vQ@0}n`emj!T)X5+va?9PS`I6x3Gc&WSN!<>KmJ1MZA_tKAP$mhpwWvJTGAY%U;CH(c!&;aa%^1 z3uNCmGBJumxeYx~MqH{&`HH*JK04#u$!W-nJHcNAN3rhxZ3~+mYV_G+jxs zvDi-^knW@orm=S-ziXFEkv>k1*VrZs^WpfE^*-Op5{B{@+%CslAw z9h?+s{=~Ow;GS8e4?=G@?TX?By}& zpz(JXuoNyT->iPQB^U;6yThin{(0BS(z{~p#?W>{xKmga2=?X z_(r{p7?WK<&>~l%|8u=aj_?T5 zRu9P>qJxgU=x?qNYoMz2?zbl6DmnWUboF@xxA7~&b966E%oNsp2NGaN)Om?dAJdf9 zBcwiU_Pz(cIMhsHwr5~s_SHfD5R}A8xetdkmcsVwu`&JvNqbpw=1m;C7VSZPazRa| zLqc4T2_Z;CFqOxmmpLy5`c+jZLe*hFuWsYuDojldnTkI4O3aULQ**lQVOwIxf9EuO$#Vz#ii>$ECvPqj#Hs5 z5~_}jv(6r-kDL3bt6$Jdgf)xRi(|rp;7f{iFhA=C;iR$1rFd}A`sn&+&VxSU>>(_1xX8Kt-)Z zA`p{hARhf!dIXjWoEMIu;uPE4%(K%LiVQo#TP*R`Z%n?}Q(SOz}4c1|w_Gu3N zSQWV%lv5hOmps!J849=I|M?>gOO>qb2t3i7TvklT@BFyo9oWI?6(y*3O8|S5OX;J5 zu>@q~G~j>lm4D#}F5LzA5uxgeTGX*b9CF*o`W{=OFwqs?u3g^|fZBM;4r(GCN_`8z za|n11rKo^Mpp`>j?$iY@1eB{>9f89G9l`6!%yO%U+(b}h4}En#c@{bUfYa+mRsi&p zTATbw#`Np1^wfRUw5j`|YZ5sohiIXSE_#m&lM;rHj?R5)>1+a|BuGPy@ApeB3o2U`*Ji+H@AM`60a8hs zyantW087lBc-zT|O7u>b!z;`)UxyS`zzbeEgw@++6U4j{I6*DdJ z?|&3BA3;=VP$}h)AeNdDbP2sgbyl^a`}fnDGtTFXBxqDE*F^;^y?3Y*$U_TS?L{_a zjLxw}Q<{<{ze}RLO1e1$uzE9eSt1cOtYpWc&=V>87;=GL5q+5NaE@QXn5=#{NzY)Q()%?H> z9sG_6vc|gKC)c`{%4?5NV=P)$%(dl-nF`DFBJSFZU$#l=5b8g69hkWYSu%E>F;reM zX}poHJcuMcH`s1RM`Uuh8@;qwZJt*EVR`0(A(;rx4OC?;*=(Exp&jpKRx z6{hlK$$S=rG#haF9>S1t3($Lk6vF{dJt_mtLdO13xkyoa!qoA>9`@qlaCCD~F&cu> zTFif>Z}oNB5+Gi?n70yVCifj)0juwKSVz$-Kc0s^+$?QCC53+Zt}3DE>qrWLBgnIo zF}Osa#fOED#vGyo_Q_f7dcO(-x0te{?JlgFxQL86#*vhtZf>!CeUTc!!>O|Uzpb%- zg_a}e8z|-p<0lMyc-79Z5$@w4FbWs`cJ`0GA?dKVTvn7oBS zlK>;Sa9Ar7`c@Beh(qb5ljSZs{%7z3++6XE#X+_hEW_EmbsNTp)y|KhK9^$^e~|x+ z-iqptjZp|*m0a$50ZSy%F)*=Gb50MeR3)~hpGN&PITQ^y(8`Lydq1_xGcI#@$@<40 zDjs`?m@3d1&e!u2S<)jS>$TALz(`KUuurslY~Wsi;`x#1bF>}Q&hlMYpHuQ>bPq^^ zLCE?<4Q9oJ4G6ocT6tcVy09X4bXjKxiU{pbu?g0}nyv07cGj$B^zon{?J;{>nE@x@ z55A2b3E?Jc3*QGE_(;c3T{A6&2~spZ@wp})ffE-jx&Nt9oL=z!@e@YoTqh~4UR1R|a|K$HzfLmsTx3J6NXzjISMaEfwp^!?V$-a`qcJlnwnFy{1>(Y}Y zHls3kJYo!#7=tb>_seK`5K(>oHmJ3BD8jn6*N?%cyC~5+c6;9^`n5&hZ{3}H$uL3N znq`dS6h9SM+{iI@zCcjR6e)yIIvMFU z^x!g0InsL>@GbKk=Hqtn$xt8MqxrI*-|KS9kADH3J<2Y4;sbt+io-~{og!W>{aIW2 zi=OY9#H#sx=Rx;AsW0KR^X(<|5npRCeR8BX8Hzj}35m$uE91nEV+b&a&2kZKKo3Od^rCPdiF@f!>=Oy3QkxIJe%% zMdnz`#1HXVPB)l!>uxMhhA)?9Fu=cm9phdH$URJ79bS@imw_T7EvU&gcp3pbasdi{ zB1%S{l)KBe@qxW|uA0(Uko|k$2KASe&tDRd#Anpc2we0LfVscCQufqb;a?o3%X(w0 zTM%n&pE>ywHumEa8XSA+r}9=W+ZXBJAI-&ji$9LX1!S*qq>SJHA5C8w)m9rV9o(S> zifeIqDehL>ic_?>JH=gtQ{3H(ySqzqcXxO0dGGh#wE`hOSSvYs&X(CTvp*-Cqp%Q# zgnB)s)h?rY8oZQ(5#SW6)3hhw?N;Un0|6n`iQ?HLNF^G3$>YJ;_H@cw3rO&K+7Wx- zq63k+m@+QjdAT6W(_}J;{N@qz8uAWyXX73R62(ZqbTHdOh$EdgBgU;*4bk^kqpf}FptL+g zpOxV*R6{DL;^;L;tVkbZr09Xi1L6-HRp-<7v{-*S?#xsURmla%#;7@^ZQ{Lx?!=I+ zljVi=8?TPKd^USHf@krjlYawc)KV=a;PuIphYNhhjWfXB8KT8reL zc#$hi6WwgJYx>-uQ?0-H?qGN}xnKQY$5kog(5u7a0rPlG<)atE81n{ds7XzdSBNMz z92;HSs@<@zsp3rOgCErwQ;D#m;X{Nq=0%HrodsDa*j{r5Vus~W zSX+IITrmJHiTFWuyv!aP`maDziwoL{BehJ;^G?PB&j7!Cj(a6`=V26*!~h9LR)ss3 zt+>4H{Ha+&N;*JRW%s8zF}S+3{%yw=KZo8nms)OF1`U}}Mw7UZnB zn+jt_c=?eI+_=A&#-r$OF1LTa=1KWS!VA5hvuW;tj7K#Kjh=lq0QXGRHvbqFgY_X@1EO_*`o>q$aIrKnO%u5I!|<5v00o7K%` z2y9lG!YX#!%kqK=;b)S(;^D$nYLp0!( z(e&)Huo0wAd~v)s`b$SfG2GWUU2_42SkYOHIDi_=kUTuCO1yUbl!PsO#FspdXr&`s z)fU~c5Lz+K{^eV&NF4b@dNX=6QwY(Kp4au3-Y;hjjYC#$3OQ}I3OQHj3q~*xWIhmY zsd5U#sfHdk`IWY_)?VSi%;_s5r_!r*h>EmR;jp*(=ef(ltOBO$*@4La@&?MhgfU@o z)?8{3fTVCga1R`g?pVArn=ac7l@vX%bRZcpVQCN?{`wanK6p1wFt_18skluX0ufWK zdo7PL)YQe26>vnuZI5~00k!^wmAB>?a^-`O@#|&G1c%9DXr_XHLjpgu4U<@?0b1A3 zfbDO9zmcMEBxLm3!7zm%C92&i*LBme!I?jQ5^+Zx{D(`s!CvmY;@SP zY9Xz0t=k&W%^L9!TPF0q**vN*-4Y~2k9d}TZt<)wMQ)CrqRXSnJ~Juen`xhKo=nDjnB2}ROCXO&INh1y|e zI9*@VLtvkYJ^Wid>G#e*)d5<@CVe8WlibQ8uwiEC&%!!^j<$z1CYhT?jbQ=WLyLR7 znB00oImNa!>Q&d|9O`w~Q6@X=xaM46_F+&^LXA)^jn?!V2OUBHnUL4Yn``d5rKNl3BIvsEJ*bO~ zE1-Q1F%Hh>v*}=fz5X?ez9eM>H#HL7d`PuNy+0#z-iltOf-3 zs_?@A5e~gBx>*l3_*q1d{&<58gdQQu^oGUF4fS!#h@&v*dFFEO-uin(5k@&XP(cUC zo|=|I%ZZX@D@zENMMcOlnDsYFugIEss6t7O{I+YA{T(O|a%i9^7reb(L z{Pz?Pk8z4mY*eahT*EEQgKM>E<4GP8+En$XI(Xhh*yU{fgRmFU*!8>v`~Ho=sC7f# z_h|rN_+qXu4Xb&{hOXh499K_Tn+xt*k<%YU?l<85HAXq&F=iz@O;Vv}u$D;O6OH}D zp*f(VsKHjT5FybKTH5R1+l<^)22<|7D``&rWG^MYiaHKGcq>JGwlI7JeX)nIeo`#L zLj!g(V?S$%n?6x5`-?|Xtc#`Ey0*Gt(9M$JuiR~Rgqcvrtmm6gFi3*SBosZ+C43K! zXJE%Z*AMt|=|}zGrAI}ECY&Nx=-m*IFW)cV>!1N$qtI{Nh#fdRWV*QW!oi4Bwc4!A zTWf%heknoJe91+YiFq6>1`rf&NI9KNGq@oS2}1)mjF-2O!UbOr#1!^_1#wh3^2suh z`!|`s-{OW{e5n>`HLTq~)+fCffX1+^FQqm8q9F8Sc|3{OucUgSUComLUZ?>oas%k- zYzLe}BzIwmndaVIp_B#LUEm;pg-#Vk{?SmgCLNU~m)<@q%HwCtx!lwmG|7?cyD9nC5w zO(Fd|!muvI?DCI1F73{4@l|TgdE@%AtA1w+us{WQr-}c4nqzdi`%8t-q#{Akd668c z06$`pQA^?1O*5FGY>M9^t>rI7h1%F3O_gAKdH(L?Wx>88Jf9Gs0`EwCNk@YAm6bDs z{(YT$s3Zz%V`rOsahfd*33p`37$u!~uTR_PA2`XQ%cwCMA6x89Hc!|{V}%Ql93Yg8 z!rXrERbi0w7Y(bX<|=VDpe3IL&|a4C>n2AC7bgFIvs-YnrbFtyn>`y!UYM#xduc?M zO?rN898!cXANZcbnX$0T!aK`!;niAG&KaGrqZi`GzjA{AVH5lGxy2C9R+$nRz^K{H#Nc!mM0oToI{qYZyG#Q)Xk-RS?RsW%vd4q*~oVUnn9I|L7%^QHXN8$?}LC3?-|ZW6bq)T7A6d^>zE=j4!=6lbtRnTUR5vWd9h?nB`|> zVx({wPZ-;|epXoMv}K=LM0*tzKfZ!yN`%~wKm2PK)3)Pw#)egMQuOP(?S)Pes*GOQ_5) z+duETDDn$fOABKr!S+|%LE30wBu?1^Qq>((jkw^9nXDKhCai3#9IZ5R z$*Gf9t51uQOF}u=3u6m{wb&iPSaq+u0*`LEg4Sx`Cp}s%hGI##l)(B;jC|_{Bdrk6 zoC_uk+xs*ciIF|vV`=>U;GOVc@^YXv*9T+Gbn@JEV$9ffOkDqH8v=?sfZ=3i*&%kd zyk>xrVtZGDCP~bbi0Bx~^{;F_+ zR8|YItXq?hlP5PT_3yTJajnn8bRIHmy_vXZY%PH+a)WRm`D5JcpJ%?&iGW7rIEk4g zB5z^39hvEyKqPD8q>Pv`+ha`iZttF^Z3sXfSOkrhu=MJ90rrV29E)tQH{b16wl=fR z5WoU3Yk45!7`H*dNR%Fi^-7_$@4PB$F+q}hFiglIxA^VnFzF?esc%~cIUiae>`*r2 z2{Q;c4IAX0T8_LoC|=WrJVka;1~vTsYkG#?PzJf%*vgRrtsF&5qkkDxP|i8ZySdEc!?+NtRVX zfM14`U*`M#Q@|iNx6{_r%xlk9wwW4-1!8wq`EREW8(oaj4&j+8pnZjkRu?LtEHsxR zH;G6_1AY+xQ4We-r5{@tI z{q0WKYlU%_VbA98+-pLyD^u{9ZKUrHH_Ze`#*IkJ0}CGEiD7n!6cR@s7P1KXLbeYta+Zel>Ez zwE3{_e)#@gnupLr2A5OwNy{DD^`V}?@T-cV2NE7jE@4)k>&D)LmIFCSJ;JAzTbF^#@1X~r?x#x>~_-dnyH!VIA$>*GFldae_LUzSG!=_yH?b@jmz(CQ~GMzfp# z-aXw~F!OGz-L{S-sUVhUvFDA;#xqw)M*#Fb5Hoc5)4={INMl{Q$^`Po3eo2EP6f`c zLzFzfR@$ZU7WtEosYRIDe#8-F80Rds!ClH#V6kpH!AkJt%kB2u zZ3LQ|MkA1Ol*#-Y<*JHE__HpG_h`bPRFc%5<<)oo(r=K9_I+B4Yo54odx3qqk**P%6dmbrU-2FNF+i{3{)Lkju)w>rE@07NUjI-1fdJQYKTR_=fy_zWmv^-j)4)u+(XB?W(4;eBPu z6lrniop~lzP^)3G->=*R-3+C3@wJphuaT0Fp@L1@cM>y%-Re@~X`7wL+}?H}a(I;5q#xksae#4?qgI#!+{MzUsr3J~rr4 z@9OtgOAO>;vTf+=nm(Nz{eu4W*r*VGT-Vh%p$KK6-lFMx_bqqU1pB<3c9A=2HSj{HgW{9c*B`j>; z?6FsaI9TjdWfXZW*nim7`Q>_G!hd$uMf${TJs{XZtHly7-HOuGhWJB0GK34&F3>yq zPDoInRdq-a%K;l08MZetM~<4TJ-`F-=*NuG>{|pm1`WDHc_y_x+ zr|iHhYEw!P05&k`ak-K8wHm5UADxtiEz>Uih{+Lwtv>la4X%xu<9E#t1DL>#Yzv;e zQ_v8Dsxa51%-)~x`C&pj5gOlo>;`=}^}xUXJ$_V=^4mc_&XuqO`*W}zERCoS;yrFh zNEW?Z2+hBTy)MZ!T9*AcTu~(w|I(j?e)c;f&h%C0@$^e<22W|e%Llfi&vxU~`W?)z z7pz91vO9V|=givHXnzw#e%7&>8nG9|1Sh+-IQ);td0wi3hpPz4N%;w~%BB`ma#anT zg_+nR)c3>KI8(@|Wz(8SE6-%Zw9v!}dlxlH(968o6K#_{ahI>61YlqGk zXsP7Rl1hBgx2ctMFjMl>5JjDzf(tL?NkdT0I7}gCwhR1-E5LP~+=q2>BL@TtzVq;Z zoVj$vf4zt_u9!C%EQP{B0UyZ#q#V9^eZSJf?6Ozg5ybvMuJZ(ydf}BIYUJwR_9|~G zfhq|rJ~(JG3lVMzJ=Af{Cz8<>>VpY0)Lh_J*^A#EM2aI0z8QtZfraS1ictL9HaV^* z(>pqhrFDmHROQX;;w!78L1u5j>@BpcJmh0R!z$!K11?;<@fHmtfCYGP#%$!=TCO?O z=d5{keHMa5$V$_qS?!B3(s$PShF=}wYVf=x?UySRftn(m#5<{O@2K)LkaxbSgIC-W zJ?_Pyf`WV>W;GP3ns#tH5nARM7NDkRc*Dr0NsjpLW|49xXku2LSJ}Wy)P=r8=6w86 zFC5JL7vb^8@G*9W*0r`FiEINHIP<#gE53s6%WfQ=qAFaV>kdfc{GXRNQ@SYK0KV7Z zgR#zqL_w&0OVtH^tbU&(Un5d!SvLHG2LhM!k0OW`+wq!)9g!YGzoxe|8-Br|ztQa;|B*LX z;SO#-Dcc(OOXBnUZM$r@MsCI^NnWUvZd%D1`alabOmNys9PLBi*7}TJb$i$=149XX z-7)euCmxptK^*JTsst42|Ln&$7FI4#Ys5HwN4S}#Xh~YfZiE2{Xmc7`>g9a_7`rha z8}b%PyQnf}viyZ~=gZuAS)qajxL1v2#s{!7t2;xJ-mp(hK8bw?-obtP@3hyoSuC>y z9zG=m*3UM0A(EP}o|1|(EVhs+2}QrRQNGf~RPMP$g7T9gaVeAr&`2r}jnf%VBzSig zr{aF^gfle~1Z$xbhY0Ub$(x?@} zFfZc@go7d%fX#$@ke=z}-l4qbm_Krm8?f`;FbJy`IGwcM!mt5BiQyU-s3Fs?#Z*<+ zVKdT64N?W#6p?tXboUw&{-1Gs>+mPvoz01lX9SGe?BSaTK*K>8QYZ&sJ{I&Eg_gPe zi6c_Dcb9> zTP@XaOITl9etd~K8y7(53Tv71mpN#$Y9wz^1w4Rof=JKU*zEKIl5Kj@;of+@z;9UN zl;&-)b|aCTjTpiIS8O?d*H)7G-RO~ep{`I{j&mOI3o9E7eO+1LE?w@S{ve=ST*2-t z+=oEo)ezH{0O7Ntk?GG3M7uhu-LXE*lcIOi4<Cpzb$-7SI&5X3ckwyV-$N>=|Mr60FtR+P3h4JmpZjQg=Gn|7$ zK9KxCGh zmQOyw>4zcQ!@1lQYRGd+|C}p12y1-OSLbiEz<)dLSrcFodhrgZ9g1`zX09DWH6(rW z$lg1$($4HVwE4$}u7x50j|-4D_pf|0oPB;7Rw%eSP-rYH>!sz3XvTg%GF%Zde0`6h z>Q4d*`bD!LbiNMU#laoH`78F{+lmn~Tg=S|{i~A(W2Jm9SqE8*{|(02)~8WO^gpjO zf5n=ch9fyJgdNPc;+CeAh-{y`;DvN0OfZBJW1`!CHrVNpxN51=m%$%*?m6lclmJ9~ zfQW~rHtUP)=-cJFWlr=k5b7a^oN7Luw5xKUsQu$p8&DRQyfq6`?jXkVyfl{u-Ld*X zutq*>_-ykZ&e<&pTkOVS%ios7EsejumKANz_#gbjV;j$0}BVX|gDnoR4jFt-ngi zB^F1*b4qbh^Hc|i?V!6PL*E@A_a0)BwXW$48(xTfHFsp7anV_TL#z;(=Q1Mvk$|o+ zYqR6Xd$Aj~Yz;NVP5NNmn;1R~$uC8875zqrMcvp6Wy}y9W>g0v`-a2hFiKzk%#F_4 zJjwLV!ns$C`s_SK40onLdXqYGP$sBwBEzl5g3Jw+{Luibg_%3GA(qo6=c+n;&mCxD zr^HfZXBKCyYT#d}Hg^S@9+=y1pxZ87^4f0#uY8K`yWP4q1~?P%6|->5ju+2t)L~YGEi~c~7f9g6VyZ^GrYc!B^5<&!UWGwT+#k|- zs!6h}wZ*nvsmECW2))mF#HO#>f}+O(?lM$8`ah_A6TA+k$ZtHR8@iPjoKgJN7DRw0 z@1&#^H7HuGOy-)Cw@7~9zo&IqbzX*Fr(8S)Sbd&pn5fm7ck4K2j^N>Er}ID~Xys-C z#mqyekX#$xaFO7aP7(FZs=pqdSxKT9qoVqScLy(9*O0F z;-MB-Lt~Qy12YsGn)quK#EQ;+?(2MMRTR(|1yBs-jN}PD>xaA|I_V()a`}E?Sba8n>T8qkvPB; z6}NLk(4aUnDALnyFeq(6t6`dmNGRGk<3cAsPa)G=8|C0$aFF-$f zT|+lxWhsrO6w=xu6jzlP{H1etVJi%}R`vDh5nh|?`6|;9e!nG1#iqS8E9k58;j8%R zXt(9Q7ojs19D&N~c;8Ld#lQW84qQ)Gs4yoqJUXn27|(b|x}sW}C7Y+`yTq;Tou0(9 zHw^O}AF31^ssRJvnn7MIwZXzOKSnNO?1uOy3}e^{36i%p#F)Zs4U|mlgcu4Nd}|(& zJY5r~8ieMLBjn=_c^KqLr*I!+9?eC+Y9ApsJ*w~TXc?hlE#&}HUE{x76b{`?On1Hz zSnF)I0Xmx$df_=#2oQkN*rAm}cnQ7Qp<0Li$3ozvpM}$k;dLs1F--AQC|I4So9ms$ z$(=~pQqI2y?OXua_8TNxI{W6OU;MyiktqsuIxU`z5hb}%RT72zXnVD#1}Now>qZ1O z=z7>UqLvxh*8+@0Vn{!I;D|fdrK&*6&ALgpR{SWnQn&O|9q49J7ySqK5=bost3;I7 zRL1`@)!Kp7MTS4%#gIXQDaJ!Fk&pg;O-@z@n$|x$Z7vDmT>u8^=d1&jPv8VJq!gvnuKu8&6 zV?YnZ{;|+o0qm9&B{8thO1C*g?QiSJ+I=Q=x7dh7u}Ruh0oFw@03<+11Hz#aK5#Sj zdeGqh7Mh;Ff&i?~0lvh`uF>Ta+td^(FVE_`XLpXC?keaA=D(&j&hKsSlNXy5q#RB% zKpaBGzc@6$hfbwVnJ+DzHfy9=z%M^jck?ny3>s&sIZ`D_wBKx2UwhGLN1pT=e|gIV zW?<*X9YzRSElCfnV(UhHd`!!HEe0erl>*^o1~!$JmbtPUJztNQ?ct;{rRjf2(ev^# z9eu{(Mctu_gf&@xUfnR+KX7vL{fD|)l|cC!3F2Wq79v_TTYkbZvc{;qDbQ!UFg zD{2|#TDQW6bm@l?qu^e#C>kw7_~6{L>QQ%u8tDg1sy*N$!ihOFr(O4PR{<^gZ$Ejn z!6+sj#tS091U04tl8d+MYwRZ?!)B+Nk6#!hAJdHn6-+hGJNgw_3bmCO=^2}N|pT8{w?%!>Al$_I8U zwkhnrQ`d}-H&DrfK4ts5{qN1Ph8P+$Ugv@d&vONy{4EFD-Iwdk2Veucx63z_&9yqG`i`Q2FzdCYBUzJItLo&edbSKs_fP-f+MYQ zf*eUUI5OCo^YSw}NrU9xQfFV4c811DB&394bKloCOdQ1Es>&m(5i;^t@X0u>8U6Y_ zE~gq{2|*eqYpR99Pn#f|XS$+Ld!s+_jtFn96WEO4l~;AWpBM)!Eh- z(1`FM`3##X>%X#d`*wN-|1muDqpM6pTWnT%>` zGHwi8DBm-(^hnh;Ix0{^2qnZrG2*rqfxY$iAj~L~`;ZkKC@8@8ZhvrV>!zU2#5CYA zJ`Rm{=VU|u-&E-zap0aL_;zj5qD;JgIzOk*oW`QKz2qS>Not6-Kqtw8Uq=&(X>Ypj zpaWL$_9lj|xeT1lpIJe2ZXs?SCmZg}Q+8~&j4>K;iG%sIf_Ea+LMcK!;PMdj0$x$J zX_fAuSNmy|v2?`*Ih)I>yW^L}Gyh#^c1~nBXsMApEsmO{uSiJUmi&m_J5i$tw?0?# z1Bw>9Vw#P>u8n~yb`)hzD6FE3z=Da)uqAoC^EOjZzHWm<|1<3GJqoFTv>;Lrwd-Gv zQx3!HF3iH#oDj_f>=9Fl`=SklyvyTG`WN=NF&J}Z2UQutBN)>;)kp}8;bX=KNsYM+ zvH$6}6p2=7Rq-U!N99o{2ZY`(>>e^=;zG~>=1D#vbeP&)2N;+TLmJ(H=I&Z$#|+Iz zac$M0jLoCH1(m7lxhzchG_L_h)@95r>d)M5Kf{MS#upNCwZZei4d5SPhW?fsefTei z`dB5WM{G$tYohtC{aPq_TNb#r&%=OOQX3d=AYX)?r)VPYEo1#FxkFfCVsYY8o&#T? za|=;1cON1TOtMf&&~s z10OkM#$IX8OB|b-Cs&I)p8R5EAN4{6*R1hoK0T?uP37QjY0APAHL811h#X_VNY*3& z)m{#Th$)*f0vu)+h7Z23`)NFIq#B2SGAofQc20ovOcqi zj6mPekK^>;-U=jcP7y#DdQ$u4hRHa+Q@}_0^Hk5QBk2 z!Sf;IrA8Zuhvf8GVE`inGTI&l2pEhoeP119OTDj-i*(l(?3>D8GOOfY^AKXbju>Gq z#zr>4(96J5+kA)R=0TJ}vLmVQwp^C7DjRiTUBW)VQTMVcF?49d2CRVW`DI<}b0+~C6Tl2l$;F2to*EV=Afh8X=niQUsn?Ik_`qHZ=*A>k2WNM z_DP)~m#fg=2sUIvG;PWCX!NLEV$(X^y?6L_E7S0B)~M=no!4f|+7CEq{l02gJ^sK* zBT~h^?M(!Y(g_z!kSE;X(C61h)CSqodT9NHEF`gO3iq~){!4TD&R@Wh1(PHzk|uMJ z?wy74|MedF1@CC4?u;q>q05F6A!-OVGRzx(A{aQm<+!Mm&X_I3JU@Qy=er;%sP z`mny&`(S^P`Q05#ynyWMRNJ(=pamx6_L=Y=7zhyP$qr-N8+lItri%~HcF%j|4Y z&?dZ+I%5zNJ;hc{ZEw$erPK>%q{QCGhGSzjq8Z=;SB`v@fjj24Ila=n624$a2P84Q zcUwg(j77SKR8&jbtB=-du?&iS3NnT&vJa_@qYv*a0AnORT4OgRMAv0c{Se5)vYdo`RZk;8|f2GDdi) z*GXliTS;N$a5yF}sNG5OD@;MJp>1McfoRb(SdYTv_VW~4fJ@`maYI$TafyEd-)iT$ z@#e5N^XG2a>}EC7q>Ji+9T$}S`OQ`T>HYagoPq334Tr3;IbEQTU<(AITWeWqD zM=>>`3J{=0D&qS>pkdR=ytJUxS^Gzb#s#*0mYhre6_zXd16Ia@_4=u)xeSi-B0eUx z$#OF&ZEGEy+=kICHxjs!6EB|}1m1rw=n3)N9VROJ7MEIM76{jB7EDvClvyiP*eI4+ zPn#u}bC$8=?rlMRFh%VepB_|CHjvJC5A&3W-OgS0NeOGjMj{cMfy^r)%-422{s5tH zj)hhz^^^Olk)87w8!Ox|Q2P2TdbU>G7E6niE$evcY;jyi=e*k{o^eEoie=$i50zfD zDH}?T)9mOrh4h_HGq#ny!=aEk0F&x--&{Dik#|;t=?fa;n zs9ir~BT2uXcO4}TZ4dX#W^^v(xhY|pNUN*V+jiiAY@_gYsr`wH(a#=ft`n{75NRlQm`|J~lp z57p6zrU=Ak{jtb+>n>b^Sa&vS#^;eM@izi(9t&3DZMLhFmPNqtDs*P+<)5w5~6eR)q*N{eG<^| zUNtmmNi_9dU#q`fHdPxB>1!dnX!F~F$VOWf{AyP$@ujp;b{$ltoShtgAVKBt-L!Rm zCfFvlgs^aLq7eIn_FxUc4vJRhR}Jwtg#di5D{rCl)J()?{I z=tzi?M_UST5;Hhy?d*p^gBgEr=p=NXg?{?7yT&0PL<>W(q9)&gfU2$I9F={=b5_G( z+NQNAT|rS=P6AR&X|B5L*m-rKQP~+tbM__CUf~n{?}TQ-IXw4%Q*#@Jr*xBeqOHrQ zXrX$Oy#Ym$Cn@Q>tC=B&{O_6}6^AMa83FNkQ(511Z?UPxKs6N&Uo+AWVxD>abc7hR zlmRsd^+;X=(w<;IqHn>a?B4{xg?$4Lr1d-h>3iaY%r-|XgFs?K>NaXO)EO@jLnD>1jPL+p1R4k+UeyuR0~E zpA|kD@nBYWt@lL%8N(61b$u3M2)@nnFqwI2s*>EZ<8ZOCemqu_YRvYU+F)QP<+;~= zk$*M%R2Cdc!nH%Z!!F0gNlVN1v)!;wt5Fs(qIv6xQvi-JV!jjYt3AMaXjFC&lz-jX z;FoG8&!KXb{24iF5wb=#Kn;4D{)Cjwe11|~nnx`p5#{WZ4rir}6CCDBkhp0Fc`n`a zGIRN(_A(6ecPV@pVSueXPE{6dkLVM7+J{-BR)>2E(i|Y0O5H3zyT8v;fEz2SpG1mJ zyKG>)G}_?QljCss&TvaIsfu6GwQ%kIm;6LyP70^u!g#gxr$#2eGyZ_b>AX$j;xZy< z&P&fuPl00#`Dh(OfU&3_NBz*~8AiaRalLb1fJ|xhtg^KJl8YK*_lE8nT#WK}vg);K z7|GDh2FAxRR-*G0l3`YLZFB7A_4F-;_!yjs`%f}N4jxmFgBC+^6!v|BsmI&ub7qOb zN=+~7i+{QvtxbQT&7@U0+ah~;YKCz9ui`F`<=QH@^oIU*cjr`-ZU8k>m%FZy_Zh<2 zT8seFAE4eJPz1j4oFiLu0j;wM@HRHQQEjL%xAGzUJb7w|h?X9oB-t*n_+FnwA^bk< zG2h#uf_(&RUSCV%u3o?gQJtMsCNJrjV+n=R#t3Hzlpzj`&tT;BJ4a8V@P03ASIrd$ zS>Pzx-Kr34Tkz2xpPl?|jtyaYb1V1!v7)>1pPz)V&VsrfJ501 zLlOm|DeCpr7yHK3&S7N*FbCgO-_5k=Bz13GwF9dE$yP5&S}+92B_%fo@)j^@+g7cS z#Q!MK-mKOMkq{}YSgEX8GqG$b<;5%4u^~-PGb|{y~hy(*NrVbgTI$@+sLBL6$bG3q9k>J!Rc|w*5ahYg^|O z4FS){lc2O!y@(?*SXIgDv(_=O)Yo&(`w5@r#`H-^vcT`q!48hzTEMvZoHlcjn&z-JnDBI?cvuYOA3;a?5xd|Bn0B;cD&-M^kf*Pq*=}J>`U#*@BDoAxZ zHVrC7024MkE4tQ?QlM~Bn~KnaDg06fob+uUMiwHl;PlP0Q-CiZ=eBAa)i`qfZJSlr zxE)v*r*Fgh=>o8w8t#3+BhKr|lX@FWGVC(cK0{lGfd^~~QGI18A}K8vY@HB8soq*k zoZ>Xbtl?6-M)HuTTLe_f2C^TPZwVT!`q5%+uA(ddE=lKqb2?hkgVa3iP z$*g5V{714&0k&2HC!J9W*wt&EdoR5Y3ZwP6iPvla)cyWDaO|TK(EzbZ z=3fY7y(>oS;+0-l{Q%FwX~#V36!P-k5t+!#Cc7iZ)Y7=WQ7hk{iEQ0g4|RoySt0LA zz@@?DqnLY}=f_fI=1g|C%ET4@-EU*+e5+;LP4OdUjRBgXtmQP9r|7SnIc{o$9U+q0 z??(KvRgxl{4U(&?XFssz%#b_TafpPlAQ|(ut()9ntgjls0G~tBdm5YFJaww_q8az@VU5u5JBw32TnSW4PccWH=QQrjw z7d@EXH6UN1=Hwn&IgZ$@>3X%fC?9M(L9K2o-E~gJnFb85)j@#uuDV>@R%50H%SrKg zA|kdeS}&B;8+e2rQoQF%wc5tv-41z=8X#g4u~e7eMzt+{T53d3rr@7P7^P_lmvz@N z9+sV@x@;_x4Ocdb%0ko#BOoNTB3?Z74OKKuoC5uv6hyR2@T?WOK4Db34f;jfFRpnJ z)`@{1j#n0!OJ)u)D3u#|SI#rgWK%z~VuGOng%PA}*Dev`QV-pbHt0^Q}oq#{F>hzgbQHI zP{vcN{9m%ZIoH+xc`TrL)K50h4Ln=Ro@@uTwWX3F%uCEdQ(+VmRFJ5S}Nxwr=88 z&@n_uwHGgwDjK|>k-YlQ5@(&a%k)qWQSdeV`W-PoOL6o*QSK3WBz9ztO5;qA=}+)} z&%Me8@@bRKJQRqgmuW*kH})I+$%bFr$DA4PbX?%pH7)E8Q119jjTmQMDq#BSd!)}} zW@6XoydoH~c2l@VZnzmcuC*HXYVitQ?M@B|Y7-5YfAhazJlYHxQJ4$}qT&*e>O5t7 z0W;)iAnrD~B(iKOSzVUZMf7aKGbgfxSQ+CJ*vEgI7rs7q$~@ipBrY4FO=WHnBai?! zRS(4&Ag$KhgtxPp-2+1>4nnNR1#ClrEmnL7#$oclEslgY_3V14l(w$G@@Y{%B<}i6 z#O?p|iHn>k0?cZ6$s9NHJ`L+y!vX4aRmtD4AvPSuNTk2{Qk1l`{DbC+GAjtSmqthx z9_q+I`EU`Ksppmk7@@Af`sku(u)z+Y+|My?EBj{KG3wZ#OrB;RPf>Nsec#HMpjO5j zqG?8#TZE>n0^SMC^x^hagp8tJ8a_~F8SzVvRvV5hQ2QWagm(yaiO;szbUMx&pQOHG zznIvSz2Oucn+5g9wpGk({$eM_5(4Hil}%Gh#5;A9*llckKs5EcGCrJqaMXP0w(q*; zA7lz@s}k7r%uPD1rcI7gAIHX_^LL!>TYop|pt7vvF1NcM94g<4QXkEkbEvB%%zYm6 zTzWrMQ1RMr2VRPlxeE?XR=`3@9wr`Fr|K_dMnWlbL+r+xh*lc)7c%oLvsDppRVO)Z zq<;G8I_@iR@;3SK6KH3wtU`YTUi}~{GnpkgOscl6UCz<|W&+ALA2)5}Q`iVQo|q~y zr|o5B0+6jjs;qQ8o65<`9vxnVXTtA6!vP&pf<29_Vtd-U{t0$u<44)Mr6C+V3R zc@tpPCm0MnQ#)< zCyc&(dJEPM5*?~gWc*gPX z7=-n|P81j<$&G79v}?Ecmh{G7eTOi}-|g8ct1ZqYd?2I#jtJy?;BdXFy0I%=I)YC@L{rB+c;qUWjN^_xP3@cQ=Hy4|PfrjBn`StZd*m#s!>?{~&- zCdUj_RZY8X(8(ymKxYx6A6e5RzfCpxTk=zHC0`{E`9(Tj1=;{VVprZNr>xa7sCsd@ z+1FMG?F*x z%uvDzS5hCjXM3fHM$oNDn8DYzwC*a6XJvDanALcJ*l%8LMd%OVZr zoQId?hnO{;^c)wtOs5fk+yLtO=s}&y%DLcLY+kQx+QTx&Y&U+q$K;~Z8|yJpGbY~_ z1GP}nMI^JkJU5u5et2_bY7ImL_fB>xy?NoXOvU^0K>l~mJc3X{b3Ina;7VOn)%RAJ zu+_)|xf+}q#%hBFh}i?L7h#OSkAq%Z+pYTQ56+5q?4;Et#j{!rF8Fc{Cn&%J_+uoB zawo_QmW>K9=%B6!myGx3b9#2)m#7BDBvxA!B`3MRF=QiUHWa!uOc9VQ_d2RI1SVFr zB%mBaNvTDm8Hd!T*EL1mir|N1$g?%E@lo#DY#Fk(*?G0U^2Tme*VH=8o=XW)@>Q}c z*AWJc{~~kl%RV;J@I_WF90Z)-zreW$4A@LI{tiwDQlM{hKQN?Wd#@&#op^<4O=(oDX*@mKO zd+BGA!^K?`W^ldp^OlOm7;@mAF7jHjZ0kUf1Pi}a`O$vzL05IZO+~Ymv$ccPp0EEO z0C_=%z7IT_>Z7FCpz#}Lk-l*jG#RGe>cEbYdksjlu65_v8yn?ciwuvo+V&M`wr>4g z)*312gD8%}mVdapm+I&TkYQ9uJ3y+r5dKi7;^2PJ*j=jFh$NX0!U&&H1GvNmDy+B%%QVYF-OFy9HEnmad~ z#u^IQl?i4<5&d)?cJ7eC*wD@;><31HlW+znK71M0M zt5a??sk%U-pB%}a$0Qp4C! zNKsUyEqQ2bVU)+}goZS?(S=57(Z~#vcH7RoQAo0piCweRE|TT$0A*%yi@L78;jy_* zW`B>yw_2SO)16lwQn>+}(Mo~10c2EgG_5rmf_$vS!9}65yVP?yA3^uEU%cbM{c>|( z2UN5AfYI7nI*C~Oj>H3yO&@rCv&26Jby>T?;#Rdk8E&G(Mb&Mi#cgGuf%{Z`o0Itn zU%WIL`565GQrKlJ2#wt_dPwki=Op~=_ZVA4mk8wcFj-{cj^tQ z%@!?1Mb-2e`b&Z%Q{gk5yy6|L7Sw&6oqww{q@_c6A5Op4{pX98*kkp8p+dR=q#rz5 zUDr8*WXnd&TY2mQNC9}fT3SH*+;d3Zd9@3TU8^XD%9x)VghwaQF9D2o#)Qt|V`Jn0 z=kC3OB+2gk&QF^1RqeZ{$M@{atigKhE|vfY8UQ3j5=T)Gk`AKCpbU?pV_Zl& zDHM#;A9uRo1x20Ch_WOl(TI{DLL^8K0D(2&E_QcjvDn$!@us_{U3HgFbAPV#YYvmfJAGIvz`hP!1vy}xSTcpsi08iEj~{hZR7cOIcAt@qcXVXZ zNr&qK(-Z)C=W2XS(%8vca+ewpAn)wvx2uf|K>9PVlU&vVTkMdt+YvtplJo77FJ}GL zB{=g06mn9j_9W+pKfC%?`V8!h%A?eRNTx*3f_fv&TX|e4!)Km^M?;=_tpwluglNOR z|IO_C!e=$vEbPZm*^~=vppqu@I+N-+0Sm+U$G4{E3-=E_mg_V0Z(pcV+p*Aq;tUk$ zpn3$#i%^^u@EEi=UU1o3zr~rwBdN9~Ow>fmWg6u&`(5>ru_V>eZ-0CaUYFdOv$zqS z&{Qlcs^&h`5#XJG_sy*ffGqtM7=dY6rBqK{Vt9IM35)^QF)HP`nXbsXPJ+jR0hNOI z8J7CJKM?;{TZFc+GJqyHZhC?(Ku!4WG|QpYy~pj_axY5)9$f}DXqv``w}E?*VBL9f zVFO11#&z#QGu+x_CiM4|+lwxi^tl{x(J_nN&w`hIz%I1Glv zwO75aI0NNnD9w$bQN=EQ9iYc$Ym-NgAI*Hd<-+tFwzjmyH}Bq|Z>bGpVf7uOOty57 zzdTPAK*p8qnOsPGV{$Rw6US_65+J*OM8IR|w?I4a*h2zxO;MvbE+nNnyx;jN@c00B z=ivG&@pI~VR)-3Y9T7^PYtSfgoQQe1*i%^OtVG8)#b}_lX|R- z*6EDN<7!)@ZF>P|!Kdp^KK1R^r2AS+pJAPw>Uc1GlYPbOaP0Ki61o#pkO`WH)qLa0~+E zAP2B$Dkwjx5@>R8SqA}fsvSp_=uvc0N10vB=1X2E!%sgCKmBdf;0QoS{9mmk0P?*O z2N`9uNiv~O-Vp#PYf9yLk{k1M{No{jBMA5P#Unq;iBG`!KMn8u@5OQcPs7>|!m;my zg-4-wTpb**ygr_qo1G5LPUleWA+7s5xU)=>$t2JC%kzYpwCg&cI(qg_MC^aijV(K- zo91{|CqFQH$LRdS^UdwT6R(S-6c}o2rbEqi!j;E@AzaHta5OqLGqnhHpg#P4qbQCr zaP;CH^@7+HuwX#JKpTcb?Ci)D3Xg@y*8X@^-W(Y53LQ+yLfXaKV?s!w2sveg<|7rg z?C2zL1n4PFI-P;ouSm9%^u$X~Af$xknb{24ux$%oxei8e5*pK!RQNwH9ZMJg*c+(- zPTCCY)RUlF;O%vdSFXXko7rG->bXUfS<3?;_fmv1DFfsoxU7Q!ITi1i6Eu$AZ)qVD zIR4Kc4tGTVG`6XJ15n?H{q}4pGE-2m4&0{Vjmx&yR#HgM0a(5a|5`u6)~vF3|d z^CfC8ZJ~El&e{ioH>#{WxuHF_vW1RK^?VgEWfR&L-3aI>j#=>R@_)}@$3 z!ilyUz4}s(f0T`P44v2-!hIc#OUc7!ZMlWe*MAtc5VMFL^h6BgTFNjjS(kMbKqk7d z4>8u!@4}wQv|h-6;R@bjFgDtl*Y_~i+vwZk-{Q;|{+@g83;eZB`1!|RrI_F4`;|vAZCPAi zPw&1Cb6NXM#eJ;`j-JK2ucH!WCf?PsWKZn2#?QSZ05ZhhNvJyF*5UTYVE-S(Y*oOb zt_gKf*Cuz_Q!zW_a!Wm-K>A(T54%;aYz<5K6%65~)~CPJXj?;=HE@Go;2Gb0uFgYi zGa4Q}PLt&g8}Q(FLrSvZ<8bX^Bj!C=+%{cfsMSpkirCps0A z*7Hl$Uc8qcAcNMol6N`>v8?c3WWj*b3u*val9&-9l4s^kb(i(crpDD>@e|;|d6x?n zcz+)8RWS)zssNcrbsTk^92025U@;WN+8XoK{3vok!u#T7VAoQpQ_T$9#Caur{*Rka zMt>-0m-Q4tu3Y0({XuG1+O(c29@_Wx>Zg>|#;By2@GnUI$a^6U(hrb%sE$GPF)Dc` zw?vt!q4m2O7+sCQ%hzz9J5BMm4T={xr2#Q<8d()FK^uMe(F;01|Co~tKnB5a)USu` z>zPU=_h;p~uiw6#yg9e3%i3>I$um)jGGXdtuNUD4i*Q$ucqSz7aSDvz2j6}cE=pw3 zJFdv5O$tGY5d4FO|!>duB3+-YFZjo?pUI-IMwt0>D&V*ItmS2apBH z>nKQW>k{I=K3bAXmRWCUeEGV@FI~|2@^x(ljsTAgJvXmpjjxi(*!B`=q~7l!+vRu( zG-e7#)p84?BYBNsO#ozW@F-dO=hh{4_w`;5`wJ(Q@Hs5yak&mp9!39SPjUBq8q~gU zgW8L`7`uZ*`MNZr#?&n z#47Xu>TQ<(%IBE*KVPN%<(m{PZD$5YfbNO$&29AIXD;V6Mg(2fetR#tuT_#|f)-Y~ zCn-^8LXU}ydkiGWgjtgMs9hfS$s}v{#ey%%nQ2QruDMJ!B#;vPfbQ8U4{yw!skvYa-Zj@+aZE5G%+g7ABeLEkY%>R5ME=(vtE>9U4t<*`u~ zTCZh;WIKF!)(hB53H;xJizuW-j$~PkHq0 z5B4m?Z1l7^AH#Xpzm`A6- zHXNR2#vb1&^Fo@MU0A42C(i@{(vU!)^Sa4P37_#2TTC+y9s^%eo)tF|6#Lqn&^R&T z4R;vZW;ug=;L)BvfL+)R7AP9It%IX+;NRw|L!Z4|XL3pg?MI_025Epi=;li=L8Y-) z6i3{Cc1fAmQ=cEZucHcs2i?+%^K&(y0Yy`o%$APDyRWmvJB9(|d~I@LqdZ~pFaWY> z%0*111<2-7KKB_J|Cq*Q9RrG?@AdU{^gp;syHH_fR>;}L?tt1$yVPD1jj{doEUgdD zX9CHjEegG)n1z%T!(7EV_FGjC(jV9R+I0iRH7FNc7V0iXW?UBPP9~TPi+#)^Q6>nB zLA$dlC}B|37}PWht^7hyg5c=c&s^5{`N!PU?&~;k9QAQU_jOXdk@BA~| z{r@(m!}3_TmhycDp#Yhr zJT^*jdLGU$N`YeNJN-|h>hfUFVKC@WC`?b1)mQhZzPdL8$@a5zbRVo?(4V^+|u zX&0t+QcNtJru#LOILIu?GGWPJx~Ai}4&8yq?H!HVI{?{WGE99;n<$e6jDEXzdu{aR z{i8Z_*KG8h#-GmGo1y>ixv&5CN79WCaV~4WU0&bh(G$n+d-3qBs-tIrZ3+JJ4TIS$ zA4FA4D3|J!@F=M|LVVn#r#g=2&;<3--+%g6d@6-)0Wk}TttY`@8NHmFyc?<)`fjyo0laxSH=%?ybcKsA9)@|Otr}M?D>50hivd#>U zis3Xl1LoT2^3;Q7y>}qhZGZq318m{Zom#jz=3y*_-4;Se6t;xh{y#jV%lCWJcw0Llg zP#=@r)zScYAeo`8TCQhchX(=0#Xpu(NPNZ|1|?qYJ%`cz0?qS6SMt#o^EW}l2eYPgs%#de)TafQ6>v4PE6#T z8tu+xXUofd9i=+@EsgtH792;SJ8eO!KU-&--sbN`3TAh?-`MOZF6W zaZELWIj{m^mR^M2+femQ8U=kgXvsC&Sa`GsJt#oOViF!pb3%QbxFKgEV$?kou@iU* zK+3qQ;{dY$rFD$1Ek9OAxtgQg)p4bHqT(M_lt*(BipRj1!?ruHdjY$06nd9)S02@g zCH=1k6KGW89cA?aKa9TD0Wu3XdKR?@_TC|Z<6}ekFIqc1{P-{M=KKFwI`hX4?U{Nm z>xs!)LVc7`CH?Mg0B#*u33<_M3YzMQh0PnsH~x~543q$*5qUVzz; z6!=={=%D~(lF5rf zVt&h=s{Yd4_>=fUBrFddlNeyMHZu0T-n{hINyu z0+3puV~Xv_&+?G6;{;^?Q_-#RH-Mpk{)GP=zC z2{cKnV;YB2Kk#bu07#Wm7%Bi6wX2L$Q;djz%)@2Ko&^>2z?bAQS-*W#3kIeS-zacxnQ+aM|KR`ysJ7y_tpB5fvVDjuEF3;OG zf2#ja4}D?x?v1nI&!zyor;XGADFcq4sk*OKl}B%^x?k>V8Pzck7N-QUN4q@Vzr=60 zo*H+V>VSg>b~$)pcM>LV)+yerP}~)6fkL5-c6tL>QFVmrxJQ4YjI8rfVv-alC^3CV|0sYcq?$5FKZ!bpvUl#Q-6jXNy)V?;LReOm~fBeVzxzfMn z+49wVZz$*FFn=86-QE0vaRMH_GHf|pcc8(Jh5j{|`6x6ihubHw-iprV&JzFVuk9#f zvhM3N;5Y)12={ebm-R%S9J;UHy|=;HC&L@`<`r1KoD7Q-qS3Il;|Vjq_5>+<*uFp$Fp;=G@O-B z)!>hv5=WzupZX}Xql_2-7^XTJb5MC7G(IAJ8ZiNhU0(q#y&wQm(NtX4u?55>ii67n z=ZvRXglwG<17Q&c*OM~p>}~PT%YTz%Z|lCO zjzRS?FS}N`!L`Z_Ivvqher)|m__6gLVZ+X;j^!aW!CDDE{uq4Z5p4vGd2XSp>-ynb zcX=?%Q~cOhVD;YN^|d%~9KD`l{G;-iqo6#3eDUx!F6)WK-60nbFPAiQGz{+q|6}oU{T+F51n56HY!`Z$ny$8+T7SMv=lDLn_}%dDei**;A-Hi8I`S)$SA5f~ zCJ`OBJfpT)3koH{S7Vo9J7N>BM%Y55@e5j_xpHbY>E>eNq&u8$l7e!gnM^3<- zH-+$=hsC+U)4H$I4!}w3V;(N+Fo1mfGVtxwu$ZS^4m=VMAoElp!(ttSmY4c?t>W`h z^~TGXuXWe>3wJ)v-&y*N>?)+qp;*rnd4W4Em;GLol2OGp@{4!Va=zYLc`&tJuXf>M zFT&kL`07*8F6aB5Q7-GL;Y~6AQ576LlW|{XQ69Z<>oDEd4=gTo;WwMPl|-|Ao64Lp zxA|sYEslEWcTS)+UE1ux?PKBqsB~cI9vr(ZepbR4oZdQBlT0fJs*g#}A*enErdTQfjBbwB}H|&{_3i-9}+It2bdzN_L+XrIkDi@${ zx$O5^?DtwYZa%JNP0J4-{ooij=K#K}kV*TrEM_h0`^4fGp@jb(9(B zJQea(ZF)2Yxq{%b#R>O1u(b=z-v()l_c}drLPi+901M}(0W!_bGNJBk#Z2oUForR4 zy)2HbCL8bgUI56f;5e{hyCu5Wo44SVtK#Ridm<~F7p(vQAOJ~3K~%undI6ly;oZ}P zh)J*5+hXnF&)yes9L){woz&nM{&2%*fv30??dA)RVI_K!0CNARWNpQi$|DH(^^acALjm%Q-^(vl;PUz=mg7jpJBC4` z8S#7Dtxj&Lqvx{rEGh1BN=x~=m|%3>K-Uw1Cjusmy$Zj4O{3UU_+B4-$~+4%c_uht zH4}vKnYj4anSX(6MvQ^IAvA_LDK%XjQ{&W-f1F{zR{+SwRyzoeC54)hH~kqu`yJ@p zxYbhBh!kio!?&R80vuHV@?;~to>7#AsgU=Y@`2%%agXKORm!)=lbmTPZb~qON$Z6VpldFdei+I>48}Q0l&E<9P4<0^ z+x@^wRviJBYjOSa&(d=$bh~>KN59{K-`^NR;Pi+Tu0DEWh4*`Aj_^1&cbMhy-|F>X zC1P;Z12=T0KBi$1uys^2mZ@G4$JD4L5i{XeAv-;2D?mk9`g-3QL*w3XLfh|)`<+SQ zMheC|PEpV>{xQa~x~xY4*-3^)TOr)d@r?}y6V9(@1IXSX#D?b?aP9}lIOQ=;RorPM z=W5Ll&+T*$2OwXTh}c+r^Ji1LuagpGLcyxDCk>9t{aUWSc=3?ZDx{UJP4BcsQShBs zd}K}3de*Y7Os?!K%3~BXj)t$8{+<^=%EmiR!O>AY)_pBg7+l6g{Nwl;XH^{w2FzAn zjxRVowPy3ZPYyV@+C(>vRFD|@Y&!YH6$HU?q$f$LkDlr{y8pE0yEM`7*H*wd1?3-v z+TVcE55|L}w?mkv(6zj%H_J*^9U*+{M8Q$VagFlyZg+s=Sg;#0_|JY_tO|_~Nz=8( z%?!+OgOaqw>APzMKIM_Vp-tkq>%wkpIxR_zNP(78nS@6W+v-$JXvd70p1V4Wc`D>) z54K9MYeCW;*_DDAtsA}vyVrqB>(e8=KUHeRfu_RX(36P$NGRWCYKZ>%dPy)UVc6Y%QvVRH z>{_v+lu(lf9+RNa-(KU)1IW*PIDHb0Or~|{7!^~#CuuuERr8nck&Sn(6kWm9J(1lw0=rdAT|*iy+Xx5n_;lZg4RM{QB4KLfgn8|B&DPYA&?i_>bL zV~K*5mN6FcBw)(K%h3)<{OnS3^qb#8D=Jr@=oT7yc0eyW5^~Q^Ti~z`7l4V?x z+^x^&Gg#UYk17j5S}CCGegkByZ?N2O*lD@k+0AEP?=aPIqQB+{kQWs4#L@z!O1xtb zAdAIXHgJTHpl&%8@@w6-c!-pE*J+?}1dkHu_gvYgVNxqr&^0+w(FiYqoCO?{l*c>( zGA%fcURlKUg?XutqeVy`e*86^zdC?I5vrz3yC){FYDs|MY*oNvr6|Br#X9{mVAC5| zlnU81@jaJy9sn7oJf1BipDU<33hWSZkB%chA?Pa`=qo~~xwH+>MKEg=NG>(tref%I zvkSY=BDIfqXsI6kxHvq(i+)*xQ^L%zJQ_n0&ht(T;nAp6!eb`Wz+*uhgQ#W}$AgL) zptU2ju+tIcdrFPq8)kHtGxcbA^x_}G05U1NTE*&txQ(&_$1CFS!{nU~-0ecOAnt3U z84Zo289gv#tEi8IJ;~NTjDOsnt}~D}=5e%J<|E?>IS$&vQqg3$Eb{JR~yAV7G`JB+j6s2Y^yTN0tLodawUHtPw@Je8{_44Fd)qz z^LQdb4hc)3Dd=#bF;*NsRacu-%W^XhCZAn^PacCa1vqFYgCm3ku1x%+EI5uv;_3Lu zG~jrz4X@u7$JVg_-RZ#MEZC2OTg?7A?F5NmH3NH)F_Sf~1R7n_C>RFSQi(>TLZecl zS}IX6hO0Q;z;R%^rRQ46*|U`xuT+0i9QR9k3<9J~3}hOYwO@J642}?9CpHB{PQ^p^ zrd3ASct$xjNbU2o2{j%(<_(T8?U0fINR=4KG|4hk&nZy8Cp9=~6);ai`B`ZE5t#dn zu<%o`{4?U`++X55KBKX+VYu%lcj$)(^(87{|cQIWRJKZQOx4lI+xy zy!*PJLtaZ77q(wnG;|$3VUpb+)?9O3*lFv`SDmTcvBLmI&zi5KcVAz~pg4OaeBOTl z-;G|Frbv{TnB-5-v$<^1UcZyEA>{Kyg7J{&`gdv2&E%Rsj6Pu57W=jp>B^2TPn;ec zAvxwT3Ls?^NYxTONdUQjRHt#bm+Z1O=0TqWV+jf;!8juRoY3i>p1sW4@dJ842%G;p zzjonQ9szm-E0g;=%w>I8%41a0Oq}WnU>I@=hgRuO{J_f-|91O8{XX`-D8!X}imq#q z$+VULNas7jjZi)7Md)=xR7b$AMc=P>FJZBun_ve9ci*JjtHD8A&Lb4tq6%h_qShLz zdamK?{UzU^vrrey%6dt_qo+Q4OHEHUf3PtPj~LMkw&M?6h z^WBcmb7iv#zu5a#*@ zWweR_Li5NF5En&2xRE|6{D)S*BBs$barCa{500LtbzjT4tYu>#lfV(iC3{*=Zk6~`?MhUh$)2btY8z)e8 z2jVD4e8wnF!=rN@Secmja#0>B0$u1?I<{5_U$J}DGySgS-Ij|Z23Oi!yzqBdFl~fRE=Dhms?85GKO5Gj4_I)dS=#|@WmE6tB zVFBe&sHvDGOv4OyTjv3eX_QA5fXpM_QGA&JM~mo7MOq(lK>wLTf&ULLTpUj=pM;Hn z4t6u!IrqEagM#P2UTUaSDv5Jhs{&+_%i6C#`m?T+R7XER+IDn14Z5rScC9H7k6XhX zX;i`tygKG#bDHPI4yur&jO&2v7zU4CyrW-zbWaS+uFQTt@SqCOikqpS*P&FATjj+r zMa*JpBn-M}xVld+EetQ9XpSSH{qSfz0w661`h#iJQ3f9GyaMeFxblVQy`8-n$iuSw z>iExR6OsU`88X|f3?oOe&dJ3Jmv;?zyKr#>zIYYh+)xKcfaM@$6w}8)+KP1tlWIWb ztvVJ91IooBv(+k%a+zweNWn022S?AET`hC`tHJ2~b6I;9+^R51bsWQE{Nxl>ng`&U_vBT__Pf4w zD#ocs#p-)1r14osv5>PRlY%*|Iz|EH#QE0ulSy{!0S(|}87zmQWM*3|JQj;(bWPO$ zI;cH>Q(BrHbQ?a>2v#TWSpv5;!V_iUB&7Y@&>Ys*>L0*`Ux9vx;NN=o%J?JoG0JKZ zbQP976SXa&tTm2t=iFc5?e@|{;!En=3>$+|0;H2-{P8ld!w1291~V_RqGv&?pA#+l zn{UJ(vtImT7JxjO!ds>SkT2)>IKluj%w;VDj1!Z^PIyf-Wq`~Ik5+F28vS;xEFQMD z2KuLgwLF0@|AmcZTUd^Trjsi)j@m-Tsxj3reC$Q|*b9@1G)mw&Z^HTN6fAPKB0$pr zeQ)$A-|SIZ&v$(AT-Fm{y(W&-<4t!);xiqE@k?#sj!T}A1xJ{Sdz4ijAwFp)PIUzN zq?xTRW^2z$eb!SU2TfS1KEOc#!N||X!ed1mm5;~oShaNi)a9G%Hy5ahELoT%3`ktZ~V$wfeg#Kj@o4=4{-GCbj+ldw#K8i zul_qTC*ah#!()FEPJagj*W}`cM$gU`!?h513q_GYbC}>bdSwwA*lCqVKP-mrPSebB zm7VxpG6LjEN)^j1;ge(+a%O1{AnO}%Qh4?HC{e~fF{1>Gelzby7sOQp$Sg6Bon5&7 z^(klssGAmb)7Q{u$W?_L98%n`gMp?Q7{*-g=PsK0oM&}wG86marN<&@bEqnQ=N$ae z75IzC#Lw@X3x!3$Ro)zTf`u3MFxvS%@TX@l^YrZH@Ui!kLWNA#6#L1LrJ_E{x~yeP z)@2QoTNTQxju2lgJWh24xult(>IiaS7(t;g5g=PodJ$$5wrh+_ni<7ChJHcdg1Rk{ znKSgXIr|-SawyfazgV5j4s~rX=OzKtzgu`z@yZ7yw$yVX8uI>a*bm8~>>o@529p2i_RvCIZ;n%$c8X2=QZj~3@ z16wuTF%A~To!Q`9VQB$!B_ntwmGHUHKb-q5_O^KFjsJ?B#OZVXEWkds4cuWx#Q)<+QLR2GKMw>nQb6MR^1` z$u9LfC27?W#>K*?{yjl;906p4eiKw3(=LW&7Jd3PsOvXa)qg&E{+N%Ss3X-!-54jt zb%&pEoslr;_AJU}Irn1h$XO}ZCKK<1uxIL%Pf*tgtwrMQs==eDI*y)%fA(v^8@~#J zgYi}f{$mONkC3}X0i;Tq9zQ^KZY6y71gw2GocZnvV4Sd)AcEbV$!;(G68}V90t(N` zxv$d*suL^5K>TBp>X@W1CRtwUW0cD}3?Q3bHMi-}9CrO_z7p-eemQ4&T&coSEAZ*Z z;g3F4W&3+yr~8K<#6FRYg5#7&KR_lzWLW_o196S96JR9kl+7hojd={lZh^9Rr4#40HyVQF7E4KyyFyLuc^DQ{k0tv$8L$*kN2}M zc$j@idxz!UKBhhyp2S&fjm;}L{abWjbyOq@L>tz|?QZoM0Ze@Se9za64 z5}ySY4-+8eAwXkb%LC*}4ld8Q_{S{DqX&wgdJum68Tfl25kEim5Iiu4$mY{D(C%iw zhp3pxD2R-{Xo*#j0yKKIR!wgF+FCjlG7oSJLgVP)BZ`D)1<1U?5x}iL-R$w_cm7GP z=M93RXIb6XN#Hp89!2-Hit-4_?&~Zr>!A9m;<7f|P`?K6`?WTUW%U}cg~H_IC967u z*l8xp!c<3DfDD_j65;VI%=z+*k7z$P1(9x&P}K^IkCi1j_H8it@bqUJr@*X?F0fjY z^Ipbw=~``OQh5ySOc1s|n&I1u5a4J`h5oSpD1)lwXr%QmjXi+8@T>6Hr^HSxVe_w) z>KIY3C(LC%>fIq_d(wcTSGs5BBs6abV4PcvRu?0ecO7wf4b;|o^JHTKt`4Dku44`3y_bVgfCpo4-z5TW!>y{nJJgKU)Jnu%vasS0^%z^Ej>x_Lso?B(ZyR%xkYd;xbpbnQMQnzYyNOJookH zM;B6^H_l}}@jXNLwM_AFRdDnyuJ&WrENg%1o;c;vY(wP^RIiDj0L;PHKV0W)gI9o8 zsN6kD{pLw3ca!UQ29-xy)e+!Q#2y_{s-u5Vq_UWk>AUtxC=9{Wf9@mN&#~_Qm=ru} z?cwCBmtplO0grLW=gFjfo0X;G+2L`nF+a&!bUAv}>$$IcN7On%T%t_q_pHek4_9$nPXJ0p?Z-S_)`RmJ z{#ddgESez#r@aUZC)@C@0QAo49XhLm36en4i+gk&TS;{U*zLh?7v=&(ew^y)Es9#b zXckk2NBg7?DGIO``1}YUT~`3)UKm6du?M!O@p$rkzz@}xxo9CzH_&=EwVK?#7u%&* zSIZ7TkStbb}A*P`Cu*i2Bwb5^7BL(3}s*o!1C<~2*46#W7 z3GymrT6lD0#-wgZ9G>L?kWs4R#L)l39vk1YfUOW$DXTh8e2+x&@U+S!gl}D#SC+M{ z9m9QSgX%Sl#^q>OoNC+8!jZW?GXK<`d%LnvdHw&6KnsAZDs2gvZ{`8E^EK}i-s>scH*5b zytM^yZ9}s!im4AW`eK_mV^&o12{b2CvUgYlO`781-Vv_oiJ!+7)H`hI$9kXRALBBx z4+9+i0LjK!(peTnOz{P%1=`_c$1%@YNR-}_sk+YlyMFzzu)byU^LyfHqi!iKXdjuAX2 zbl=(KI?KyvnVDIoTy9K&W54P$XoWn3cAz4w6)6@T8;$wlfHHhS z{_TIjrkwJa+ODRX%;n8oQUHmo7-p;f;{IJYlG<+fEfj4__ zX$M}qBYrl01JwL*Wsn6RTbr@}Et^2IoVLnONCJ&ajmIQloM`j?@k(!PB|AXMmgrFd z$TTkNu)!rP13NF5b$1|2uU@+cZ{8BejeVF~g0t7sxua!lT6y#Vr2R#ubES!S30_^R*h4VsSXmXna#q z3mQB-{_|R0e%$qg@JH}{yqJBr#LwJA$l{X4<8`UE{eBS4tbP2U082T!r~No`JirK z$&u7l?}^_+OE&&-Axn;kFM-BwM#Ew{(3|XV9zb5*IJ{!XL4drU2{d7QV5dIg7XNY{U= zZ#+Qf?opiG8T8qG^!Z(kqZ=5@_s}ZwK&fv#@`i-ppzC)_wrHsIVcttjl^df>fd7o7tN^ zSZ@n(l!C6T653>@by|R2$YUHTf^!^+s1wG~8V7IkWOOK@a0tNbA>O zu$@0Nh7Gl80TQNTAy;PegT|5amUD=^it-5Z+1FW=M+i@z$wPGBNiXKwta=5V@vfT>MFQK@;2#kc+iduuyfpi zGh##O%}%_^7~1jz&r4N(Oi&$z02!4)vs}q%AaYh<@M=ELIBF)H=uUfV0j_N-@^;Ey zW$q_Hdhw5oHHTvh`GI1X9q}uTZ7}~!z5to1Jc1-1W?v%5N=1EC1;^1GlaPI# z6&y#4`#|zcns`Usap+mXP2KAC=nMvQ2LsB50@YHH2Uk~fdH*+ec6=ZSs|jJTLDdnU zZmPkf-YGHj^25WnSH!lb->2ItV_iRib^SynOkR4Y%<69sm?$@x)CPPR*Sf^i5>{@` zdRPq}0USddddQ_(CEsLG9*z}OZ@PMfR~%fk;O-W^uEoM!4sPq^rQ_O1z6aOnx3P;= za00Vb5ESoh!%P_z22Kr^#I?TVf}w>kja@ieYEOaV=o0(;EWG_o^kz=eTK!;tu55b% zgBEm~(A|TrrBSw${0OU!?<*}pw$#YUOQ4x6O}5|@8CUT)hJdF#aO4cI1?1#mY{!i2 zwbFYd13Qm+$FSb9)7EMCT(0%p*ey%5*wRw)f#(O1a^NTrkZF|1FlZb@y00JXYw(LD z0go9}$XcGNW5CYVVD1dOeAxg#BNYGuAOJ~3K~!b?K+C_yY5ASgQz0E^myITzUdj%Q zqj5}9c#{Q4RhRVy*u}W7(_~qPK1*Ni$0Tr^fW&@J#5G!03BEAT~ zb!}d{(&g;3ny4>pi%U_&o)17uxvUicQYGdw4jRW3ZOGW+sgQrJmkJ>Big)ze>~KgO z&W}8I)?({Gyl z=6vlf9ZR^l9DMY*xpB_mnN`t;``4lK7W6K~j5EVmu1h+!g7ehi@rXsJ%of09Cl2Z?|y-zHsEBb z#mQ1DQav1sFq3;(Biz+TOKNqgO3iC5y!}foy!}g*nh9~4_5ixOuyYOWz5%<}#NV9| z`KJO@6epTu4mD>Kl}8xIKl(GUWnvz0bS97Z2MyX2;%63XON{AxWnilSWG)l6u8YjY za*3H*m6=+VYPm$wG|@G&m7nWs3~ZOnn}-9(%gb>OUjQT-O24Toj{$%Tl<<)S$3n4^ z9vnT}*91UbQz!O3;~i)3j=>Q?FZm~Ym*>yhEY#iHzE8^;9tW<&z;$VL4SwbK;pV-Z zlt;y86_EZ`{1arS%PNZtt+uYSZrb zGlQe9XM6v5_NHku4j{u+#}Ru-s*lyTP6rk%=_Ug0=~u;=xMZW(L9avkeK7wiSpAzY z_bDhpBK|!JAY}!Sk=PQUI>rIy`o^Cb{kdP6B48%!#_#*)s+Mod$3jV{JbuuwYZMA5 ziwm<-`+X)NdZRv1qx}u`IR>SQNSblANr1F{gO~0?VFd<(>h7(3P+N)}a8H+8T_B0VK?Z|(cA3l$i&g|gb-nN}BNY{i9Jntb?VG2uRC)JKJw#~?s@@sDxf zcq5B9b_kGJ6vzX~@0U%WNgMANr$EZMtPNeq&~;4H82wr5Jq`M!)#|NXSX;~&9_NzE z^tDA2js3Cv1&%&|w7&?(_l^5}g3DS482vWkvW^1C6mIN1l*ic!=Zzj%jFk&+t}0|+ z@HlXtz_WHZ=)kXk1x_r%$DfA!dn8#Vi_3aqxC_KTs>VC+4>WGIbgu3jd}G(-W=rR8 zTW7s99xINx4uAKF1HSqP4%yS2h6~8qMgYFF)b8t`@(6HyM`LBil~x@`EMwYi^$JR= zqaPq0N1-1sZHfa}iHN5ypBD#!b0Ad7_Uq8UCeWy@Fjrc($kGmrb)1050<`+^%45*_ zHNW!cW0i}CURB1f@2;2Y9i&FQH2#@u~yx0Y1GzwVY57rLCW335h6(qT<+kJ04UR#GTzvDNv2pf?p|zFmWabbcZ%cM+nJo?D08%p3I&$^iGdUQToSU<8 z-0|MZS%C4_^?U)ctuU^?S2D2Ef@0|F_a&)$Icc?0KG$C?iX#yy*2SS(!bja@9b83E zmhdUwY~=!tW93n@ktn*cD;eN%HX$^f^;=Zk+r?){2|?)&0coX#0frPsLQ1-k2I=kw z$w3;WySuxGmhK+9I|bhJyw~;pVfX_!`PH85B`>s;mrlQnW@Jv)UDgDxF z5jha%%=68EweqO(2MavILGZjK-=+OkHd7yR&X1Jr5>i$4i{;f`frs%!#^Q#JGe>%~ z^_U;g5-6g1%9M}JcJ-q-spbyFe^x;PUg>)vrMD}b*ff(y`lgjYY1$7aKy-{(Yc<=N zFjEshE~SW5ODT3S{yE=`if85QG-~WL zx^-;LI|oC8^XwZY4K3hCKgAeI717bpx?eI5$hM+)_rxqGuH*~&`__I4svM{0W%{Q2 zDzHu!^2^hDFXF&p_KyOk*p#&Yt?a@0l`6lxL9C2hB;i#sF4cn$iDN(wF~rdM!x(Zu zhw=E-G=d^`yI@;quKmy70aa}bHgsuHvFv!i=%R4jO^lN4zZaG^a2W)^kV#`332uFG zBb^H||6P{7CqVQLD21}jw=ttYkDWA*z3yAO!h`!0Rb3&32x@P}y&EC_w6gHQ9xVA+*mWgObCBqS%u&O% zl4bKxU-SUc)%Ce2_w&XhFBD#l! zl4SeeSrnZ!GuI&M9;%Al&}&KcJST%&12|D^sTH?YV&WCPX|uf-PB$ybe$yC6gEL~| zngy=J*TdmEOtwQ(Ots!IT`nqu&T+LgE0q_+fnVpfzT)JhukB5p@`R)AO)%)N>kJcW z?kR7IRlx99*gSLm7r{4x+nlc^+VY3$7j$KpEEHI}{}6e;D*yR0 z@2|~Pg-SkpbS|rXh6q~su3?M2PVkXP`h)A~cn3&_z0vd3vrdd=)VMSTijN*evo92- zFDdSXlEfa|{s-c6)j&BEd9^TEF0M~~=tAhse58Xfxsg5b7I!w`!giK{yG-(tlg+%I z@5SF5-a#Y^+&cY+XyK|i?NZWjpsFxoS@?-y4RYl4W$Jf>fbqChp58H4M@Fuu zoGhD-3*N_L1(+~HNVFQQN{8ZSs}z)9o5J+j;?6P~{oPOJtv;e(EsOjNvS{?h&uo{d z#I|1d9UruGDo+0k>ih_Ycg_W{WDAJbEDDV39N7$o9`Uc0T^~ku9SFM`^c#dtx|JeT zw_|Av3PSI>%StQY_O_r!zZk{OQXs@r-iuEq!vO{rw&J=o@-180l1Nc^=MzRWzmre> zxe8!ZqG?RE5ETKaElOo}ND{#@099E1xBjR*$wlf59jfRP+G<6KHs7Gk22Jox>2sED zVMHUdYP=pmik7T8)Jp|LYjlny_l1<$L`>&}JYzqi%T--X0zEBPG z9UNfp@p@C`3#qGeP~W&V!+d#+az9%Ug0T(`m#qKrvaY>s6SKuQ&R#P7WZla85>b`*9mLNr7cUwpoEI0G?% zV*=Xh(gFiJ7fU5GmJ#u*g1MM6Em7hgDLEk*VM$kQjE;2Oz$;`DZ1h*$9w};k2BK`F zr@L96-clyb)W-+*eSgetO-YyEvJdGI$b$fmJ-HrFhKxvEnvV6m#zrQR8Ri2W7tY#BV(L z_2dH>G$Lf&G-@*M(p|+VX^i9H^uDr6ltK@E?2)aeo8|9i-LPjIBifIX_<4bc#|*dU zSHqYa?WtGH*QM#c(}G$ActUT7lWpa7AL(oWll;#8F$q~BH9q|Cj1z)H!oagO7xS;- zH^iar9p#mmnAj8=*_^EliLbd7HWTSITr!~thv{$WAYrwfA^v;e$OnP*6}qL_X>4O6 zG$Ni8u}5_l{S^I%PRH+0Uf*bTf+YiyekG2~=&@V(jG>t-s|bE&G0A+>YMxT$e5oB$i`|V!pgS7s@eLDp8y`$ zomVfok(NVM)^+T;I-$-?-CzS>N_l*HS7cUoQQ)3W^q1+pRFyp7kmaL zdfiqiX}x|YPmy>H@2po&UkG1SLbOZc95W#+=Hjd3EH+HJ_pU6Px9HYzA;!?<(m{vipVRxdKb#B-aU5{J&RQs;vNLLKG|v;jGc~{b%eDZ3Q=)*G zJrCqLn))HY(_sfSn(Hjez}Zr5)T^~74)Fj~*H0nj7mXl9PxX(KNLB7&>mMq$J2W+_tnw z&Cf-MpE(W+)G`=hpU?m^eSfSkyic6(w>Zn{ve6?WEu0$kFKQ7s;tzUZhNmMWv}Og+ zLHYsNZo?NefpOay)x{p12*scKFBHBMLeanzFU(ZAq__jZWGYd6iH4VLHM}N`kAr0j z_a;Qhl)Uo~JZwx*wUZvY1XHwx-YhHTTDutR(N91AF^suMo9K95C>6DIqk?83({on^ z$s%%dLo(VeqLuP0VJg4^?673JhFfv8m;6_onZ1wijY5>b(oRW#(W3JxsTc1APgb5$ ze$!8LVi4u4hUz@)^R9VoX!=sjsSd8nMEPiI3j~r|1)T9WoLVQ(x>z)VvR5^+$~S7*;d>Rv5KCskKQ3j3Bs<4{Fv(@`M3tuD2Nl^5o@-mRjb8P4;v@e|M&{ zjp~37CEzra>(<3KXk~TMeQcWD#tJ5>b#=W2Rb9H-xw06I@X#SBC6T1MbKBM{Y)5h_ zTp1p7D}=DU_|yyhGQW*Y=}Ti$iJW#|{ z_}ek(r8AXjGXzGo{tP}A^2tsjqXBrgO$0Se=kM(gs4vR3z`Q;u4%O*wBzgxOU;9P_sQnb<@uSYS2F?%x0@y z#`L18Wl`QKCOIc4wr|96+#t7GpM#zn_AdW^2ZU4qF0EcMFu9iClqoUZUPR{hRBU(t zzeAyDCj@9DbDfzg5IyDZRyC zl*@+pi5dED<%(}(z*5^@IGx6nwjhxK7Sq7Mjs_By_h)FYj8@5sLQzHf#!1Iuz3fHw z>WXyzSfqpAU)4$Cg_~j+`N$))SpYMDu_OF9;e5en3m+ZmpY3 zo(j_tjWg9R#S~EtPHzP_^Dl+5LGHo!4~(nwd!>{n&#U1if~_xHl^Yt|F%k~!JJiFj zdw}=ri+iH^%d|WDGubf1X&Cl5rOui0y>C{|LiL#lZPL#o5zg`k2 z4e=YJxO^|WYTa&AWgn-*icUjf&K5vZ>5XLF_?v11h3pfK6=!jelAhu-o-5KIVvN80 zyr;$2!lasHitOFIN%<5sj0E$}Cr|eKCW0~)9{V^|c=VmpD`ds6U2=!6 zUG~4^kVPds_Tg&#kYOY%&jlDESF<{|(FSS35Eg+J+H!*=KsX}#V9X7oT<0F6-n z=KeV7r}}q_d94tS*|lSaGY1=;f6ZYKwHF;vDHuK<(0yg46GE*lnYk;g8}gY48ignN z#Pj^%^kx8K$+JeO%a$Vm@pnJW1~l=>KlajY3IJ`q{y)PRz#|>OWvh*I@5K^kjUDfF zwny#15pjE=Gz?u!SePq5k?Y<(uRY7^owijBvGtjd1r7y?1=a4unH@m~l%@0CiDm;W zzK&T7oopZUHzMTW5}r0hO+_vETwp?ihOIgN%J8>>W(*pf$yZsC(x7GbcDEV(b`(GB zZ4MrE(6c`sN*8N>1V+eqTse>7mmXejEB zjbu@2s+$1dgA3R?a9mW)I_dD}si-K1CC?yHDh13V{p%U{0b-72i$$Y}tRS=10H!XW z!q)EY{(jY<&##P?Q*y2x&7S$dY3+f}ai`WLEQjv6S#*;kOfB6^?;=sgEr5!BdDOc8z2b>lCUkR|G*?Z=X zbiMTP$_mnWG{#k>_~0taH1krFi0mCfsOQ12Y{m)XO%Ga)&!{x$AsNsF8A9n^%vqPA zB~LZehlHjgrrNa7xpL>xVYKKXP8jgH0K?owvrSd5(2KLg_wf?*ks`BAH~VGYn$t{t z9n(MQg4BYbKF!AViaJ$zE#1+j+^-6cj zt`~n<+LI9(DSA^s9x<{t6uecJV%6b+${(k9uE{peb-jhQ0P7FX9h=y zF4;RlS0H{1wV7v8okq$2t_|VsKJgb=GU@yTuG%{Mp$N#>h15qvL+mIzjPJjnHvU8N zgsY+_pQg*y37#^LM5RME4_8x{79H!^uWu{fA#h@`q(p_x*FY~n3!T|wnZ?!7wj#czMqzq0*G8Ab`OLW ziSTW&QUsxf zk6eF6t4n>Db8@s+9ra2b>+wI{f9;X8hE%H6>xl^X2$gE6dy&MpfEKQ&ct9-82{X{i z0^RSO>De%zwmQSluMDWa04^2x(5@uH3V{lMb)00JUQFPDQfc5TFAIWPZn;`YR@d-Z zM+5S*8)pE*(;^D06fz1T!@%{?m*P=a^qot=1bcv_N<1E6UihtVX8&R{?#Wd}-j38iYkC&`EAfqVN;peVj%B`HS9q%Rus$kh?k^RChp8h-$&kQk4%%aei9HfHYB6MdMRdG###!c zx;dI$`NAK?E^)c14W3}~GYmH!Yl+vxRSxxGV{%3-N6XP;k6B`=nx0h`8FO{fGJO<9 zWR!nxXo%vIK(rpp1+oJ3-3+SdBxA1DRnob43M53;BlrZ8H{bRt$V8*0FTec86gByp zaQ2aolGCh-o55B)BI@H~aM1jyn_!!`_YT%K+AAgClKuW+Z-!;ZV#Kk{i31khC!==1&g>3=OOCeUc3YC53)7U4WL?Uh_6@UatqAR5VMtFFff^OQD-q33 zfw|5MTtED3ne%7WOB&T3{9bhi>m{iDiS&U+hVuheK*!v^WP3!d!BK~>Ehmphg^Jv1mE#I}7x)xgaT%q>NU>WE?PhM7kb+(c>1w$#Yzdg&*L zi%UhVD|nL}V&Z{lKCXq+;BW8pSq^_#{el{04(1w&0dduNk%D7f*uYV%w^#o_O1w{% z(MVLtXRuF}A|E*Jk8Xz>(2bD33|JW(V)==raHt zof$Q)${_Uvwg5;G_{h#*`U>Yq6@%C|+bx){T`G9X6&xuN(o^i;Y@)#K(kF4F)6SFl zAW?A%zV}Jt`@8cSpae8=CVtszyBE<0gV`)>a?Ti$);9J~>N#G%`g(`k;)NJjlj?mpQ=00Mg4&uui)BMKbL#8+xb%;Hf1y8Q$*P_6y9f$>vUpsoNiT_}B zq7^0S$y^in581aON^PAm?zD1#0-WG5<%vRbF zrmVjBL?C(}6d9z{@6)gkDje{qi?|tWUIqMpnunj^D}T_X?9*I{DQe6;K)0MgQ_#p} z?x2=lsp^w-9MfPNMhwkQ541IG4wswZ%VnpXMjs<%6%*B!WNTW188qZiwz)X%JGEcd}Z-YNKnK4U~L?2hPZIl=rUKy z6MeiaFjqmeYnD+mD4ug%A}ItjnHxeB|MmJu%Wzs`qPgWvmqJNrVLQq<^ot{OHx`(# z?+?c&l3+iYj)BEk7;dS)p%idHkjn?8N`ie=pWcBROqaaHB z=^4Ih*V6PmKjzP75wki+Plrhb-;SG<;o*xI@WbLLry>HJU!Q%NUa$-~(ET7g?-A5I zG5xyHb`}Mi{#48(|M-d^#LLa(yZ^lA(Ja2m4t=}lZU<*ubtQHX#vErxmPX^=%3zW- zevK>Z?T=nBZ=-BJvU@&eQMz8wcLn-H{^0YQQ9p-R#vW8jTjIfQnzm2!>!Z{oxHPyU z9UAG?kja85xyiu_2V&yIzu7L;N6;fc*GA;I7>I8-o1MCuE>usfTIQ>#a|AsQ{G94X zNsMhMaxYG6x9zlhm6%n%??DDIS1ydjum3<+#{cHsgYMe(?9zjle|ykS@w`pNckX)b zQ=rX6n2@r{3HwGL#xiigTaJrHWO8nHLUG=Dv)IXMf{met^^F zv#!H~g_iY>h72i-nTCXh1St!{pV=2f{Mod#29Hq|_``HBLXMP_70T>Sn?#ZoN}JsO z3G%$O^Xhq#*uM29u^fGMj+LBzsj@;;Y|w?OTILplpldxPf$LZ;Qhqx%rDaC*KDIc4 zQVz-ZtdbLnF35btBQ!!qNoSZw`*PxFoOIh%Y=wm{)LN81>4~6>w_=jlc&VK)zBC`} zCOq<%cHliVzdhepJ+Jw|C#Hg#f%Qq_XbPL`;dWQ6=um9KRFN0oD9q7@JBwC-rhh`r18-2xDHaEbOjB^O{ZZzNI@IOA})*)Fc?B9GD=9{|-Bc(yisV-VfV(5<%o|w7!=}bMlM0|Uo z|LtSv_9FU7h|vN0^X!`{{KuO*%tDna4XU-g4IA=lpt+p3X+%%a>%G@G5Py`fttuQN~8;9GrM zsE|^KkbQ_oGe=`{gsc>_SdQuKa*m z><90=&Bv6MGraUA-$IIOx}oCEp%%(#2#)U(pHOe_)yo>;TK?r3yX9%*%r{vC+R@GK z5Er;8Wk|DoMOse~oan_Wwn*+kq4>3OftqjhtCEU!wif&)z1G2O*K9vqFT!Qg@0(0S z-m~K$5g~9x7m=?AF723CcySe@LW{c{N(L{K$xTD_RdjuiBNc#^N%sKS7nJ~iiWD8n z*%lD9L}8@ZV7ApxJD4%eNvbRPaE$HWngEl#z*eP|ddJ*lvR{sn?N0USOrjEU4ma?G ztnjYqfPTTu8JCYL5!B`U&N-13YvX%DL@)7cv+2mW7=Gzg{{csvmUjxFmwpmV&~$*b z|E)8mdG0k)ZXHZ#Y_gA?v_6k9kI5J?Jhl_&QFOA#d{qW?`{G&Zs%JD7+lFh32SbZF`vnIA#CdNC$#C z{PP)5zDS`c5^VUmm&u*+GrVK|@({Z5PDVW>E7O*8ttg1q)moyt5SXjTr2>W}U=Gr!kfiKV*eTEln#Pe2oKcGb}t*1wJ>3kZx!A zh%3wOT1}iMAWiJQ^HE1Fy3G39?I0Uj9#gt5?Df4B>! z@V$e}jLXvL3ss%CQU|{^$b{lUJJIZav7VFsZ(F|-tifRcwDi$jk-lQQ*1jq4(265i zF;kAz%N3Kezx&Y2VnmCbMTLUx86VhxFJfgGVXSM2a+>)eamdDwI18%z#=VK6Ylz1; zn=sLkWQqQBsuuJzV#U?}v}en)O-GPUkbuUp2PzP&FTH%5JN))Lrg$A@GJWP-TwwV| z_E@4@y7i>Z_;&3KUYc&1c36}o2z5*Y)`9SaK==$ zb>e<XWPE}+<_W7Y?yOj?Nwr0tZ~hq znW#$v%wzCaB3Z)3x9&%3h>()n=wx70nuvz4sm+N}ykVCg=|uJ+%I}t8xs1Yoj)VE& zK1I^*HBJUmDml;-;P%?B%`jwp%JIXw$}j6VaDUg?5Y*o<=@X7*v=?or1Q>A2Rw6InPD+FEth4B=r!pg^8G6 zwvbSzdi5b_oA23~$Jq1s!#MEc6mhP9HeD7rMqR}bP+yD@%C+B@J?J@oVY@iNX$rF~ zsRH0Fw0`8PwZ)~M`_09o&GPI>}!`f(an;!P`Kt|M~HzuAYyd1hWq$^G0 zwRgzJZU<_OJEeNLIVC%Mef3;#mjW9r^W7UiB1AQ0G6i~7Xk!8HJD#)uT$%djsQNcn zor4l}d((y3^V6SkucJr@$B}r{=TtW(LVGLjQ~`HmL%La{4ILH3()o?S8Q%BLb$#`1 zFVxy`mjjJ$V7pVfaZV?vqUNE~?h7>i@f1>l?E6#THJK7k9f> zylj7@BS~+ez9{ojVAZf1)bsmr^R=P1%Yr(t7Y|NEJoz_B>UnML*F&bqG7_Urlh)a_ z`ib4h99)^-_*#s3SWfTtglO}wBF#7Td>_4h8HHb8(}c$md&sS3gcA`Y`fll>-&nhU zr)e8s#v^-LtIGOD;GcW}F&?{ya(!ya=H0R8h;V7L;x+wwu2}YR(kTCrgQm+UD4A!4 z6GOB~`cfiNFvPT@m;-IT$#CxLNz160>64f>i229P#gZph`0UhDCMOnD97K5 zPNi=d?Mr_1!c{A<&s-u7wP zx-B2cjNntHg7`_b?U}4wveO$!JEAfL+{F~JlGJLS*wdJ_*l{N#*My7q1*C%qKCrBC z;`$Dwrg?$5%Jz2yKZyi!Vp5>fWR&cnMN8qEh_4Q1ifih#0GJB$yOAUm@;vct@ik6X zj;KZBBS$sZQgy_>Vf6kko;Y4pc{Xrz-{z_BgzaL?Rrx02#(Ajyby-ylKkV%cW2a4G z%gEX|T;#pO6w#r~y2};|+;?WbjQ+F3ruajV3<=Tg zghc~xYa@i-N!!3iVcTsO#oo~`VHWW3^LgQZER(wme4}64b4NCoA^1B1&w> z_Yper2fOZ=zK7T3U{G>Jl@$Eqp=!rk*_fMxCXxi1^`+=Lo6d`!wfc6`R^1ifGK~S zUzxEY_2bfX*~uRs!o`U&xNpBz5W*hPya&*-Q)fDN^z^uGVt z`oQ;P;rQs7wJ=4b0295GiP6W!>t+SK;;27SHyB0e^E&8tYjF^iKEKn6 zGF#`xXWU$ervaDyqi?Fvv=J8P7Q~v|~!N8RcB;6_;#Hl^Fxf1AKmG`KheFck|Q?dGk&r2tn;cZH_?g z3%8uHyey~#o%f5oONZg_@HB2}VR4zaWK)Z_6?Xp`a5eSq`<+DjLpOS@Ul-ydL~iKv zCd#q~L4BiK{IlyejO_tzr2euWstrhY%+tjYLNOfdO!xD-(IZAu8arwA-tndP%Pmfu zJ9~t&y-b^1RnA`*l>EL|`qLAtx7{cHvQ#TPdHhD-7=N(Cw%Knty0MY(gD(9kHto$J zCfl~L7%=nPZ9{1#__GWtLWo)&G?y38cB$~%NTqe`wMMgL_c$B+*dtq?z(-20wqsvf zo9a`i{kc#fC2zIPX-Yz+Pi4vBH|A1v5B|)zT&eo5-bo@R!jkgTyVYCll#M8de=d_D zi{rQHZE2~1wCI-r$NL3P^h?Q?76{wr|4uyHsB!05*p}w1E1u=_6v6mV91=>dTlau( z^79_T;Dr7UIpMrvAtF;0tsu%9v=`<_AbPsDnCe?Qdjd+dP@u?3qtUl9C9NwDW{nuq zlKSk#i>d^>+ZD2lmO@Z!g{fd)U9xySVE!8roHhSyJe@wSr1l)rg z&+LcqlGWn=Ny69Rlr5_h1dTAt;PqRm09H}IhTY726ybR`eYbUc~es$BY-Sz;RbKiK*JR9@L z^V+#!@>mrsw}$^qdwigt)DaTJ{i`Oc`HEtxDBFqssa-7%T)i!CV#8CMZ9aePxtR^c z>3Ci858KpBva)9&_lJXW(b2#-0@g7-IfF`=BLU)OZ(bkAh?Ya0@aU^e-!ItDw&hTu z!9`__PJN})$|NSJ51q4%_Hitf(8AZ=`Pf%=bx~o8soQ4Jm{o0qHOU(7?*CGfnq~`2 z&#JN2yp*!zC2N~@^xKrfb?=6)%iy3Q8k#GUGK6Az6{gQn{ylFcQx%=Y- zWIggLJGA&sjFjKGcunf!Trp~BuK6f>4I`T?#1e0%g(Ny9D#%5jRcu@OIvB~8(I}nL z((+d0o;V$_k6qQ*d(0u#IPOm5txb_tH>v9_3u)pO!*6VkKdvl<7bt&TSL88d7=VnZ z>~MYrZu`4)4J1tb#}DJ6Z|7h*sFP*NSSTsu(%TFElZK}iWzaqPYkuNP?rclBmP zcg5rfY?O0R`*UiS!#>m?{@TIrZ9wtUL?yNC>W2jxZ-45vmRf%RJu-bM_lsUnV4@xL zPZgb%?tckb{dj{^x7kdKH;>yt5>vl3^qH=SN+8y zC`7>Cx}Y9yqHtx@CFMqTd@sc`yYBZ1i^|Jj068v8No-|7#^8u1sCJRfl?UZjOs*aO zI;TvGY|=RsQO3;QSUg7#p^JeVm;0w5)LY}u8nB2fFMR7QMN-}VDBZQf2WrIXAun|0 zxy*ypiZ7L|@7=F&uN*P`sS1E)S5ESqU4AR<9E$F0h4`;jnhLku_88yP#8?_=Ic;U; z1HNOde8A=VeHVGi12TV$PN89Qaa79iR?C!I=h!PMkhA*5ec=yKp#(?ggzWh(To>~# zFgB7^I~QtW!w~I@bcsL?^X64UtwtYO^&g~QgM#ek^lmi6df!P=7)>CpkGl5to4&u( z^VDV8l=!?gjujceKW}P1jQj>rj=>eFV~e7srX^3zxk4XKJ%$3R%P0Nmd^MA@Qb1I= zF6_1?Hy`WXG8zcwcYo($D`Fl*HDScMhIL>i-IoG84QO)5O18f|oqeN1 zXK?>v8)(o%*Rj0_b!A%QG${N|bt3Hk{dPK%(q(_ld8kylD?=Oh%?V|Qke6$oj7mwq zKmZ6HpGO@w1AsA6ssd7iD=L4J9w1w;wB1s@K!aUgh738GfXWH=Xo0lMu}beo@h=$D zWL$~no1YX97fv`_!pu*qj(1UZE*Ei8db}$HD9S)FEG;%M}@n|r*@2z zX+OR-UO-e1EBGa;@aE-@*4fJJtAXjiHej{QKd zNO5p+?N28v^wHn=?$GO}f8acGR=X|s5i4$%_m4fZ}?InT9;w*%dS07Wl7uS zQY)7$Td;lXJcuIojq(dfR6$f)bxEPRan!M}qeosZ)751A!L?xNd)m+as9wGVC`k!R z1SF2`L8ZaF?gG=}Ske2&9A)>6qqn;D5zY3-=*lC$&i+TK+qX|A!l3w+SgIJ1QB07g z+S-C;fNMDS#$%%&VvYZ+h84wW5&?||sZb|hbs)?@6T!#|R0;lnsiY1z(W7U%SE>r! zqZQ|80G~~cl%C!2dX;|Qtr}MLwa2IKdkeq0k!)$ela-GdMMQX#Zt}8!f5m2UgA|t- zk;)AxD+K3WTx#=vQryB8#Y`=Wk2IwDlzX;h_SXX>`O)Ln4`GYX7DY*OaxcXPG4Yc$ zcc7~Lz^}>FqSe#D74o#9SR>L%eC0y@@gde@Tn`#n3FV&iEdKi19?HF32vyzooH2n5 ze6ZCJoHcy#cM^?noSC9bZZCd{b zGR;P{3IbgB6UI#Zn=pF`S<`6jUx$Gr>M&RJj(uv6am4vd*y0bzmRM15J1F^p1VW(e zupPFz2_i_EfoZZBWnsMYBOa5>2(5xJS_i(PYEJQE4||ivy*KG`O!HoGit699 zc}fJRuiOa-^Qm_~YF*a;7F3Clx!7H8#Q*Q`p0}lVQ3K?<=rn*%jKrN!QW@U)9rBM; z){UV2*2PRM&nXk>H=2@dnrcb8J`i-BXiIZ`5`^EON zO+d35i65O_c6SN|9ngGkF;&Dwhl_YGx>HJj4@&C+@B^vpR&(@WMtKqm#i>x z({~RpHS*47!$Bt>FHtsA1+n8=bF%C4k^9_UBfkKIKhNoB`3}uDN|Fs(f?^)K&1xn8*ZFKb@eY-Ca1^6 zqKW9Os+Xs(lGfGCHvsM(0Rc_CA04;+hM_$>u??NrZZVYmIx|0Vm%y5v&20+av`3K$ z2D~4IumF-N1L|!#^j@_e>CIV~&v1g>(ra?(Qt&Eusmje};e$05^d0*l{`KGY{%iq7 zz8fTLi`fAiN*=gHum!>%UYR3L(8LRU>PpMPXfe=UzOb&1=E&@CPA&xC8DK|0CIL1| zf2j5~RJ!71TCv-l_}(YNvd2;$*e7y5quwSN;!bpDks60lX0IHb+;27*+axPbEtAQx9 zYKojE?P14sHICbCzS?dCn&DC>D(%$4%hufjQKv_B@_Lqf=$|m6T%bJWz5gBuG>U5} z59BRF9=RRZMn+9XmxgP^g)YXrAIIPLW{C^Z{86+V<6(65_(-;%R4@juC3qPvcCwBt zIgPyUPj%S$&g1Q2okd}kA7w2*0=oG91x*y4-&;JIpsBT_sSSU`8rOHAPbRf^j}A7P zag|&>*BhkcY;6JDKL=wY%*Ct4!k>$xIDN-aVXOmh--<*Z4krA` z%fqN#o}wH2>fZooTiZqL&q^u#c*oG614MSJW!|?kq8;x862G{0{vkxxQA*Of;^E7{O+37L1o%g}_0+Jgl^F^dZ^}l+U+U^@v#lX{2a1zO{<{ zqH5ZP*iBpz^o@hO)O^ZuSAz`%J-|fh`Y`m@_Q+ zJc9FbkYxGSg2u?36Av5He#6|`mAM^JT+sFYj+lbkG(pilrSSWUgSdBF9@jO$CNVg? zooG+eNyM}DO8ZJwhhM;&Xl<>uM%kCT>rBc&wPNl-8I%SInbbBe8?FAw0%Qt+ub2yD zMRP44J@Bq7`6#ml}Vxj3~c|Ld7}*^2J3|s{zTLZvL+;{q=6gvS)uoE!G)VU z^hC+$JeJH+0uDJnAZK|=D@e{1^q1QEQupa`BpA*pw=8LJU{w>^oz#RK`L4#DV;%gZ z74=bc+QjB^SL{9XU2QdT&MyT>_43^Q%6PWlzYU5<(`NElXfUO~I1{+Y)9l5jb?M4( zwH*tLlmHF^B`dp8fHoDQ<#&n&1V-EdXCYTO1nx19dZiUMC}DR4<|^)W(#ZdhPpi!C zQtkMa6vKOod5I%e;cn|ckxRtwQ{g|=Z(9EL^w`J4kbd}$-7Bsz~)LR!&nN}Tha zvoASV{^`XMK4}$>ge)#Xm%ifs^2qC22UruwetL|Y@FT>5!9G5T%ZES6cd@hjPe$|Ep* zf`dA@Hb5h7yHoIFbc@u~AD$cJ)iq#x{U^V9`!q^-*>{n+qyb)tQ7=KSCTeAGF0*!< z!Vl^z!<-JpSJy;US`O|~{)y?ftVDCv$iXu@{&2HQxqGaifUK&;AG~4>X#P`0xs&be zo14~!gZlb$qU_jSLU7Rb;-2qj2UWdi>v@n*v_r~bC<(GvD*u72$n7KyRIls=0&WCx z{$st&nTdFUL$6}E$do1oA)S%w0zJdjB}S^WUkxrHl~CC~!aa@6Pr}b9Gt?KXmd{o{ z{sh!uaEqK~=VDMl?EHyf^^`(Bgd%(i|2IP;~_p78fC!#ya7A?_;u9KI3cJ0xmum*EbwMbJD& zO7ONJ6~VHyeia4wFHNzvpi(lKEgUPItiqv!v(| z;{xH;mCXG!GZm+rVp^ z!s_IMD;Z9Cu5*9Hc8rN%_H?1A&q?zZ*|V)ls1KyN$7N_-DnZN+hCl;W_xB7FIbccO zy;rW-_G-B6oLTMHu|{wB!S(y#e^^*$|>pNzjUkgxsR&cL`&#)K2? z_yt$acUXz47wyZRkO+$2>RcoOJmd-C$UO^Nrnz-UVzHxYENOm2S^GCTUX^zT&&(cbD+g9t|9tz=tyO&sYx3dta)Yytqtzxu_I`eoo^Sf! ze<1-#vgl?U2FLsQhZiLu9_f>NW*t`^G5ip3`dSz;--hICvc(G?GBXiK1o)d(s>0hY_CVPZv6^(JJqRQ6*#@!Ec8g$d3N?u7BCqn7t|+P!@efW5j)Jrr=iu2 z5G4@Zws=$k@Uo6rbOYLfrj2}IX^dqEqUhOd^_H)UhDUiQB>kI#xyTh~@jirPD(YNF zT0FA@BXo3|M{7AL{rl`;2$+U=|IW+#m8dmZhoR(G+z4wGMgi+L;h4$%lMCo%==!v? z%tgnaXl)3Uq(SWnWs20}{a=+a_@YEZw)>d|{ol%woOth2?kqh=|J4q^O71Z(9ja*5 zGj(V`MuU@_?$$7Uu%DTHbdhnoME)X}{8W}spyWq)%L)Th7-vt=z61c%%I??6JCsLq z)0{C>hU&CN^Lo73;s!M~FCve3&x&QH4X*qXK7>GTHoF^zCWk+fU9+=cFs9Gjl<9R1 zN)kVeXGP+a@O3_9JOG7P6u*}aNS12UcGs;O%TA^~s8Il zU*OCi`|Q2e`bm#Vo)?0&SsUx}N%cO8!Vb=> z=$pIYFE~eSiOlfA11SG!{P(0N#p8tekT&klt6Zb%92nkUc?D~r_eOqy^zgALqt_ep z#?94~;HR@)hY)Wpx5s?|>doOo!*RQ1dF;k#kd|J^T4$~%vDODRamo^O@JAoNdlOVh z?M5m1C;`@E>)?d5`YCtS9I^ICnjDRUuN#%Iqji-(CP{J9_vkC3xK{7j(u%(l z0U!a06j}$c^_#X-q2Ys$!*U*NivF^WRZAI6^8(P&i;%3JbcfM*@!i~8<{5v|$((KB zSlgN6=Qkc|Rg$3jk*gHz_3Fl#8~RGJ3X5SOyAsRLKC6ZZGd%ArZaK5qz5pr1N7^I| zPo5H4ardaB8War@5Y4gq*5a#JaQA2vtc?(r1khGRNiy?e59~P#+^F&bH3r4&F<$Yu zSXjvCj5ZdG)cdvJ{qWAf|7=Ztz0l`y8%2o^rHiyRS*j;G1!{vJNhQ+8Y=G}Ll#_Pc z6jFLF|6TEUJwF8A{WT6WYogl^f2A!=8ajUl3iFeqL?UC?Rg=5!5{r^OzTUgou37yN zTFbs|TzFg;LP(mZoNh<4a^QHoNe^}?6ITqYG3#3WD&b!=18+UUk~a6x5*hd;Ix=o= zr6KjN0y<-EMS`W5f-*A5>^o7CRSfAIngoZZ=!*EGxM~o}lK}kyF$*Z#@*7C+9(Q{_ z|D>Ut0D$g8U8Uq(u^ZpX_6#b~z&3hl%{fRbpgLX%DvY$I2l`lxZ}EyG^?4fyne}?4 zol5@{B)8!&DyyjzB6CQ2ufA$k20+KMOBro-)jp}-zg>JE7`iaIv?{$lZeL)nuXM>J z*I%h!J$@_gQQWnG<2m*5YqkmvqSR!vu2uN7)=_#mDcu@K{Z#gy;=pzH-u?8-ueHq9 zyu9Cm3#2+pqV7EwCgQ)GasE5{W<|}vq0H>Yph84u6xa+tixqA;C=_;j3^kI%HR`NL z66IjZzW{6mCwUAb&JJneMkhoFdC;d&y5EpVCW1VEp#Qu+HrLNnT}$ccaP$C6w3mIl zWBQ;vq^WvMdlY>XB<9N~CWGoTO3K!S{>Fk5;Lhc@;7A1aGEf1!j{GE=t)HrGdK-}x zBd$Ghn}?kx^7W;e1Axlp3-$4!_C&ALPjz*&XoT;PWC+hWMXYv1tTCVyoAmoGNFa@_ zyWjx9SdQ@hk~Y9F^zGTnt(n|7_VCS7fvF?NA62n+pHDfv3;Nw_^;vz>I3Ao53HP9 zm(-G&T4;{Z1$V|u8|qQDgA z&Z`s`ubV`>axH()iysT(ri;nM0GVsG@=smeDs7j$2TzAgC#yCM3s_2=j;Q;xpvc44 z>WK3^H<0}0j6qordk`ihAXy2ue8>81BU}#;@>f2%IWSH<37TZDEY50XoS0y)AD|tc zM5WYt;}1xC#Y_2y2^HrEARXvM-vw4sE0a@^xUk;w2x>_Jm>X-o#v>ZH{sjl_@J@ZYuL=_Mj=_@^LL+O~E=rV=hrZG2Yc z09FF0HaoOysm^Q3;@}e*3);kdY`_V>zv;6lOlEX1}dDviP6W$s|_V0 zMm!{igqwy|PSV0&R(JtWRh<`QlV3`Czy2rM9zRF3>Pwt zF+M4dPDKy12B!t~9dGJXu>eFP_VZ$!H{ZairRN3AlmX+c>Tv5bRv%9eGFjJ6j40xn zE&@zhDZ@BOJ+QAE)un);bPr9L`|nn>?-x0!6I(_QTaMGvjj$r5EJcf3y;pac7J^C~ zHYIG_bvZ?2*tpt}@*=pK^yrv!w!-KTbynjVB;$N(NaP?lW&kh4(vY`!1s_xS9?)7iwWxS z8u$b6qD9EpNJy6SGM;!f^}##h(KUnlB*c>yVlv|ctAik?#_&-0;QFP5x<``jlF3If zA1Ut75Yk2^4uXF8BSi-xDG}QuI_bfR?)O8jTMQ7s5>)>B7ny2Ni9_VtWYXu$6%JW# z{0E*&MzcBqyj3#Y@8s!MH@aB z1k&~eTe)oZP6nDyZgpYA0TW6I`4ysps#pA4hG#xbY0&6(42BE}Q3p+_UQ2}s>pcBkkl(aX9&3nrai1e@sj zEC2At>$5;h2}2)w1p*k)jLy44+ud4c60|vsB#AAZhCOZD+y)LI$08yF=tKP@lF3vy z!`+=_P;nxZh*P7x)O~<`$f(LBM?_N!YJH6`qfS>obWhoacB6Z<>z%cJ%{>H32$vb+ z6QAG6u|hA;x?eSu)|-ZOa*pszL^9wbgVw-KRLQd=qRfJ4+2VgwXqFLV(diEAs%`f- z|40-aHQtZp%u=eSjqBdF&{2PS80J?e1Pgf4=v<0Qdc8O0g?V-NTQ+OvN&5hhJ>0&iKNtK;9iUcbjDw0cESesk04 z_~PQ{5qk_aOGPNJR#~?+!U{g$h6~bcs@jGl8y@d-YOmj;i}&a#D?C3gW$6@?)5ySjuY@rfHuZW{e))vn})>h z*CnWbaY-$b5y)ZzSG!2`WsYMU?$S*7raAiUzKD~A@?w_neaHGb>~DWDr**qmS6{z< z=K$ylYRta6qqExcLgNx4_~;h2b7+iZklU*P|o<9PSt$NF22gAr3zyp+#_4$xiS}`V>3A((43J zd8fGF?h_U7uDT3RchyY=3LhcyH3?n0yibGB+Tdvvm1*0C5*7hej-cNT_f$L zRg2-gMrfOj)A6Z zlRJWbhHM)JJ7@KeLQCRg-y95p5pGDJ0}koD5p)iMrE9>GvWF@TK{45CYE}R6V7QA> zgC{>MFsqC+R~t*tE-F%7C5UCxSqWqx!{G_le9=uDV$K14%(cu2YL6WV6;CwBSbyE> zpLsBMf1~_KonT>{VMu|LC1v*IH~z_XjE!``L}eJrqRUY26VA&+KJh8|V~PpTlI;7% zFi_XQ;4ar0F$qsH7;4A?m5%#J;#Q7G>%=btJwhwtjk~Zg-PmXvWGds z_|1AUqW8Gg^aSy&fAcN0JA87bdIDvUjf#cU#|Xwp-^beGU1;FX`mZur~vrg}Sbioea%Kg?O$nkoY2|yJx_Gt(_*8{I^m8xA| zDqf3QO-*M421(I~K%`~cELolAt%DgceXi7C|Fg%Ez+hmHgLeMTT#jxze^1e~q3 zkDx+eHKphg0L8QV_<%UHzDxRHo@R0rJCS5wuysxl=eS2afwW+SjXYV^8{xADK4Ki% zw4hG@6G5HHy>ex#EeeOR?^wwW_Z59PM^YQeAg9FJ>0b5YuOCyJnl{;Ehw# zoaZ31w`MNY$Kq$5myx*al`q*5-|h!NBo7}Gx1;&5`{LNNyU~&RsACa2tpd<#+X4c| zhTq~Hl#smS;A{Hep=y=PFxGFJoM-%5g^wxqo%AH`1w@+D*ve7b2Gjn~!>^H}&`oz9 z*cYc4xHEgtv{38-5e8tJhy|~YGff3G0-JoLb#p5p6{qSsT3B%0#4gABOD{YAZ220Q z-Y*AcKcbON#N8NoJFh|q2?=190N{z--0$)y$!8*6P_iOiRD zn)YoDqG(6Bfkur~96)0GvrveshX}=*muA`odgKDBG-KWB$6$m1?gZF~@uMl>P3Ux%ZiwnFmRevVEWI_+;c}^Us9y z13!&lmeN9&s3ZGo^13*>V`YZe_xFT3Qc1`N*a7Qz9JJl|A>|>rt-;OsbW)7kVCHhPrpT>p;XDB}=h#Vx-vkC9HT_NFgR`u%X7ude=#( zdT{QfeBmD9s+`Uz(a(>sr=~iA%yq;YpU0KlI)%*dBdY4e*;TA`cg~e9b%3bL@oXwD z@248p*JuLqbe128WD_J7M`2;x!CG0Zz!R-rPtWvQV>^)Q50pV=p)h*xFX`tBlT)ce0!gRW& z?K6TL17;;{uxy8{BkLZ8xpmBcWNs+<1CyuW*Y>!q(ex6HY=_gJ5gHFQv~<2s!EJvR z+y)l(%oxE0keNc3S{uEQ!e(k98Q}2f>;BQTc$N&P-E$Ca0o+%Q6l{BneB{JLP9J^O z<6>~Yaxf=R)SNn<14Oz7v(m;+34QvGxiG@t-=id-O|MwL(ekmJ)ZDbzS1WDpeQt93 zFT5Lbwl5A#k*B^+2W6+jfuf7Ez#KO{ks(L&mVriDg^K3NYj`0Qgk*`dap-nuJP0NX zsSk!w_lf30?rgXY_Yfw!&}<5wd*MH_L=r)PmT+W8rzsHWEe}yMh*b|Xc6v+8jD%qj z=?9GpYc|}jaR~U8!+T^oj*K`+o)_D%V*6J-dMh%jBLZyIwoA?yROG;CzIM33dZT^~ zEGQtr9IV1OwFlP+CG#9-tx`ov>aCu7IaAr z@wZ#?^WlliXW+23OY4AWUo{zq8F20YoMQb9ZRjR6W58bKeQ3UeVF~2zItlm}&~~;_ z7r0A0(i@UxC|ucxT05JqrpLhAG%|$GGa)Ie5ba>_^6~@%T`r(TT1$S6l`A8dnAArt z**v)G|n^=XR87fRS{jl+ARi%O=Q zjotK5EaeF4Tyvg|5IL>LzNg;=99bZUz~|!Ol#+Jl`);UY4@`>#y_ob2!tDL%(-H7n zl6GG%k$OBnb{rg_{90VFBw)(p6NJEk23nn21VnTFg_uNrge&|A@iOvg{WM_aW-iTTi^e^reGY3&@||vx|Xa$Tg>THLEf8oB5mFBONJUlm`=)pLD}u zN^;YD-n%!Y!+bD6h}hhZL4zhPBOl(n-+E0gxrMbV!T}Y8y33KUvF^|8Ho9f3G_b;2 zTNcnA^&h%R%Ai`x1#-W<73@3EOV1{1g@>~AMycq$%v`P^q*)pm*;o0V4@+V3?J?7L2 z@!sKIoYLBHlwyYMDD%P1LZXO;rTB9Jj}SL1FL)b;*AWZC3_mvMsi2Qh@=-;Dd@>S4j zRW;L~O)J9JJ9CmP-(E*her4)!`Hae^6LK$BgiDs4z7W9Ofm@F^2tEQuQv;eu7o4%!&IDe>; z1g}~?2S#zcKRDhq?X~~X`)0Q0fqH1+mkB#0gG%IQ7k71=XU+e}Ij9AXhmr6Meysd9 z+KSvhRwM4h(Fj4R48_#39fu_HA^XMqiSTvznLWM@ht|TDz}~HVQ&wgc#qkdEgwsvV zETB>#gqXW(q$X~7wZY!m57S5PGOpXX_ANtQ*n(goooB-uD?P6U|#X@oOVROr^viDKgB;Z0WQ}EQN7o{)6tv{z80;C=+>F z#I@{~w)$+NpPS>Sz1eX!WWmwgRr4Dks(;mZ^wp{j_hY%wRld#&qJfvGhTfbEgKS8Z8xzc@?XM;0?M}Dv z&poE{c`J-VGgG@DkIpq-1{$WBw(s52x@SnnE5p~(`jJPSJKf#eP=s&o#ymgq7bnG~ zrHGd5$hU2{LJy3ZIv33Kg*$s~FnpKNg463BA2_A=E($ZHa@;pF)oMk9j2uFph@85C zJ@ecT>+_KW>m~iR8f_VNlJvi7!bUEJ#GBfL5c}Z?xXNqF5#aHnr3h)P36a?~=YedU zlJ;gU$H-TvJe6GJ0-g&%RVM4TK!u!tU@;vCaWNpf7sxm)?acMpwV05axM|Ez8-!Lq zsGr0n1MW1tGNy34KW*CI1}yZzGV<^DtE25@y=?nb>7!NJSoo9?E8UGwYeiSpca}fQ*>#w0N;6L*tgaA{srFJR)Nff+vVkkIsiS%#d0hLl+utZQuD$-Cz0-ezPA9ta-NdXP>!-oPeGT7VryfcXff(XNTkSYFTWKvnz)jYn@c&Mz zOY&0u%mS#pxnKzn^Kx~!!2KeR+ulgeQCL8S0iETk$1B+G@9)4bnEIU)p3sO^UfICr zvsL)Gqs1L7NTg`#R2N-;q-;6Nst2>7!1p)YojM;Dod}PCg|zHBRI-En*h)2P)s z-IdETbx0;}$|oVrWD(4tP-Uyoe81T@D5Q*K7U5vnoB=5jzfDJB5+$}*`MgQsc7F-F zf11Yot@)5;t{!_@oE$O;J0JxCInYi*fMw5;1~ZgvgtGNy~#b}q}mqDP=x9jzWlIMe^;|GEy*~LK$ zy)DVuo2Ztzr}Bl+JWS){i0gTG=w2}1VQx;W@G&g22@D8S2-WQwgesTq=snb%9j?`Lye%0K9cCFPzs$bI!Qf}@GpIh>Q~dIthnQ&PFZ(Bf6LOl)PIl0a|iAdOX8=r&>ZYFO-+Rrb7Dx zS zh7>=(^}JOYJnw```Nv){-_bPu;5!@Or?7=ML`N(5sjRCLM2Wwli$IosGnvJKHP(b9 zR0~dO>RkhIwSmg~x8F1T7hPHOcG)sWTS1=6Yvg!8hL1(a*Ol|O3$@`7a*U~tVw4~! zK%*}*8+RU&(N2IrlZSsA@;vkAT!0zq**Vzkuu-vYv3+;7#LybWCG$eRo2*8O^Ux&n zmrJy)UJ%KQL5&VNn-PE2wH>pXuxxdvDWkFrb!oyuHqDNCqU}ev7O*?!{5&}YwpBWZ zOig)bafRCqZ7oX$P=1S5 zL=BSeh`CXa+P_faP0rLFzkyTL30VJfCKPA=)f1a-n8i8C%I((V)1$(m)qDz9NQJnS z^_&GDKXFp+yAe<&wu1N}a~J`jdFk~*y%>$rAbNs*Ur>B^(N0XHOz+=S$AxkWu3B`G z>&>DxhX9I=qJ0;0`u3>&INomM5p)t{(OxvDl%5L5NAB$=KO}QM;+l6Mn{Jqmw8&Py zBUY6xK+NUo4&T{FH*5P;g3}yfC(IiiF*Lk3#4BgK!Sl1ZtF3pmkBi4;Ix3t1h!S`p zpL(|1*$wTri~LO#$_Jy!f5JTJj6g)WZ=SIvzS1Xt{4YSNwdO+=7=)pFD=^1`&89v( zJ`&OuXfraBeP2Wg2a%v^Xm^QcZEZgD5cNP+fk`F^cjlMl)l>eHm}72x2t`0M;1`Kg zsv;weR$z#hW_SEWwGQhY%PehN6Vi}dA=ZE1w*OS7B9d*@a}`m(B?PSe@-`Z-D49sr zLovSxe#?igj32`=D`L-%$3>Yz?(=;IW~5g06i0a{Rlw9oFh=-V=Q;PD(4VAktGk+`)^o8N@7* zH{ovw9~=p#3qEKx83IIKPzEyrHpD@v&E-)RDmHsu{-beaoRnOTMg@7xo{ZL;fyE-} z*8Dyi=I*|q&VC!~v^rqvtplE1520q|ac1G#Bq=Vo)Zr@#zsLbJ6JSYZRn+4J#G0cV z<#0a6&ERaw5SCkEkRLKl9QI9Fw!i8vs?7V-J26Zkp|)gKthB1C9mZCx3q)W703>vU zS4CN>O*OV!Xdb{y*82Lyt~M3RC@3laHpAC{;vpRDp-MQW%Uih+cF+rTzlbWL7Vs#N z@K_(H)G54n86jg7-o{@HcI!H{1#Z(u&viX<9dRh?l81L-v`uM3D~Ch>(f3S+i97sLwj@rD_XI?Xkg>eXsgnqeRxDt55q8C;${$*)sD{sdtUSQstzvw zcZ{!UT!HD@qDc4l_xPv+QgrhLa;}99_HAyBZ&ox1H*+bQrcTFJvL4Q1#w0{+1o^C# zhum(-Bq52g(`V{4=nW9mh&sW<3YC zqk)@wAA^;Rx$eoE<#!f8t?^VC$HiJRXy@a*+)C*!PRZspUH-qqnviP*#i; z(g2LDKf}DF-g4;aUVBO}(v9JBTTM2ow`)!8?(@@J%DR9j(M*=TE*J%%h!6TXvcFVa zJP0Joa>^DHbaB*-)dL=r=~$k=Rej(eXODlF#tOWWn0xZJ{uS>wOM%MZsMxcknO+7v zaW;w_FeQeHd)kCikrGW6+2AM9Bub|lN6H>L8GV#5gZH!7ICQ4-kLKoF163GN=O71- z-a>`Sx^h+1INwPA4*S+Yq2y~@)n0pvxDl-pM#)_EN9OK^%`Se2=^S(%$Wc=JX(BM* z2Omu-Hk{SBp4GRSG1At6ab|s!r=$QDD;fUB3-q3={hpnq(9;-75v@`cM5pR_IeyZu z?!v6@)&dYKqNbV1x#*<{6QwiNbLY?G$AkWDEP&170-_kbsulA2`mc|5Q^yDlyH0*< z{mBUUE~74(Sk5#B@>J+9*8Uu&?3KFe*lMp+1D5F_#+rVYy}YC69<}oyuadwAkKhxoeepHSP@)GJcEkge^M^01Hpe>tQ(kJIErdQ zSv7{Mw3fQpn5>tn4$!ynNIgS^-CwApG*XOyfD&G$d~6mJS?Zk3(5~>x7=pjee1n#@ zr-&{ORH}z%uLEV1oEF+PPz43%Q#dcORh!bNJFr zDIgT(-hu=kst6|I(+k*eotyX)nwF?1yGeHMhr&L$V^Nhq)uG5C0dPA z-yPk;Z$!|c+{P>ROJuTeQ#a(@)K%U<*qqRw5Ls#Z7J7#|&OZ zVs8%o-lx9af`LbycWKEk$j-%^*JK{0XRF|Gfn+Q?#6O;+*~Xnez-Ld|xgaVGc=VQP z!1LNRmd)(pq7Lz5V60s^iVjkzQF~BTVpvbyJqxuGloThG-#;Iqa_OMJejxZGB@q7x zleQlRFoC5TCdNaNfr{Qu1hDVn7}@&toJiD7D>6>Cin^FtvbjSBV*Lx`>GRgh zQ|EH(X);zo137sg3Hh@u6>s=YX{3Eiw1lqHaEOA)Zg)K*td|C*9XK#d487E(q&Mvc z;nUnFJ)2&o>$%a|K{!^Sz-dO2p;D)(NcFWG|1%$ZL*-&!WmeR#@r8j}PDmANrBksw z#Do+Ovi^+7TG*Y#M>nhg6{q0t9$iKR?I57!e*JB;rxLUhf3$G+Hb@;+_pY@O1a6Q+ z>Mj+~z6|rje2;5G(w3r#&UVb^lA#`7e5FgvbBRkahz||_^IVtVh^#@)FvG(_z{b)e;O9|~q)sk)$kR2|gJMY$ zeG#n0*SPz#kzoPe72{OEp(;4}ysf+U*3oJ+28EcKz-C)sILiZFzK;Sh!j zkxExg)JSaC7I%cJt-VV(itnZ=D*e7T&ff=>=Lc5iL%-Rb(K>jaUhJd2Y|Fy*euZzosF6I%3gei7C}v({?tS1ON&m8vI?4()&a$t7SBWAdapA zTS)v{E?a!{g&?ZiAF`vu^K~-rK}QPm4w}9CS3N*UKd90m?7_aZGxk*s+UfSNt;&s< z%Q2+%1{TEOqkX;n47`YzD18NAyzjT77b8XJR zN4nlYKUld`?djnd)d)F1KrKLV)WpD29Aqid7{9_1eetZnuts^IC|Ukd^#nLa z^=L{rK=Ul#)F4>Pw{{wJQAdl@?<2($tCbXve&ovQw;}!2jS=dzY}vWFAbGD;JA06I zL~+?9=;-2qs(qMq1EFEuKo@rd#ztx9%KTpK&<}d1+x-nmD~raQy|Pode;BLhJW@|v zH13CInBixb|LprsD6tZp@%b=oIZ<)9pvT<&zlp07h%#?yJ;!vVU+IEgVT1M&oil0& zH*WK}@CIm$-{ihuk&@)LKMC#B6)uurn*9X^`>3zn9*$fs9#}TJd=;82|ppT1vJ{b)`vd!N9gwuxNfF^1JIVcX*2pK?`ZREoR=@4We38wEoJT{i_ zvd=}TECpa}SFbTLSkl7Msu-`9pk%^&xHmKBEH5MtmR6thHe3q7Eh`U^qO34V^WX57 z&nyDZ!kT{*7ZIqt>V#A!Jb!ni@y0X9t(zbON@G%{xT}-;2d3DtsGttiY7QP@j&U%Z} zFmL;(5VbeL21Llxufbs~Y#wj)EeWUGm!H3kKuU%$=GM%!DS0$Le>={605OHz-YY*= z&F7I22g;V51=3Z%7CjPC#8uA`c#iM(M#p9Sri(k&^BU>Pvj}t|A{D}K{AO)+C)d_l zlEz6FKKNJ_w)PL$grE}$3*2659?OvOSb8A@O@=3za0z>$a41S%8vpPz^xz*>Bee=m z7czjh?!inm`v%_SS=1i|G?7E}D?D7X2C-ec3{oDHZYd<_@61&7aX&n=bn8`z_efi? zq^<@%>^M*;xc#$6ws1T1m!7w{g#X==!s~9xl(LmWa&)tAR_Q@H}ui;iKjV!<~esAzSd$_ad4|4+myQGu?A=H~Co*Ro=QqmV+IMtQr&;P`O{gNHidvW38fTNYEy zd(-Kf!1S(Q@*{3YK98g~K8kKizE4)6U?9YZaiAJy-**m2V-i>csag^blXt))9eEG{ zNl%WiM8#D02TSl}O@MLEDFBa0%-{j_EHZ}JN1duZ4Ti*a>Ha1bRaJ89a_QRdxvea& z38VrNyF*a9dM9}^_;RsZhixGBpY6_iwHp5E(q0ZI*W@jW`z>}=oV$7)*!BB7NaQuSuQ~uRl7o}G8Kes2xyFNuL7TeBrkg}JtT*D^HFZhNP_heXBmVR< zD%I@P46DrevXeT_Wdf+1E=_n{`Y3K5Z3+QX^|daUN2y9G-&U;jTNaU& z{XNlDuq<#M9cv05lMt(#jk*zj!CfNwT(Ve`aWMKbl=c15B;W6f`n;S!|LR()1N&9x z--sKGl8koZ5~Ie&HnzKeZKN3$`Cn-(Z7C$sWuRnJOE)k?r5gdTtviR5dO;_cuJFBBf-6;#&%yCcAo*d5mZQ2}5J8Dqyba~R zXId{HdnvCa;gh`x=~f(+FZ+<+jlioHBHplQUErHRmh=od3`^xfuIyfYPN9g(urnLt z8kN@Cfx+CrO0C-{)7b&I@ia-%=k8q+DRtq<+9MOfz=7D$hd;`qce)`;B zj;o_>oFQUb2F($4FzyeY0RV`&BLJOII53c*_-^ja%Lf>5;M zZqNYvnhZ*+nsV_Gf-r`v4szbf{Je=}-DYL@WS!`H%!lWY72{-(eBW!Fzlo-jfr#ry z*mw2yS@MW7B08v3y|;I)F0Sa{Vrrm^b8?|%gQ+RJ&$Tz;C2>`H$Chz=ev@fvpiBnB zEjbScMQqv>sgcvnDQbrtVxQu6_peueCA<#(MFLH0+V`r*-VAr})BIqHBEE9Fo?;Td zjh%T38_lYldd#`%Ub%1WnQxrm*9)MD5s6f>USZf7{91EOu2Ia)$U=j(+ z@Q_17!0%79Sr|>6U!J?0x$4@xDR$g4(d(I{Jpm_1qN0$T22?d%rrsypXx3Cs%_Ma1 z+O~4rPK20;%3UHBhLzMR(72{}bTNv4=u1o+5{SaUs10GMT* z#tSEFyoJO0wZGeK;9wAl<1(R>K=T`7V&|6$fMQI0os<`kRZ+)0;gAA(ilW z-U8>JwWt%%->C}LmTlj2kv7uZ*gB4CCY_fpJN4p#Abt+pmx-`g62UcVG$MX5}9tZ#_|C3pH$!{YI2eV z6gJg}v;7mpb1&ehqS4~T%iSaj#X0HD^4byQp3(q9828{Qa?6$^W(1PtgRY<=+az@1RS9rhfeZS`k0j{cb@%mUJ<+Zx{Z>FQq^q83N^kym`>);FevEUGm5_V>a4dS3*Cr+bJ{cp zUu?VNW2^t7k=V(O%wp zTQ9CH#G)7Q%VO73q{Df$jkZoLR<@P@W{!XKu^rqA)@?)SuD8eh(OIg~>=?fYGca@9 z{ay1!!^BFx;FUp5JST@maUf_)%_M3DHR=s(UQ}}g7jSv{+4_Bz5|V})6l%f8)nVi$ z4=bhdS^@BI4Fq2GWYUldwEYEr$4P-Y3RAvsVM!Y&J8CU?8cLh@$yF!dMFu`lwE+`t z`x?7qpgti;fI(^-0Y7{;+DuS;+-o(e)ZP8YMj6U zc_QW;5V6L%Y}b!VteJtNf!dHZgHOxsgy~f%0;1P+U-78~Qtqo5usDy)1@OY)GUEyO zo}{K*W>0>JyWX|=D`Ed$A_|a9cOT}-lpb+pt2r<8?J?Ls-Y5bY>s=L1#|pSmsuA(m z7d$^l3! zqz(R*ptRtVx;V!}X@Pk(i}{#{CAGZ0<#}!UJEb#&T4mn8F~lu#o!oI)CBm24t8j!i z>xcZh_S>vR9lptu$Z`aHB_RRYi-sGjy6-XdAEA;xZFKuSOQLOHM*+fGV<^!dOzE?> zdO};qm#8T-XE#nKzdu;U9tP5rM;LCR*|vEYpiX?;&mUF))%=w#s)k5_XQO;7DmRA( z!nwJ8@ty>^sGs%c&-Gne`9%YH9K07*E}be1gD?HFDiC4@)|Yv>w_IRLNqj|k;r6Vy zq+S%?d$)FlKQ?38nm74fBpD-VSGm<;t~GNAoYNFcp-4Ef9Z(6Lz)j-LKzw571BGcP zO!I`SXsZebAVX(b`=Vj??aT{KOXf|5||PKJabc<-3WsRz)A`rEbo`K4<80%)H!5RI;^f zLuhJLf22Qd0@5J*XaWsJx068WGGeq39%Luqq}&-ttnRz1zWy@_7adK#pPAs->2(dn z3SDN|8uGBBUykvfZcT1ooU#k*Z_~H8S5Zi*zX&h4KU(cje1S*`4sy^jR^T){Mk|zd zr{8*$z$v)7gDX9m`({wIxZlZEE?rp}|2)`+3|%>3v{mZq*#B{1bP1Fb(aP!dy(-70 zCp=2iM1p%ka8-kzle8qLBF@?0`i$-wFE@Dc+fpL|&+$GdKM2TuZ@nA-dV*fPV2muz zc#dt_2zV_^mT2G$INh5T<4RNrXv|QmLd!W>I}=vNRMi3(hc;CxW0;d)mTQ~SE8m2f zTvO_%G2v9{w>DcwvGqzFBmApOKWU`J`|yrC)hB<)qCa_XaXNvLS&T3jqHgDNR?DA@ zbqhVB9)5ePphRZ+^VTF3*t*?`am4)CjoeLn-NtRLq8d?||?wX#;cbShU3X``rq zU>oXhTi^0y3$xp8uit>!(-m5tUJkpX7*`DcxR0&wRY&gD3~sM12d6HN93tJ2$i=D*EY7YzmoI>G;M>!EGlO&rs z#)wuFa?ibLZIO$Aq)Ek&tqQ)WZ&)bd(L3EvuJJw`2aSs2tDK5HgX+Mkci#17sWH}> z_3U**`WzY_O{z`1=(#3X1HIU`>`C`=4+CY`> zhAn)cK;vP@-vt>PT< zvC^ve(zocXTdUZhUj_TN@nB*G07A}>FDxHhMm+H7IwAUVcG>7arwd^lIf{SqIi!_Jm^s#m1nfx zOdA1RG0=f~`a|3$;J|#3c^eh_ClshQq(hCoH(r)cN@rgcjy>T>KlUDoMSO>hf@<48 zLZvlwMOo7YdqiFRNi~nVL{nE2f=KcyE0+087yg!W>9p77r0>T81Uj(B^L|I=PxRhX zPkbFyS*D$K1(iP-I{2%E)Q|GWN1vT-lUsE5vhdbUgbhVnYq({Kh1!AN@C8xz>P}UJ z1Z2ZEA|ia8LF{3)VhDj+xG5%anhTQ!Pk6egqBK#&F6%~1F77gQb2f7WNC!vp#zJ=d zVu3oD<7K`nJJQ2|e`87W=a}cMUl>lmKw+t_tvJ{tF{Vb}5lRxL-tg70oX2XPoC5P_ zt_o0elXiP)3K7cZ?*d$K@xlxToy;l_8CvtHutqp!i{wgN5|)P<(Asoy;W?#Br>>Xl{({@;e++N7mNx_f z6qoV{4u*~c8>k4t{-S=KIjs0+{z+@80Zg?_rwl6-{77n!nT^bW#hw^!6IEDYwnj;T zM?5;%SdcL$7HzG<{?Vtbhz$8pQZG0!8h+)yL+ExeK$&>QALNJz%fav7s#!b- zTsvqrhe^atjSM*8=_(058;6w*8v4rY5)Xi}*&8}%^5-&MS#&m4IC)4FCWB)ol*Nu> z9lJ%~z{ap$FXvMHs)!r4Yfh8;JIli+gjo3GYj;y;cA$Wdw1lM0~ zJ15Isp7N}nT7dJyOy%3aPuHTx-dt?mqQOp}APM-MuJu65&n;)yDPvDD996{Htrxnr zEYNST7lq2fLYicqt?Ge_dHns+=IFTgSoH1XFHh4nCcT}^HG)utF}B*OtiVsVwd)Q8 z5P)^qAp<0?)B1C|f)7@o-3#z&YOn3;#e3x?4*8=)fZM`BkiE}tR{GQC)c%*nx!V)e z)_@v0Y5P4hZRYRf_c_{FCYDgqJuu1sD^nbQhKhm%=;#lx3qsU4_;_Djm+k3T{5u(P zwsYt7+ZS~c_#DBeo2@-Umyi|9Fr6M)yRSPIkBvSE;AcX5f#Tv7B)gfA1D)P^I?iI9 zZTpj6$wxdl>t#=Pp{wBwR8|+EzMp2Nem8vcQNAEbH(JyQ%=*2V@5l2B7_--$Lnwea z+k9vDp(|4_IKolrb7Sj^Ng#9u6LsV7`qu{OabG&B#HJ=XFa)YB7Og-425f(Xf(#x) z(poiwNSTzXccy90oPAiBT8$JE1eJH|H8VSLxmjBvlsUtr}>y@iBdpu|77^9gV8SvIlQD-c*6lTDBx zTBP~ce5ODd2t$h}F5EzgnowO!cJqDuQU*5K$CQGq(j64k5}qWeNBx~6P;;K>9|+bd zK@AG63>xNf@5DeWx4xZ*%d^@Qc>RV08$Ueg724-Wx*&;51a6m2bWy5B&Svi*hYw2X4f!NxD!WTU938gO24wq__3On7E|f5C&&vTBk!@*>UlXX`rz`9t+9E1zCWUg-2Ov?2ie_Zj=-v4J&$} zMa4CSS+SVDPlQ2aXGSD2x^C+IR2#iETMD1kWg*gAre`m}5eB7-Wx#Rkec~08DUV89 zqwv+kC@qc7f}~PVFtKlWi}9(g$RBedvRfW)UJFwln}&tr1;_RGptL%mt2pyxy#L9^ zc=E9a51i_F;NTI2`lzZZ&2e+sJ+!t9%Ue*I9r>N59Rn+FKJtN@lCj!Gw*$=;uqM*q z#VZ#7xYde)<(v~}+zf14GvedSk^kM*qe8~jcuWe7{eGLUOJ!9eW#Sq`hj|Xg z2)qzktKGu*+9ei#_rkursYjo8JY2~_WL9vD1Q3ZX?*~T#k8*vd5032wouy#H^&%U) zs>MIkyFwc<;Na04`$$O}j@LW44aH-_ zlZm;`TGQdROMcovjF6nKm*A#u-j{L zKw#^9V|PyKXKQJCwFE#m8`*EoUMrBzZg;3Px?L3YU<`9NKJuGuI%hX@teZf+Ih%_* zuuJ9mUS3!BRQ)XacB4v?ke4=N=<|v2^J6$F%G`eP{+>a!M$fPdnAS>p`KsQ+M z-kmzT8!te6Gne<2EfC&^#t?7>m@3LEb5RhP1RTRom)VXGC*qXHAaE4$7=L}Kz!Bi^ zWhj`Lz;S5p3hCbKrp~Ijxc<5bflv&S%ArY~eC#2feC(kEsX87=cm(*xKmYyWJgIU~ z^saVSwDlG=Iz!;-+EN|PH)4`!0LF4W=eR5imbNdX2goZC|EM(MwU~WNpfR?k-*&TG zVY3@wly@d=l6DM0wsVrS@&HIbB)V;6VW^1vbx;5jV4`9&H*T`}5?p=`TAuxbL%Q>q zhbHSi_|7w})7)|KSI}O(NU4}FH1-$DVm{9(2q32l`9fn>cuWG3XSS1Sr0!@yD2^q1$7 zgX36JG@z!-fr7mgC3=7z5T4ey6kAxo+HkB%>s~n8vocZB(8T4;vZ!I z(x@kTTbWa6CbIPE0$9gFzG{wCk^~@SAW_B+0zghx#UODvd95|L__S!Pwe0V2_FA5{ z(JAoE+7wT(Ow+pM2)p-A^-qGy6&`bZPrnK|QOFk>UCS#H!V8fuPhrhnLE5-8{6dY~f7X65&u$vReJVaBwv2 z=&_=1luurY%ZO`EZJ=KwRm&()_Pd@e#r{4SUfmIz3 zEIa~y_G5y&jgmdQJ=wBRTeBgZ6H?2ge}ADldr4^D(Wo zN<6bR#WQPDY;+1ZI5>)mIbCG;QOD3(#fcs@U94111wKxD0I z;;1lQ$*wjIu|dX|M;980`kT*9v1bq|0gbN7#5;y=2AZ(*bgw@;4o!2Lrn#N?eYPdz z=);spwN3}4WrUOLTTfV5Mld+tg7 z$3`+Z%RPZ63Lv}nRBsstkmEV^X!F>x`?{SYl@tntRw>(b>~`*ucu)Y+4~?#cDM?u& zGHbl!N~^^4>l6IJ#d)6Jm|&w5OdNJeW%u4GcJH0a4ID)HCJ^Eyp}v6G66V3bf$IWyt)MI|E&j* zzBMcfL~bWoKY|iwyd`9Tn5*UBmjI+*56~ z#z*eBi&O7^@NYEtPT_2(ZHEicQaId%i!F>8WM8gR5@F4rIL_@*HM zvK1hFDC@iIo>}HopZM!s4R)a6@zi@CR_^=3Q*@?ON=45=t#@%sN9fF*#PJ;+|o&AxhBv+a0wsJnbvs$=>MFzH$+Pdkx6SjMnPkLt+D0H1SE#Y6wv6}d|Cd%SWTfe8cV)? zeayNQ1d*1VtV8%+)=3g%{K}&YI0n{u^d!g(4JAFjCnzktddnKPtar^`hxDqB3sXG# z;rDXt$@d<3)$zc?BfvlYz8?rwAE|e6w898LZnuXgr?n1r!oYAW9w40zsh=`&kU^F? z{?V&GUP`C# zmmo7VmjxutNV}^YMhe!t?rPT>R&iu7HO?&YnNR;st`N;b=vS zsoREr#|w}x3(O<{xDSamL3Y~==z0;=NjXEeUINoZIiEf{eoqr<%$8_5mtbw zYgM9^uCxG|#%=9&QJX2Jl{643lW8prjs5A&PG9?{J=bLAflHkIGjFhR@5P>&@%x#< z5nx6l=FuD97-UJ~OzWL4mZM`MziIL+k5QS`LAKr*d`{}j>VP=M{?PIz%VeqN2m^OD zK*Q{HMvv-PX&8L)iAS#*)$yu;M+i|L*LFQS{weIzz=g2tb1|-Upj`kZC~j>m1R%$~ z5H&Othc5ul7=W~*Gp_rSvIzxXuB@YPorA55uzp6o?yf?62aE=E+r5nC-blAC@%L`K z_y4cr94^3EG4*QgCeY*#kn3By_K}XQ^Tmzp`DUw<2n^+BD)BNslRrRqLX?-Q1t62C zjlc(KB;L`dIJ)tUUUxOXmRTBrqhfi!Kew>ZIsrYN3lBn#F<)ey)Yfv=)$q2F6eP! zH=k>8Oz2#K(y?JkjIvp+$&G50*Ug@)W*MS&*&8dem$?Sc-|FsA5z^`SF$BjG!pPe^x!Osv-#NsW;$Zcbl8fYBF8wu-t8N z9LTkZN)H}Y6{cojYeU?sy#hxsKw4Hh%8E*si2_JR^1{*pV;;(5s2aHi`T}qgUfh^u zsZ~4>bC>JN);A5xZ#U65L(Z9(fyb#X%o|d`IIKL%EBr1%KJj%4=#fzU+FfxvYI_fh z3AhkhEyCQmc$HBe(*R>0s$+k=H5~LmHc&5F7={h&1(Yuzg1HvXE!!}c+%q#Bp*lvn zsne>Ct**qz$TJ(O3_6V)r4g4|J@T2BM$6LDC&+}vIR=5Fym%4*e;0gj*H4yF9jLXa zoPo!le3VnCt{&C#s)0vWef-eBdJ3>9702l`hUG;^E76&|zYmbB%>g_Dv~;M?Lu~`F z0@TOGl>x~1GFX!HPA)3f@ddbOi|wSYf?mWK8y)a*T)oq^z@ASYtQNT#UJt8Z%gLCpe?sTQPn)c~exiuywEt;PhSr)GjEdcvVetI^3mR@y}G?7!l{#A99{#nJo1jVN`W&v?owD zZo<(ms2Q-L(%x?H*-!r%fAqQEyqehY>Vd}(K77A&-^V}Uy!QY6cO2Uh+m_unjF;kBhLXm;R+#e4wr=6RT@L1P}8E4lQMiQb?$>>L5o=rteI=K+un$wc@o z4UjHG`T;TzXv`BF`_SkKcbcl${Po+nd2_qK=Pr)%=62yg&10@ByZYMBkP6wI)JLK5 zP$#j<7-W?U)h|E)U^qaUX1>svmw6%b@FWzpL5;_((AeKh+v$#{^s0@qmI5MA`1*_0 zgsm@+W>ZH(p@gEG1|0piIBjtFh2+2YAT3_euV<&^2(MW;fnXOA=jhsOFIgtb2{QJK zfXX+0_i`NtrP#%pHbqg|j>f4EzwfG39pNg8d35cceBTdXOp32u(yFNS(O&LoC5)-t zP&I8Rab~$G&zU z8-(`nckf0Vei-z8Vu!{(J4VBXHyb&;X-KlV3y@jV;Uo*}m5zaQ0kUNnspA?$cdQv_ zol+TQ7e(`eBjsKLZl3J&qjzlaqjzj&KCUfq^Jit7Qw7YLK7hsq@r-*7k1jw4#Y1}C zn|l${m*WOZ##(w7$pFXTr+x#O{~P zy$8q1af7tQ1wMD8#`;areXNdgYKzXPEsV$Nn0Ga?jv6@Q0ryvRP>#ieBfvz#hy_PC z-m&AxIXc75cHqtKXcCQX%wf-Vq2Ry;4Slo0XMXxexf<=Ng2xX&e7|zf4}BP=*Xx{R zPtt`w=b4wCzRB1Z z?*^^nQ(y}Y&ZLQTtb=)X1LJ)=bpEGJjQ8ze-rc~i*>vWLII0G%DwsKVBb4Yk$F1(L zySl%jnJ5YF=m{H-e&+``^`XbFI@R&2gGU%vAB$_@e!tG-ZJ26_x}l<}!>+PUwRh#y zK7bsPosmeVLi!fGIDk~McBz8zf+otMWr4>$08&?n<)<6~aw4Rt>zfUsLZ)9OVxEx- zQ&XZP;jB)|g;oY2(!^WwR}+c~EFFhS*TK0HaQZg!dg(e? zJtUyAXSJK33Wx?Hx7|4d?DKcJwoGzz3?xQHN@h_EOtR}!w+G2k8=E*KMPj2z%c7fk~Yl1Cf zfA-GG(l*MORTNLTF>TvG`_Et<)##qov5w~ReA6V$#Mwg)Fq^Pz!S)e2y_8QROJ2}8 zUKx(i@&388F;E~I3$S$^v{Ibt6$g(=VjB%3Lwt=J7kRjY^Ux*cKG$Ut8&{m3{_0~Q=uaPN@? zR=_>(_PRuO0&YaK{&!zjaf-bK-J?2w=mU>lm8v6Lg~}tqKl-5`V*7)~=}ha`mp4PV zN|svUQuSWOtE1G`srmpi?OYuK3L~!T5O9oAAH$SKY4>%h7CgHqjeiWAQFdCQCADhE zM3g~+9M{5W%mv8A3S={MV;TIs62w97W?a+ra$$QF#-x^3kt}M_(CDg+<)k?YlA`*4 zxU!YCI_fiL(0=~~)Mw5Nf#c9~y=YPX{SM{d@1X2DnLoEmGq-Das3DqIczZxlg}bg3 zuls45DwM{V+UV3)3F)3=D+4t$iA%fd=yaX%`E$?I#;CccJVo(_Kg(

TJ)Zf1mCi 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 + Setup +             + Setup + +
+``` From b6ceec6d4722a249e9b4861b5e8974a71273317a Mon Sep 17 00:00:00 2001 From: Bernd Hofmann Date: Tue, 12 Nov 2024 12:31:44 +0100 Subject: [PATCH 443/528] add information + improve on structure --- docs/make.jl | 86 +++++++---- docs/src/bases/brezzidouglasmarini.md | 2 + docs/src/index.md | 20 +-- docs/src/internals/assemble.md | 152 ++++++++++++++++++++ docs/src/internals/overview.md | 85 +++++++++++ docs/src/internals/quadstrat.md | 82 +++++++++++ docs/src/manual/efie.md | 11 ++ docs/src/manual/examples.md | 20 --- docs/src/manual/tdefie.md | 108 ++++++++++++++ docs/src/operators/identity.md | 54 +++++++ docs/src/operators/maxwelldoublelayer.md | 76 ++++++++++ docs/src/operators/maxwelldoublelayer_td.md | 4 + docs/src/operators/maxwellsinglelayer.md | 80 ++++++++++- docs/src/operators/maxwellsinglelayerVIE.md | 4 + docs/src/operators/maxwellsinglelayer_td.md | 14 ++ docs/src/operators/overview.md | 2 + docs/src/operators/planewave.md | 5 +- docs/src/refs.bib | 10 ++ src/identityop.jl | 11 ++ src/maxwell/farfield.jl | 24 ++-- src/maxwell/maxwell.jl | 33 +++-- src/postproc.jl | 6 + 22 files changed, 806 insertions(+), 83 deletions(-) create mode 100644 docs/src/bases/brezzidouglasmarini.md create mode 100644 docs/src/internals/assemble.md create mode 100644 docs/src/internals/overview.md create mode 100644 docs/src/internals/quadstrat.md create mode 100644 docs/src/manual/efie.md delete mode 100644 docs/src/manual/examples.md create mode 100644 docs/src/manual/tdefie.md create mode 100644 docs/src/operators/maxwelldoublelayer_td.md create mode 100644 docs/src/operators/maxwellsinglelayerVIE.md create mode 100644 docs/src/operators/maxwellsinglelayer_td.md diff --git a/docs/make.jl b/docs/make.jl index ebc40742..3ad716f3 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -2,44 +2,71 @@ using Documenter using DocumenterCitations using BEAST -bib = CitationBibliography(joinpath(@__DIR__, "src", "refs.bib"); style=:alpha) +bib = CitationBibliography(joinpath(@__DIR__, "src", "refs.bib"); style = :alpha) makedocs(; - modules=[BEAST], - authors="Kristof Cools and contributors", - sitename="BEAST.jl", - format=Documenter.HTML(; - prettyurls=get(ENV, "CI", "false") == "true", - canonical="https://krcools.github.io/BEAST.jl", - edit_link="master", - assets=String[], - collapselevel=1, - sidebar_sitename=true, + modules = [BEAST], + authors = "Kristof Cools and contributors", + sitename = "BEAST.jl", + format = Documenter.HTML(; + prettyurls = get(ENV, "CI", "false") == "true", + canonical = "https://krcools.github.io/BEAST.jl", + edit_link = "master", + assets = String[], + collapselevel = 1, + sidebar_sitename = true, ), - plugins=[bib], - pages=[ + plugins = [bib], + pages = [ "Introduction" => "index.md", - "Manual" => Any["General Usage" => "manual/usage.md", "Application Examples" => "manual/examples.md"], + "Manual" => Any[ + "General Usage"=>"manual/usage.md", + "Application Examples"=>Any[ + "Time-Hamronic EFIE"=>"manual/efie.md", + "Time-Domain EFIE"=>"manual/tdefie.md", + ], + ], "Operators & Excitations" => Any[ - "Overview" => "operators/overview.md", - "Local Operators" => Any["Identiy" => "operators/identity.md",], - "Integral Operators" => Any[ - "Helmholtz" => "operators/helmholtz.md", - "Maxwell Single Layer" => "operators/maxwellsinglelayer.md", - "Maxwell Double Layer" => "operators/maxwelldoublelayer.md", + "Overview"=>"operators/overview.md", + "Local Operators"=>Any["Identiy"=>"operators/identity.md",], + "Boundary Integral Operators"=>Any[ + "Helmholtz"=>"operators/helmholtz.md", + "Maxwell Single Layer"=>Any[ + "Time Harmonic"=>"operators/maxwellsinglelayer.md", + "Time Domain"=>"operators/maxwellsinglelayer_td.md", + ], + "Maxwell Double Layer"=>Any[ + "Time Harmonic"=>"operators/maxwelldoublelayer.md", + "Time Domain"=>"operators/maxwelldoublelayer_td.md", + ], + ], + "Volume Integral Operators"=>Any[ + "Maxwell Single Layer"=>"operators/maxwellsinglelayerVIE.md", + "Maxwell Double Layer"=>Any[], ], - "Excitations" => Any[ - "Plane Wave" => "operators/planewave.md", + "Excitations"=>Any[ + "Plane Wave"=>"operators/planewave.md", + "Dipole"=>Any[], + "Monopole"=>Any[], + "Linear Potential" => Any[], ], ], "Bases" => Any[ - "Spatial" => Any["Raviart Thomas" => "bases/raviartthomas.md", "Buffa Christiansen" => "bases/buffachristiansen.md"], - "Temporal" => Any[], + "Spatial"=>Any[ + "Raviart Thomas"=>"bases/raviartthomas.md", + "Buffa Christiansen"=>"bases/buffachristiansen.md", + "Brezzi-Douglas-Marini"=>"bases/brezzidouglasmarini.md", + ], + "Temporal"=>Any[], ], - "Geometry Representations" => Any["Flat" => Any[], "Curvilinear" => Any[]], + "Geometry Representations" => Any["Flat"=>Any[], "Curvilinear"=>Any[]], "____________________________________" => Any[], "Internals" => Any[ - "Multithreading" => Any[], + "Overview"=>"internals/overview.md", + "Assembly"=>"internals/assemble.md", + "Quadrature"=>"internals/quadstrat.md", + "Parametric Domain"=>Any[], + "Multithreading"=>Any[], ], "Contributing" => "contributing.md", "References" => "references.md", @@ -47,4 +74,9 @@ makedocs(; ], ) -deploydocs(; repo="github.com/krcools/BEAST.jl.git", target="build", push_preview=true, forcepush=true) +deploydocs(; + repo = "github.com/krcools/BEAST.jl.git", + target = "build", + push_preview = true, + forcepush = true, +) diff --git a/docs/src/bases/brezzidouglasmarini.md b/docs/src/bases/brezzidouglasmarini.md new file mode 100644 index 00000000..6a9af90f --- /dev/null +++ b/docs/src/bases/brezzidouglasmarini.md @@ -0,0 +1,2 @@ + +# Title \ No newline at end of file diff --git a/docs/src/index.md b/docs/src/index.md index 86b8736c..0dd395eb 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -32,16 +32,20 @@ pkg> add BEAST The following [operators](@ref operator), basis functions, and geometry representations are implemented. To see details and all variations, have a look at the corresponding sections of this documentation. -#### Operators +### Operators -- **Integral operators** +- **Boundary Integral operators** + Maxwell (3D) - - Single Layer - - Double Layer + - Single Layer (time-harmonic & time-domain) + - Double Layer (time-harmonic & time-domain) + Helmholtz (2D & 3D) - Single Layer - Double Layer +- **Volume Integral operators** + + Maxwell + - Single Layer (time-harmonic) + - Double Layer (time-harmonic) - **Local Operators** + Identity (+ variations thereof) @@ -50,13 +54,13 @@ To see details and all variations, have a look at the corresponding sections of
``` -#### Basis functions +### Basis functions - **Spatial** + Low Order - - Raviart-Thomas (Rao-Wilton-Glisson) + - Raviart-Thomas / Rao-Wilton-Glisson - Buffa-Christiansen - - Brezzi-Douglas-Merini + - Brezzi-Douglas-Marini + High Order - Graglia-Wilton-Peterson (GWP) - B-Spline based @@ -70,7 +74,7 @@ To see details and all variations, have a look at the corresponding sections of
``` -#### Geometry Representation +### Geometry Representations - **Low Order** ("flat") + Triangular diff --git a/docs/src/internals/assemble.md b/docs/src/internals/assemble.md new file mode 100644 index 00000000..00824548 --- /dev/null +++ b/docs/src/internals/assemble.md @@ -0,0 +1,152 @@ +# The Matrix Assemble Routine + +A lot of the design of this package derives from the need to express boundary element and finite element matrix assembly in a concise but general manner that is compatible with a wide range of linear and bilinear forms as encountered in the solution of variational problems. + +In this section the matrix assembly routine at the center of this package will be discussed. As a case study we will go over the steps required to extend support for new kernels and new finite element spaces. + +The matrix assemble routine is surprisingly short: + +```julia +function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store) + + test_elements, tad = assemblydata(tfs) + bsis_elements, bad = assemblydata(bfs) + + tshapes = refspace(tfs); num_tshapes = numfunctions(tshapes) + bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes) + + T = promote_type(scalartype(biop), scalartype(tfs), scalartype(bfs)) + zlocal = zeros(T, num_tshapes, num_bshapes) + + qd = quaddata(biop, tshapes, bshapes, test_elements, bsis_elements) + for (p,tcell) in enumerate(test_elements), (q,bcell) in enumerate(bsis_elements) + + fill!(zlocal, 0) + strat = quadrule(biop, tshapes, bshapes, p, tcell, q, bcell, qd) + momintegrals!(biop, tshapes, bshapes, tcell, bcell, zlocal, strat) + + for j in 1 : num_bshapes, i in 1 : num_tshapes + z = zlocal[i,j] + for (m,a) in tad[p,i], (n,b) in bad[q,j] + store(a*z*b, m, n) +end end end end +``` + +Support for direct product spaces, linear combinations of kernels, non-standard storage of matrix elements, and parallel execution is provided by layers on top of this assembly routine. In this section we will focus on discussing the design and implementation of this inner building block that lies at the basis of more general functionality. + +Finite element spaces are usually stored as a collection of functions that in turn each comprise contributions from a limited number of geometric elements that make up the support of the function. In FEM and BEM matrix assembly, however, we need the *tranposed* information: given a geometric cell, and a local shape function, we need the ability to efficiently retrieve the list of basis functions whose definition contains the given local shape function on the given cell and the weight (aka coefficient) by which it contributes. The data structure that contains this information is referred to as the assembly data `ad`. In particular, `ad[e,s]`, where `e` is the index of a geometric cell and `s` is the index of a local shape function, returns an iterable collection of pairs `(m,w)` where `m` is an index into the iterable collection of basis functions making up the finite element space and `w` is a weight, such that shape function `s` on geometric element `e` contributes with weight `w` to basis function `n`. + +```julia +test_elements, tad = assemblydata(tfs) +bsis_elements, bad = assemblydata(bfs) +``` + +One final note on the `assemblydata` function: as you can see from the above snippet, the function returns, in addition to the actual assembly data, an iterable collection of geometric elements. This collection is a subset of the collection of elements making up the geometry on which the finite element space is defined. The elements returned are those that actually appear in the domain of one or more of the functions that span the finite element space. The double for loop that iterates over pairs of trial and testing functions will only visit those used elements. Elements that are part of the geometry but do not appear as part of the support of a function are skipped. This behaviour is required to guarantee scalability when using multiple threads in assembling the matrix: each thread is assigned a subset of the basis functions; visiting unused elements in all threads is harmful for the overall efficiency. + +With this assembly data in hand, matrix assembly can be done by iterating over geometric cells, rather than over basis functions. Doing this avoids visiting a given geometric cell more than once. When computing matrices resulting from discretisation with e.g. Raviart-Thomas elements, this can speed up assembly time with a factor 9. + +The problem of matrix assembly is now reduced to the computation of interactions between local shape functions defined on all possible pairs of geometric cells. The space of local shape functions can be retrieved by calling + +```julia +tshapes = refspace(tfs); num_tshapes = numfunctions(tshapes) +bshapes = refspace(bfs); num_bshapes = numfunctions(bshapes) +``` + +Here, `num_tshapes` and `num_bshapes` are the number of local shape functions. For example, when using Raviart-Thomas elements, the number of local shape functions equals three (one for every edge of the reference triangle). + +Based on this dimension, and based on the types used to represent numbers in the fields over which the spaces and the kernel are defined, the storage for local shape function interaction is pre-allocated: + +```julia +T = promote_type(scalartype(biop), scalartype(tfs), scalartype(bfs)) +zlocal = zeros(T, num_tshapes, num_bshapes) +``` + +Note that the computation of the storage type ensures that high precision or complex data types are only used when required. At all times the minimal storage type is selected. Not only does this keep memory use down, it also results in faster linear algebra computations such as matrix-vector multiplication. + +Before entering the double for loop that is responsible for the enumeration of all pairs of geometric cells (a trial cell pairs with a test cell), the implementer is given the opportunity to precompute data for use in the integration kernels. For example when using numerical quadrature rules to compute the double integral in the expression of the matrix entries, it is likely that a set of quadrature points for any given trial cells will be reused in interactions with a large number of test cells. To avoid computing these points and weights over and over, the client developer is given the opportunity to compute and store them by providing an appropriate method for `quaddata` . If memory use is more important the runtime, the client programmer is perfectly allowed to compute points and weights on the fly without storing them. + + +```julia +fill!(zlocal, 0) +strat = quadrule(biop, tshapes, bshapes, p, tcell, q, bcell, qd) +momintegrals!(biop, tshapes, bshapes, tcell, bcell, zlocal, strat) +``` + +For a given pair `(tcell,bcell)` of test cell and trial cell (with respective indices `p` and `q` in collections `test_elements` and `bsis_elements`), all possible interactions between local shape functions are computed. After resetting the buffer used to store these interactions, the quadrature strategy is determined. The quadrature strategy in general could depend on: + +- the kernel `biop` defining the integral operator, +- the local test and trial shape functions `tshapes` and `bshapes` (functions of high polynomial degree and functions that are highly oscillatory typically require bespoke integration methods), +- and the geometric test and trial cells `tcell` and `bcell` (cells that touch or are near to each other lead to quickly varying or even singular integrands requiring dedicated integration rules). + +The method returns an object `strat` that: (i) describes (by its type and its data fields) the integration strategy that is appropriate to compute the current set of local interactions, (ii) contains all data precomputed and stored in `qd` that is relevant to this particular integration (for example a set of quadrature points and weights). This explains why the indices `p` and `q` where passed too `quadrule`: they allow for the quick retrieval of relevant pre-stored data from `qd`. + +The routing that is responsible for the actual computation of the interactions between the local shape functions takes the quadrule object `strat` as one of its arguments. The idea is that `momintegrals!` has many methods, not only for different types of kernel and shape functions, but also for different types of `strat`. For example, there are implementations of `momintegrals!` for the computation of the Maxwellian single layer operator w.r.t. spaces of Raviart-Thomas elements that employ double numerical quadrature, singularity extraction, and even more advanced integration routines. + +*Note*: the type of `strat` depends on the orientation of the two interacting geometric cells. This information is only available at runtime. In other words, there will be a slight type instability at this point in the code. This is by design however, and not different from the use of virtual functions in an c++ implementation. Numerical experiments show that this form of runtime polymorphism results in negligible runtime overhead. + +When all possible interactions between local shape functions have been computed, they need to be stored in the global system matrix. This is done in the matrix assembly loop: + +```julia +for j in 1 : num_bshapes + for i in 1 : num_tshapes + z = zlocal[i,j] + for (m,a) in tad[p,i] + for (n,b) in bad[q,j] + store(a*z*b, m, n) + end + end + end +end +``` +For both the test and trial local shape functions, the global indices at which they appear in the finite element space (and the corresponding weights) are retrieved from the assembly data objects. The contributing value `v = a*z*b` is constructed and its storage is delegated to the `store` method, which we received as one of the arguments passed to `assemble_chunk!`. In the simplest case, `assemble_chunk!` can be used like this: + +```julia +Z = zeros(ComplexF64, numfunctions(tfs), numfunctions(bfs)) +store(v, m, n) = (Z[m,n] += v) +assemble_chunk!(kernel, tfs, bfs, store) +``` + +In other words `store` will simply add the computed value to the specified entry in the global system matrix. Allowing the caller to specify `store` as an argument allows for more flexibility than hardcoding this behaviour in the assembly routine. Indeed, when computing blocks of a larger system, or when e.g. the transposed or a multiple of a given operator is desired, a fairly simple redefinition of `store` can provide this functionality. This is also the reason why `assemble_chunk!` ends in an exclamation mark: even though strictly speaking none of the arguments are modified, the function clearly has an effect on variables defined outside of its scope! + +## Case Study: Implementation of the Nitsche Operator Assembly + +In the Nitsche method for the Maxwell system, penalty terms are added to the classic discretisation of the EFIE. When discretized using a non-conforming finite elements space (typically because the underlying geometric mesh is not conforming), the penalty term will force the solution to be divergence conforming in some weak sense. The penalty term derives from the following bilinear form: + +```math +p(v,u) = \int_{\gamma} v(x) \int_{Γ} \frac{e^{-ik|x-y|}}{4π|x-y|} u(y) dy dx +``` + +Note that ``u(x)`` is supported by a 2D surface ``Γ`` whereas ``v(y)`` is supported by a 1D curve ``γ``. The complete implementation of this operator could look like + +```julia +mutable struct SingleLayerTrace{T} <: MaxwellOperator3D + gamma::T +end + +function quaddata(operator::SingleLayerTrace, + localtestbasis::LagrangeRefSpace, localtrialbasis::LagrangeRefSpace, + testelements, trialelements) + + tqd = quadpoints(localtestbasis, testelements, (10,)) + bqd = quadpoints(localtrialbasis, trialelements, (8,)) + + return (tpoints=tqd, bpoints=bqd) +end + +function quadrule(op::SingleLayerTrace, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd) + DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j] + ) +end + +integrand(op::SingleLayerTrace, kernel, g, τ, f, σ) = f[1]*g[1]*kernel.green +``` + +Every kernel corresponds with a type. Kernels can potentially depend on a set of parameters; these appear as fields in the type. Here our Nitsche kernel depends on the wavenumber. In quaddata we precompute quadrature points for all geometric cells in the supports of test and trial elements. This is fairly sloppy: only one rule for test and trial integration is considered. A high accuracy implementation would typically compute points for both low quality and high quality quadrature rules. + +Also `quadrule` is sloppy: we always select a `DoubleQuadRule` to perform the computation of interactions between local shape functions. No singularity extraction or other advanced technique is considered for nearby interactions. Clearly amateurs at work here! + +`BEAST` provides a default implementation of an integration routine using double numerical quadrature. All that is required to tap into that implementation is a method overloading `integrand`. From the above formula it is clear what this method should look like. + +That's it! diff --git a/docs/src/internals/overview.md b/docs/src/internals/overview.md new file mode 100644 index 00000000..51bcbf60 --- /dev/null +++ b/docs/src/internals/overview.md @@ -0,0 +1,85 @@ + +## Features + +- General framework allowing to easily add support for more kernels, finite element spaces, and excitations. +- Assembly routines that take in symbolic representations of the defining bilinear form. Support for block systems and finite element spaces defined in terms of direct products or tensor products of atomic spaces. +- LU and iterative solution of the resulting system. +- Computation of secondary quantities of interest such as the near field and the limiting far field. +- Support for space-time Galerkin and convolution quadrature approaches to the solution of time domain boundary integral equations. +- Implementation of Lagrange zeroth and first order space, Raviart-Thomas, Brezzi-Douglas-Marini, and Buffa-Christianssen vector elemenents. + + + + +```@meta +CurrentModule = BEAST +``` + +# BEAST.jl documentation + +BEAST provides a number of types modelling concepts and a number of algorithms for the efficient and simple implementation of boundary and finite element solvers. It provides full implementations of these concepts for the LU based solution of boundary integral equations for the Maxwell and Helmholtz systems. + +Because Julia only compiles code at execution time, users of this library can hook into the code provided in this package at any level. In the extreme case it suffices to provide overwrites of the `assemble` functions. In that case, only the LU solution will be performed by the code here. + +At the other end it suffices that users only supply integration kernels that act on the element-element interaction level. This package will manage all required steps for matrix assembly. + +For the Helmholtz 2D and Maxwell 3D systems, complete implementations are supplied. These models will be discussed in detail to give a more concrete idea of the APIs provides and how to extend them. + +Central to the solution of boundary integral equations is the assembly of the system matrix. The system matrix is fully determined by specifying a kernel G, a set of trial functions, and a set of test functions. + +## Basis + +Sets of both trial and testing functions are implemented by models following the basis concept. The term basis is somewhat misleading as it is nowhere required nor enforced that these functions are linearly independent. Models implementing the Basis concept need to comply to the following semantics. + + +- [`numfunctions(basis)`](@ref numfunctions): number of functions in the Basis. +- [`coordtype(basis)`](): type of (the components of) the values taken on by the functions in the Basis. +- [`scalartype(d)`](@ref): the scalar field underlying the vector space the basis functions take value in. +- [`refspace(basis)`](@ref): returns the ReferenceSpace of local shape functions on which the Basis is built. +- [`assemblydata(basis)`](@ref): `assemblydata` returns an iterable collection `elements` of geometric elements and a look table `ad` for use in assembly of interaction matrices. In particular, for an index `element_idx` into `elements` and an index `local_shape_idx` in basis of local shape functions `refspace(basis)`, `ad[element_idx, local_shape_idx]` returns the iterable collection of `(global_idx, weight)` tuples such that the local shape function at `local_shape_idx` defined on the element at `element_idx` contributes to the basis function at `global_idx` with a weight of `weight`. +- [`geometry(basis)`](@ref): returns an iterable collection of Elements. The order in which these Elements are encountered corresponds to the indices used in the assembly data structure. + + +## Reference Space + +The *reference space* concept defines an API for working with spaces of local shape functions. The main role of objects implementing this concept is to allow specialization of the functions that depend on the precise reference space used. + +The functions that depend on the type and value of arguments modeling *reference space* are: + +- [`numfunctions(refspace, domain)`](@ref): returns the number of shape functions on each element. + +## Kernel + +A kernel is a fairly simple concept that mainly exists as part of the definition of a Discrete Operator. A kernel should obey the following semantics: + +In many function definitions the kernel object is referenced by `operator` or something similar. This is a misleading name as an operator definition should always be accompanied by the domain and range space. + +## Discrete Operator + +Informally speaking, a Discrete Operator is a concept that allows for the computation of an interaction matrix. It is a kernel together with a test and trial basis. A Discrete Operator can be passed to `assemble` and friends to compute its matrix representation. + +A discrete operator is a triple `(kernel, test_basis, trial_basis)`, where `kernel` is a Kernel, and `test_basis` and `trial_basis` are Bases. In addition, the following expressions should be implemented and behave according to the correct semantics: + +- [`quaddata(operator,test_refspace,trial_refspace,test_elements,trial_elements)`](@ref): create the data required for the computation of element-element interactions during assembly of discrete operator matrices. +- [`quadrule(operator,test_refspace,trial_refspace,p,test_element,q_trial_element,qd)`](@ref): returns an integration strategy object that will be passed to `momintegrals!` to select an integration strategy. This rule can depend on the test/trial reference spaces and interacting elements. The indices `p` and `q` refer to the position of the interacting elements in the enumeration defined by `geometry(basis)` and allow for fast retrieval of any element specific data stored in the quadrature data object `qd`. +- [`momintegrals!(operator,test_refspace,trial_refspace,test_element,trial_element,zlocal,qr)`](@ref): this function computes the local interaction matrix between the set of local test and trial shape functions and a specific pair of elements. The target matrix `zlocal` is provided as an argument to minimise memory allocations over subsequent calls. `qr` is an object returned by `quadrule` and contains all static and dynamic data defining the integration strategy used. + +In the context of fast methods such as the Fast Multipole Method other algorithms on Discrete Operators will typically be defined to compute matrix vector products. These algorithms do not explicitly compute and store the interaction matrix (this would lead to unacceptable computational and memory complexity). + +```@docs; canonical=false +elements +``` + +```@docs; canonical=false +numfunctions +scalartype +assemblydata +geometry +refspace +``` + +```@docs; canonical=false +quaddata +quadrule +momintegrals! +``` diff --git a/docs/src/internals/quadstrat.md b/docs/src/internals/quadstrat.md new file mode 100644 index 00000000..ac2b37e4 --- /dev/null +++ b/docs/src/internals/quadstrat.md @@ -0,0 +1,82 @@ +# Quadrature strategies + +There are many ways to approximately compute the singular integrals that appear in boundary element discretisations of surface and volume integral euqations. + +BEAST.jl is configured to select reasonable defaults, but advanced users may want to select their own quadrature rules. This section provides information on how to do this. + +!!! warning + TODO: discuss directly available/implemented singularity treatment + +## quaddata and quadrule + +Numerical quadrature is governed by a pair of functions that need to be designed to work together: + +- `quaddata`: this function is executed before the assembly loop is entered. It's job is to compute all data needed for quadrature that the developer wants to be cached. Typically this is all geometric information such as the parametric and cartesian coordinates of all quadratures rules for all elemenents. It makes sense to cache this data as it will be used many times over in the double for loop that governs assembly. Typically, near singular interactions require more careful treatment than far interactions. This means that multiple quadrature rules per elements can be required. In such cases, the developer may want to opt to sture quadrature points and weights for all these rules. The function returns a quaddata object that holds all the cached data. +- `quadrule`: quadrule is executed inside the assembly hotloop. It receives a pair of elements and the quaddata object as its arguments. Based on this, the relevant cached data is extracted and stored in a quadrule object. The type of this object will determine the actual quadrature routined that will be called upon to do the numerical quadrature. + +## quadstrat + +The pair of quaddata and quadrule methods that is used is determined by the type of the operator and finite elements, and a `quadstrat` object. This object is passed to the assembly routine and passed on to `quaddata` and `quadstrat`, so it can be considered during dispatch. + +Parameters, such as those that determine the accuracy of the numerical quadrature, are part of the runtime payload of the quadstrat object. This is usefull when the user is interested on the impact of these parameters on the performance and the accuracy of the solver without having to supply a new pair of `quadstrat`/`quaddata` methods for each possible value of these parameters. + +Roughly this leads to the following (simplified) assembly routine: + +```julia +function assemble(op, tfs, bfs, store; quadstrat=QS) + + tad, tels = assemblydata(op, tfs) + bad, bels = assemblydata(op, bfs) + + qd = quadata(op,tels,bels,quadstrat) + for tel in tels + for bel in bels + qr = quadrule(op,tel,bel,qd,quadstrat) + zlocal = momintegrals(op,tel,bel,qr) + + for i in axes(zlocal,1) + for j in axes(zlocal,2) + m, a = tad[tel,i] + n, b = bad[bel,j] + store(a*zlocal[i,j]*b,m,n) +end end end end end +``` + +It is conceivable that the types and functions described above look like this: + +```julia +struct DoubleNumQS + test_precision + trial_precision +end + +function quaddata(op, tels, bels, quadstrat::DoubleNumQS) + tqps = [quadpoints(tel,precision=quadstrat.test_precision) for tel in tels] + bqps = [quadpoints(bel,precision=quadstrat.basis_precision) for bel in bels] + return (test_quadpoints=tqps, basis_quadpoints=bqps) +end + + +struct DoubleNumQR + test_quadpoints + trial_quadpoints +end + +struct HighPrecisionQR end + +function quadrule(op, tel, bel, qd, quadstrat::DoubleNumQs) + if wellseparated(tel, bel) + return DoubleNumQR(qd.test_quadpoints[tel], qd.basis_quadpoints[bel]) + else + return HighPrecisionQR(tel, bel) + end +end + +function momintegrals(op, tel, bel, qr::DoubleNumQR) + ... +end + +function momintegrals(op, tel, bel, qr::HighPrecisionQR) + ... +end +``` \ No newline at end of file diff --git a/docs/src/manual/efie.md b/docs/src/manual/efie.md new file mode 100644 index 00000000..07d9cdc8 --- /dev/null +++ b/docs/src/manual/efie.md @@ -0,0 +1,11 @@ + +# Electric Field Integral Equation (Time-Harmonic) + +Text + +--- +### Geometry + +Text + +... diff --git a/docs/src/manual/examples.md b/docs/src/manual/examples.md deleted file mode 100644 index d98f8cbd..00000000 --- a/docs/src/manual/examples.md +++ /dev/null @@ -1,20 +0,0 @@ - -# General Usage - -Text - ---- -## EFIE - -Text - - ---- -## MFIE - -Text - ---- -## Time Domain Something - -Text \ No newline at end of file diff --git a/docs/src/manual/tdefie.md b/docs/src/manual/tdefie.md new file mode 100644 index 00000000..0b2a0b2b --- /dev/null +++ b/docs/src/manual/tdefie.md @@ -0,0 +1,108 @@ +# Solving the Time Domain EFIE using Marching-on-in-Time + +!!! warning + Make the following an @example which is actually executed. + +If broadband information is required or if the system under study will be coupled to non-linear components, the scattering problem should be solved directly in the time domain, i.e. as a hyperbolic evolution problem. + +### Building the geometry + +Building the geometry and defining the spatial finite elements happens in completely the same manner as for frequency domain simulations: + +```julia +using CompScienceMeshes +using BEAST +D, dx = 1.0, 0.3 +Γ = meshsphere(1.0, dx) +X = raviartthomas(Γ) +nothing # hide +``` + +### Basis functions + +Time domain currents are approximated in the tensor product of a spatial and temporal finite element space: + +$j(x) \approx \sum_{i=1}^{N_T} \sum_{m=1}^{N_S} u_{i,m} T_i(t) f_m(x)$ + +This package only supports translation invariant temopral basis functions, i.e. + +$T_i(t) = T(t - i \Delta t),$ + +where $\Delta t$ is the time step used to solve the problem. This time step depends on the bandwidth of the incident field and the desired accuracy. Space-Time Galerkin solvers of the type used here are not subject to stability conditions linking spatial and temporal discretisation resolutions. + +We need a temporal trial space and test space. Common examples of temporal trial spaces are the shifted quadratic spline and shifted lagrange basis functions. In this example, a shifted quadratic spline `S` is used for the trial space, while a delta function `U` is used as the temporal test space to obtain a time-stepping solution. + +```julia +Δt, Nt = 0.25, 300 +S = BEAST.timebasisspline2(Δt, Nt) +U = BEAST.timebasisdelta(Δt, Nt) +nothing # hide +``` + +### Excitation + +We want to solve the EFIE, i.e. we want to find the current $j$ such that + +$Tj = -e^i,$ + +where the incident electric field can be any Maxwell solution in the background medium. To describe this problem in Julia we create a retarded potential operator objects and a functional representing the incident field: + +```julia +x = point(1.0,0.0,0.0) +y = point(0.0,1.0,0.0) +z = point(0.0,0.0,1.0) +gaussian = BEAST.creategaussian(30Δt, 60Δt) +E = BEAST.planewave(x, z, BEAST.derive(gaussian), 1.0) +T = BEAST.MWSingleLayerTDIO(1.0,-1.0,-1.0,2,0) +nothing; # hide +``` + +### Setting up the LSE + +Using the finite element spaces defined above this retarded potential equation can be discretized. + +```julia +V = X ⊗ S #Space and time trial basis +W = X ⊗ U #Space and time test basis + +B = assemble(E, W) +Z = assemble(T, W, V) +nothing # hide +``` + +The variable `Z` can efficiently store the matrices corresponding to different delays (`assemble` knows about the specific sparsity pattern of such matrices and returns a sparse array of rank three fit for purpose). + +The algorithm below solves the discrete convolution problem by marching on in time: + +```julia +Z0 = Z[:,:,1] +W0 = inv(Z0) +x = BEAST.marchonintime(W0,Z,-B,Nt) +nothing # hide +``` + +### Post processing + +Computing the values of the induced current is now possible in the same manner as for frequency domain simulations by first converting our MOT solution back to the frequency domain using the fourier transform, along with some adjustments. + +```julia +Xefie, Δω, ω0 = fouriertransform(x, Δt, 0.0, 2) +ω = collect(ω0 + (0:Nt-1)*Δω) +_, i1 = findmin(abs(ω-1.0)) +ω1 = ω[i1] + +ue = Xefie[:,i1] +ue /= fouriertransform(gaussian)(ω1) +fcr, geo = facecurrents(ue, X) +nothing # hide +``` + +For now the package still relies upon Matlab for some of its visualisation. This dependency will be removed in the near future: + +```julia +include(Pkg.dir("CompScienceMeshes","examples","matlab_patches.jl")) +mat"clf" +patch(geo, real.(norm.(fcr))) +mat"cd($(pwd()))" +mat"print('current.png', '-dpng')" +``` diff --git a/docs/src/operators/identity.md b/docs/src/operators/identity.md index e69de29b..944fce7b 100644 --- a/docs/src/operators/identity.md +++ b/docs/src/operators/identity.md @@ -0,0 +1,54 @@ + +# [Identity Operator](@id identityDef) + +The identity operator is implemented in two flavors `BEAST.Identity()` and `BEAST.NCross()`. + +--- +## Definition + +The identity operator +```math +\bm{\mathcal{I}} \bm b = \bm b +``` +returns the function it is provided unchanged. + + +### As bilinear form + +When handed to the [`assemble`](@ref assemble) function, the operator is interpreted as the bilinear form +```math +a(\bm t, \bm b) = \int_\Gamma \bm t(\bm x) ⋅ \bm{\mathcal{I}} \bm b(\bm x) \,\mathrm{d}\bm x = \int_\Gamma \bm t(\bm x) ⋅ \bm b(\bm x) \,\mathrm{d}\bm x \,. +``` +Hence, the resulting matrix ``\bm A`` contains the entries +```math +[\bm A]_{mn} = \int_\Gamma \bm t_m(\bm x) ⋅ \bm b_n(\bm x) \,\mathrm{d}\bm x \,. +``` + +### API + +```@docs; canonical=false +BEAST.Identity +``` + + +--- +## Variant with ``\bm n \times`` + +As a variation, the identity operator is provided with a cross product with the normal vector ``\bm n`` of the surface ``\Gamma``. + +### The bilinear form + +The [`assemble`](@ref assemble) function, interprets the operator as the bilinear form +```math +a(\bm t, \bm b) = \int_\Gamma \bm t(\bm x) ⋅ \bm{\mathcal{I}} (\bm n(\bm x) \times \bm b(\bm x)) \,\mathrm{d}\bm x = \int_\Gamma \bm t(\bm x) ⋅ (\bm n(\bm x) \times \bm b(\bm x)) \,\mathrm{d}\bm x \,. +``` +Hence, the resulting matrix ``\bm A`` contains the entries +```math +[\bm A]_{mn} = \int_\Gamma \bm t_m(\bm x) ⋅ (\bm n(\bm x) \times \bm b_n(\bm x)) \,\mathrm{d}\bm x \,. +``` + +### API + +```@docs; canonical=false +BEAST.NCross +``` \ No newline at end of file diff --git a/docs/src/operators/maxwelldoublelayer.md b/docs/src/operators/maxwelldoublelayer.md index e69de29b..7b75b0bd 100644 --- a/docs/src/operators/maxwelldoublelayer.md +++ b/docs/src/operators/maxwelldoublelayer.md @@ -0,0 +1,76 @@ + +# [Maxwell Double Layer Operator](@id MWdoublelayerDef) + +The Maxwell double layer operator is encountered in many time-harmonic BEM scattering formulations in electromagnetics. +So far, only the 3D variant is implemented. + +--- +## Definition + +The operator is defined as (see, e.g., ...) +```math +\bm{\mathcal{K}} \bm b = α \int_\Gamma ∇_{\!x} g_γ(\bm x,\bm y) \times \bm b(\bm y) \,\mathrm{d}\bm y +``` +for a vector field ``\bm{b}`` and a parameter ``α`` with the free-space Green's function +```math +g_{γ}(\bm x,\bm y) = \dfrac{\mathrm{e}^{-γ|x-y|}}{4π|x-y|} \,. +``` +The parameters are typically ``α=1`` and ``γ = \mathrm{j}k`` with ``k`` denoting the wavenumber and ``\mathrm{j}`` the imaginary unit. +As variation, the rotaded double layer operator +```math +\bar{\bm{\mathcal{K}}} = \bm{n} \times \bm{\mathcal{K}} +``` +can be used which simply involves the cross product with the normal vector ``\bm{n}`` of the surface ``\Gamma``. + + +--- +## As Bilinear Form + +When handed to the [`assemble`](@ref assemble) function, the operators are interpreted as the corresponding bilinear forms +```math +a(\bm t, \bm b) = α ∬_{\Gamma \times \Gamma} \bm t(\bm x) ⋅ \,( ∇_{\!x} g_γ(\bm x,\bm y) \times \bm b(\bm y) ) \,\mathrm{d}\bm y \mathrm{d}\bm x +``` +and +```math +\bar{a}(\bm t, \bm b) = α ∬_{\Gamma \times \Gamma} \bm t(\bm x) ⋅ \, (\bm{n} \times ( ∇_{\!x} g_γ(\bm x,\bm y) \times \bm b(\bm y) )) \,\mathrm{d}\bm y \mathrm{d}\bm x +``` +resulting in the matrix +```math +[\bm A]_{mn} = a(\bm t_m, \bm b_n) \,. +``` + + +### API + +```@docs +Maxwell3D.doublelayer +``` + + +--- +## As Linear Form + +When handed to the [`potential`](@ref potential) function, the operator can be evaluated at provided points in space. +Commonly, this is used in post-processing. + + +### Far-Field + +In the limit that the observation point ``\bm x \rightarrow \infty``, the operator simplifies to the far-field (FF) version +```math +(\bm{\mathcal{K}}_\mathrm{FF} \bm b)(\bm x) = α \bm{u}_r \times \int_\Gamma \bm{b}(\bm y) \mathrm{e}^{\mathrm{j}\bm{u}_r \cdot\, \bm{y}} \,\mathrm{d}\bm{y} +``` + +### API + +```@docs +BEAST.MWDoubleLayerField3D +BEAST.MWDoubleLayerFarField3D +MWDoubleLayerRotatedFarField3D +``` + +!!! tip + The provided points for the [`potential`](@ref potential) should be in Cartesian coordinates. The returned fields are also in Cartesian from. + +!!! warning + Singularities are not addressed: the case when the evaluation point is close to the surface is not treated properly, so far. \ No newline at end of file diff --git a/docs/src/operators/maxwelldoublelayer_td.md b/docs/src/operators/maxwelldoublelayer_td.md new file mode 100644 index 00000000..5e82365e --- /dev/null +++ b/docs/src/operators/maxwelldoublelayer_td.md @@ -0,0 +1,4 @@ + +# [Maxwell Double Layer Operator](@id MWdoublelayerTDDef) + +Text \ No newline at end of file diff --git a/docs/src/operators/maxwellsinglelayer.md b/docs/src/operators/maxwellsinglelayer.md index 760416a7..6edb85ae 100644 --- a/docs/src/operators/maxwellsinglelayer.md +++ b/docs/src/operators/maxwellsinglelayer.md @@ -1,26 +1,92 @@ # [Maxwell Single Layer Operator](@id MWsinglelayerDef) +The Maxwell single layer operator, also known als electric field integral operator (EFIO) is encountered in many time-harmonic BEM scattering formulations in electromagnetics. +So far, only the 3D variant is implemented. + +--- ## Definition +The operator is defined as (see, e.g., [raoElectromagneticScatteringSurfaces1982](@cite)) +```math +\bm{\mathcal{T}} \bm b = α \bm{\mathcal{T}}_{\!\!s} \bm b + β \bm{\mathcal{T}}_{\!\!h} \bm b +``` +for a vector field ``\bm{b}`` and parameters ``α`` and ``β``, as well as, the weakly singular operator (also known as vector potential operator) +```math +\bm{\mathcal{T}}_{\!\!s} \bm b = \int_\Gamma g_γ(\bm x,\bm y) \, \bm b(\bm y) \,\mathrm{d}\bm y +``` +and the hyper singular operator (also known as scalar potential operator) +```math +\bm{\mathcal{T}}_{\!\!h} \bm b = ∇\int_\Gamma g_γ(\bm x,\bm y) \, ∇_Γ⋅\bm b(\bm y) \,\mathrm{d}\bm y +``` +with the free-space Green's function ```math -\bm{\mathcal{T}} +g_{γ}(\bm x,\bm y) = \dfrac{\mathrm{e}^{-γ|x-y|}}{4π|x-y|} \,. ``` +The parameters are typically ``α=-\mathrm{j}k``, ``β=-1/(\mathrm{j}k)``, and ``γ = \mathrm{j}k`` with ``k`` denoting the wavenumber and ``\mathrm{j}`` the imaginary unit. -When applied to +--- +## As Bilinear Form + +When handed to the [`assemble`](@ref assemble) function, the operators are interpreted as the corresponding bilinear forms ```math a(\bm t, \bm b) = α ∬_{\Gamma \times \Gamma} \bm t(\bm x) ⋅ \bm b(\bm y) \, g_γ(\bm x,\bm y) \,\mathrm{d}\bm y \mathrm{d}\bm x + β ∬_{Γ×Γ} ∇_Γ⋅\bm t(\bm x) \, ∇_Γ⋅\bm b(\bm y) \, g_γ(\bm x,\bm y) \,\mathrm{d}\bm y \mathrm{d}\bm x ``` +for the complete Maxwell single layer operator, +```math +a_s(\bm t, \bm b) = α ∬_{\Gamma \times \Gamma} \bm t(\bm x) ⋅ \bm b(\bm y) \, g_γ(\bm x,\bm y) \,\mathrm{d}\bm y \mathrm{d}\bm x +``` +for the weakly singular part, and +```math +a_h(\bm t, \bm b) = β ∬_{Γ×Γ} ∇_Γ⋅\bm t(\bm x) \, ∇_Γ⋅\bm b(\bm y) \, g_γ(\bm x,\bm y) \,\mathrm{d}\bm y \mathrm{d}\bm x +``` +for the hyper singular part. +Note that the gradient in the hypersingular operator has been moved to the test function using, e.g., a Stokes identity [nedelecWaveEquations2001; p. 73](@cite). +The corresponding matrices contain the entries +```math +[\bm A]_{mn} = a_x(\bm t_m, \bm b_n) \,. +``` -with the free-space Green's function +### API +The weakly singular and the hyper singular operator can be used as such or combined in the single layer operator. + +!!! tip + The qualifier `Maxwell3D` has to be used. + + +```@docs +Maxwell3D.singlelayer +Maxwell3D.weaklysingular +Maxwell3D.hypersingular +``` + +--- +## As Linear Form + +When handed to the [`potential`](@ref potential) function, the operator can be evaluated at provided points in space. +Commonly, this is used in post-processing. + + +### Far-Field + +In the limit that the observation point ``\bm x \rightarrow \infty``, the operator simplifies to the far-field (FF) version ```math -g_{γ}(\bm x,\bm y) = \dfrac{\mathrm{e}^{-γ|x-y|}}{4π|x-y|} +(\bm{\mathcal{T}}_\mathrm{FF} \bm b)(\bm x) = α \bm{u}_r \times \int_\Gamma \bm{b}(\bm y) \mathrm{e}^{\mathrm{j}\bm{u}_r \cdot\, \bm{y}} \,\mathrm{d}\bm{y} \times \bm{u}_r ``` +where ``\bm{u}_r`` is the unit vector in the direction of the evaluation point. + -## API +### API ```@docs -Maxwell3D.singlelayer -``` \ No newline at end of file +MWSingleLayerField3D +MWFarField3D +``` + +!!! tip + The provided points for the [`potential`](@ref potential) should be in Cartesian coordinates. The returned fields are also in Cartesian from. + +!!! warning + Singularities are not addressed: the case when the evaluation point is close to the surface is not treated properly, so far. \ No newline at end of file diff --git a/docs/src/operators/maxwellsinglelayerVIE.md b/docs/src/operators/maxwellsinglelayerVIE.md new file mode 100644 index 00000000..4d4f4b56 --- /dev/null +++ b/docs/src/operators/maxwellsinglelayerVIE.md @@ -0,0 +1,4 @@ + +# [Maxwell Single Layer Operator](@id MWsinglelayerVIEDef) + +TODO \ No newline at end of file diff --git a/docs/src/operators/maxwellsinglelayer_td.md b/docs/src/operators/maxwellsinglelayer_td.md new file mode 100644 index 00000000..48eb41a8 --- /dev/null +++ b/docs/src/operators/maxwellsinglelayer_td.md @@ -0,0 +1,14 @@ + +# [Maxwell Single Layer Operator](@id MWsinglelayerTDDef) + +Text + +--- +## Definition + +Text + + + +--- +## As ... \ No newline at end of file diff --git a/docs/src/operators/overview.md b/docs/src/operators/overview.md index e5a06451..bdab8bc7 100644 --- a/docs/src/operators/overview.md +++ b/docs/src/operators/overview.md @@ -6,4 +6,6 @@ using TypeTree using BEAST print(join(tt(BEAST.Operator), "")) +print("\n\n\n")#hide +print(join(tt(BEAST.SpaceTimeOperator), "")) ``` \ No newline at end of file diff --git a/docs/src/operators/planewave.md b/docs/src/operators/planewave.md index 88b9875f..085b9b60 100644 --- a/docs/src/operators/planewave.md +++ b/docs/src/operators/planewave.md @@ -1,4 +1,7 @@ # [Plane Wave Excitation](@id planewaveEx) -Text \ No newline at end of file +## Time-Harmonic + +## Time-Domain + diff --git a/docs/src/refs.bib b/docs/src/refs.bib index 8b42808a..dbd949b6 100644 --- a/docs/src/refs.bib +++ b/docs/src/refs.bib @@ -19,3 +19,13 @@ @article{buffaDualFiniteElement2007 number = {260}, pages = {1743--1769} } + +@book{nedelecWaveEquations2001, + title = {Acoustic and Electromagnetic Equations}, + author = {N\'ed\'elec, Jean-Claude}, + year = {2001}, + series = {Applied Mathematical Sciences}, + publisher = {Springer}, + location = {New York}, + isbn = {978-1-4757-4393-7} +} diff --git a/src/identityop.jl b/src/identityop.jl index c55d1dce..b8e65c91 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -1,3 +1,9 @@ + +""" + Identity <: LocalOperator + +The identity operator. +""" struct Identity <: LocalOperator end @@ -5,6 +11,11 @@ kernelvals(biop::Identity, x) = nothing integrand(op::Identity, kernel, x, g, f) = dot(f[1], g[1]) scalartype(op::Identity) = Union{} +""" + NCross <: LocalOperator + +The identity operator where the trial function is rotated. +""" struct NCross <: LocalOperator end diff --git a/src/maxwell/farfield.jl b/src/maxwell/farfield.jl index 615b3991..348e61b2 100644 --- a/src/maxwell/farfield.jl +++ b/src/maxwell/farfield.jl @@ -1,14 +1,5 @@ abstract type MWFarField <: FarField end -""" -Operator to compute the far field of a current distribution. In particular, given the current distribution ``j`` this operator allows for the computation of - -```math -A j = n × ∫_Γ e^{γ ̂x ⋅ y} dy -``` - -where ``̂x`` is the unit vector in the direction of observation. Note that the assembly routing expects the observation directions to be normalised by the caller. -""" struct MWFarField3D{K, U} <: MWFarField gamma::K amplitude::U @@ -23,6 +14,11 @@ struct MWDoubleLayerRotatedFarField3D{K,U} <: MWFarField amplitude::U end +""" + MWFarField3D(;gamma, amplitude) + +Maxwell single layer far-field operator for 3D. +""" function MWFarField3D(; gamma=nothing, wavenumber=nothing, @@ -38,6 +34,11 @@ end MWFarField3D(op::MWSingleLayer3D{T,U}) where {T,U} = MWFarField3D(op.gamma, sqrt(op.α*op.β)) +""" + MWDoubleLayerFarField3D(;gamma, amplitude) + +Maxwell double layer far-field operator for 3D. +""" function MWDoubleLayerFarField3D(; gamma=nothing, wavenumber=nothing, @@ -74,6 +75,11 @@ function integrand(op::MWFarField3DDropConstant,krn,y,f,p) op.coeff*(y × (krn * f[1])) × y end +""" + MWDoubleLayerRotatedFarField3D + +Rotated Maxwell double layer far-field operator for 3D. +""" function MWDoubleLayerRotatedFarField3D(; gamma=nothing, wavenumber=nothing, diff --git a/src/maxwell/maxwell.jl b/src/maxwell/maxwell.jl index 4943a4f5..079841c9 100644 --- a/src/maxwell/maxwell.jl +++ b/src/maxwell/maxwell.jl @@ -7,13 +7,11 @@ module Maxwell3D singlelayer(;gamma, alpha, beta) singlelayer(;wavenumber, alpha, beta) - Bilinear form given by: + Maxwell 3D single layer operator. - ```math - a(t,b) = α ∬_{Γ×Γ} t(x)⋅b(y) g_{γ}(x,y) dx dy + β ∬_{Γ×Γ} ∇_Γ⋅ t(x) ∇_Γ⋅ b(y) g_{γ}(x,y) dx dy - ``` + Either gamma, or the wavenumber, or α and β must be provided. - with ``g_{γ} = e^{-γ|x-y|} / 4π|x-y|``. + If α and β are not provided explitly, they are set to ``α = -γ`` and ``β = -1/γ`` with ``γ=\\mathrm{j} k``. """ function singlelayer(; gamma=nothing, @@ -34,20 +32,33 @@ module Maxwell3D Mod.MWSingleLayer3D(gamma, alpha, beta) end + """ + weaklysingular(;wavenumber) + + Weakly singular part of the Maxwell 3D single layer operator. + + ``α = -\\mathrm{j} k`` is set with the wavenumber ``k``. + """ weaklysingular(;wavenumber) = singlelayer(;wavenumber, alpha=-im*wavenumber, beta=zero(im*wavenumber)) + + """ + hypersingular(;wavenumber) + + Hyper singular part of the Maxwell 3D single layer operator. + + ``β = -1/\\mathrm{j} k`` is set with the wavenumber ``k``. + """ hypersingular(;wavenumber) = singlelayer(; wavenumber, alpha=zero(im*wavenumber), beta=-1/(im*wavenumber)) """ doublelayer(;gamma) - doublelaher(;wavenumber) + doublelayer(;wavenumber) - Bilinear form given by: + Maxwell double layer operator. - ```math - α ∬_{Γ^2} k(x) ⋅ (∇G_γ(x-y) × j(y)) - ``` + Either gamma or the wavenumber must be provided. Optionally, also alpha can be provided. - with ``G_γ = e^{-γ|x-y|} / 4π|x-y|`` + If alpha is not provided explitly, it is set to ``α = 1``. """ function doublelayer(; alpha=nothing, diff --git a/src/postproc.jl b/src/postproc.jl index b720a9b5..8fb426c7 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -129,6 +129,12 @@ function facecurrents(u, X::DirectProductSpace) fcr, m end + +""" + potential(op, points, coeffs, basis) + +Evaluate operator for a given bases and expansion coefficients at the given points. +""" function potential(op, points, coeffs, basis; type=SVector{3,ComplexF64}, quadstrat=defaultquadstrat(op, basis)) From b00ffebaa482667fc21eb7ae0a794afd7dc4cdc0 Mon Sep 17 00:00:00 2001 From: Bernd Hofmann Date: Tue, 12 Nov 2024 19:23:01 +0100 Subject: [PATCH 444/528] interim deploy config to host on fork and feature branch --- .github/workflows/Documentation.yml | 1 + docs/make.jl | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index fda05bab..c6d5799a 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -4,6 +4,7 @@ on: push: branches: - master # update to match your development branch (master, main, dev, trunk, ...) + - feature/docs tags: '*' pull_request: branches: diff --git a/docs/make.jl b/docs/make.jl index 3ad716f3..d85c9309 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -75,8 +75,9 @@ makedocs(; ) deploydocs(; - repo = "github.com/krcools/BEAST.jl.git", + repo = "github.com/HoBeZwe/BEAST.jl.git", target = "build", push_preview = true, forcepush = true, + devbranch="feature/docs", ) From eb90bb851b30c3527615a77422a2dc42f86326b9 Mon Sep 17 00:00:00 2001 From: Bernd Hofmann Date: Tue, 12 Nov 2024 23:11:20 +0100 Subject: [PATCH 445/528] add some more details & structure --- docs/make.jl | 25 ++- docs/src/assets/logo_README.svg | 12 +- docs/src/assets/logo_README_white.svg | 202 +-------------------- docs/src/bases/buffachristiansen.md | 18 +- docs/src/bases/gragliawiltonpeterson.md | 4 + docs/src/bases/overview.md | 9 + docs/src/bases/raviartthomas.md | 26 ++- docs/src/contributing.md | 2 +- docs/src/excitations/dipole.md | 15 ++ docs/src/excitations/linearpotential.md | 11 ++ docs/src/excitations/monopole.md | 11 ++ docs/src/excitations/planewave.md | 42 +++++ docs/src/geometry/flat.md | 0 docs/src/index.md | 9 +- docs/src/internals/overview.md | 22 +-- docs/src/manual/efie.md | 11 -- docs/src/manual/{ => examplesTD}/tdefie.md | 0 docs/src/manual/examplesTH/efie.md | 11 ++ docs/src/manual/examplesTH/mfie.md | 4 + docs/src/manual/usage.md | 19 +- docs/src/operators/planewave.md | 7 - src/maxwell/maxwell.jl | 9 + src/maxwell/timedomain/mwtdexc.jl | 4 + src/volumeintegral/vieexc.jl | 18 ++ 24 files changed, 234 insertions(+), 257 deletions(-) create mode 100644 docs/src/bases/gragliawiltonpeterson.md create mode 100644 docs/src/bases/overview.md create mode 100644 docs/src/excitations/dipole.md create mode 100644 docs/src/excitations/linearpotential.md create mode 100644 docs/src/excitations/monopole.md create mode 100644 docs/src/excitations/planewave.md create mode 100644 docs/src/geometry/flat.md delete mode 100644 docs/src/manual/efie.md rename docs/src/manual/{ => examplesTD}/tdefie.md (100%) create mode 100644 docs/src/manual/examplesTH/efie.md create mode 100644 docs/src/manual/examplesTH/mfie.md delete mode 100644 docs/src/operators/planewave.md diff --git a/docs/make.jl b/docs/make.jl index d85c9309..b4a701f1 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -22,8 +22,11 @@ makedocs(; "Manual" => Any[ "General Usage"=>"manual/usage.md", "Application Examples"=>Any[ - "Time-Hamronic EFIE"=>"manual/efie.md", - "Time-Domain EFIE"=>"manual/tdefie.md", + "Time-Harmonic"=>Any[ + "EFIE"=>"manual/examplesTH/efie.md", + "MFIE"=>"manual/examplesTH/mfie.md", + ], + "Time-Domain"=>Any["EFIE"=>"manual/examplesTD/tdefie.md"], ], ], "Operators & Excitations" => Any[ @@ -45,25 +48,27 @@ makedocs(; "Maxwell Double Layer"=>Any[], ], "Excitations"=>Any[ - "Plane Wave"=>"operators/planewave.md", - "Dipole"=>Any[], - "Monopole"=>Any[], - "Linear Potential" => Any[], + "Plane Wave"=>"excitations/planewave.md", + "Dipole"=>"excitations/dipole.md", + "Monopole"=>"excitations/monopole.md", + "Linear Potential"=>"excitations/linearpotential.md", ], ], - "Bases" => Any[ + "Basis Functions" => Any[ + "Overview"=>"bases/overview.md", "Spatial"=>Any[ "Raviart Thomas"=>"bases/raviartthomas.md", "Buffa Christiansen"=>"bases/buffachristiansen.md", "Brezzi-Douglas-Marini"=>"bases/brezzidouglasmarini.md", + "Graglia-Wilton-Peterson"=>"bases/gragliawiltonpeterson.md", ], "Temporal"=>Any[], ], - "Geometry Representations" => Any["Flat"=>Any[], "Curvilinear"=>Any[]], + "Geometry Representations" => Any["Flat"=>"geometry/flat.md", "Curvilinear"=>Any[]], "____________________________________" => Any[], "Internals" => Any[ "Overview"=>"internals/overview.md", - "Assembly"=>"internals/assemble.md", + "The Matrix Assemble Routine"=>"internals/assemble.md", "Quadrature"=>"internals/quadstrat.md", "Parametric Domain"=>Any[], "Multithreading"=>Any[], @@ -79,5 +84,5 @@ deploydocs(; target = "build", push_preview = true, forcepush = true, - devbranch="feature/docs", + devbranch = "feature/docs", ) diff --git a/docs/src/assets/logo_README.svg b/docs/src/assets/logo_README.svg index ba367792..877c7be0 100644 --- a/docs/src/assets/logo_README.svg +++ b/docs/src/assets/logo_README.svg @@ -3,8 +3,8 @@ + transform="translate(-47.165911,-203.92483)"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="defs2" /> @@ -246,7 +60,7 @@ inkscape:label="Ebene 1" inkscape:groupmode="layer" id="layer1" - transform="translate(-47.165911,-208.15819)"> + transform="translate(-47.165911,-203.92483)"> BEAST + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:47.0606px;font-family:Calibri;-inkscape-font-specification:'Calibri, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;stroke-width:0.490209px">BEAST diff --git a/docs/src/bases/buffachristiansen.md b/docs/src/bases/buffachristiansen.md index 0304d621..91181233 100644 --- a/docs/src/bases/buffachristiansen.md +++ b/docs/src/bases/buffachristiansen.md @@ -1,4 +1,20 @@ # [Buffa Christiansen Basis Functions](@id bcDef) -Another citation [buffaDualFiniteElement2007](@cite) \ No newline at end of file +Another citation [buffaDualFiniteElement2007](@cite) + +--- +## First Order + +```@docs +BEAST.buffachristiansen +``` + +### Triangles + +Some details. + + +### Quadrilaterals + +Not implemented yet. \ No newline at end of file diff --git a/docs/src/bases/gragliawiltonpeterson.md b/docs/src/bases/gragliawiltonpeterson.md new file mode 100644 index 00000000..8c8d1d17 --- /dev/null +++ b/docs/src/bases/gragliawiltonpeterson.md @@ -0,0 +1,4 @@ + +# [Graglia-Wilton-Peterson](@id gwpRef) + +TODO \ No newline at end of file diff --git a/docs/src/bases/overview.md b/docs/src/bases/overview.md new file mode 100644 index 00000000..1d4f3489 --- /dev/null +++ b/docs/src/bases/overview.md @@ -0,0 +1,9 @@ + +# [Bases Overview](@id basesRef) + +```@example introductory +using TypeTree +using BEAST + +print(join(tt(BEAST.Space), "")) +``` \ No newline at end of file diff --git a/docs/src/bases/raviartthomas.md b/docs/src/bases/raviartthomas.md index db88b5c0..f8ddc491 100644 --- a/docs/src/bases/raviartthomas.md +++ b/docs/src/bases/raviartthomas.md @@ -1,6 +1,28 @@ # [Raviart-Thomas Basis Functions](@id raviartthomasDef) -Text +Raviart-Thomas basis functions are also known as Rao-Wilton-Glisson functions [raoElectromagneticScatteringSurfaces1982](@cite) and commonly employed for ... -A citation [raoElectromagneticScatteringSurfaces1982](@cite) \ No newline at end of file + +--- +## First Order + +```@docs +BEAST.raviartthomas +``` + +### Triangles + +Details on definition. + + +### Quadrilaterals + +Details on definition. + + + +--- +## Higher Order + +... same as GWP? \ No newline at end of file diff --git a/docs/src/contributing.md b/docs/src/contributing.md index 5453ae43..f207c5d8 100644 --- a/docs/src/contributing.md +++ b/docs/src/contributing.md @@ -1,5 +1,5 @@ -# Contributing +# [Contributing](@id contribRef) In order to contribute to this package directly create a pull request against the `master` branch. Before doing so please: diff --git a/docs/src/excitations/dipole.md b/docs/src/excitations/dipole.md new file mode 100644 index 00000000..2c471d6e --- /dev/null +++ b/docs/src/excitations/dipole.md @@ -0,0 +1,15 @@ + +# [Dipole Excitation](@id dipoleRef) + +A dipole ... + +--- +## Time-Harmonic + +TODO + +### API + +```@docs +BEAST.dipolemw3d +``` \ No newline at end of file diff --git a/docs/src/excitations/linearpotential.md b/docs/src/excitations/linearpotential.md new file mode 100644 index 00000000..33217437 --- /dev/null +++ b/docs/src/excitations/linearpotential.md @@ -0,0 +1,11 @@ + +# [Linear Potential Excitation](@id linpotRef) + +Text + +## API + +```@docs; canonical=false +BEAST.HH3DLinearPotential +BEAST.linearpotentialvie +``` \ No newline at end of file diff --git a/docs/src/excitations/monopole.md b/docs/src/excitations/monopole.md new file mode 100644 index 00000000..330f2332 --- /dev/null +++ b/docs/src/excitations/monopole.md @@ -0,0 +1,11 @@ + +# [Monopole Excitation](@id monopoleRef) + +Text + + +## API + +```@docs; canonical=false +BEAST.HH3DMonopole +``` \ No newline at end of file diff --git a/docs/src/excitations/planewave.md b/docs/src/excitations/planewave.md new file mode 100644 index 00000000..c87e2436 --- /dev/null +++ b/docs/src/excitations/planewave.md @@ -0,0 +1,42 @@ + +# [Plane Wave Excitation](@id planewaveEx) + +A plane wave can be used as excitation in time-harmonic and time-domain scenarios. + +--- +## Time-Harmonic + +A time-harmonic plane wave with amplitude ``a``, wave vector ``\bm k = k \hat{\bm k}``, and polarization ``\hat{\bm p}`` (vectors with a hat denote unit vectors) is defined by the field +```math +\bm e_\mathrm{PW}(\bm x) = a \hat{\bm p} \, \mathrm{e}^{-\mathrm{j} \bm k \cdot \bm x} \,, +``` +where the polarization and wave vector are orthogonal, that is, +```math +\bm k \cdot \hat{\bm p} = 0 +``` +holds. + +### API + +!!! warning + There are currently two APIs for the BEM plane wave. Fix in future. + +```@docs +BEAST.planewavemw3d +Maxwell3D.planewave +BEAST.planewavevie +``` + +--- +## Time-Domain + +A plane wave in the time-domain is defined as ... + + +### API + +Some more details would be helpful here. + +```@docs +BEAST.planewave +``` \ No newline at end of file diff --git a/docs/src/geometry/flat.md b/docs/src/geometry/flat.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/src/index.md b/docs/src/index.md index 0dd395eb..71e46c99 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -6,13 +6,16 @@ via the boundary element method (BEM) and the finite element method (FEM). To this end, the (Petrov-) **Galerkin method** is employed. Currently, the focus is on equations encountered in **classical electromagnetism**, where frequency and time domain equations are covered. -Several [operators](@ref operator), basis functions, and geometry representations are implemented. +Several [operators](@ref operator), [basis functions](@ref basesRef), and geometry representations are implemented. !!! note SI units and for time-harmonic simulations a time convention of ``\mathrm{e}^{\,\mathrm{j}\omega t}`` are used everywhere. !!! tip - The code is designed such that users can also easily hack into the code and implement new features. + To use the code have a look at the [general usage](@ref usageRef). + + However, the code is designed such that users can easily hook into the code at any level and implement new features. + To do so, have a look at the [internals documentation](@ref InternalsRef) and the [contribution guidelines](@ref contribRef). Design goals are extendability and a performant execution. @@ -29,7 +32,7 @@ pkg> add BEAST --- ## Overview -The following [operators](@ref operator), basis functions, and geometry representations are implemented. +The following [operators](@ref operator), [basis functions](@ref basesRef), and geometry representations are implemented. To see details and all variations, have a look at the corresponding sections of this documentation. ### Operators diff --git a/docs/src/internals/overview.md b/docs/src/internals/overview.md index 51bcbf60..7d1924ad 100644 --- a/docs/src/internals/overview.md +++ b/docs/src/internals/overview.md @@ -1,31 +1,15 @@ -## Features - -- General framework allowing to easily add support for more kernels, finite element spaces, and excitations. -- Assembly routines that take in symbolic representations of the defining bilinear form. Support for block systems and finite element spaces defined in terms of direct products or tensor products of atomic spaces. -- LU and iterative solution of the resulting system. -- Computation of secondary quantities of interest such as the near field and the limiting far field. -- Support for space-time Galerkin and convolution quadrature approaches to the solution of time domain boundary integral equations. -- Implementation of Lagrange zeroth and first order space, Raviart-Thomas, Brezzi-Douglas-Marini, and Buffa-Christianssen vector elemenents. +# [Internals](@id InternalsRef) +- In general, the framework is designed such that it allows to easily add support for more kernels, finite element spaces, and excitations. +- Key are assembly routines that take in symbolic representations of the defining bilinear form. Support for block systems and finite element spaces defined in terms of direct products or tensor products of atomic spaces. ```@meta CurrentModule = BEAST ``` -# BEAST.jl documentation - -BEAST provides a number of types modelling concepts and a number of algorithms for the efficient and simple implementation of boundary and finite element solvers. It provides full implementations of these concepts for the LU based solution of boundary integral equations for the Maxwell and Helmholtz systems. - -Because Julia only compiles code at execution time, users of this library can hook into the code provided in this package at any level. In the extreme case it suffices to provide overwrites of the `assemble` functions. In that case, only the LU solution will be performed by the code here. - -At the other end it suffices that users only supply integration kernels that act on the element-element interaction level. This package will manage all required steps for matrix assembly. - -For the Helmholtz 2D and Maxwell 3D systems, complete implementations are supplied. These models will be discussed in detail to give a more concrete idea of the APIs provides and how to extend them. - -Central to the solution of boundary integral equations is the assembly of the system matrix. The system matrix is fully determined by specifying a kernel G, a set of trial functions, and a set of test functions. ## Basis diff --git a/docs/src/manual/efie.md b/docs/src/manual/efie.md deleted file mode 100644 index 07d9cdc8..00000000 --- a/docs/src/manual/efie.md +++ /dev/null @@ -1,11 +0,0 @@ - -# Electric Field Integral Equation (Time-Harmonic) - -Text - ---- -### Geometry - -Text - -... diff --git a/docs/src/manual/tdefie.md b/docs/src/manual/examplesTD/tdefie.md similarity index 100% rename from docs/src/manual/tdefie.md rename to docs/src/manual/examplesTD/tdefie.md diff --git a/docs/src/manual/examplesTH/efie.md b/docs/src/manual/examplesTH/efie.md new file mode 100644 index 00000000..3e2430ee --- /dev/null +++ b/docs/src/manual/examplesTH/efie.md @@ -0,0 +1,11 @@ + +# Electric Field Integral Equation + +Text + +--- +### Geometry + +Text + +... diff --git a/docs/src/manual/examplesTH/mfie.md b/docs/src/manual/examplesTH/mfie.md new file mode 100644 index 00000000..15f86992 --- /dev/null +++ b/docs/src/manual/examplesTH/mfie.md @@ -0,0 +1,4 @@ + +# Magnetic Field Integral Equation + +TODO \ No newline at end of file diff --git a/docs/src/manual/usage.md b/docs/src/manual/usage.md index 84da4998..90910391 100644 --- a/docs/src/manual/usage.md +++ b/docs/src/manual/usage.md @@ -1,5 +1,5 @@ -# General Usage +# [General Usage](@id usageRef) !!! info The fundamental approach, which applies in most cases is: @@ -55,6 +55,10 @@ assemble The linear system of equations can now, for example, be solved via the iterative GMRES solver of the [Krylov.jl](https://github.com/JuliaSmoothOptimizers/Krylov.jl) package. However, other solver could be used. Subsequently, different post-processing steps can be conducted, such as computing the scattered field from the determined expansion coefficients. + +!!! tip + Key functions for the post-processing are the [`potential`](@ref potential) and [`facecurrents`](@ref facecurrents) functions. + This is shown in the following: @@ -76,14 +80,23 @@ EF = potential(MWSingleLayerField3D(gamma=im*2.0), points, u, RT) ```@raw html

Setup             Setup

``` + +--- +## Plotting & Exporting + +Plotting details. + +Export VTK. + +... \ No newline at end of file diff --git a/docs/src/operators/planewave.md b/docs/src/operators/planewave.md deleted file mode 100644 index 085b9b60..00000000 --- a/docs/src/operators/planewave.md +++ /dev/null @@ -1,7 +0,0 @@ - -# [Plane Wave Excitation](@id planewaveEx) - -## Time-Harmonic - -## Time-Domain - diff --git a/src/maxwell/maxwell.jl b/src/maxwell/maxwell.jl index 079841c9..7bcccb3e 100644 --- a/src/maxwell/maxwell.jl +++ b/src/maxwell/maxwell.jl @@ -78,6 +78,15 @@ module Maxwell3D Mod.MWDoubleLayer3D(alpha, gamma) end + """ + planewave(; + direction = error("missing arguement `direction`"), + polarization = error("missing arguement `polarization`"), + wavenumber = error("missing arguement `wavenumber`"), + amplitude = one(real(typeof(wavenumber)))) + + Time-harmonic plane wave. + """ planewave(; direction = error("missing arguement `direction`"), polarization = error("missing arguement `polarization`"), diff --git a/src/maxwell/timedomain/mwtdexc.jl b/src/maxwell/timedomain/mwtdexc.jl index 42cff3c0..1ab074e6 100644 --- a/src/maxwell/timedomain/mwtdexc.jl +++ b/src/maxwell/timedomain/mwtdexc.jl @@ -5,7 +5,11 @@ mutable struct PlaneWaveMWTD{T,F,P} <: TDFunctional{T} amplitude::F end +""" + planewave(polarisation,direction,amplitude,speedoflight) +Time-domain plane wave. +""" function planewave(polarisation,direction,amplitude,speedoflight) PlaneWaveMWTD(direction,polarisation,speedoflight,amplitude) end diff --git a/src/volumeintegral/vieexc.jl b/src/volumeintegral/vieexc.jl index 125cb361..689edc24 100644 --- a/src/volumeintegral/vieexc.jl +++ b/src/volumeintegral/vieexc.jl @@ -13,6 +13,16 @@ mutable struct PlaneWaveVIE{T,P} <: Functional PlaneWaveVIE{T,P}(d,p,k,a) end +""" + planewavevie(; + direction = error("missing arguement `direction`"), + polarization = error("missing arguement `polarization`"), + wavenumber = error("missing arguement `wavenumber`"), + amplitude = 1, + ) + +For volume integral equations +""" planewavevie(; direction = error("missing arguement `direction`"), polarization = error("missing arguement `polarization`"), @@ -58,6 +68,14 @@ function LinearPotentialVIE_(d,a = 1) return LinearPotentialVIE{T,P}(d,a) end +""" + linearpotentialvie(; + direction = error("missing argument `direction`"), + amplitude = 1, + ) + +Linear potential for volume integral equations. +""" linearpotentialvie(; direction = error("missing argument `direction`"), amplitude = 1, From 07f2878874859cc28c452c69628583d7c12ee3d4 Mon Sep 17 00:00:00 2001 From: Bernd Hofmann Date: Wed, 13 Nov 2024 18:40:29 +0100 Subject: [PATCH 446/528] update README --- README.md | 10 +++++----- docs/src/operators/maxwelldoublelayer.md | 2 +- docs/src/operators/maxwellsinglelayer.md | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2826e005..9b427a6a 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ -[![Docs-stable](https://img.shields.io/badge/docs-latest-blue.svg)](https://krcools.github.io/BEAST.jl/stable/) -[![Docs-dev](https://img.shields.io/badge/docs-latest-blue.svg)](https://krcools.github.io/BEAST.jl/dev/) -[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/HoBeZwe/SphericalScattering.jl/blob/master/LICENSE) +[![Docs-stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://krcools.github.io/BEAST.jl/stable/) +[![Docs-dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://krcools.github.io/BEAST.jl/dev/) +[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/krcools/BEAST.jl/blob/master/LICENSE) [![CI](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml) [![codecov.io](http://codecov.io/github/krcools/BEAST.jl/coverage.svg?branch=master)](http://codecov.io/github/krcools/BEAST.jl?branch=master) [![DOI](https://zenodo.org/badge/87720391.svg)](https://zenodo.org/badge/latestdoi/87720391) @@ -26,8 +26,8 @@ Several operators, basis functions, and geometry representations are implemented ## Documentation -- Documentation for the [latest stable version](https://hobezwe.github.io/SphericalScattering.jl/stable/). -- Documentation for the [development version](https://hobezwe.github.io/SphericalScattering.jl/dev/). +- Documentation for the [latest stable version](https://krcools.github.io/BEAST.jl/stable/). +- Documentation for the [development version](https://krcools.github.io/BEAST.jl/dev/). ## Hello World diff --git a/docs/src/operators/maxwelldoublelayer.md b/docs/src/operators/maxwelldoublelayer.md index 7b75b0bd..83685609 100644 --- a/docs/src/operators/maxwelldoublelayer.md +++ b/docs/src/operators/maxwelldoublelayer.md @@ -48,7 +48,7 @@ Maxwell3D.doublelayer --- -## As Linear Form +## As Linear Map When handed to the [`potential`](@ref potential) function, the operator can be evaluated at provided points in space. Commonly, this is used in post-processing. diff --git a/docs/src/operators/maxwellsinglelayer.md b/docs/src/operators/maxwellsinglelayer.md index 6edb85ae..e4324a68 100644 --- a/docs/src/operators/maxwellsinglelayer.md +++ b/docs/src/operators/maxwellsinglelayer.md @@ -63,7 +63,7 @@ Maxwell3D.hypersingular ``` --- -## As Linear Form +## As Linear Map When handed to the [`potential`](@ref potential) function, the operator can be evaluated at provided points in space. Commonly, this is used in post-processing. From e13d43241eeedbcc7f8bb0b295de93091ee6c578 Mon Sep 17 00:00:00 2001 From: Bernd Hofmann Date: Fri, 7 Feb 2025 18:58:13 -0800 Subject: [PATCH 447/528] revert changes to build docs from master branch again --- .github/workflows/Documentation.yml | 1 - docs/make.jl | 36 ++++++++++++++--------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index c6d5799a..fda05bab 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -4,7 +4,6 @@ on: push: branches: - master # update to match your development branch (master, main, dev, trunk, ...) - - feature/docs tags: '*' pull_request: branches: diff --git a/docs/make.jl b/docs/make.jl index b4a701f1..e834fe04 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -2,22 +2,22 @@ using Documenter using DocumenterCitations using BEAST -bib = CitationBibliography(joinpath(@__DIR__, "src", "refs.bib"); style = :alpha) +bib = CitationBibliography(joinpath(@__DIR__, "src", "refs.bib"); style=:alpha) makedocs(; - modules = [BEAST], - authors = "Kristof Cools and contributors", - sitename = "BEAST.jl", - format = Documenter.HTML(; - prettyurls = get(ENV, "CI", "false") == "true", - canonical = "https://krcools.github.io/BEAST.jl", - edit_link = "master", - assets = String[], - collapselevel = 1, - sidebar_sitename = true, + modules=[BEAST], + authors="Kristof Cools and contributors", + sitename="BEAST.jl", + format=Documenter.HTML(; + prettyurls=get(ENV, "CI", "false") == "true", + canonical="https://krcools.github.io/BEAST.jl", + edit_link="master", + assets=String[], + collapselevel=1, + sidebar_sitename=true, ), - plugins = [bib], - pages = [ + plugins=[bib], + pages=[ "Introduction" => "index.md", "Manual" => Any[ "General Usage"=>"manual/usage.md", @@ -80,9 +80,9 @@ makedocs(; ) deploydocs(; - repo = "github.com/HoBeZwe/BEAST.jl.git", - target = "build", - push_preview = true, - forcepush = true, - devbranch = "feature/docs", + repo="github.com/krcools/BEAST.jl.git", + target="build", + push_preview=true, + forcepush=true, + #devbranch = "feature/docs", ) From fdd5d73a982e2b4170c0831291e505a7d88098f8 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 20 Feb 2025 15:47:22 +0100 Subject: [PATCH 448/528] quadstrat is either a function or qs object --- src/BEAST.jl | 1 + src/operator.jl | 8 +++++--- .../commonfaceoverlappingedgeqstrat.jl | 2 +- src/quadrature/doublenumqstrat.jl | 5 +++++ src/quadrature/doublenumsauterqstrat.jl | 2 +- .../doublenumwiltonbogaertqstrat.jl | 2 +- src/quadrature/doublenumwiltonsauterqstrat.jl | 11 ++++++++++ .../nonconformingintegralopqstrat.jl | 2 +- src/quadrature/quadstrats.jl | 20 ++++--------------- .../selfsauterdnumotherwiseqstrat.jl | 2 +- .../cfcvsautercewiltonpdnumqstrat.jl | 2 +- src/quadrature/strategies/quadstrat.jl | 5 +++++ .../strategies/testrefinestrialqstrat.jl | 2 +- .../strategies/trialrefinestestqstrat.jl | 2 +- src/solvers/solver.jl | 6 ++++-- test/test_assemble_refinements.jl | 3 +++ 16 files changed, 46 insertions(+), 29 deletions(-) create mode 100644 src/quadrature/strategies/quadstrat.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 879a8e94..b6d95d40 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -192,6 +192,7 @@ include("bases/tensorbasis.jl") include("operator.jl") +include("quadrature/strategies/quadstrat.jl") include("quadrature/quadstrats.jl") include("quadrature/doublenumqstrat.jl") include("quadrature/doublenumsauterqstrat.jl") diff --git a/src/operator.jl b/src/operator.jl index 3397d413..af3be944 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -92,16 +92,18 @@ Assemble the system matrix corresponding to the operator `operator` tested with function assemble(operator::AbstractOperator, test_functions, trial_functions; storage_policy = Val{:bandedstorage}, threading = Threading{:multi}, - quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) + quadstrat=defaultquadstrat) Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy) - assemble!(operator, test_functions, trial_functions, store, threading; quadstrat) + qs = defaultquadstrat(operator, test_functions, trial_functions) + assemble!(operator, test_functions, trial_functions, + store, threading; quadstrat=qs) return Z() end -function assemble(A::AbstractMatrix, testfns, trialfns) +function assemble(A::AbstractMatrix, testfns, trialfns; kwargs...) @assert numfunctions(testfns) == size(A,1) @assert numfunctions(trialfns) == size(A,2) return A diff --git a/src/quadrature/commonfaceoverlappingedgeqstrat.jl b/src/quadrature/commonfaceoverlappingedgeqstrat.jl index f7c98f6e..183223ba 100644 --- a/src/quadrature/commonfaceoverlappingedgeqstrat.jl +++ b/src/quadrature/commonfaceoverlappingedgeqstrat.jl @@ -1,4 +1,4 @@ -struct CommonFaceOverlappingEdgeQStrat{S} +struct CommonFaceOverlappingEdgeQStrat{S} <: AbstractQuadStrat conforming_qstrat::S end diff --git a/src/quadrature/doublenumqstrat.jl b/src/quadrature/doublenumqstrat.jl index 90a6d766..5282d619 100644 --- a/src/quadrature/doublenumqstrat.jl +++ b/src/quadrature/doublenumqstrat.jl @@ -1,3 +1,8 @@ +struct DoubleNumQStrat{R} <: AbstractQuadStrat + outer_rule::R + inner_rule::R +end + function quaddata(operator::IntegralOperator, local_test_basis, local_trial_basis, test_elements, trial_elements, qs::DoubleNumQStrat) diff --git a/src/quadrature/doublenumsauterqstrat.jl b/src/quadrature/doublenumsauterqstrat.jl index f869393f..02f2bafa 100644 --- a/src/quadrature/doublenumsauterqstrat.jl +++ b/src/quadrature/doublenumsauterqstrat.jl @@ -1,4 +1,4 @@ -struct DoubleNumSauterQstrat{R,S} +struct DoubleNumSauterQstrat{R,S} <: AbstractQuadStrat outer_rule::R inner_rule::R sauter_schwab_common_tetr::S diff --git a/src/quadrature/doublenumwiltonbogaertqstrat.jl b/src/quadrature/doublenumwiltonbogaertqstrat.jl index 99c97bee..f1aff891 100644 --- a/src/quadrature/doublenumwiltonbogaertqstrat.jl +++ b/src/quadrature/doublenumwiltonbogaertqstrat.jl @@ -1,4 +1,4 @@ -struct DoubleNumWiltonBogaertQStrat{R} +struct DoubleNumWiltonBogaertQStrat{R} <: AbstractQuadStrat outer_rule_far::R inner_rule_far::R outer_rule_near::R diff --git a/src/quadrature/doublenumwiltonsauterqstrat.jl b/src/quadrature/doublenumwiltonsauterqstrat.jl index 70918398..6853ce55 100644 --- a/src/quadrature/doublenumwiltonsauterqstrat.jl +++ b/src/quadrature/doublenumwiltonsauterqstrat.jl @@ -1,3 +1,14 @@ +struct DoubleNumWiltonSauterQStrat{R,S} <: AbstractQuadStrat + outer_rule_far::R + inner_rule_far::R + outer_rule_near::R + inner_rule_near::R + sauter_schwab_common_tetr::S + sauter_schwab_common_face::S + sauter_schwab_common_edge::S + sauter_schwab_common_vert::S +end + function quaddata(op::IntegralOperator, test_local_space, trial_local_space, test_charts, trial_charts, qs::DoubleNumWiltonSauterQStrat) diff --git a/src/quadrature/nonconformingintegralopqstrat.jl b/src/quadrature/nonconformingintegralopqstrat.jl index 7d0bd004..abd2c394 100644 --- a/src/quadrature/nonconformingintegralopqstrat.jl +++ b/src/quadrature/nonconformingintegralopqstrat.jl @@ -1,4 +1,4 @@ -struct NonConformingIntegralOpQStrat{S} +struct NonConformingIntegralOpQStrat{S} <: AbstractQuadStrat conforming_qstrat::S end diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index 581caf98..3a974716 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -1,23 +1,11 @@ using InteractiveUtils -struct DoubleNumWiltonSauterQStrat{R,S} - outer_rule_far::R - inner_rule_far::R - outer_rule_near::R - inner_rule_near::R - sauter_schwab_common_tetr::S - sauter_schwab_common_face::S - sauter_schwab_common_edge::S - sauter_schwab_common_vert::S -end -struct DoubleNumQStrat{R} - outer_rule::R - inner_rule::R -end -struct SauterSchwab3DQStrat{R,S} + + +struct SauterSchwab3DQStrat{R,S} <: AbstractQuadStrat outer_rule::R inner_rule::R sauter_schwab_1D::S @@ -26,7 +14,7 @@ struct SauterSchwab3DQStrat{R,S} sauter_schwab_4D::S end -struct OuterNumInnerAnalyticQStrat{R} +struct OuterNumInnerAnalyticQStrat{R} <: AbstractQuadStrat outer_rule::R end diff --git a/src/quadrature/selfsauterdnumotherwiseqstrat.jl b/src/quadrature/selfsauterdnumotherwiseqstrat.jl index 857d1b46..1cbca3a4 100644 --- a/src/quadrature/selfsauterdnumotherwiseqstrat.jl +++ b/src/quadrature/selfsauterdnumotherwiseqstrat.jl @@ -1,4 +1,4 @@ -struct SelfSauterOtherwiseDNumQStrat{R,S} +struct SelfSauterOtherwiseDNumQStrat{R,S} <: AbstractQuadStrat outer_rule::R inner_rule::R sauter_schwab_common::S diff --git a/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl b/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl index 5726e610..5e86b67c 100644 --- a/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl +++ b/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl @@ -1,4 +1,4 @@ -struct CommonFaceVertexSauterCommonEdgeWiltonPostitiveDistanceNumQStrat{R,S} +struct CommonFaceVertexSauterCommonEdgeWiltonPostitiveDistanceNumQStrat{R,S} <: AbstractQuadStrat outer_rule_far::R inner_rule_far::R outer_rule_near::R diff --git a/src/quadrature/strategies/quadstrat.jl b/src/quadrature/strategies/quadstrat.jl new file mode 100644 index 00000000..dd69c6c4 --- /dev/null +++ b/src/quadrature/strategies/quadstrat.jl @@ -0,0 +1,5 @@ +abstract type AbstractQuadStrat end + +function (qs::AbstractQuadStrat)(a, X, Y) + qs +end \ No newline at end of file diff --git a/src/quadrature/strategies/testrefinestrialqstrat.jl b/src/quadrature/strategies/testrefinestrialqstrat.jl index f36b48c6..e295c6e5 100644 --- a/src/quadrature/strategies/testrefinestrialqstrat.jl +++ b/src/quadrature/strategies/testrefinestrialqstrat.jl @@ -1,4 +1,4 @@ -struct TestRefinesTrialQStrat{S} +struct TestRefinesTrialQStrat{S} <: AbstractQuadStrat conforming_qstrat::S end diff --git a/src/quadrature/strategies/trialrefinestestqstrat.jl b/src/quadrature/strategies/trialrefinestestqstrat.jl index b7f69546..7cd70e03 100644 --- a/src/quadrature/strategies/trialrefinestestqstrat.jl +++ b/src/quadrature/strategies/trialrefinestestqstrat.jl @@ -1,4 +1,4 @@ -struct TrialRefinesTestQStrat{S} +struct TrialRefinesTestQStrat{S} <: AbstractQuadStrat conforming_qstrat::S end diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 92542222..73d6fac6 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -221,8 +221,9 @@ lift(a,I,J,U,V) = LiftedMaps.LiftedMap(a,I,J,U,V) lift(a::ConvolutionOperators.AbstractConvOp ,I,J,U,V) = ConvolutionOperators.LiftedConvOp(a, U, V, I, J) + function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; - materialize=BEAST.assemble) + materialize=BEAST.assemble, quadstrat=BEAST.defaultquadstrat) T = Int32 @assert !isempty(bf.terms) @@ -258,7 +259,8 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; end a = term.coeff * term.kernel - z = materialize(a, x, y) + # qs = quadstrat(a, x, y) + z = materialize(a, x, y; quadstrat) Smap = lift(z, Block(term.test_id), Block(term.trial_id), U, V) T = promote_type(T, eltype(Smap)) diff --git a/test/test_assemble_refinements.jl b/test/test_assemble_refinements.jl index 04d7ef60..1e73f245 100644 --- a/test/test_assemble_refinements.jl +++ b/test/test_assemble_refinements.jl @@ -28,6 +28,9 @@ Kop = Maxwell3D.doublelayer(wavenumber=0.0) qs = BEAST.defaultquadstrat(Kop, X, Y) qs = BEAST.DoubleNumWiltonSauterQStrat(1, 1, 12, 13, 12, 12, 12, 12) +@show qs +@show qs(Kop, X, Y) +@show qs(Kop, Y, X) K1 = assemble(Kop, X, Y; quadstrat=qs) K2 = assemble(Kop, Y, X; quadstrat=qs) From 7a75c86d4e1a0b0b084b70b768833d4b42a078f6 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 21 Feb 2025 15:27:12 +0100 Subject: [PATCH 449/528] quadstrat can be passed as a function - quadstrat can be set in local scope - quadstrat setting for complicated bilinear forms is now tractable --- src/operator.jl | 2 +- src/quadrature/quadstrats.jl | 2 +- test/test_assemble_refinements.jl | 3 +++ test/test_quadstrat_as_fn.jl | 25 +++++++++++++++++++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 test/test_quadstrat_as_fn.jl diff --git a/src/operator.jl b/src/operator.jl index af3be944..95aea633 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -96,7 +96,7 @@ function assemble(operator::AbstractOperator, test_functions, trial_functions; Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy) - qs = defaultquadstrat(operator, test_functions, trial_functions) + qs = quadstrat(operator, test_functions, trial_functions) assemble!(operator, test_functions, trial_functions, store, threading; quadstrat=qs) return Z() diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index 3a974716..ec9f2e58 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -45,7 +45,7 @@ macro defaultquadstrat(dop, body) error("@defaultquadstrat expects a first argument of the for (op,tfs,bfs) or (linform,tfs)") end -struct SingleNumQStrat{R} +struct SingleNumQStrat{R} <: AbstractQuadStrat quad_rule::R end diff --git a/test/test_assemble_refinements.jl b/test/test_assemble_refinements.jl index 1e73f245..294b5a7f 100644 --- a/test/test_assemble_refinements.jl +++ b/test/test_assemble_refinements.jl @@ -34,6 +34,9 @@ qs = BEAST.DoubleNumWiltonSauterQStrat(1, 1, 12, 13, 12, 12, 12, 12) K1 = assemble(Kop, X, Y; quadstrat=qs) K2 = assemble(Kop, Y, X; quadstrat=qs) +@show K1[1,1] +@show K2[1,1] + K1[1,1] - K2[1,1] @test K1[1,1] ≈ K2[1,1] rtol=1e-7 diff --git a/test/test_quadstrat_as_fn.jl b/test/test_quadstrat_as_fn.jl new file mode 100644 index 00000000..b46ca0bf --- /dev/null +++ b/test/test_quadstrat_as_fn.jl @@ -0,0 +1,25 @@ +@testitem "quadstrat as function" begin + using CompScienceMeshes + using LinearAlgebra + + pd = dirname(pathof(BEAST)) + fn = joinpath(pd, "../test/assets/sphere45.in") + Γ = CompScienceMeshes.readmesh(fn) + @show length(Γ) + + X = raviartthomas(Γ) + @show numfunctions(X) + t = Maxwell3D.singlelayer(wavenumber=1.0) + + qsp(p) = (op, X, Y) -> BEAST.DoubleNumWiltonSauterQStrat(2+p, 3+p, 6+p, 7+p, 5+p, 5+p, 4+p, 3+p) + Z = map(0:3) do p + assemble(t, X, X; quadstrat=qsp(p)) + end + + W = map(0:3) do p + qs = qsp(p)(t, X, X) + assemble(t, X, X; quadstrat=qs) + end + + @test all(Z .≈ W) +end \ No newline at end of file From 1b2f669efa2d9f0365c796f88b2402cad0be3f4d Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 25 Feb 2025 16:45:07 +0100 Subject: [PATCH 450/528] quadstrat as function works for linear combos --- src/localop.jl | 8 ++++++-- src/operator.jl | 14 +++++++++----- test/test_quadstrat_as_fn.jl | 22 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/localop.jl b/src/localop.jl index a169ae7d..419c034b 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -59,8 +59,10 @@ end function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, threading::Type{Threading{:multi}}; - quadstrat=defaultquadstrat(biop, tfs, bfs)) + quadstrat=defaultquadstrat) + quadstrat = quadstrat(biop, tfs, bfs) + if geometry(tfs) == geometry(bfs) return assemble_local_matched!(biop, tfs, bfs, store; quadstrat) end @@ -74,7 +76,9 @@ end function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, threading::Type{Threading{:single}}; - quadstrat=defaultquadstrat(biop, tfs, bfs)) + quadstrat=defaultquadstrat) + + quadstrat = quadstrat(biop, tfs, bfs) if geometry(tfs) == geometry(bfs) return assemble_local_matched!(biop, tfs, bfs, store; quadstrat) diff --git a/src/operator.jl b/src/operator.jl index 95aea633..22ea7c6f 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -96,9 +96,9 @@ function assemble(operator::AbstractOperator, test_functions, trial_functions; Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy) - qs = quadstrat(operator, test_functions, trial_functions) + # qs = quadstrat(operator, test_functions, trial_functions) assemble!(operator, test_functions, trial_functions, - store, threading; quadstrat=qs) + store, threading; quadstrat) return Z() end @@ -182,7 +182,9 @@ end function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, threading::Type{Threading{:multi}}; - quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) + quadstrat=defaultquadstrat) + + quadstrat = quadstrat(operator, test_functions, trial_functions) P = Threads.nthreads() numchunks = P @@ -199,8 +201,9 @@ end end function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, threading::Type{Threading{:single}}; - quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) + quadstrat=defaultquadstrat) + quadstrat = quadstrat(operator, test_functions, trial_functions) assemblechunk!(operator, test_functions, trial_functions, store; quadstrat) end @@ -219,8 +222,9 @@ function assemble!(op::LinearCombinationOfOperators, tfs::AbstractSpace, bfs::Ab store, threading = Threading{:multi}; quadstrat=defaultquadstrat(op, tfs, bfs)) - for (a,A,qs) in zip(op.coeffs, op.ops, quadstrat) + for (a,A) in zip(op.coeffs, op.ops) store1(v,m,n) = store(a*v,m,n) + qs = quadstrat(A, tfs, bfs) assemble!(A, tfs, bfs, store1, threading; quadstrat=qs) end end diff --git a/test/test_quadstrat_as_fn.jl b/test/test_quadstrat_as_fn.jl index b46ca0bf..0d056b8e 100644 --- a/test/test_quadstrat_as_fn.jl +++ b/test/test_quadstrat_as_fn.jl @@ -22,4 +22,26 @@ end @test all(Z .≈ W) +end + +@testitem "quadstrat for linear combinations" begin + using CompScienceMeshes + using LinearAlgebra + + pd = dirname(pathof(BEAST)) + fn = joinpath(pd, "../test/assets/sphere45.in") + Γ = CompScienceMeshes.readmesh(fn) + @show length(Γ) + + X = raviartthomas(Γ) + @show numfunctions(X) + t = Maxwell3D.singlelayer(wavenumber=1.0) + k = Maxwell3D.doublelayer(wavenumber=1.0) + + qsp(p) = (op, X, Y) -> BEAST.DoubleNumWiltonSauterQStrat(2+p, 3+p, 6+p, 7+p, 5+p, 5+p, 4+p, 3+p) + + a = t + k + qsp0 = qsp(0) + Z = assemble(a, X, X; quadstrat=qsp0) + # @which assemble(a, X, X; quadstrat=qsp0) end \ No newline at end of file From 41511b4ebb1c1bcef8aae2deb1d585d35a4213b9 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 28 Feb 2025 19:21:26 +0100 Subject: [PATCH 451/528] add docs for quadstrat and variational forms --- docs/make.jl | 2 + docs/src/manual/bilinear.md | 75 ++++++++++++++++++++++ docs/src/manual/quadstrat.md | 119 +++++++++++++++++++++++++++++++++++ docs/src/manual/usage.md | 2 +- 4 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 docs/src/manual/bilinear.md create mode 100644 docs/src/manual/quadstrat.md diff --git a/docs/make.jl b/docs/make.jl index e834fe04..38121ec2 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -21,6 +21,7 @@ makedocs(; "Introduction" => "index.md", "Manual" => Any[ "General Usage"=>"manual/usage.md", + "Customising Quadrature Rules" => "manual/quadstrat.md", "Application Examples"=>Any[ "Time-Harmonic"=>Any[ "EFIE"=>"manual/examplesTH/efie.md", @@ -28,6 +29,7 @@ makedocs(; ], "Time-Domain"=>Any["EFIE"=>"manual/examplesTD/tdefie.md"], ], + "System of Equations and Bilinear Forms" => "manual/bilinear.md", ], "Operators & Excitations" => Any[ "Overview"=>"operators/overview.md", diff --git a/docs/src/manual/bilinear.md b/docs/src/manual/bilinear.md new file mode 100644 index 00000000..0cbbe8d6 --- /dev/null +++ b/docs/src/manual/bilinear.md @@ -0,0 +1,75 @@ +# Systems of boundary integral equations and bilinear forms + +For the most simple variational formulations such as the EFIE and MFIE, the boundary element matrices and right hand sided can be manually assembled. + +For more complex formulations, such as those encountered in solving the transmission problem and in problems involving composite systems, this approach quickly becomes unwieldy. To facilitate the formulation and solution of these problems, BEAST.jl provides the ability to define quite general linear and bilinear forms. + +## The single body transmission problem + +THe transmission problem is defined by the material properties of the exterior and interior domain and the incident field: + +```@example transmission +using CompScienceMeshes +using BEAST + +κ1 = 1.0 +κ2 = 2.0 + +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ1) +H = -1/(im*κ1)*curl(E) +nothing # hide +``` + +The PMCHWT can be used to model this transmission problem. To write down the variational formulation, pairs of placeholders for the trial functions and test functions are declared by the `@hilbertspace` macro. + +```@example transmission +T1 = Maxwell3D.singlelayer(wavenumber=κ1) +K1 = Maxwell3D.doublelayer(wavenumber=κ1) +T2 = Maxwell3D.singlelayer(wavenumber=κ2) +K2 = Maxwell3D.doublelayer(wavenumber=κ2) + +e = (n × E) × n +h = (n × H) × n + +@hilbertspace j m +@hilbertspace k l + +a = + (T1+T2)[k,j] + (-K1-K2)[k,m] + + (K1+K2)[l,j] + (T1+T2)[l,m] +b = + -e[k] -h[l] +``` + +Note that we are able to define the formulation without specifying or constructing the mesh, the boundary element spaces, or the system matrices. The above constitutes a lazy representation of the formulation that takes neglible time to construct! + +Now this formulation is applied to a concrete geometry. + +```@example transmission +Γ = meshsphere(;radius=1.0, h=0.35) +RT = raviartthomas(Γ) + +X = BEAST.DirectProductSpace([RT,RT]) + +bx = assemble(b, X) +Axx = assemble(a, X, X) +``` + +The type of `Axx` reveals that the structure of the underlying Hilbert space (with two generators in this case) is preserved. Also noteworthy is that only non-zero blocks in the system matrix are stored. This is important both for to limit memory consumption and to avoid unnecessary computations in performing the matrix-vector product. + +BEAST.jl provides wrappers for iterative solvers that conform to the `LinearMaps.jl` interface. For example, `SXX = GMRESSolver(Axx)` acts like a `LinearMap` representing the inverse of `Axx`. Computing the action `uX = SXX * bx` runs the Krylov iterative process with right hand side `bx` and returns the solution to the linear system: + +```@example transmission +SXX = BEAST.GMRESSolver(Axx) +uX = SXX * bx +typeof(uX) +``` + +The solution of this iterative process *remembers* its block structure. This makes it easy to select the electric and magnetic components: + +```@example transmission +using LinearAlgebra +println(norm(uX[m])) +println(norm(uX[j])) +nothing # hide +``` \ No newline at end of file diff --git a/docs/src/manual/quadstrat.md b/docs/src/manual/quadstrat.md new file mode 100644 index 00000000..bdd12a1c --- /dev/null +++ b/docs/src/manual/quadstrat.md @@ -0,0 +1,119 @@ +# Customising the choice of quadrature rules + +At the heart of any boundary element code are routines that can compute matrix entries that take on the form of double integrals over pairs of panels of an integrand that is the product of a test function, a trial function, and the fundamental solution of the equation under study. The fundamental solution is singular at the origin, implying that simple quadrature methods are not sufficient to compute the entries to satisfactory accuracy. A mechanism to activate the most appropriate rule is required. BEAST.jl attempts to offer a reasonable default, but the advanced user may wish to take control beyond this. + +This page describes how the user can intervene in this system. + +## List of implemented quadrature strategies + +The algorithm that determines which quadrature rule is used for any given geometric constellation of interacting panels is called the quadrature strategy. The list of strategies currently implemented in BEAST is: + +```@example introductory +using TypeTree +using BEAST + +print(join(tt(BEAST.AbstractQuadStrat), "")) +nothing # hide +``` + +The type of the quadrature strategy object determines the quadrature rule selection algorithm. The value of the fields contained by the quadrature strategy object typically selects the order or number of quadrature points used by the various possible quadrature rules. + +The methods responsible for caching quadrature related data and quadrature rule selection are named `quaddata` and `quadrule`, respectively. For a given combination of a discrete boundary integral operator and quadrature strategy, the methods that will be dispatched to can be queried as follows: + +```@example introductory +using CompScienceMeshes +using BEAST + +Γ = meshsphere(radius=1.0, h=0.45) # triangulate sphere of radius one +RT = raviartthomas(Γ) +𝑇 = Maxwell3D.singlelayer(wavenumber=2.0) +qs = BEAST.DoubleNumWiltonSauterQStrat(6, 7, 7, 8, 6, 6, 6, 6) +``` + +```@example introductory +BEAST.quadinfo(𝑇, RT, RT; quadstrat=qs) +nothing # hide +``` + +## Explicitly providing the quadrature strategy + +The assembly function takes a keyword argument that allows to specify a specific quadrature strategy to use. + +```@example introductory +using CompScienceMeshes +using BEAST + +Γ = meshsphere(radius=1.0, h=0.45) # triangulate sphere of radius one +RT = raviartthomas(Γ) +𝑇 = Maxwell3D.singlelayer(wavenumber=2.0) + +Z1 = assemble(𝑇, RT, RT; quadstrat=BEAST.DoubleNumWiltonSauterQStrat(2, 3, 6, 7, 5, 5, 5, 5)) +Z2 = assemble(𝑇, RT, RT; quadstrat=BEAST.DoubleNumWiltonSauterQStrat(6, 7, 7, 8, 6, 6, 6, 6)) + +using LinearAlgebra +2 * norm(Z1-Z2) / norm(Z1+Z2) +``` + +This method works well when only a single integral operator appears in the boundary integral equation. For systems containing multiple equations or when linear combinations of different operators appear, specifying a single quadrature strategy at the callsite of `assemble` will likely not be appropriate. + + +## Setting the default quadrature rule + +To query the set default quadrature strategy for a triple `(op, testfns, trialfns)`, + +```@example introductory +using CompScienceMeshes +using BEAST + +Γ = meshsphere(radius=1.0, h=0.45) # triangulate sphere of radius one +RT = raviartthomas(Γ) +𝑇 = Maxwell3D.singlelayer(wavenumber=2.0) + +BEAST.defaultquadstrat(𝑇, RT, RT) +``` + +A new default can be set using the `@defaultquadstrat` macro. This creates a new method for the function `defaultquadstrat` that will be dispatched to for the arguments of the provided types. + +```@example introductory +BEAST.@defaultquadstrat (𝑇, RT, RT) BEAST.DoubleNumWiltonSauterQStrat(6, 7, 7, 8, 6, 6, 6, 6) +Z2 = assemble(𝑇, RT, RT) + +BEAST.@defaultquadstrat (𝑇, RT, RT) BEAST.DoubleNumWiltonSauterQStrat(2, 3, 6, 7, 5, 5, 5, 5) +Z1 = assemble(𝑇, RT, RT) + +using LinearAlgebra +2 * norm(Z1-Z2) / norm(Z1+Z2) +``` + +The above number provides some insight into the accuracy of the selected quadrature strategy. The advantage of setting the default quadrature strategy like this is that it is global and will automatically affect all subsequent calls to assembly. This means the set strategy will be used even if assembly is called as part of the assembly of a more complicated larger system, potentially containing linear combinations of integral operators. + +The downside is that the default can only be set for concrete types of `(op, testfns, trialfns)` and that the call to the macro needs to be at Module scope. This makes it less appealing for complicated simulations and sweeps over the quadrature accuracy. + +# Specifying a quadrature strategy selection method + +This method is somehwat more involved, but is the most general and should allow for full control of the quadrature rules used in assembly. + +```@example introductory +function myquadstrat1(op, testfns, trialfns) + if op isa BEAST.MWSingleLayer3D + return BEAST.DoubleNumWiltonSauterQStrat(2, 3, 6, 7, 5, 5, 5, 5) + end + return BEAST.defaultquadstrat(op, testfns, trialfns) +end + +function myquadstrat2(op, testfns, trialfns) + if op isa BEAST.MWSingleLayer3D + return BEAST.DoubleNumWiltonSauterQStrat(6, 7, 7, 8, 6, 6, 6, 6) + end + return BEAST.defaultquadstrat(op, testfns, trialfns) +end + + +Z1 = assemble(𝑇, RT, RT; quadstrat=myquadstrat1) +Z3 = assemble(𝑇, RT, RT; quadstrat=myquadstrat2) +2 * norm(Z1-Z2) / norm(Z1+Z2) +``` + +Best practice is too return `BEAST.defaultquadstrat(op, testnfs, trialfns)` by default to ensure that all operators are supported, also those for which no explicit overwrite is specified. + + diff --git a/docs/src/manual/usage.md b/docs/src/manual/usage.md index 90910391..625a2150 100644 --- a/docs/src/manual/usage.md +++ b/docs/src/manual/usage.md @@ -31,7 +31,7 @@ RT = raviartthomas(Γ) # define basis functions # --- 3. compute the RHS and system matrix e = assemble(𝑒, RT) # assemble RHS T = assemble(𝑇, RT, RT) # assemble system matrix -typeof(T) #hide +nothing #hide ``` --- From 7d01d413cbf9a7d8af08270f4189762eab9ca67f Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 28 Feb 2025 20:05:50 +0100 Subject: [PATCH 452/528] Calderon for PMCHWT added to docs --- docs/Manifest.toml | 242 +++++++++++++++++++----------------- docs/src/manual/bilinear.md | 52 +++++++- 2 files changed, 176 insertions(+), 118 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 83f451f8..b2f1dd7a 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.11.1" +julia_version = "1.11.3" manifest_format = "2.0" project_hash = "c93b20c7adfdce7dcd32bda66638d598d39160a5" @@ -34,9 +34,9 @@ version = "1.1.2" [[deps.ArrayLayouts]] deps = ["FillArrays", "LinearAlgebra"] -git-tree-sha1 = "492681bc44fac86804706ddb37da10880a2bd528" +git-tree-sha1 = "4e25216b8fea1908a0ce0f5d87368587899f75be" uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "1.10.4" +version = "1.11.1" weakdeps = ["SparseArrays"] [deps.ArrayLayouts.extensions] @@ -48,7 +48,7 @@ version = "1.11.0" [[deps.BEAST]] deps = ["AbstractTrees", "BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "ConvolutionOperators", "Distributed", "ExtendableSparse", "FFTW", "FastGaussQuadrature", "FillArrays", "Infiltrator", "InteractiveUtils", "IterativeSolvers", "LiftedMaps", "LinearAlgebra", "LinearMaps", "NestedUnitRanges", "Requires", "SauterSchwab3D", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SpecialFunctions", "StaticArrays", "TestItems", "WiltonInts84"] -git-tree-sha1 = "757efc563022d39e95f751b7e5a29d020a57e049" +path = "C:\\Users\\krcools\\.julia\\dev\\BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" version = "2.5.0" @@ -80,10 +80,10 @@ uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" version = "0.16.43" [[deps.Bzip2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "8873e196c2eb87962a2048b3b8e08946535864a1" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1b96ea4a01afe0ea4090c5c8039690672dd13f2e" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+2" +version = "1.0.9+0" [[deps.Cairo_jll]] deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] @@ -99,9 +99,9 @@ version = "0.2.1" [[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "bce6804e5e6044c6daab27bb533d1295e4a2e759" +git-tree-sha1 = "962834c22b66e32aa10f7611c08c8ca4e20749a9" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.6" +version = "0.7.8" [[deps.CollisionDetection]] deps = ["Compat", "LinearAlgebra", "StaticArrays"] @@ -188,39 +188,41 @@ version = "1.6.0" [[deps.Expat_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7" +git-tree-sha1 = "d55dffd9ae73ff72f1c0482454dcf2ec6c6c4a63" uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.6.2+0" +version = "2.6.5+0" [[deps.ExtendableSparse]] deps = ["DocStringExtensions", "ILUZero", "LinearAlgebra", "Printf", "SparseArrays", "Sparspak", "StaticArrays", "SuiteSparse", "Test"] -git-tree-sha1 = "426576ad51731a375042a0887a0c9761d95ed00b" +git-tree-sha1 = "d70aad52f10c8fc945873ad7a8bd4a1a285c5d99" uuid = "95c220a8-a1cf-11e9-0c77-dbfce5f500b3" -version = "1.5.3" +version = "1.7.0" [deps.ExtendableSparse.extensions] ExtendableSparseAMGCLWrapExt = "AMGCLWrap" ExtendableSparseAlgebraicMultigridExt = "AlgebraicMultigrid" ExtendableSparseIncompleteLUExt = "IncompleteLU" + ExtendableSparseLinearSolveExt = "LinearSolve" ExtendableSparsePardisoExt = "Pardiso" [deps.ExtendableSparse.weakdeps] AMGCLWrap = "4f76b812-4ba5-496d-b042-d70715554288" AlgebraicMultigrid = "2169fc97-5a83-5252-b627-83903c6c433c" IncompleteLU = "40713840-3770-5561-ab4c-a76e7d0d7895" + LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" [[deps.FFTW]] deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] -git-tree-sha1 = "4820348781ae578893311153d69049a93d05f39d" +git-tree-sha1 = "7de7c78d681078f027389e067864a8d53bd7c3c9" uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" -version = "1.8.0" +version = "1.8.1" [[deps.FFTW_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "4d81ed14783ec49ce9f2e168208a12ce1815aa25" uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" -version = "3.3.10+1" +version = "3.3.10+3" [[deps.FLTK_jll]] deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] @@ -236,9 +238,15 @@ version = "1.0.2" [[deps.FileIO]] deps = ["Pkg", "Requires", "UUIDs"] -git-tree-sha1 = "62ca0547a14c57e98154423419d8a342dca75ca9" +git-tree-sha1 = "2dd20384bf8c6d411b5c7370865b1e9b26cb2ea3" uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -version = "1.16.4" +version = "1.16.6" + + [deps.FileIO.extensions] + HTTPExt = "HTTP" + + [deps.FileIO.weakdeps] + HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" [[deps.FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" @@ -262,15 +270,15 @@ version = "1.13.0" [[deps.Fontconfig_jll]] deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] -git-tree-sha1 = "db16beca600632c95fc8aca29890d83788dd8b23" +git-tree-sha1 = "21fac3c77d7b5a9fc03b0ec503aa1a6392c34d2b" uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.96+0" +version = "2.15.0+0" [[deps.FreeType2_jll]] deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc" +git-tree-sha1 = "786e968a8d2fb167f2e4880baba62e0e26bd8e4e" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.13.2+0" +version = "2.13.3+1" [[deps.GLU_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] @@ -297,15 +305,15 @@ version = "1.3.1" [[deps.Git_jll]] deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "ea372033d09e4552a04fd38361cd019f9003f4f4" +git-tree-sha1 = "399f4a308c804b446ae4c91eeafadb2fe2c54ff9" uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" -version = "2.46.2+0" +version = "2.47.1+0" [[deps.Glib_jll]] deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "674ff0db93fffcd11a3573986e550d66cd4fd71f" +git-tree-sha1 = "b0036b392358c80d2d2124746c2bf3d48d457938" uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.80.5+0" +version = "2.82.4+0" [[deps.GmshTools]] deps = ["Libdl", "gmsh_jll"] @@ -327,9 +335,9 @@ version = "1.14.3+3" [[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "dd3b49277ec2bb2c6b94eb1604d4d0616016f7a6" +git-tree-sha1 = "f93a9ce66cd89c9ba7a4695a47fd93b4c6bc59fa" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" -version = "2.11.2+0" +version = "2.12.0+0" [[deps.ILUZero]] deps = ["LinearAlgebra", "SparseArrays"] @@ -345,15 +353,15 @@ version = "0.2.5" [[deps.Infiltrator]] deps = ["InteractiveUtils", "Markdown", "REPL", "UUIDs"] -git-tree-sha1 = "38298a8eabe09e49e6f60927c9e1ca3481688ba0" +git-tree-sha1 = "139299acf639f1bbfecd3058f116e91e212d359a" uuid = "5903a43b-9cc3-4c30-8d17-598619ec4e9b" -version = "1.8.3" +version = "1.8.6" [[deps.IntelOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] -git-tree-sha1 = "10bd689145d2c3b2a9844005d01087cc1194e79e" +git-tree-sha1 = "0f14a5456bdc6b9731a5682f439a672750a09e48" uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2024.2.1+0" +version = "2025.0.4+0" [[deps.InteractiveUtils]] deps = ["Markdown"] @@ -361,9 +369,9 @@ uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" version = "1.11.0" [[deps.IrrationalConstants]] -git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" +git-tree-sha1 = "e2222959fbc6c19554dc15174c81bf7bf3aa691c" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.2.2" +version = "0.2.4" [[deps.IterativeSolvers]] deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] @@ -373,9 +381,9 @@ version = "0.9.4" [[deps.JLLWrappers]] deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "be3dc50a92e5a386872a493a10050136d4703f9b" +git-tree-sha1 = "a007feb38b422fbdab534406aeca1b86823cb4d6" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.6.1" +version = "1.7.0" [[deps.JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] @@ -403,9 +411,9 @@ version = "1.4.1" [[deps.JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c" +git-tree-sha1 = "eac1206917768cb54957c65a615460d87b455fc1" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.0.4+0" +version = "3.1.1+0" [[deps.Krylov]] deps = ["LinearAlgebra", "Printf", "SparseArrays"] @@ -421,9 +429,9 @@ version = "18.1.7+0" [[deps.LZO_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "854a9c268c43b77b0a27f22d7fab8d33cdb3a731" +git-tree-sha1 = "1c602b1127f4751facb671441ca72715cc95938a" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.2+1" +version = "2.10.3+0" [[deps.LazilyInitializedFields]] git-tree-sha1 = "0f2da712350b020bc3957f269c9caad516383ee0" @@ -466,9 +474,9 @@ version = "1.11.0" [[deps.Libffi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" +git-tree-sha1 = "27ecae93dd25ee0909666e6835051dd684cc035e" uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.2.2+1" +version = "3.2.2+2" [[deps.Libgcrypt_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] @@ -477,34 +485,34 @@ uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" version = "1.11.0+0" [[deps.Libglvnd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] -git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll", "Xorg_libXext_jll"] +git-tree-sha1 = "ff3b4b9d35de638936a525ecd36e86a8bb919d11" uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" -version = "1.6.0+0" +version = "1.7.0+0" [[deps.Libgpg_error_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "c6ce1e19f3aec9b59186bdf06cdf3c4fc5f5f3e6" +git-tree-sha1 = "df37206100d39f79b3376afb6b9cee4970041c61" uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.50.0+0" +version = "1.51.1+0" [[deps.Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "61dfdba58e585066d8bce214c5a51eaa0539f269" +git-tree-sha1 = "be484f5c92fad0bd8acfef35fe017900b0b73809" uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.17.0+1" +version = "1.18.0+0" [[deps.Libmount_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "0c4f9c4f1a50d8f35048fa0532dabbadf702f81e" +git-tree-sha1 = "89211ea35d9df5831fca5d33552c02bd33878419" uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.40.1+0" +version = "2.40.3+0" [[deps.Libuuid_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807" +git-tree-sha1 = "e888ad02ce716b319e6bdb985d2ef300e7089889" uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.40.1+0" +version = "2.40.3+0" [[deps.LiftedMaps]] deps = ["LinearAlgebra", "LinearMaps"] @@ -525,9 +533,9 @@ version = "5.0.0+0" [[deps.LinearMaps]] deps = ["LinearAlgebra"] -git-tree-sha1 = "ee79c3208e55786de58f8dcccca098ced79f743f" +git-tree-sha1 = "7f6be2e4cdaaf558623d93113d6ddade7b916209" uuid = "7a12625a-238d-50fd-b39a-03d52299707e" -version = "3.11.3" +version = "3.11.4" [deps.LinearMaps.extensions] LinearMapsChainRulesCoreExt = "ChainRulesCore" @@ -541,9 +549,9 @@ version = "3.11.3" [[deps.LogExpFunctions]] deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" +git-tree-sha1 = "13ca9e2586b89836fd20cccf56e57e2b9ae7f38f" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.28" +version = "0.3.29" [deps.LogExpFunctions.extensions] LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" @@ -560,16 +568,16 @@ uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" version = "1.11.0" [[deps.METIS_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "1fd0a97409e418b78c53fac671cf4622efdf0f21" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "2eefa8baa858871ae7770c98c3c2a7e46daba5b4" uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" -version = "5.1.2+0" +version = "5.1.3+0" [[deps.MKL_jll]] deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] -git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f" +git-tree-sha1 = "5de60bc6cb3899cd318d80d627560fae2e2d99ae" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2024.2.0+0" +version = "2025.0.1+1" [[deps.MMG_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] @@ -579,9 +587,9 @@ version = "5.6.0+0" [[deps.MPICH_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "7715e65c47ba3941c502bffb7f266a41a7f54423" +git-tree-sha1 = "e7159031670cee777cc2840aef7a521c3603e36c" uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" -version = "4.2.3+0" +version = "4.3.0+0" [[deps.MPIPreferences]] deps = ["Libdl", "Preferences"] @@ -591,9 +599,9 @@ version = "0.1.11" [[deps.MPItrampoline_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "70e830dab5d0775183c99fc75e4c24c614ed7142" +git-tree-sha1 = "97aac4a518b6f01851f8821272780e1ba56fe90d" uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" -version = "5.5.1+0" +version = "5.5.2+0" [[deps.Markdown]] deps = ["Base64"] @@ -613,9 +621,9 @@ version = "2.28.6+0" [[deps.MicrosoftMPI_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f12a29c4400ba812841c6ace3f4efbb6dbb3ba01" +git-tree-sha1 = "bc95bf4149bf535c09602e3acdf950d9b4376227" uuid = "9237b28f-5490-5468-be7b-bb81f5f5e6cf" -version = "10.1.4+2" +version = "10.1.4+3" [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" @@ -642,9 +650,9 @@ uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" version = "7.7.2+0" [[deps.OffsetArrays]] -git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e" +git-tree-sha1 = "5e1897147d1ff8d98883cda2be2187dcf57d8f0c" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.14.1" +version = "1.15.0" [deps.OffsetArrays.extensions] OffsetArraysAdaptExt = "Adapt" @@ -670,20 +678,20 @@ version = "4.1.6+0" [[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10" +git-tree-sha1 = "a9697f1d06cc3eb3fb3ad49cc67f2cfabaac31ea" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.15+1" +version = "3.0.16+0" [[deps.OpenSpecFun_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1346c9208249809840c91b26703912dff463d335" uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.5+0" +version = "0.5.6+0" [[deps.OrderedCollections]] -git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" +git-tree-sha1 = "cc4054e898b852042d7b503313f7ad03de99c3dd" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.6.3" +version = "1.8.0" [[deps.PCRE2_jll]] deps = ["Artifacts", "Libdl"] @@ -698,9 +706,9 @@ version = "2.8.1" [[deps.Permutations]] deps = ["Combinatorics", "LinearAlgebra", "Random"] -git-tree-sha1 = "f92b0a7b722b1ecfd5c0d77a7eda24b4eea5c56a" +git-tree-sha1 = "b1f03a4943c62552a12c7f95965b76c3f91cf5b7" uuid = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" -version = "0.4.22" +version = "0.4.23" [[deps.Pixman_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] @@ -821,9 +829,9 @@ version = "0.3.9" [[deps.SpecialFunctions]] deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" +git-tree-sha1 = "64cca0c26b4f31ba18f13f6c12af7c85f478cfde" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.4.0" +version = "2.5.0" [deps.SpecialFunctions.extensions] SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" @@ -833,9 +841,9 @@ version = "2.4.0" [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "777657803913ffc7e8cc20f0fd04b634f871af8f" +git-tree-sha1 = "e3be13f448a43610f978d29b7adf78c76022467a" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.8" +version = "1.9.12" [deps.StaticArrays.extensions] StaticArraysChainRulesCoreExt = "ChainRulesCore" @@ -922,51 +930,51 @@ version = "1.11.0" [[deps.WiltonInts84]] deps = ["LinearAlgebra"] -git-tree-sha1 = "9d61cac63c100e936194e5db8613e33572fd2bb8" +git-tree-sha1 = "30444f863e76cc609b52d5b8e3e047acd7deaccf" uuid = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" -version = "0.2.5" +version = "0.2.8" [[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "6a451c6f33a176150f315726eba8b92fbfdb9ae7" +git-tree-sha1 = "b8b243e47228b4a3877f1dd6aee0c5d56db7fcf4" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.13.4+0" +version = "2.13.6+1" [[deps.XSLT_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"] -git-tree-sha1 = "a54ee957f4c86b526460a720dbc882fa5edcbefc" +git-tree-sha1 = "7d1671acbe47ac88e981868a078bd6b4e27c5191" uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.41+0" +version = "1.1.42+0" [[deps.Xorg_libX11_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] -git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" +git-tree-sha1 = "9dafcee1d24c4f024e7edc92603cedba72118283" uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.8.6+0" +version = "1.8.6+3" [[deps.Xorg_libXau_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" +git-tree-sha1 = "e9216fdcd8514b7072b43653874fd688e4c6c003" uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.11+0" +version = "1.0.12+0" [[deps.Xorg_libXdmcp_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" +git-tree-sha1 = "89799ae67c17caa5b3b5a19b8469eeee474377db" uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.4+0" +version = "1.1.5+0" [[deps.Xorg_libXext_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "d2d1a5c49fae4ba39983f63de6afcbea47194e85" +git-tree-sha1 = "d7155fea91a4123ef59f42c4afb5ab3b4ca95058" uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.6+0" +version = "1.3.6+3" [[deps.Xorg_libXfixes_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "6fcc21d5aea1a0b7cce6cab3e62246abd1949b86" uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" -version = "5.0.3+4" +version = "6.0.0+0" [[deps.Xorg_libXft_jll]] deps = ["Fontconfig_jll", "Libdl", "Pkg", "Xorg_libXrender_jll"] @@ -975,34 +983,34 @@ uuid = "2c808117-e144-5220-80d1-69d4eaa9352c" version = "2.3.3+1" [[deps.Xorg_libXinerama_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] -git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll"] +git-tree-sha1 = "a1a7eaf6c3b5b05cb903e35e8372049b107ac729" uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" -version = "1.1.4+4" +version = "1.1.5+0" [[deps.Xorg_libXrender_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "47e45cd78224c53109495b3e324df0c37bb61fbe" +git-tree-sha1 = "a490c6212a0e90d2d55111ac956f7c4fa9c277a6" uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.11+0" +version = "0.9.11+1" [[deps.Xorg_libpthread_stubs_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9" +git-tree-sha1 = "c57201109a9e4c0585b208bb408bc41d205ac4e9" uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" -version = "0.1.1+0" +version = "0.1.2+0" [[deps.Xorg_libxcb_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "bcd466676fef0878338c61e655629fa7bbc69d8e" +git-tree-sha1 = "1a74296303b6524a0472a8cb12d3d87a78eb3612" uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.17.0+0" +version = "1.17.0+3" [[deps.Xorg_xtrans_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77" +git-tree-sha1 = "6dba04dbfb72ae3ebe5418ba33d087ba8aa8cb00" uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.5.0+0" +version = "1.5.1+0" [[deps.YAML]] deps = ["Base64", "Dates", "Printf", "StringEncodings"] @@ -1023,9 +1031,9 @@ version = "4.13.1+0" [[deps.libaec_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "46bf7be2917b59b761247be3f317ddf75e50e997" +git-tree-sha1 = "f5733a5a9047722470b95a81e1b172383971105c" uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" -version = "1.1.2+0" +version = "1.1.3+0" [[deps.libblastrampoline_jll]] deps = ["Artifacts", "Libdl"] @@ -1034,9 +1042,9 @@ version = "5.11.0+0" [[deps.libpng_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "b70c870239dc3d7bc094eb2d6be9b73d27bef280" +git-tree-sha1 = "055a96774f383318750a1a5e10fd4151f04c29c5" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.44+0" +version = "1.6.46+0" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] @@ -1045,9 +1053,9 @@ version = "1.59.0+0" [[deps.oneTBB_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "7d0ea0f4895ef2f5cb83645fa689e52cb55cf493" +git-tree-sha1 = "d5a767a3bb77135a99e433afe0eb14cd7f6914c3" uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" -version = "2021.12.0+0" +version = "2022.0.0+0" [[deps.p7zip_jll]] deps = ["Artifacts", "Libdl"] diff --git a/docs/src/manual/bilinear.md b/docs/src/manual/bilinear.md index 0cbbe8d6..8019b164 100644 --- a/docs/src/manual/bilinear.md +++ b/docs/src/manual/bilinear.md @@ -72,4 +72,54 @@ using LinearAlgebra println(norm(uX[m])) println(norm(uX[j])) nothing # hide -``` \ No newline at end of file +``` + +## Calderon preconditioning for the PMCHWT + +Let's try to speed up convergence of the iterative solver by constructing a Calderon preconditioner. We opt for a block diagonal preconditioner containing only single layer contributions. The wavenumber used for construction of the preconditioner is purely imaginary to avoid the introduction of resonances. + +```@example transmission +Ty = Maxwell3D.singlelayer(gamma=κ1) + +c = Ty[k,j] + Ty[l,m] +``` + +We also require discrete versions of the duality pairing to map primal test coefficients to dual expansion coefficients + +```@example transmission +Nx = BEAST.NCross() +d = Nx[k,j] + Nx[l,m] +``` + +The corresponding block matrices can be built as before by appealing to the `assemble` function + +```@example transmission +BC = buffachristiansen(Γ) +Y = BEAST.DirectProductSpace([BC,BC]) + +Cyy = assemble(c, Y, Y) +Dxy = assemble(d, X, Y) +``` + +A *lazy* inverse for the discrete duality can be constructed by wrapping the block matrix in a Krylov solver object: + +```@example transmission +DYX = BEAST.GMRESSolver(Dxy, verbose=false) +DXY = BEAST.GMRESSolver(Dxy', verbose=false) +``` + +We have now all the ingredients to write down the system we want to solve: + +```@example transmission +PAXx = DXY * Cyy * DYX * Axx +PbX = DXY * Cyy * DYX * bx + +PSXx = BEAST.GMRESSolver(PAXx) + +u1, ch1 = solve(SXX, bx) +u2, ch2 = solve(PSXx, PbX) + +norm(u1-u2), ch1.iters, ch2.iters +``` + +Even for this small example, the solution was reconstructed in a much smaller number of iterations. Because objects of type `GMRESSolver` effectively behave as the inverse of the wrapped `LinearMap`, an internal iterative solver to compute the action of the inverse of the duality pairing is triggered during each outer iteration without introducing notational overhead! \ No newline at end of file From e854d133bb3effc3baa208bd53cd33e9547248cd Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 3 Mar 2025 16:44:10 +0100 Subject: [PATCH 453/528] efie on graded mesh example --- examples/efie_graded.jl | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 examples/efie_graded.jl diff --git a/examples/efie_graded.jl b/examples/efie_graded.jl new file mode 100644 index 00000000..de19c264 --- /dev/null +++ b/examples/efie_graded.jl @@ -0,0 +1,36 @@ +using CompScienceMeshes +using BEAST + +# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +# Γ = meshsphere(radius=1.0, h=0.1) +Γ = CompScienceMeshes.meshrectangle_unit_tri_2graded(0.3, 3) +@show length(Γ) +X = raviartthomas(Γ) + +κ, η = 6.0, 1.0 +t = Maxwell3D.singlelayer(wavenumber=κ) +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +e = (n × E) × n + +@hilbertspace j +@hilbertspace k + +qs = BEAST.DoubleNumWiltonSauterQStrat{Int64, Int64}(10, 11, 10, 11, 8, 8, 8, 8) + +U = BEAST.DirectProductSpace([X]) +A = assemble(t[k,j], U, U; quadstrat=qs) +b = assemble(e[k], U) + +u = AbstractMatrix(A) \ b +fcr, geo = facecurrents(u, X) + +import Plotly +using LinearAlgebra +Plotly.plot(patch(geo, log10.(norm.(fcr)))) + +efie = @discretise t[k,j]==e[k] j∈X k∈X +u, ch = BEAST.gmres_ch(efie; restart=1500) + +include("utils/postproc.jl") +include("utils/plotresults.jl") + From 9b6f0dc71d1b550f65b2f073028afe775401b3cc Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 12 Mar 2025 12:07:39 +0100 Subject: [PATCH 454/528] test pass but TD quadstrat mechanic not working --- examples/efie_graded.jl | 28 +++++++++++++++++--------- src/excitation.jl | 5 +++-- src/quadrature/strategies/quadstrat.jl | 4 ++++ src/solvers/solver.jl | 5 +++-- src/timedomain/tdexcitation.jl | 3 ++- 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/examples/efie_graded.jl b/examples/efie_graded.jl index de19c264..da2988a5 100644 --- a/examples/efie_graded.jl +++ b/examples/efie_graded.jl @@ -3,34 +3,42 @@ using BEAST # Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) # Γ = meshsphere(radius=1.0, h=0.1) -Γ = CompScienceMeshes.meshrectangle_unit_tri_2graded(0.3, 3) +Γ = CompScienceMeshes.meshrectangle_unit_tri_2graded(0.25, 3) @show length(Γ) X = raviartthomas(Γ) -κ, η = 6.0, 1.0 +κ, η = 4.0, 1.0 t = Maxwell3D.singlelayer(wavenumber=κ) -E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +E = Maxwell3D.planewave(direction=ŷ, polarization=x̂, wavenumber=κ) e = (n × E) × n @hilbertspace j @hilbertspace k -qs = BEAST.DoubleNumWiltonSauterQStrat{Int64, Int64}(10, 11, 10, 11, 8, 8, 8, 8) +qs = BEAST.DoubleNumWiltonSauterQStrat{Int64, Int64}(11, 11, 11, 12, 8, 8, 8, 8) +qsl = BEAST.SingleNumQStrat(13) U = BEAST.DirectProductSpace([X]) A = assemble(t[k,j], U, U; quadstrat=qs) -b = assemble(e[k], U) +b = assemble(e[k], U; quadstrat=qsl) u = AbstractMatrix(A) \ b fcr, geo = facecurrents(u, X) import Plotly using LinearAlgebra -Plotly.plot(patch(geo, log10.(norm.(fcr)))) +Plotly.plot(patch(geo, real.(getindex.(fcr,1)), caxis=(-10,10))) +Plotly.plot(patch(geo, real.(getindex.(fcr,2)), caxis=(-10,10))) +Plotly.plot(patch(geo, norm.(fcr), caxis=(0,10))) -efie = @discretise t[k,j]==e[k] j∈X k∈X -u, ch = BEAST.gmres_ch(efie; restart=1500) +# efie = @discretise t[k,j]==e[k] j∈X k∈X +# u, ch = BEAST.gmres_ch(efie; restart=1500) -include("utils/postproc.jl") -include("utils/plotresults.jl") +# include("utils/postproc.jl") +# include("utils/plotresults.jl") +Id = Identity() +Nuu = assemble(Id[k,j], U, U) +v = AbstractMatrix(Nuu) \ b +fcr, geo = facecurrents(v, X) +Plotly.plot(patch(geo, real(getindex.(fcr,1)))) \ No newline at end of file diff --git a/src/excitation.jl b/src/excitation.jl index 17bc17f0..e6a88f78 100644 --- a/src/excitation.jl +++ b/src/excitation.jl @@ -37,10 +37,11 @@ end function assemble!(field::Functional, tfs::Space, store; quadstrat=defaultquadstrat(field, tfs)) + qs = quadstrat(field, tfs) tels, tad = assemblydata(tfs) trefs = refspace(tfs) - qd = quaddata(field, trefs, tels, quadstrat) + qd = quaddata(field, trefs, tels, qs) tgeo = geometry(tfs) tdom = domain(chart(tgeo, first(tgeo))) @@ -49,7 +50,7 @@ function assemble!(field::Functional, tfs::Space, store; for (t, tcell) in enumerate(tels) # compute the testing with the reference elements - qr = quadrule(field, trefs, t, tcell, qd, quadstrat) + qr = quadrule(field, trefs, t, tcell, qd, qs) blocal = celltestvalues(trefs, tcell, field, qr) for i in 1 : num_trefs diff --git a/src/quadrature/strategies/quadstrat.jl b/src/quadrature/strategies/quadstrat.jl index dd69c6c4..38c88ca7 100644 --- a/src/quadrature/strategies/quadstrat.jl +++ b/src/quadrature/strategies/quadstrat.jl @@ -2,4 +2,8 @@ abstract type AbstractQuadStrat end function (qs::AbstractQuadStrat)(a, X, Y) qs +end + +function (qs::AbstractQuadStrat)(l, X) + qs end \ No newline at end of file diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 73d6fac6..23715ae5 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -146,7 +146,8 @@ end scalartype(lf::LinForm) = scalartype(lf.terms...) scalartype(lt::LinTerm) = scalartype(lt.coeff, lt.functional) -function assemble(lform::LinForm, X::DirectProductSpace) +function assemble(lform::LinForm, X::DirectProductSpace; + quadstrat=BEAST.defaultquadstrat) @assert !isempty(lform.terms) @@ -178,7 +179,7 @@ function assemble(lform::LinForm, X::DirectProductSpace) x = X.factors[m] for op in reverse(t.test_ops) x = op[end](op[1:end-1]..., x) end - b = assemble(t.functional, x) + b = assemble(t.functional, x; quadstrat) B[Block(m),Block(1)] = t.coeff * b end diff --git a/src/timedomain/tdexcitation.jl b/src/timedomain/tdexcitation.jl index e6805273..23f9b7c4 100644 --- a/src/timedomain/tdexcitation.jl +++ b/src/timedomain/tdexcitation.jl @@ -40,7 +40,8 @@ function quadrule(exc::TDFunctional, testrefs, timerefs::DiracBoundary, p, τ, r end -function assemble(exc::TDFunctional, testST; quaddata=quaddata, quadrule=quadrule) +# TODO: implement quadstrat support as in the frequency domain +function assemble(exc::TDFunctional, testST; quaddata=quaddata, quadrule=quadrule, quadstrat=nothing) stagedtimestep = isa(temporalbasis(testST), BEAST.StagedTimeStep) if stagedtimestep From 788ea759f9bfb764e866e49dd2dcbba586824146 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 12 Mar 2025 15:53:03 +0100 Subject: [PATCH 455/528] tdpmchwt can be assembled again --- src/maxwell/timedomain/mwtdops.jl | 2 + src/timedomain/tdexcitation.jl | 34 +- src/timedomain/tdintegralop.jl | 1 + test/Manifest.toml | 671 ------------------------------ 4 files changed, 21 insertions(+), 687 deletions(-) delete mode 100644 test/Manifest.toml diff --git a/src/maxwell/timedomain/mwtdops.jl b/src/maxwell/timedomain/mwtdops.jl index 86f69d2b..65b56344 100644 --- a/src/maxwell/timedomain/mwtdops.jl +++ b/src/maxwell/timedomain/mwtdops.jl @@ -103,6 +103,8 @@ end function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, store, threading::Type{Threading{:multi}}; quadstrat=defaultquadstrat(dl,W,V)) + quadstrat = quadstrat(dl, W, V) + X, T = spatialbasis(W), temporalbasis(W) Y, U = spatialbasis(V), temporalbasis(V) if CompScienceMeshes.refines(geometry(Y), geometry(X)) diff --git a/src/timedomain/tdexcitation.jl b/src/timedomain/tdexcitation.jl index 23f9b7c4..f0ae6718 100644 --- a/src/timedomain/tdexcitation.jl +++ b/src/timedomain/tdexcitation.jl @@ -1,8 +1,9 @@ abstract type TDFunctional{T} end Base.eltype(x::TDFunctional{T}) where {T} = T +defaultquadstrat(exc::TDFunctional, testfns) = nothing -function quaddata(exc::TDFunctional, testrefs, timerefs, testels, timeels) +function quaddata(exc::TDFunctional, testrefs, timerefs, testels, timeels, quadstrat::Any) testqd = quadpoints(testrefs, testels, (2,)) timeqd = quadpoints(timerefs, timeels, (10,)) @@ -13,14 +14,14 @@ end function quaddata(excitation::TDFunctional, test_refspace, time_refspace::DiracBoundary, - test_elements, time_elements) + test_elements, time_elements, quadstrat::Any) test_quad_data = quadpoints(test_refspace, test_elements, (2,)) test_quad_data, nothing end -function quadrule(exc::TDFunctional, testrefs, timerefs, p, τ, r, ρ, qd) +function quadrule(exc::TDFunctional, testrefs, timerefs, p, τ, r, ρ, qd, quadstrat::Any) MultiQuadStrategy( qd[1][1,p], @@ -31,7 +32,7 @@ function quadrule(exc::TDFunctional, testrefs, timerefs, p, τ, r, ρ, qd) end -function quadrule(exc::TDFunctional, testrefs, timerefs::DiracBoundary, p, τ, r, ρ, qd) +function quadrule(exc::TDFunctional, testrefs, timerefs::DiracBoundary, p, τ, r, ρ, qd, quadstrat::Any) MultiQuadStrategy( qd[1][1,p], @@ -41,23 +42,23 @@ function quadrule(exc::TDFunctional, testrefs, timerefs::DiracBoundary, p, τ, r end # TODO: implement quadstrat support as in the frequency domain -function assemble(exc::TDFunctional, testST; quaddata=quaddata, quadrule=quadrule, quadstrat=nothing) +function assemble(exc::TDFunctional, testST; quadstrat=defaultquadstrat) stagedtimestep = isa(temporalbasis(testST), BEAST.StagedTimeStep) if stagedtimestep - return staged_assemble(exc, testST; quaddata=quaddata, quadrule=quadrule) + return staged_assemble(exc, testST; quadstrat) end testfns = spatialbasis(testST) timefns = temporalbasis(testST) Z = zeros(eltype(exc), numfunctions(testfns), numfunctions(timefns)) store(v,m,k) = (Z[m,k] += v) - assemble!(exc, testST, store, quaddata=quaddata, quadrule=quadrule) + assemble!(exc, testST, store; quadstrat) return Z end -function staged_assemble(exc::TDFunctional, testST::SpaceTimeBasis; - quaddata=quaddata, quadrule=quadrule) +function staged_assemble(exc::TDFunctional, testST::SpaceTimeBasis; quadstrat=defaultquadstrat) + # quaddata=quaddata, quadrule=quadrule) @warn "staged assemble of the right-hand side" testfns = spatialbasis(testST) @@ -69,14 +70,14 @@ function staged_assemble(exc::TDFunctional, testST::SpaceTimeBasis; for i = 1:stageCount store(v,m,k) = (Z[(m-1)*stageCount+i,k] += v) tbsd = TimeBasisDeltaShifted(timebasisdelta(Δt, Nt), timefns.c[i]) - assemble!(exc, testfns ⊗ tbsd, store, - quaddata=quaddata, quadrule=quadrule) + assemble!(exc, testfns ⊗ tbsd, store; quadstrat) + # quaddata=quaddata, quadrule=quadrule) end return Z end -function assemble!(exc::TDFunctional, testST, store; - quaddata=quaddata, quadrule=quadrule) +function assemble!(exc::TDFunctional, testST, store; quadstrat=defaultquadstrat) + # quaddata=quaddata, quadrule=quadrule) testfns = spatialbasis(testST) timefns = temporalbasis(testST) @@ -86,8 +87,9 @@ function assemble!(exc::TDFunctional, testST, store; testels, testad = assemblydata(testfns) timeels, timead = assemblydata(timefns) - - qd = quaddata(exc, testrefs, timerefs, testels, timeels) + @show quadstrat + qs = quadstrat(exc, testST) + qd = quaddata(exc, testrefs, timerefs, testels, timeels, qs) num_testshapes = numfunctions(testrefs, domain(first(testels))) z = zeros(eltype(exc), num_testshapes, numfunctions(timerefs)) @@ -97,7 +99,7 @@ function assemble!(exc::TDFunctional, testST, store; ρ = timeels[r] fill!(z, 0) - qr = quadrule(exc, testrefs, timerefs, p, τ, r, ρ, qd) + qr = quadrule(exc, testrefs, timerefs, p, τ, r, ρ, qd, qs) momintegrals!(z, exc, testrefs, timerefs, τ, ρ, qr) for i in 1 : num_testshapes diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 73bcb201..8d25dafd 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -249,6 +249,7 @@ function assemble_chunk!(op::RetardedPotential, testST, trialST, store; V = refspace(trialspace) W = refspace(timebasisfunction) + # qs = quadstrat(op, testST, trialST) qd = quaddata(op, U, V, W, testels, trialels, nothing, quadstrat) ugeo = geometry(testspace) diff --git a/test/Manifest.toml b/test/Manifest.toml deleted file mode 100644 index 8479a59f..00000000 --- a/test/Manifest.toml +++ /dev/null @@ -1,671 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.10.2" -manifest_format = "2.0" -project_hash = "1b78102b70e82631772273acf6687b12f1d8fa74" - -[[deps.ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" -version = "1.1.1" - -[[deps.ArrayLayouts]] -deps = ["FillArrays", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "4efc22e4c299e49995a38d503d9dbb0544a37838" -uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "1.0.4" - -[[deps.Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" - -[[deps.Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" - -[[deps.BlockArrays]] -deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] -git-tree-sha1 = "14d688a2254ca2242d834176a385cc9b3bceeb02" -uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" -version = "0.16.28" - -[[deps.Bzip2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" -uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+0" - -[[deps.Cairo_jll]] -deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2" -uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.16.1+1" - -[[deps.ClusterTrees]] -deps = ["DelimitedFiles", "LinearAlgebra", "Pkg"] -git-tree-sha1 = "4114d60c95974edf9272d88571d14abeed598942" -uuid = "5100927d-02aa-593a-b4f9-7235df19f0db" -version = "0.2.1" - -[[deps.CollisionDetection]] -deps = ["Compat", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "8d86c864d69f72e23adcd7c2014d205bf32c90a1" -uuid = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" -version = "0.1.5" - -[[deps.Combinatorics]] -git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" -uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" -version = "1.0.2" - -[[deps.CompScienceMeshes]] -deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays"] -git-tree-sha1 = "133f6ac95849d0f96ecc82378e625ffa3790b940" -uuid = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" -version = "0.5.2" - -[[deps.Compat]] -deps = ["UUIDs"] -git-tree-sha1 = "7a60c856b9fa189eb34f5f8a6f6b5529b7942957" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.6.1" -weakdeps = ["Dates", "LinearAlgebra"] - - [deps.Compat.extensions] - CompatLinearAlgebraExt = "LinearAlgebra" - -[[deps.CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.1.0+0" - -[[deps.DataStructures]] -deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.13" - -[[deps.Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" - -[[deps.DelimitedFiles]] -deps = ["Mmap"] -git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" -version = "1.9.1" - -[[deps.Distributed]] -deps = ["Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" - -[[deps.DocStringExtensions]] -deps = ["LibGit2"] -git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" -uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.9.3" - -[[deps.Downloads]] -deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -version = "1.6.0" - -[[deps.Expat_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "bad72f730e9e91c08d9427d5e8db95478a3c323d" -uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.4.8+0" - -[[deps.FLTK_jll]] -deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "72a4842f93e734f378cf381dae2ca4542f019d23" -uuid = "4fce6fc7-ba6a-5f4c-898f-77e99806d6f8" -version = "1.3.8+0" - -[[deps.FastGaussQuadrature]] -deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "0f478d8bad6f52573fb7658a263af61f3d96e43a" -uuid = "442a2c76-b920-505d-bb47-c5924d526838" -version = "0.5.1" - -[[deps.FileWatching]] -uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" - -[[deps.FillArrays]] -deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"] -git-tree-sha1 = "589d3d3bff204bdd80ecc53293896b4f39175723" -uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.1.1" - -[[deps.Fontconfig_jll]] -deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" -uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.93+0" - -[[deps.FreeType2_jll]] -deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "87eb71354d8ec1a96d4a7636bd57a7347dde3ef9" -uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.10.4+0" - -[[deps.GLU_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] -git-tree-sha1 = "65af046f4221e27fb79b28b6ca89dd1d12bc5ec7" -uuid = "bd17208b-e95e-5925-bf81-e2f59b3e5c61" -version = "9.0.1+0" - -[[deps.GMP_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" -version = "6.2.1+6" - -[[deps.Gettext_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" -uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" -version = "0.21.0+0" - -[[deps.Glib_jll]] -deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "d3b3624125c1474292d0d8ed0f65554ac37ddb23" -uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.74.0+2" - -[[deps.GmshTools]] -deps = ["Libdl", "gmsh_jll"] -git-tree-sha1 = "299aa66053646db77f8aa7fafcebe0f9e5c0d1dc" -uuid = "82e2f556-b1bd-5f1a-9576-f93c0da5f0ee" -version = "0.5.2" - -[[deps.HDF5_jll]] -deps = ["Artifacts", "JLLWrappers", "LibCURL_jll", "Libdl", "OpenSSL_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "4cc2bb72df6ff40b055295fdef6d92955f9dede8" -uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" -version = "1.12.2+2" - -[[deps.InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" - -[[deps.IrrationalConstants]] -git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" -uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.2.2" - -[[deps.JLLWrappers]] -deps = ["Preferences"] -git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" -uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.4.1" - -[[deps.JpegTurbo_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "6f2675ef130a300a112286de91973805fcc5ffbc" -uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "2.1.91+0" - -[[deps.LLVMOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f689897ccbe049adb19a065c495e75f372ecd42b" -uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" -version = "15.0.4+0" - -[[deps.LZO_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" -uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.1+0" - -[[deps.LegendrePolynomials]] -deps = ["OffsetArrays", "SpecialFunctions"] -git-tree-sha1 = "78e5288b179f2ae90ccbaa1799e9b0cb82ef5e04" -uuid = "3db4a2ba-fc88-11e8-3e01-49c72059a882" -version = "0.4.4" - -[[deps.LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" -version = "0.6.4" - -[[deps.LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "8.4.0+0" - -[[deps.LibGit2]] -deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" - -[[deps.LibGit2_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] -uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" -version = "1.6.4+0" - -[[deps.LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "MbedTLS_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" -version = "1.11.0+1" - -[[deps.Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" - -[[deps.Libffi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" -uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.2.2+1" - -[[deps.Libgcrypt_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] -git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" -uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.7+0" - -[[deps.Libglvnd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] -git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" -uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" -version = "1.6.0+0" - -[[deps.Libgpg_error_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" -uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.42.0+0" - -[[deps.Libiconv_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c7cb1f5d892775ba13767a87c7ada0b980ea0a71" -uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.16.1+2" - -[[deps.Libmount_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73" -uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.35.0+0" - -[[deps.Libuuid_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066" -uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.36.0+0" - -[[deps.LinearAlgebra]] -deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" - -[[deps.LinearElasticity_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "71e8ee0f9fe0e86a8f8c7f28361e5118eab2f93f" -uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" -version = "5.0.0+0" - -[[deps.LinearMaps]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "ee79c3208e55786de58f8dcccca098ced79f743f" -uuid = "7a12625a-238d-50fd-b39a-03d52299707e" -version = "3.11.3" - - [deps.LinearMaps.extensions] - LinearMapsChainRulesCoreExt = "ChainRulesCore" - LinearMapsSparseArraysExt = "SparseArrays" - LinearMapsStatisticsExt = "Statistics" - - [deps.LinearMaps.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[deps.LogExpFunctions]] -deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "c3ce8e7420b3a6e071e0fe4745f5d4300e37b13f" -uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.24" - - [deps.LogExpFunctions.extensions] - LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" - LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" - LogExpFunctionsInverseFunctionsExt = "InverseFunctions" - - [deps.LogExpFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[deps.Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" - -[[deps.METIS_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "1fd0a97409e418b78c53fac671cf4622efdf0f21" -uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" -version = "5.1.2+0" - -[[deps.MMG_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] -git-tree-sha1 = "70a59df96945782bb0d43b56d0fbfdf1ce2e4729" -uuid = "86086c02-e288-5929-a127-40944b0018b7" -version = "5.6.0+0" - -[[deps.Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" - -[[deps.MbedTLS_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.2+1" - -[[deps.Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" - -[[deps.MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2023.1.10" - -[[deps.NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" -version = "1.2.0" - -[[deps.OCCT_jll]] -deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "acc8099ae8ed10226dc8424fb256ec9fe367a1f0" -uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" -version = "7.6.2+2" - -[[deps.OffsetArrays]] -git-tree-sha1 = "e64b4f5ea6b7389f6f046d13d4896a8f9c1ba71e" -uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.14.0" - - [deps.OffsetArrays.extensions] - OffsetArraysAdaptExt = "Adapt" - - [deps.OffsetArrays.weakdeps] - Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" - -[[deps.OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.23+4" - -[[deps.OpenLibm_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" - -[[deps.OpenSSL_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1aa4b74f80b01c6bc2b89992b861b5f210e665b5" -uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "1.1.21+0" - -[[deps.OpenSpecFun_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" -uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.5+0" - -[[deps.OrderedCollections]] -git-tree-sha1 = "d321bf2de576bf25ec4d3e4360faca399afca282" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.6.0" - -[[deps.PCRE2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" -version = "10.42.0+1" - -[[deps.Pixman_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29" -uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.40.1+0" - -[[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.10.0" - -[[deps.Preferences]] -deps = ["TOML"] -git-tree-sha1 = "7eb1686b4f04b82f96ed7a4ea5890a4f0c7a09f1" -uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.4.0" - -[[deps.Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" - -[[deps.REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" - -[[deps.Random]] -deps = ["SHA"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" - -[[deps.Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.0" - -[[deps.SCOTCH_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "7110b749766853054ce8a2afaa73325d72d32129" -uuid = "a8d0f55d-b80e-548d-aff6-1a04c175f0f9" -version = "6.1.3+0" - -[[deps.SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" -version = "0.7.0" - -[[deps.SauterSchwabQuadrature]] -deps = ["FastGaussQuadrature", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "80a8bf94c550ff26ef7778921b3e3833ac422be6" -uuid = "535c7bfe-2023-5c1d-b712-654ef9d93a38" -version = "2.2.1" - -[[deps.Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" - -[[deps.Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" - -[[deps.SparseArrays]] -deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -version = "1.10.0" - -[[deps.SpecialFunctions]] -deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "ef28127915f4229c971eb43f3fc075dd3fe91880" -uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.2.0" - - [deps.SpecialFunctions.extensions] - SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" - - [deps.SpecialFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - -[[deps.SphericalScattering]] -deps = ["LegendrePolynomials", "LinearAlgebra", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "6acefb2a8f8142e601f68e4bda5b37f6731e1455" -uuid = "1a9ea918-b599-4f1f-bd9a-d681e8bb5b3e" -version = "0.7.2" - - [deps.SphericalScattering.extensions] - SphericalScatteringExt = "PlotlyJS" - - [deps.SphericalScattering.weakdeps] - PlotlyJS = "f0f68f2c-4968-5e81-91da-67840de0976a" - -[[deps.StaticArrays]] -deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] -git-tree-sha1 = "8982b3607a212b070a5e46eea83eb62b4744ae12" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.5.25" - -[[deps.StaticArraysCore]] -git-tree-sha1 = "6b7ba252635a5eff6a0b0664a41ee140a1c9e72a" -uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.0" - -[[deps.Statistics]] -deps = ["LinearAlgebra", "SparseArrays"] -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -version = "1.10.0" - -[[deps.SuiteSparse_jll]] -deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] -uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.2.1+1" - -[[deps.TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.3" - -[[deps.Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" -version = "1.10.0" - -[[deps.Test]] -deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[deps.TestItemRunner]] -deps = ["Pkg", "TOML", "Test", "TestItems", "UUIDs"] -git-tree-sha1 = "cb2b53fd36a8fe20c0b9f55da6244eb4818779f5" -uuid = "f8b46487-2199-4994-9208-9a1283c18c0a" -version = "0.2.3" - -[[deps.TestItems]] -git-tree-sha1 = "8621ba2637b49748e2dc43ba3d84340be2938022" -uuid = "1c621080-faea-4a02-84b6-bbd5e436b8fe" -version = "0.1.1" - -[[deps.UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" - -[[deps.Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" - -[[deps.WiltonInts84]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "446d97faa3e974e8a4d406ecc873284f5aa9558a" -uuid = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" -version = "0.2.4" - -[[deps.XML2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "93c41695bc1c08c46c5899f4fe06d6ead504bb73" -uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.10.3+0" - -[[deps.XSLT_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] -git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" -uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.34+0" - -[[deps.Xorg_libX11_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] -git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527" -uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.6.9+4" - -[[deps.Xorg_libXau_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e" -uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.9+4" - -[[deps.Xorg_libXdmcp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4" -uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.3+4" - -[[deps.Xorg_libXext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" -uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.4+4" - -[[deps.Xorg_libXfixes_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" -uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" -version = "5.0.3+4" - -[[deps.Xorg_libXft_jll]] -deps = ["Fontconfig_jll", "Libdl", "Pkg", "Xorg_libXrender_jll"] -git-tree-sha1 = "754b542cdc1057e0a2f1888ec5414ee17a4ca2a1" -uuid = "2c808117-e144-5220-80d1-69d4eaa9352c" -version = "2.3.3+1" - -[[deps.Xorg_libXinerama_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] -git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" -uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" -version = "1.1.4+4" - -[[deps.Xorg_libXrender_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96" -uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.10+4" - -[[deps.Xorg_libpthread_stubs_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb" -uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" -version = "0.1.0+3" - -[[deps.Xorg_libxcb_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6" -uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.13.0+3" - -[[deps.Xorg_xtrans_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845" -uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.4.0+3" - -[[deps.Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.2.13+1" - -[[deps.gmsh_jll]] -deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "d15409a4b9f1d14f1e1f9e910cd00f7d6695c261" -uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" -version = "4.11.1+0" - -[[deps.libblastrampoline_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.8.0+1" - -[[deps.libpng_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c" -uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.38+0" - -[[deps.nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.52.0+1" - -[[deps.p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.4.0+2" From 4416a188c2c5764ce4b9e54606cf13e79f4752e4 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 18 Mar 2025 13:42:03 +0100 Subject: [PATCH 456/528] tdexc quadstrat refactored and tested --- src/BEAST.jl | 5 + .../timedomain/excitation/multiquadqrule.jl | 22 ++++ .../timedomain/excitation/singlequad2qrule.jl | 42 ++++++ .../excitation/numspacenumtimeqstrat.jl | 91 +++++++++++++ src/timedomain/tdexcitation.jl | 123 +----------------- 5 files changed, 163 insertions(+), 120 deletions(-) create mode 100644 src/quadrature/rules/timedomain/excitation/multiquadqrule.jl create mode 100644 src/quadrature/rules/timedomain/excitation/singlequad2qrule.jl create mode 100644 src/quadrature/strategies/timedomain/excitation/numspacenumtimeqstrat.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index b6d95d40..3159b673 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -205,6 +205,7 @@ include("quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl") include("quadrature/strategies/testrefinestrialqstrat.jl") include("quadrature/strategies/trialrefinestestqstrat.jl") + include("excitation.jl") include("gridfunction.jl") include("localop.jl") @@ -235,6 +236,10 @@ include("timedomain/rkcq.jl") include("timedomain/zdomain.jl") include("timedomain/td_symmetric_quadstrat.jl") +include("quadrature/strategies/timedomain/excitation/numspacenumtimeqstrat.jl") + +include("quadrature/rules/timedomain/excitation/multiquadqrule.jl") +include("quadrature/rules/timedomain/excitation/singlequad2qrule.jl") # Support for Maxwell equations include("maxwell/mwexc.jl") diff --git a/src/quadrature/rules/timedomain/excitation/multiquadqrule.jl b/src/quadrature/rules/timedomain/excitation/multiquadqrule.jl new file mode 100644 index 00000000..53766782 --- /dev/null +++ b/src/quadrature/rules/timedomain/excitation/multiquadqrule.jl @@ -0,0 +1,22 @@ +mutable struct MultiQuadStrategy{P,R} #<: NumQuadStrategy + quad_points::P + inner_rule::R +end + + +timequadrule(qr::MultiQuadStrategy, p) = qr.inner_rule + +function momintegrals!(z, exc::TDFunctional, testrefs, timerefs, τ, ρ, qr::MultiQuadStrategy) + + for p in qr.quad_points + x = p.point + w = p.weight + f = p.value + dx = w + + tqr = timequadrule(qr,p) + timeintegrals!(z, exc, testrefs, timerefs, x, ρ, dx, tqr, f) + + end + +end \ No newline at end of file diff --git a/src/quadrature/rules/timedomain/excitation/singlequad2qrule.jl b/src/quadrature/rules/timedomain/excitation/singlequad2qrule.jl new file mode 100644 index 00000000..218f76d0 --- /dev/null +++ b/src/quadrature/rules/timedomain/excitation/singlequad2qrule.jl @@ -0,0 +1,42 @@ +# TODO: consolidate with the existing definition of SingleQuadStrategy +mutable struct SingleQuadStrategy2{P} #<: NumQuadStrategy + quad_points::P +end + + + +function timeintegrals!(z, exc::TDFunctional, + testrefs, timerefs, + testpoint, timeelement, dx, qr::SingleQuadStrategy2, f) + + num_tshapes = numfunctions(testrefs, domain(chart(testpoint))) + for p in qr.quad_points + t = p.point + w = p.weight + U = p.value + dt = w #* jacobian(t) # * volume(timeelement) + + for i in 1 : num_tshapes + for k in 1 : numfunctions(timerefs) + z[i,k] += dot(f[i][1]*U[k], exc(testpoint,t)) * dt * dx + end + end + end +end + + +function timeintegrals!(z, exc::TDFunctional, + spacerefs, timerefs::DiracBoundary, + testpoint, timeelement, + dx, qr::Nothing, testvals) + + num_tshapes = numfunctions(spacerefs, domain(chart(testpoint))) + # since timeelement uses barycentric coordinates, + # the first/left vertex has coords u = 1.0! + testtime = neighborhood(timeelement, point(0.0)) + @assert cartesian(testtime)[1] ≈ timeelement.vertices[2][1] + + for i in 1 : num_tshapes + z[i,1] += dot(testvals[i][1], exc(testpoint, testtime)) * dx + end +end \ No newline at end of file diff --git a/src/quadrature/strategies/timedomain/excitation/numspacenumtimeqstrat.jl b/src/quadrature/strategies/timedomain/excitation/numspacenumtimeqstrat.jl new file mode 100644 index 00000000..1fa95dac --- /dev/null +++ b/src/quadrature/strategies/timedomain/excitation/numspacenumtimeqstrat.jl @@ -0,0 +1,91 @@ +struct NumSpaceNumTimeQStrat{S,T} <: AbstractQuadStrat + space_rule::S + time_rule::T +end + + +function quaddata(exc::TDFunctional, + testrefs, timerefs, + testels, timeels, quadstrat::NumSpaceNumTimeQStrat) + + r = quadstrat.space_rule + s = quadstrat.time_rule + + testqd = quadpoints(testrefs, testels, (r,)) + timeqd = quadpoints(timerefs, timeels, (s,)) + + testqd, timeqd + +end + + +function quadrule(exc::TDFunctional, + testrefs, timerefs, + p, τ, r, ρ, + qd, quadstrat::NumSpaceNumTimeQStrat) + + MultiQuadStrategy( + qd[1][1,p], + SingleQuadStrategy2( + qd[2][1,r] + ) + ) + +end + + +function quaddata(excitation::TDFunctional, + test_refspace, time_refspace::DiracBoundary, + test_elements, time_elements, quadstrat::NumSpaceNumTimeQStrat) + + r = quadstrat.space_rule + test_quad_data = quadpoints(test_refspace, test_elements, (r,)) + + test_quad_data, nothing +end + + +function quadrule(exc::TDFunctional, + testrefs, timerefs::DiracBoundary, + p, τ, r, ρ, + qd, quadstrat::NumSpaceNumTimeQStrat) + + MultiQuadStrategy( + qd[1][1,p], + nothing + ) + +end + + +@testitem "tdexc: multiquadqrule" begin + using CompScienceMeshes + using LinearAlgebra + + fn = joinpath(pathof(BEAST), "../../test/assets/sphere45.in") + + mesh = readmesh(fn) + RT = raviartthomas(mesh) + + Δt = 0.1 + Nt = 200 + T = timebasisshiftedlagrange(Δt, Nt, 3) + U = timebasisdelta(Δt, Nt) + + X = RT ⊗ U + + duration = 2 * 20 * Δt + delay = 1.5 * duration + amplitude = 1.0 + gaussian = creategaussian(duration, delay, amplitude) + direction, polarisation = ẑ, x̂ + E = planewave(polarisation, direction, derive(gaussian), 1.0) + + qs1 = BEAST.NumSpaceNumTimeQStrat(2, 10) + qs2 = BEAST.NumSpaceNumTimeQStrat(6, 20) + + b1 = assemble(E, X; quadstrat=qs1) + b2 = assemble(E, X; quadstrat=qs2) + + @test norm(b1-b2, Inf) < 1e-4 +end \ No newline at end of file diff --git a/src/timedomain/tdexcitation.jl b/src/timedomain/tdexcitation.jl index f0ae6718..a0e23a02 100644 --- a/src/timedomain/tdexcitation.jl +++ b/src/timedomain/tdexcitation.jl @@ -1,47 +1,8 @@ abstract type TDFunctional{T} end Base.eltype(x::TDFunctional{T}) where {T} = T -defaultquadstrat(exc::TDFunctional, testfns) = nothing +defaultquadstrat(exc::TDFunctional, testfns) = NumSpaceNumTimeQStrat(2, 10) -function quaddata(exc::TDFunctional, testrefs, timerefs, testels, timeels, quadstrat::Any) - - testqd = quadpoints(testrefs, testels, (2,)) - timeqd = quadpoints(timerefs, timeels, (10,)) - - testqd, timeqd - -end - -function quaddata(excitation::TDFunctional, - test_refspace, time_refspace::DiracBoundary, - test_elements, time_elements, quadstrat::Any) - - test_quad_data = quadpoints(test_refspace, test_elements, (2,)) - - test_quad_data, nothing -end - -function quadrule(exc::TDFunctional, testrefs, timerefs, p, τ, r, ρ, qd, quadstrat::Any) - - MultiQuadStrategy( - qd[1][1,p], - SingleQuadStrategy2( - qd[2][1,r] - ) - ) - -end - -function quadrule(exc::TDFunctional, testrefs, timerefs::DiracBoundary, p, τ, r, ρ, qd, quadstrat::Any) - - MultiQuadStrategy( - qd[1][1,p], - nothing - ) - -end - -# TODO: implement quadstrat support as in the frequency domain function assemble(exc::TDFunctional, testST; quadstrat=defaultquadstrat) stagedtimestep = isa(temporalbasis(testST), BEAST.StagedTimeStep) @@ -58,7 +19,6 @@ function assemble(exc::TDFunctional, testST; quadstrat=defaultquadstrat) end function staged_assemble(exc::TDFunctional, testST::SpaceTimeBasis; quadstrat=defaultquadstrat) - # quaddata=quaddata, quadrule=quadrule) @warn "staged assemble of the right-hand side" testfns = spatialbasis(testST) @@ -71,13 +31,12 @@ function staged_assemble(exc::TDFunctional, testST::SpaceTimeBasis; quadstrat=de store(v,m,k) = (Z[(m-1)*stageCount+i,k] += v) tbsd = TimeBasisDeltaShifted(timebasisdelta(Δt, Nt), timefns.c[i]) assemble!(exc, testfns ⊗ tbsd, store; quadstrat) - # quaddata=quaddata, quadrule=quadrule) end return Z end function assemble!(exc::TDFunctional, testST, store; quadstrat=defaultquadstrat) - # quaddata=quaddata, quadrule=quadrule) + testfns = spatialbasis(testST) timefns = temporalbasis(testST) @@ -88,6 +47,7 @@ function assemble!(exc::TDFunctional, testST, store; quadstrat=defaultquadstrat) timeels, timead = assemblydata(timefns) @show quadstrat + @assert quadstrat != nothing qs = quadstrat(exc, testST) qd = quaddata(exc, testrefs, timerefs, testels, timeels, qs) @@ -117,80 +77,3 @@ function assemble!(exc::TDFunctional, testST, store; quadstrat=defaultquadstrat) end end end - - -abstract type NumQuadStrategy end - -mutable struct MultiQuadStrategy{P,R} <: NumQuadStrategy - quad_points::P - inner_rule::R -end - -# TODO: consolidate with the existing definition of SingleQuadStrategy -mutable struct SingleQuadStrategy2{P} <: NumQuadStrategy - quad_points::P -end - -timequadrule(qr::MultiQuadStrategy, p) = qr.inner_rule - -function momintegrals!(z, exc::TDFunctional, testrefs, timerefs, τ, ρ, qr) - - for p in qr.quad_points - x = p.point - w = p.weight - f = p.value - dx = w - - # try - # @assert ρ.vertices[1][1] <= cartesian(x)[1] <= ρ.vertices[2][1] - # catch - # @show ρ.vertices[1][1] - # @show cartesian(x)[1] - # @show ρ.vertices[2][1] - # error("") - # end - - tqr = timequadrule(qr,p) - timeintegrals!(z, exc, testrefs, timerefs, x, ρ, dx, tqr, f) - - end - -end - - -function timeintegrals!(z, exc::TDFunctional, testrefs, timerefs, testpoint, timeelement, dx, qr, f) - - num_tshapes = numfunctions(testrefs, domain(chart(testpoint))) - - for p in qr.quad_points - t = p.point - w = p.weight - U = p.value - dt = w #* jacobian(t) # * volume(timeelement) - - for i in 1 : num_tshapes - for k in 1 : numfunctions(timerefs) - z[i,k] += dot(f[i][1]*U[k], exc(testpoint,t)) * dt * dx - end - end - end -end - - - -function timeintegrals!(z, exc::TDFunctional, - spacerefs, timerefs::DiracBoundary, - testpoint, timeelement, - dx, qr, testvals) - - num_tshapes = numfunctions(spacerefs, domain(chart(testpoint))) - - # since timeelement uses barycentric coordinates, - # the first/left vertex has coords u = 1.0! - testtime = neighborhood(timeelement, point(0.0)) - @assert cartesian(testtime)[1] ≈ timeelement.vertices[2][1] - - for i in 1 : num_tshapes - z[i,1] += dot(testvals[i][1], exc(testpoint, testtime)) * dx - end -end From 75d743fa461a0e9976361cff91eefb8a36f06ac7 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 19 Mar 2025 14:00:12 +0100 Subject: [PATCH 457/528] refactored exciation code and added docs --- docs/make.jl | 1 + docs/src/manual/customexc.md | 36 ++++ examples/assets/sphere45.in | 323 +++++++++++++++++++++++++++++++++ examples/customexcitation.jl | 41 +++++ src/excitation.jl | 64 ++++++- src/helmholtz2d/helmholtzop.jl | 6 +- src/helmholtz3d/hh3dexc.jl | 4 +- src/maxwell/mwexc.jl | 47 +---- src/maxwell/sourcefield.jl | 10 +- src/solvers/solver.jl | 5 + src/volumeintegral/vieexc.jl | 4 +- test/runtests.jl | 2 +- 12 files changed, 492 insertions(+), 51 deletions(-) create mode 100644 docs/src/manual/customexc.md create mode 100644 examples/assets/sphere45.in create mode 100644 examples/customexcitation.jl diff --git a/docs/make.jl b/docs/make.jl index 38121ec2..e3306889 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -21,6 +21,7 @@ makedocs(; "Introduction" => "index.md", "Manual" => Any[ "General Usage"=>"manual/usage.md", + "Defining custom excitations"=>"manual/customexc.md", "Customising Quadrature Rules" => "manual/quadstrat.md", "Application Examples"=>Any[ "Time-Harmonic"=>Any[ diff --git a/docs/src/manual/customexc.md b/docs/src/manual/customexc.md new file mode 100644 index 00000000..2c6aa1b4 --- /dev/null +++ b/docs/src/manual/customexc.md @@ -0,0 +1,36 @@ +# Defining Custom Excitation + +BEAST.jl aims to use your own fields as excitations as easy as possible. Specifying your own field can be done by defining a function of the Cartesian coordinates and supplementing this definition with a method of `BEAST.scalartype` to announce the scalar type of the value returned by the field evaluation. Scalar type is, as the name suggest, always a scalar type such as `ComplexF64`, even if the field is vector valued. + +Upon supplying this data, the field function happily interacts with the rest of the BEAST.jl infrastructure. For example, taking the trace of the field is in no way different from doing so for the supplied exciations: + +```julia + using CompScienceMeshes + + κ = 1.0 + + # Custom excitations can be defined as functions of a point in Cartesian space, + # complemented with a function specifying the return type. + E(x) = point(ComplexF64, 1.0, 1.0im, 0.0) * exp(-im*κ*x[3]) + BEAST.scalartype(::typeof(E)) = ComplexF64 + + # Such a custom field plays nicely with the tangential trace + # operators defined as part of the BEAST framework + e = (n × E) × n + + fn = joinpath(pathof(BEAST), "../../examples/assets/sphere45.in") + Γ = readmesh(fn) + RT = raviartthomas(Γ) + + t = Maxwell3D.singlelayer(wavenumber=κ) + + @hilbertspace j + @hilbertspace k + + Txx = assemble(t[k,j], j∈RT, k∈RT) + ex = assemble(e[k], k∈RT) + + iTXX = BEAST.GMRESSolver(Txx; maxiter=1000, reltol=1e-5) + uX = iTXX * ex +``` + diff --git a/examples/assets/sphere45.in b/examples/assets/sphere45.in new file mode 100644 index 00000000..f1d63191 --- /dev/null +++ b/examples/assets/sphere45.in @@ -0,0 +1,323 @@ +1 +109 212 +0.0 0.0 0.0 +1.0 0.0 0.0 +0.0 1.0 0.0 +-1.0 0.0 0.0 +0.0 -1.0 0.0 +0.0 0.0 1.0 +0.0 0.0 -1.0 +0.923879532082714 0.3826834333997559 0.0 +0.7071067795767625 0.7071067827963327 0.0 +0.3826834312295727 0.9238795329816333 0.0 +-0.3826834333997559 0.923879532082714 0.0 +-0.7071067827963327 0.7071067795767625 0.0 +-0.9238795329816333 0.3826834312295727 0.0 +-0.923879532082714 -0.3826834333997559 0.0 +-0.7071067795767625 -0.7071067827963327 0.0 +-0.3826834312295727 -0.9238795329816333 0.0 +0.3826834333997559 -0.923879532082714 0.0 +0.7071067827963327 -0.7071067795767625 0.0 +0.9238795329816333 -0.3826834312295727 0.0 +0.0 0.3826834333997559 0.923879532082714 +0.0 0.7071067827963327 0.7071067795767625 +0.0 0.9238795329816333 0.3826834312295727 +0.0 0.923879532082714 -0.3826834333997559 +0.0 0.7071067795767625 -0.7071067827963327 +0.0 0.3826834312295727 -0.9238795329816333 +0.0 -0.3826834333997559 -0.923879532082714 +0.0 -0.7071067827963327 -0.7071067795767625 +0.0 -0.9238795329816333 -0.3826834312295727 +0.0 -0.923879532082714 0.3826834333997559 +0.0 -0.7071067795767625 0.7071067827963327 +0.0 -0.3826834312295727 0.9238795329816333 +0.7712990324977224 -0.5261836316964337 -0.3580901956251096 +0.7832272477767324 0.5233357175207212 -0.3356706795464225 +0.3356706794722104 -0.183771065508847 -0.923879532419897 +0.3298749710844383 0.7884309806178305 -0.5191908052479812 +0.3364914460521806 -0.5356371469604048 -0.7745103960114611 +0.4943952293272733 -0.7818671890563442 -0.3798118690820019 +0.6320232753776562 -0.3163329681330331 -0.7074461341000068 +0.9179912585278028 0.1751715968730272 -0.3558187191752205 +0.7153911295286901 0.2972003361018707 -0.632366580404594 +0.9247531464560557 -0.2034408415038567 -0.3216262460183055 +0.3182620562185089 0.5161949160681655 -0.7951427998774474 +0.2034339032461793 0.191010725638466 -0.9602757675277923 +0.3318408257257821 -0.7290341394220538 -0.5986575731908655 +0.5143125430732531 0.08759839546161578 -0.8531173009323357 +0.5429324605256425 -0.5927866926067963 -0.5948346664207352 +0.7870222775749804 0.001326899353937282 -0.6169231507560687 +0.5709878661731511 0.6078735380892875 -0.5517813139041982 +0.5481592105556712 0.79368928263904 -0.2637779416609913 +0.2458184845179587 0.9376281485435064 -0.2458184853264101 +0.3356706784848729 -0.1837710644888045 0.9238795329815229 +0.3808964856541316 0.5323383179218478 0.7559985333904224 +0.9238795324198404 0.1837710655050317 0.3356706794744551 +0.7981217874283407 -0.4624241295859055 0.3862195448302793 +0.3239596594514724 -0.7578760046838178 0.5662809378502687 +0.3379819903774152 0.7843488125142359 0.5201587397016689 +0.7746503893874388 0.54404208556427 0.3223894901462653 +0.6762424833857329 0.3096909027609308 0.6684217593790739 +0.7952068118384303 -0.1011561765969377 0.5978407432937709 +0.9361182415423337 -0.1795770587008401 0.3023817419092033 +0.5777467983327468 -0.4173250611831379 0.7014616385268339 +0.2994166271545614 0.2150136179002493 0.9295799199105109 +0.5623828314586349 -0.7723464303375097 0.2953075387210339 +0.2757249103344169 -0.5179234431073846 0.809772240139701 +0.577313184287358 -0.02857822908601 0.8160225315947462 +0.5386170963264684 0.795865112582042 0.2765688813290775 +0.5745678182057902 0.6089374928036108 0.5468701419352591 +0.2640750467215406 0.9279472313776503 0.2630176904270309 +0.26349397886833 -0.9286880418750045 0.2609778610888406 +-0.7782808169164066 -0.5197822937894205 -0.3522858740894094 +-0.7832272498784564 0.5233357150134625 -0.335670678551431 +-0.3356706794739734 -0.1837710655065382 -0.9238795324197158 +-0.5233357175261585 0.7832272477702242 -0.3356706795531307 +-0.3223894903743268 -0.5440420859519188 -0.7746503890202773 +-0.54510520194119 0.5451051986321889 -0.636962040659266 +-0.7050491528145805 0.3255910057135039 -0.629996975479971 +-0.9313698262072214 -0.2067000597440215 -0.2997087455056128 +-0.769142567051783 -0.2521831216418175 -0.5872166420563893 +-0.4625858909497461 -0.7952774519371975 -0.3918520995662882 +-0.5066801882500737 0.3013212963140176 -0.8077627518167138 +-0.5595946567772138 -0.5549240882847868 -0.6155591574720422 +-0.2885513214281583 0.1343905017269157 -0.9479859323574646 +-0.6326688931838378 0.01593457962769268 -0.7742584586361497 +-0.294002442557577 0.5074470400059689 -0.8099753486121409 +-0.2206785158701794 -0.8109710613787934 -0.5418735371278878 +-0.9037335009357622 0.1116491840495477 -0.4132798313340016 +-0.2761954901451649 0.7695834710164464 -0.5757233123313235 +-0.2657595433294145 0.9261685617758262 -0.2675512293140113 +-0.5610283527690639 -0.3131152481702204 -0.7662936961456306 +-0.8986511462892972 -0.2234519520144048 0.3774855525896111 +-0.7832272498729063 0.5233357150155545 0.3356706785611194 +-0.4063938126531793 0.1675901986980527 0.898196857229839 +-0.3862195463776329 -0.4624241289548164 0.7981217870452071 +-0.5163412162948556 0.7883957432990804 0.3344008078684016 +-0.3223894901399288 0.5440420855535103 0.7746503893976325 +-0.5613920645927912 -0.7538101640409378 0.3414808726719812 +-0.6259579262136886 0.3574127511108542 0.6931325991133507 +-0.5654909040789513 -0.2106776780151426 0.7973925967740848 +-0.2733654792854793 -0.2010368837014314 0.9406675747184216 +-0.6948473160149551 -0.4285218225779893 0.5775432927506401 +-0.7787080013966404 -0.546208098467257 0.3086592971702507 +-0.9346316977779511 0.1510686554600859 0.3219345443505349 +-0.2953075384818359 -0.7723464300921336 0.5623828319212242 +-0.2762115444539761 0.7961810039040885 0.5383335320529707 +-0.788420074759952 0.01466700895224715 0.6149623277599258 +-0.5809154623227699 0.5832144663068862 0.5678011200452993 +-0.2626771544471102 0.9281258472912962 0.2637861333000328 +-0.2665923219327522 -0.9241423246462652 0.2736594556815108 +-0.8002939633135487 0.3205923101786786 0.5067051834530513 +72 89 83 +78 86 83 +83 89 78 +81 89 74 +78 89 81 +78 81 70 +77 78 70 +77 86 78 +4 86 77 +4 77 14 +14 77 70 +14 70 15 +70 81 79 +70 79 15 +15 79 16 +16 79 28 +79 85 28 +16 28 5 +26 72 7 +72 82 7 +72 83 82 +74 89 72 +26 74 72 +27 74 26 +27 85 74 +74 85 81 +81 85 79 +28 85 27 +26 36 27 +36 44 27 +44 46 37 +36 46 44 +27 44 28 +28 17 5 +28 44 37 +28 37 17 +37 46 32 +18 37 32 +17 37 18 +18 32 19 +32 46 38 +38 46 36 +34 45 38 +45 47 38 +38 47 41 +41 47 39 +38 41 32 +32 41 19 +19 41 2 +2 41 39 +34 36 26 +34 38 36 +7 34 26 +25 43 7 +7 43 34 +43 45 34 +42 45 43 +42 43 25 +2 39 8 +8 39 33 +39 40 33 +39 47 40 +40 47 45 +40 48 33 +42 48 40 +40 45 42 +8 33 9 +9 49 10 +33 49 9 +48 49 33 +35 49 48 +35 50 49 +49 50 10 +10 50 3 +3 50 23 +23 50 35 +23 35 24 +35 48 42 +35 42 24 +24 42 25 +25 84 24 +84 87 24 +24 87 23 +73 87 75 +75 87 84 +75 80 76 +75 76 71 +83 86 76 +80 83 76 +82 83 80 +7 82 25 +82 84 25 +80 84 82 +75 84 80 +13 86 4 +71 86 13 +76 86 71 +12 73 71 +73 75 71 +12 71 13 +11 73 12 +11 88 73 +73 88 87 +87 88 23 +3 88 11 +23 88 3 +3 107 22 +11 107 3 +22 107 104 +104 107 94 +94 107 11 +12 94 11 +13 91 12 +91 106 94 +91 94 12 +91 109 106 +102 109 91 +13 102 91 +4 102 13 +95 97 92 +20 95 92 +20 92 6 +97 105 92 +97 109 105 +105 109 102 +106 109 97 +95 106 97 +104 106 95 +94 106 104 +22 104 21 +21 104 95 +21 95 20 +20 52 21 +52 56 21 +52 67 56 +21 56 22 +56 68 22 +22 68 3 +3 68 10 +10 68 66 +66 68 56 +56 67 66 +66 67 57 +9 66 57 +10 66 9 +9 57 8 +57 67 58 +58 67 52 +58 65 59 +62 65 58 +58 59 53 +57 58 53 +8 57 53 +8 53 2 +20 62 52 +52 62 58 +51 62 6 +6 62 20 +31 51 6 +51 65 62 +51 64 61 +31 64 51 +53 60 2 +2 60 19 +19 60 54 +54 60 59 +59 60 53 +61 65 51 +59 65 61 +59 61 54 +55 63 61 +19 54 18 +18 63 17 +54 63 18 +61 63 54 +55 69 63 +63 69 17 +17 69 5 +5 69 29 +29 69 55 +29 55 30 +61 64 55 +55 64 30 +30 64 31 +30 103 29 +100 103 93 +93 103 30 +31 93 30 +31 99 93 +93 99 98 +98 99 92 +92 99 6 +6 99 31 +5 108 16 +29 108 5 +103 108 29 +96 108 103 +16 108 96 +96 101 15 +16 96 15 +100 101 96 +15 101 14 +14 101 90 +14 90 4 +90 102 4 +90 105 102 +90 101 100 +98 105 100 +100 105 90 +92 105 98 +98 100 93 +96 103 100 diff --git a/examples/customexcitation.jl b/examples/customexcitation.jl new file mode 100644 index 00000000..b951b44e --- /dev/null +++ b/examples/customexcitation.jl @@ -0,0 +1,41 @@ +macro testitem(name, tags, code) esc(code) end + +using BEAST, Test + +@testitem "example: custom excitation" tags=[:example] begin + using CompScienceMeshes + + κ = 1.0 + + # Custom excitations can be defined as functions of a point in Cartesian space, + # complemented with a function specifying the return type. + E(x) = point(ComplexF64, 1.0, 1.0im, 0.0) * exp(-im*κ*x[3]) + BEAST.scalartype(::typeof(E)) = ComplexF64 + + # Such a custom field plays nicely with the tangential trace + # operators defined as part of the BEAST framework + e = (n × E) × n + + fn = joinpath(pathof(BEAST), "../../examples/assets/sphere45.in") + Γ = readmesh(fn) + # Γ = meshsphere(radius=1.0, h=0.1) + RT = raviartthomas(Γ) + + t = Maxwell3D.singlelayer(wavenumber=κ) + + @hilbertspace j + @hilbertspace k + + Txx = assemble(t[k,j], j∈RT, k∈RT) + ex = assemble(e[k], k∈RT) + + iTXX = BEAST.GMRESSolver(Txx; maxiter=1000, reltol=1e-5) + uX, ch = solve(iTXX, ex) + @test ch.isconverged +end + +using LinearAlgebra +import Plotly + +fcr, geo = facecurrents(uX, RT) +Plotly.plot(patch(geo, norm.(fcr))) \ No newline at end of file diff --git a/src/excitation.jl b/src/excitation.jl index e6a88f78..5051517e 100644 --- a/src/excitation.jl +++ b/src/excitation.jl @@ -1,6 +1,7 @@ -abstract type Functional end +abstract type Functional{T} end +scalartype(::Functional{T}) where {T} = T defaultquadstrat(fn::Functional, basis) = SingleNumQStrat(8) quaddata(fn::Functional, refs, cells, qs::SingleNumQStrat) = @@ -137,3 +138,64 @@ function celltestvalues(tshs::subReferenceSpace{T,D}, tcell, field, qr) where {T return interactions end + + + +### Framework allowing to create Functional from Field by taking the trace +mutable struct CrossTraceMW{T,F} <: Functional{T} + field::F +end + +mutable struct TangTraceMW{T,F} <: Functional{T} + field::F +end + +CrossTraceMW(f::F) where {F} = CrossTraceMW{scalartype(f), F}(f) +CrossTraceMW{T}(f::F) where {T,F} = CrossTraceMW{T,F}(f) + +TangTraceMW(f::F) where {F} = TangTraceMW{scalartype(f), F}(f) +TangTraceMW{T}(f::F) where {T,F} = TangTraceMW{T,F}(f) + +# scalartype(x::CrossTraceMW) = scalartype(x.field) +# scalartype(::CrossTraceMW{T}) where {T} = T +# scalartype(::TangTraceMW{T}) where {T} = T + + +# scalartype(t::TangTraceMW) = scalartype(t.field) + +cross(::NormalVector, p) = CrossTraceMW(p) +cross(t::CrossTraceMW, ::NormalVector) = TangTraceMW(t.field) + +function (ϕ::CrossTraceMW)(p) + F = ϕ.field + x = cartesian(p) + n = normal(p) + return n × F(x) +end + +function (ϕ::TangTraceMW)(p) + F = ϕ.field + x = cartesian(p) + n = normal(p) + return (n × F(x)) × n +end + + + +struct NDotTrace{T,F} <: Functional{T} + field::F +end + + +NDotTrace(f::F) where {F} = NDotTrace{scalartype(f), F}(f) +NDotTrace{T}(f::F) where {T,F} = NDotTrace{T,F}(f) +LinearAlgebra.dot(::NormalVector, f) = NDotTrace(f) + +# scalartype(s::NDotTrace{T}) where {T} = T + +(ϕ::NDotTrace)(p) = dot(normal(p), ϕ.field(cartesian(p))) + +integrand(::Any, testvals, fieldval) = dot(testvals[1], fieldval) +# integrand(::TangTraceMW, gx, ϕx) = gx[1] ⋅ ϕx +# integrand(::CrossTraceMW, test_vals, field_val) = test_vals[1] ⋅ field_val +# integrand(::NDotTrace, g, ϕ) = dot(g.value, ϕ) \ No newline at end of file diff --git a/src/helmholtz2d/helmholtzop.jl b/src/helmholtz2d/helmholtzop.jl index ee3806f1..117f2fb2 100644 --- a/src/helmholtz2d/helmholtzop.jl +++ b/src/helmholtz2d/helmholtzop.jl @@ -119,21 +119,21 @@ function integrand(biop::DoubleLayerTransposed, kernel, fp, mp, fq, mq) fp[1] * dot(np, kernel.gradgreen) * fq[1] end -mutable struct PlaneWaveDirichlet{T,P} <: Functional +mutable struct PlaneWaveDirichlet{T,P} <: Functional{T} wavenumber::T direction::P end scalartype(x::PlaneWaveDirichlet{T}) where {T} = complex(T) -mutable struct PlaneWaveNeumann{T,P} <: Functional +mutable struct PlaneWaveNeumann{T,P} <: Functional{T} wavenumber::T direction::P end scalartype(x::PlaneWaveNeumann{T}) where {T} = complex(T) -mutable struct ScalarTrace{T,F} <: Functional +mutable struct ScalarTrace{T,F} <: Functional{T} field::F end diff --git a/src/helmholtz3d/hh3dexc.jl b/src/helmholtz3d/hh3dexc.jl index 59d96e30..9333e6ab 100644 --- a/src/helmholtz3d/hh3dexc.jl +++ b/src/helmholtz3d/hh3dexc.jl @@ -122,7 +122,7 @@ end dot(::NormalVector, m::gradHH3DMonopole) = NormalDerivative(HH3DMonopole(m.position, m.gamma, m.amplitude)) -mutable struct DirichletTrace{T,F} <: Functional +mutable struct DirichletTrace{T,F} <: Functional{T} field::F end @@ -138,7 +138,7 @@ end integrand(::DirichletTrace, test_vals, field_vals) = dot(test_vals[1], field_vals) -struct NormalDerivative{T,F} <: Functional +struct NormalDerivative{T,F} <: Functional{T} field::F end diff --git a/src/maxwell/mwexc.jl b/src/maxwell/mwexc.jl index 6361c5e5..ffc2ee0b 100644 --- a/src/maxwell/mwexc.jl +++ b/src/maxwell/mwexc.jl @@ -146,51 +146,24 @@ end *(a::Number, d::DipoleMW) = DipoleMW(d.location, a .* d.orientation, d.gamma) *(a::Number, d::curlDipoleMW) = curlDipoleMW(d.location, a .* d.orientation, d.gamma) -mutable struct CrossTraceMW{F} <: Functional - field::F -end -scalartype(x::CrossTraceMW) = scalartype(x.field) -mutable struct TangTraceMW{F} <: Functional - field::F -end -scalartype(t::TangTraceMW) = scalartype(t.field) +# cross(::NormalVector, p::PlaneWaveMW) = CrossTraceMW(p) +# cross(::NormalVector, p::Dipole) = CrossTraceMW(p) + + + + + + -cross(::NormalVector, p::Function) = CrossTraceMW(p) -cross(::NormalVector, p::PlaneWaveMW) = CrossTraceMW(p) -cross(::NormalVector, p::Dipole) = CrossTraceMW(p) -cross(t::CrossTraceMW, ::NormalVector) = TangTraceMW(t.field) -function (ϕ::CrossTraceMW)(p) - F = ϕ.field - x = cartesian(p) - n = normal(p) - return n × F(x) -end -function (ϕ::TangTraceMW)(p) - F = ϕ.field - x = cartesian(p) - n = normal(p) - return (n × F(x)) × n -end -integrand(::TangTraceMW, gx, ϕx) = gx[1] ⋅ ϕx -integrand(::CrossTraceMW, test_vals, field_val) = test_vals[1] ⋅ field_val -struct NDotTrace{T,F} <: Functional - field::F -end -NDotTrace(f::F) where {F} = NDotTrace{scalartype(f), F}(f) -NDotTrace{T}(f::F) where {T,F} = NDotTrace{T,F}(f) -scalartype(s::NDotTrace{T}) where {T} = T -(ϕ::NDotTrace)(p) = dot(normal(p), ϕ.field(cartesian(p))) -integrand(::NDotTrace, g, ϕ) = dot(g.value, ϕ) -LinearAlgebra.dot(::NormalVector, f) = NDotTrace(f) @@ -216,8 +189,8 @@ mutable struct CurlCurlGreen{T,U,V} position::V end -cross(::NormalVector, p::CurlGreen) = CrossTraceMW(p) -cross(::NormalVector, p::CurlCurlGreen) = CrossTraceMW(p) +# cross(::NormalVector, p::CurlGreen) = CrossTraceMW(p) +# cross(::NormalVector, p::CurlCurlGreen) = CrossTraceMW(p) function (f::CurlCurlGreen)(x) γ = im * f.wavenumber diff --git a/src/maxwell/sourcefield.jl b/src/maxwell/sourcefield.jl index 8a6222e9..4520e89f 100644 --- a/src/maxwell/sourcefield.jl +++ b/src/maxwell/sourcefield.jl @@ -1,6 +1,6 @@ -struct SourceField{F} <: Functional - field::F -end +# struct SourceField{T,F} <: Functional{T} +# field::F +# end -(s::SourceField)(p) = s.f(cartesian(p)) -integrand(f::SourceField, tval, fval) = dot(fval, tval.value) +# (s::SourceField)(p) = s.f(cartesian(p)) +# integrand(f::SourceField, tval, fval) = dot(fval, tval.value) diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 23715ae5..2419d8d7 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -286,4 +286,9 @@ end function assemble(bf::BilForm, pairs::Pair...) dbf = discretise(bf, pairs...) assemble(dbf) +end + +function assemble(bf::LinForm, pairs::Pair...) + dbf = discretise(bf, pairs...) + assemble(dbf) end \ No newline at end of file diff --git a/src/volumeintegral/vieexc.jl b/src/volumeintegral/vieexc.jl index 689edc24..57687d76 100644 --- a/src/volumeintegral/vieexc.jl +++ b/src/volumeintegral/vieexc.jl @@ -1,4 +1,4 @@ -mutable struct PlaneWaveVIE{T,P} <: Functional +mutable struct PlaneWaveVIE{T,P} <: Functional{T} direction::P polarisation::P wavenumber::T @@ -55,7 +55,7 @@ integrand(::PlaneWaveVIE, test_vals, field_val) = test_vals[1] ⋅ field_val # Excitation for Lippmann Schwinger Volume Integral Equation -mutable struct LinearPotentialVIE{T,P} <: Functional +mutable struct LinearPotentialVIE{T,P} <: Functional{T} direction::P amplitude::T end diff --git a/test/runtests.jl b/test/runtests.jl index 15214f95..fde4a5bc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -84,7 +84,7 @@ include("test_gridfunction.jl") include("test_hh_lsvie.jl") using TestItemRunner -@run_package_tests +@run_package_tests filter=ti->!(:example in ti.tags) try From d37954dd4025a98576ef06e01acca61142b4191d Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 21 Mar 2025 14:43:40 +0100 Subject: [PATCH 458/528] assemble of localop containing empty blocks --- src/localop.jl | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/localop.jl b/src/localop.jl index 419c034b..c8aca618 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -62,6 +62,9 @@ function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, quadstrat=defaultquadstrat) quadstrat = quadstrat(biop, tfs, bfs) + + numfunctions(tfs) == 0 && return + numfunctions(bfs) == 0 && return if geometry(tfs) == geometry(bfs) return assemble_local_matched!(biop, tfs, bfs, store; quadstrat) @@ -399,3 +402,30 @@ function cellinteractions(biop, trefs::U, brefs::V, cell, qr) where {U<:RefSpace return zlocal end + + +@testitem "assemble!: zero sized block" begin + using CompScienceMeshes + + fn = joinpath(pathof(BEAST), "../../examples/assets/sphere45.in") + m1 = readmesh(fn) + m2 = m1[Int[]] + + X = BEAST.DirectProductSpace([raviartthomas(m) for m in [m1, m2]]) + Id = BEAST.Identity() + + @hilbertspace j[1:2] + @hilbertspace k[1:2] + a = Id[k[1],j[1]] + Id[k[2],j[2]] + + A = assemble(a, X, X) + import BEAST.BlockArrays + + n1 = numfunctions(X[1]) + n2 = numfunctions(X[2]) + + @test n2 == 0 + + @test BlockArrays.blocksize(A) == (2,2) + @test BlockArrays.blocksizes(A) == ([n1,n2], [n1,n2]) +end \ No newline at end of file From 96964217d55de1c0ec7ac701d56e50d53c1194cb Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 27 Mar 2025 12:16:41 +0100 Subject: [PATCH 459/528] strace method for incident scalar fields taking a single arg --- src/helmholtz2d/helmholtzop.jl | 21 ++++++++++++++++++--- src/timedomain/tdintegralop.jl | 4 ++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/helmholtz2d/helmholtzop.jl b/src/helmholtz2d/helmholtzop.jl index 117f2fb2..5668422f 100644 --- a/src/helmholtz2d/helmholtzop.jl +++ b/src/helmholtz2d/helmholtzop.jl @@ -119,29 +119,44 @@ function integrand(biop::DoubleLayerTransposed, kernel, fp, mp, fq, mq) fp[1] * dot(np, kernel.gradgreen) * fq[1] end -mutable struct PlaneWaveDirichlet{T,P} <: Functional{T} +struct PlaneWaveDirichlet{T,P} <: Functional{T} wavenumber::T direction::P end scalartype(x::PlaneWaveDirichlet{T}) where {T} = complex(T) -mutable struct PlaneWaveNeumann{T,P} <: Functional{T} +struct PlaneWaveNeumann{T,P} <: Functional{T} wavenumber::T direction::P end scalartype(x::PlaneWaveNeumann{T}) where {T} = complex(T) -mutable struct ScalarTrace{T,F} <: Functional{T} +struct ScalarTrace{T,F} <: Functional{T} field::F end ScalarTrace(f::F) where {F} = ScalarTrace{scalartype(f), F}(f) ScalarTrace{T}(f::F) where {T,F} = ScalarTrace{T,F}(f) +strace(f::F) where {F} = ScalarTrace{scalartype(f), F}(f) strace(f, mesh::Mesh) = ScalarTrace(f) +@testitem "scalar trace" begin + using CompScienceMeshes + fn = joinpath(pathof(BEAST), "../../test/assets/sphere45.in") + Γ = readmesh(fn) + X = lagrangecxd0(Γ) + + U = Helmholtz3D.planewave(wavenumber=1.0, direction=ẑ) + u = strace(U) + + ux = assemble(u, X) + @test u isa BEAST.ScalarTrace + @test BEAST.scalartype(u) == ComplexF64 +end + (s::ScalarTrace)(x) = s.field(cartesian(x)) integrand(s::ScalarTrace, tx, fx) = dot(tx.value, fx) scalartype(s::ScalarTrace{T}) where {T} = T diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index 8d25dafd..ac231efb 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -14,10 +14,10 @@ function assemble(operator::AbstractSpaceTimeOperator, test_functions, trial_fun if stagedtimestep return assemble(RungeKuttaConvolutionQuadrature(operator), test_functions, trial_functions) end - Z, store = allocatestorage(operator, test_functions, trial_functions, + freeze, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) assemble!(operator, test_functions, trial_functions, store, threading; quadstrat) - return Z() + return freeze() end From da4811dbd2820551cb1f26fd257141772e4417f7 Mon Sep 17 00:00:00 2001 From: krcools Date: Thu, 27 Mar 2025 12:36:01 +0100 Subject: [PATCH 460/528] Update CI.yml --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 3de4984d..11f843ce 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -27,7 +27,7 @@ jobs: with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - - uses: actions/cache@v1 + - uses: actions/cache@v4 env: cache-name: cache-artifacts with: @@ -42,4 +42,4 @@ jobs: - uses: julia-actions/julia-processcoverage@v1 - uses: codecov/codecov-action@v1 with: - file: lcov.info \ No newline at end of file + file: lcov.info From a27fb5eef81afd3ea198b66a23a5d5700eb88677 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 27 Mar 2025 13:14:54 +0100 Subject: [PATCH 461/528] fixed path to sphere45 --- src/helmholtz2d/helmholtzop.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helmholtz2d/helmholtzop.jl b/src/helmholtz2d/helmholtzop.jl index 5668422f..61131029 100644 --- a/src/helmholtz2d/helmholtzop.jl +++ b/src/helmholtz2d/helmholtzop.jl @@ -145,7 +145,7 @@ strace(f, mesh::Mesh) = ScalarTrace(f) @testitem "scalar trace" begin using CompScienceMeshes - fn = joinpath(pathof(BEAST), "../../test/assets/sphere45.in") + fn = joinpath(dirname(pathof(BEAST)), "../test/assets/sphere45.in") Γ = readmesh(fn) X = lagrangecxd0(Γ) From e434500a7aab2b66cfdc4a791b04eaa725d93e92 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 27 Mar 2025 13:31:12 +0100 Subject: [PATCH 462/528] broken asset path fixed in localop --- src/localop.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/localop.jl b/src/localop.jl index c8aca618..a788bb17 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -407,7 +407,7 @@ end @testitem "assemble!: zero sized block" begin using CompScienceMeshes - fn = joinpath(pathof(BEAST), "../../examples/assets/sphere45.in") + fn = joinpath(dirname(pathof(BEAST)), "../examples/assets/sphere45.in") m1 = readmesh(fn) m2 = m1[Int[]] From 6cd6e5a8c444fa49f232135bac661a8153ad4a58 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 27 Mar 2025 14:40:36 +0100 Subject: [PATCH 463/528] fixed broken asset path in numspacenumtimeqstrat.jl --- .../strategies/timedomain/excitation/numspacenumtimeqstrat.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quadrature/strategies/timedomain/excitation/numspacenumtimeqstrat.jl b/src/quadrature/strategies/timedomain/excitation/numspacenumtimeqstrat.jl index 1fa95dac..a3655512 100644 --- a/src/quadrature/strategies/timedomain/excitation/numspacenumtimeqstrat.jl +++ b/src/quadrature/strategies/timedomain/excitation/numspacenumtimeqstrat.jl @@ -62,7 +62,7 @@ end using CompScienceMeshes using LinearAlgebra - fn = joinpath(pathof(BEAST), "../../test/assets/sphere45.in") + fn = joinpath(dirname(pathof(BEAST)), "../test/assets/sphere45.in") mesh = readmesh(fn) RT = raviartthomas(mesh) From 3af507e7d7bc431c9c291418d7606d880f05a9da Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 28 Mar 2025 14:47:32 +0100 Subject: [PATCH 464/528] Increase min support version CSM in compat --- Project.toml | 2 +- src/utils/zeromap.jl | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Project.toml b/Project.toml index cf1fbc32..4eb11562 100644 --- a/Project.toml +++ b/Project.toml @@ -37,7 +37,7 @@ AbstractTrees = "0.4.4" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 1" CollisionDetection = "0.1.6" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.8.3" +CompScienceMeshes = "0.9" Compat = "2, 3, 4" ConvolutionOperators = "0.4" ExtendableSparse = "1.4" diff --git a/src/utils/zeromap.jl b/src/utils/zeromap.jl index b19d2cdc..207d6fa4 100644 --- a/src/utils/zeromap.jl +++ b/src/utils/zeromap.jl @@ -15,10 +15,6 @@ function LinearMaps._unsafe_mul!(y::AbstractVector, L::ZeroMap, x::AbstractVecto y .*= β end -# function LinearAlgebra.mul!(Y::PseudoBlockMatrix, c::Number, X::ZeroMap, a::Number, b::Number) -# @assert b == 1 -# return Y -# end function LinearMaps._unsafe_mul!(Y::AbstractMatrix, X::ZeroMap, c::Number, a::Number, b::Number) rmul!(Y, b) From f7e7f1e52bebaf2a0d7b051241662928c7408b04 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 31 Mar 2025 16:28:47 +0200 Subject: [PATCH 465/528] test use the mesh generator for which they were designed --- Project.toml | 2 +- src/bases/rtqspace.jl | 4 ++-- test/test_dipole.jl | 5 +++++ test/test_gridfunction.jl | 6 ++++-- test/test_ncrossbdm.jl | 2 +- test/test_nonconf_quadrules.jl | 4 ++-- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Project.toml b/Project.toml index 4eb11562..0587053a 100644 --- a/Project.toml +++ b/Project.toml @@ -37,7 +37,7 @@ AbstractTrees = "0.4.4" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 1" CollisionDetection = "0.1.6" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.9" +CompScienceMeshes = "0.8.3, 0.9" Compat = "2, 3, 4" ConvolutionOperators = "0.4" ExtendableSparse = "1.4" diff --git a/src/bases/rtqspace.jl b/src/bases/rtqspace.jl index 7f701ee6..ce0ad06c 100644 --- a/src/bases/rtqspace.jl +++ b/src/bases/rtqspace.jl @@ -68,7 +68,7 @@ end @testitem "RTQSpace construction" begin using CompScienceMeshes - m = CompScienceMeshes.meshrectangle(2.0, 2.0, 1.0; structured=:quadrilateral) + m = CompScienceMeshes.meshrectangle(2.0, 2.0, 1.0; element=:quadrilateral) edges = skeleton(m, 1) edges_bnd = boundary(m) # @show length(edges_bnd) @@ -85,7 +85,7 @@ end @testitem "RTQSpace assembly data" begin using CompScienceMeshes - m = CompScienceMeshes.meshrectangle(2.0, 2.0, 1.0; structured=:quadrilateral) + m = CompScienceMeshes.meshrectangle(2.0, 2.0, 1.0; element=:quadrilateral) edges = skeleton(m, 1) edges_bnd = boundary(m) pred = !in(edges_bnd) diff --git a/test/test_dipole.jl b/test/test_dipole.jl index f11e277f..02af52ea 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -6,7 +6,9 @@ using StaticArrays using LinearAlgebra using BlockArrays +# U = Float32 for U in [Float32,Float64] + @show U c = U(3e8) μ0 = U(4*π*1e-7) @@ -81,6 +83,9 @@ for U in [Float32,Float64] nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(𝓚), pts, j_BCMFIE, X) ff_E_BCMFIE = potential(MWFarField3D(𝓣), pts, j_BCMFIE, X) + @show length(pts) + @show norm.(nf_E_BCMFIE - E.(pts)) ./ norm.(E.(pts)) + @test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 diff --git a/test/test_gridfunction.jl b/test/test_gridfunction.jl index f951c7ea..d23e2ae8 100644 --- a/test/test_gridfunction.jl +++ b/test/test_gridfunction.jl @@ -10,7 +10,9 @@ U = Float64 a = U(1) # Cube between (3,3,3) and (4,4,4) -Γ = translate(CompScienceMeshes.meshcuboid(a,a,a,U(1.0)), SVector(3.0,3.0,3.0)) +Γ = translate( + CompScienceMeshes.meshcuboid(a,a,a,U(1.0); generator=:gmsh), + SVector(3.0,3.0,3.0)) C0 = lagrangec0d1(Γ) @@ -102,7 +104,7 @@ glbf_toppplate = BEAST.GlobalFunction(f, Γ, idx_topplate) ## -Γ2 = CompScienceMeshes.meshcuboid(a,a,a,U(1.0)) +Γ2 = CompScienceMeshes.meshcuboid(a,a,a,U(1.0); generator=:gmsh) coeffsΓ2 = [v[3] for v in vertices(Γ2)] diff --git a/test/test_ncrossbdm.jl b/test/test_ncrossbdm.jl index bb6c9f53..753c0845 100644 --- a/test/test_ncrossbdm.jl +++ b/test/test_ncrossbdm.jl @@ -21,7 +21,7 @@ end #testing their connectivity on a mesh -m=meshsphere(radius=1,h=1.0) +m=meshsphere(radius=1.0, h=1.0) nodes = skeleton(m,0) edges = skeleton(m,1) diff --git a/test/test_nonconf_quadrules.jl b/test/test_nonconf_quadrules.jl index 01776b2a..e81bb5a3 100644 --- a/test/test_nonconf_quadrules.jl +++ b/test/test_nonconf_quadrules.jl @@ -4,8 +4,8 @@ h1 = 0.2 h2 = 0.176 - m1 = meshcuboid(1.0, 1.0, 1.0, h1) - m2 = meshcuboid(1.0, 1.0, 1.0, h2) + m1 = meshcuboid(1.0, 1.0, 1.0, h1; generator=:gmsh) + m2 = meshcuboid(1.0, 1.0, 1.0, h2; generator=:gmsh) E = Maxwell3D.planewave(;direction=point(0,0,1), polarization=point(1,0,0), wavenumber=0.0) e = n × E From 05d7f195d8fad5d9fdfd1cc40edab0bd2c5624fa Mon Sep 17 00:00:00 2001 From: krcools Date: Mon, 31 Mar 2025 17:01:08 +0200 Subject: [PATCH 466/528] Update CI.yml --- .github/workflows/CI.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 11f843ce..216a0097 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -40,6 +40,8 @@ jobs: - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v1 + - uses: codecov/codecov-action@v4 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: file: lcov.info From 62e516ff9220230e2e88f81e9921ec69ce02f739 Mon Sep 17 00:00:00 2001 From: krcools Date: Mon, 31 Mar 2025 17:27:19 +0200 Subject: [PATCH 467/528] Update CI.yml --- .github/workflows/CI.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 216a0097..245016c4 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -41,7 +41,7 @@ jobs: - uses: julia-actions/julia-runtest@v1 - uses: julia-actions/julia-processcoverage@v1 - uses: codecov/codecov-action@v4 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: - file: lcov.info + files: lcov.info + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false From 8dc730d64f2a585cd80f267f511bdafc11f536bb Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 1 Apr 2025 16:08:47 +0200 Subject: [PATCH 468/528] docs can show facecurrents --- docs/Manifest.toml | 223 +++++++++++++++++++++++++++++--------- docs/Project.toml | 2 + docs/src/geometry/flat.md | 14 +++ docs/src/manual/usage.md | 18 ++- 4 files changed, 201 insertions(+), 56 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index b2f1dd7a..464b48f2 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.11.3" manifest_format = "2.0" -project_hash = "c93b20c7adfdce7dcd32bda66638d598d39160a5" +project_hash = "53b6c150b595d31c87d8ddd4bda5237b234b4b75" [[deps.ANSIColoredPrinters]] git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" @@ -87,9 +87,9 @@ version = "1.0.9+0" [[deps.Cairo_jll]] deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "009060c9a6168704143100f36ab08f06c2af4642" +git-tree-sha1 = "2ac646d71d0d24b44f3f8c84da8c9f4d70fb67df" uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.18.2+1" +version = "1.18.4+0" [[deps.ClusterTrees]] deps = ["DelimitedFiles", "LinearAlgebra", "Pkg"] @@ -109,6 +109,38 @@ git-tree-sha1 = "88a33f2fba4ef1065edd06164c84d8b03ee4d049" uuid = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" version = "0.1.6" +[[deps.ColorSchemes]] +deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] +git-tree-sha1 = "403f2d8e209681fcbd9468a8514efff3ea08452e" +uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" +version = "3.29.0" + +[[deps.ColorTypes]] +deps = ["FixedPointNumbers", "Random"] +git-tree-sha1 = "c7acce7a7e1078a20a285211dd73cd3941a871d6" +uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +version = "0.12.0" +weakdeps = ["StyledStrings"] + + [deps.ColorTypes.extensions] + StyledStringsExt = "StyledStrings" + +[[deps.ColorVectorSpace]] +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] +git-tree-sha1 = "8b3b6f87ce8f65a2b4f857528fd8d70086cd72b1" +uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" +version = "0.11.0" +weakdeps = ["SpecialFunctions"] + + [deps.ColorVectorSpace.extensions] + SpecialFunctionsExt = "SpecialFunctions" + +[[deps.Colors]] +deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] +git-tree-sha1 = "64e15186f0aa277e174aa81798f7eb8598e0157e" +uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" +version = "0.13.0" + [[deps.Combinatorics]] git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" @@ -116,9 +148,9 @@ version = "1.0.2" [[deps.CompScienceMeshes]] deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Permutations", "Requires", "SparseArrays", "StaticArrays", "TestItems"] -git-tree-sha1 = "6bb7cd3831707872fe404e01d156e1b149fcfa3a" +path = "C:\\Users\\krcools\\.julia\\dev\\CompScienceMeshes" uuid = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" -version = "0.8.3" +version = "0.9.1" [[deps.Compat]] deps = ["TOML", "UUIDs"] @@ -143,9 +175,9 @@ version = "0.4.1" [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "1d0a14036acb104d9e89698bd408f63ab58cdc82" +git-tree-sha1 = "4e1fe97fdaed23e9dc21d4d664bea76b65fc50a0" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.20" +version = "0.18.22" [[deps.Dates]] deps = ["Printf"] @@ -164,10 +196,9 @@ uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" version = "1.11.0" [[deps.DocStringExtensions]] -deps = ["LibGit2"] -git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" +git-tree-sha1 = "e7b7e6f178525d17c720ab9c081e4ef04429f860" uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.9.3" +version = "0.9.4" [[deps.Documenter]] deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] @@ -194,9 +225,9 @@ version = "2.6.5+0" [[deps.ExtendableSparse]] deps = ["DocStringExtensions", "ILUZero", "LinearAlgebra", "Printf", "SparseArrays", "Sparspak", "StaticArrays", "SuiteSparse", "Test"] -git-tree-sha1 = "d70aad52f10c8fc945873ad7a8bd4a1a285c5d99" +git-tree-sha1 = "c04d2c808f0b595dc3be5fa98d107213f81e77fb" uuid = "95c220a8-a1cf-11e9-0c77-dbfce5f500b3" -version = "1.7.0" +version = "1.7.1" [deps.ExtendableSparse.extensions] ExtendableSparseAMGCLWrapExt = "AMGCLWrap" @@ -238,15 +269,9 @@ version = "1.0.2" [[deps.FileIO]] deps = ["Pkg", "Requires", "UUIDs"] -git-tree-sha1 = "2dd20384bf8c6d411b5c7370865b1e9b26cb2ea3" +git-tree-sha1 = "91e0e5c68d02bcdaae76d3c8ceb4361e8f28d2e9" uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -version = "1.16.6" - - [deps.FileIO.extensions] - HTTPExt = "HTTP" - - [deps.FileIO.weakdeps] - HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" +version = "1.16.5" [[deps.FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" @@ -268,6 +293,12 @@ version = "1.13.0" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +[[deps.FixedPointNumbers]] +deps = ["Statistics"] +git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" +uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" +version = "0.8.5" + [[deps.Fontconfig_jll]] deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] git-tree-sha1 = "21fac3c77d7b5a9fc03b0ec503aa1a6392c34d2b" @@ -276,9 +307,9 @@ version = "2.15.0+0" [[deps.FreeType2_jll]] deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "786e968a8d2fb167f2e4880baba62e0e26bd8e4e" +git-tree-sha1 = "2c5512e11c791d1baed2049c5652441b28fc6a31" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.13.3+1" +version = "2.13.4+0" [[deps.GLU_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] @@ -305,15 +336,15 @@ version = "1.3.1" [[deps.Git_jll]] deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "399f4a308c804b446ae4c91eeafadb2fe2c54ff9" +git-tree-sha1 = "2f6d6f7e6d6de361865d4394b802c02fc944fc7c" uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" -version = "2.47.1+0" +version = "2.49.0+0" [[deps.Glib_jll]] deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "b0036b392358c80d2d2124746c2bf3d48d457938" +git-tree-sha1 = "fee60557e4f19d0fe5cd169211fdda80e494f4e8" uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.82.4+0" +version = "2.84.0+0" [[deps.GmshTools]] deps = ["Libdl", "gmsh_jll"] @@ -339,6 +370,12 @@ git-tree-sha1 = "f93a9ce66cd89c9ba7a4695a47fd93b4c6bc59fa" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" version = "2.12.0+0" +[[deps.HypertextLiteral]] +deps = ["Tricks"] +git-tree-sha1 = "7134810b1afce04bbc1045ca1985fbe81ce17653" +uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2" +version = "0.9.5" + [[deps.ILUZero]] deps = ["LinearAlgebra", "SparseArrays"] git-tree-sha1 = "b007cfc7f9bee9a958992d2301e9c5b63f332a90" @@ -393,9 +430,9 @@ version = "0.21.4" [[deps.JSON3]] deps = ["Dates", "Mmap", "Parsers", "PrecompileTools", "StructTypes", "UUIDs"] -git-tree-sha1 = "1d322381ef7b087548321d3f878cb4c9bd8f8f9b" +git-tree-sha1 = "196b41e5a854b387d99e5ede2de3fcb4d0422aae" uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" -version = "1.14.1" +version = "1.14.2" [deps.JSON3.extensions] JSON3ArrowExt = ["ArrowTypes"] @@ -433,6 +470,11 @@ git-tree-sha1 = "1c602b1127f4751facb671441ca72715cc95938a" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" version = "2.10.3+0" +[[deps.LaTeXStrings]] +git-tree-sha1 = "dda21b8cbd6a6c40d9d02a73230f9d70fed6918c" +uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" +version = "1.4.0" + [[deps.LazilyInitializedFields]] git-tree-sha1 = "0f2da712350b020bc3957f269c9caad516383ee0" uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf" @@ -473,10 +515,10 @@ uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" version = "1.11.0" [[deps.Libffi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "27ecae93dd25ee0909666e6835051dd684cc035e" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "c8da7e6a91781c41a863611c7e966098d783c57a" uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.2.2+2" +version = "3.4.7+0" [[deps.Libgcrypt_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] @@ -587,9 +629,9 @@ version = "5.6.0+0" [[deps.MPICH_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "e7159031670cee777cc2840aef7a521c3603e36c" +git-tree-sha1 = "3aa3210044138a1749dbd350a9ba8680869eb503" uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" -version = "4.3.0+0" +version = "4.3.0+1" [[deps.MPIPreferences]] deps = ["Libdl", "Preferences"] @@ -599,9 +641,9 @@ version = "0.1.11" [[deps.MPItrampoline_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "97aac4a518b6f01851f8821272780e1ba56fe90d" +git-tree-sha1 = "ff91ca13c7c472cef700f301c8d752bc2aaff1a8" uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" -version = "5.5.2+0" +version = "5.5.3+0" [[deps.Markdown]] deps = ["Base64"] @@ -650,9 +692,9 @@ uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" version = "7.7.2+0" [[deps.OffsetArrays]] -git-tree-sha1 = "5e1897147d1ff8d98883cda2be2187dcf57d8f0c" +git-tree-sha1 = "a414039192a155fb38c4599a60110f0018c6ec82" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.15.0" +version = "1.16.0" [deps.OffsetArrays.extensions] OffsetArraysAdaptExt = "Adapt" @@ -672,9 +714,9 @@ version = "0.8.1+2" [[deps.OpenMPI_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "e25c1778a98e34219a00455d6e4384e017ea9762" +git-tree-sha1 = "fcd38c48ed4ab9763eb5770cfb786174b548ae08" uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" -version = "4.1.6+0" +version = "4.1.8+0" [[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -698,6 +740,18 @@ deps = ["Artifacts", "Libdl"] uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" version = "10.42.0+1" +[[deps.PackageExtensionCompat]] +git-tree-sha1 = "fb28e33b8a95c4cee25ce296c817d89cc2e53518" +uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" +version = "1.0.2" +weakdeps = ["Requires", "TOML"] + +[[deps.Parameters]] +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.3" + [[deps.Parsers]] deps = ["Dates", "PrecompileTools", "UUIDs"] git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" @@ -712,9 +766,9 @@ version = "0.4.23" [[deps.Pixman_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] -git-tree-sha1 = "35621f10a7531bc8fa58f74610b1bfb70a3cfc6b" +git-tree-sha1 = "db76b1ecd5e9715f3d043cec13b2ec93ce015d53" uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.43.4+0" +version = "0.44.2+0" [[deps.Pkg]] deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] @@ -725,6 +779,43 @@ weakdeps = ["REPL"] [deps.Pkg.extensions] REPLExt = "REPL" +[[deps.PlotlyBase]] +deps = ["ColorSchemes", "Colors", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "LaTeXStrings", "Logging", "Parameters", "Pkg", "REPL", "Requires", "Statistics", "UUIDs"] +git-tree-sha1 = "90af5c9238c1b3b25421f1fdfffd1e8fca7a7133" +uuid = "a03496cd-edff-5a9b-9e67-9cda94a718b5" +version = "0.8.20" + + [deps.PlotlyBase.extensions] + DataFramesExt = "DataFrames" + DistributionsExt = "Distributions" + IJuliaExt = "IJulia" + JSON3Ext = "JSON3" + + [deps.PlotlyBase.weakdeps] + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" + +[[deps.PlotlyDocumenter]] +deps = ["Downloads", "HypertextLiteral", "PackageExtensionCompat", "Random"] +git-tree-sha1 = "ff52f1faa5eefbbd96ad2f860c1a0e8131a6d76c" +uuid = "9b90f1cd-2639-4507-8b17-2fbe371ceceb" +version = "0.2.0" + + [deps.PlotlyDocumenter.extensions] + DocumenterVitepressPlotsExt = ["DocumenterVitepress", "Documenter"] + PlotlyBaseExt = "PlotlyBase" + PlotlyJSExt = "PlotlyJS" + PlotlyLightExt = "PlotlyLight" + + [deps.PlotlyDocumenter.weakdeps] + Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" + DocumenterVitepress = "4710194d-e776-4893-9690-8d956a29c365" + PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5" + PlotlyJS = "f0f68f2c-4968-5e81-91da-67840de0976a" + PlotlyLight = "ca7969ec-10b3-423e-8d99-40f33abb42bf" + [[deps.PrecompileTools]] deps = ["Preferences"] git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" @@ -771,9 +862,9 @@ version = "0.1.0" [[deps.Requires]] deps = ["UUIDs"] -git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" +git-tree-sha1 = "62389eeff14780bfe55195b7204c0d8738436d64" uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.0" +version = "1.3.1" [[deps.SCOTCH_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] @@ -793,9 +884,9 @@ version = "0.1.4" [[deps.SauterSchwabQuadrature]] deps = ["FastGaussQuadrature", "LinearAlgebra", "StaticArrays", "TestItems"] -git-tree-sha1 = "b98948c567cbe250d774d01a07833b7a329ec511" +git-tree-sha1 = "41c2ba71198ffc925603004cb19b78a4d5df9ff0" uuid = "535c7bfe-2023-5c1d-b712-654ef9d93a38" -version = "2.4.0" +version = "2.4.1" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" @@ -841,9 +932,9 @@ version = "2.5.0" [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "e3be13f448a43610f978d29b7adf78c76022467a" +git-tree-sha1 = "0feb6b9031bd5c51f9072393eb5ab3efd31bf9e4" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.12" +version = "1.9.13" [deps.StaticArrays.extensions] StaticArraysChainRulesCoreExt = "ChainRulesCore" @@ -858,6 +949,16 @@ git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682" uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" version = "1.4.3" +[[deps.Statistics]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "ae3bb1eb3bba077cd276bc5cfc337cc65c3075c0" +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +version = "1.11.1" +weakdeps = ["SparseArrays"] + + [deps.Statistics.extensions] + SparseArraysExt = ["SparseArrays"] + [[deps.StringEncodings]] deps = ["Libiconv_jll"] git-tree-sha1 = "b765e46ba27ecf6b44faf70df40c57aa3a547dcb" @@ -893,6 +994,12 @@ deps = ["ArgTools", "SHA"] uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" version = "1.10.0" +[[deps.TensorCore]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" +uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" +version = "0.1.1" + [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" @@ -908,6 +1015,11 @@ git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" version = "0.11.3" +[[deps.Tricks]] +git-tree-sha1 = "6cae795a5a9313bbb4f60683f7263318fc7d1505" +uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" +version = "0.1.10" + [[deps.TypeTree]] deps = ["InteractiveUtils"] git-tree-sha1 = "c1adbb6b9d9374babe8975a456f383a37a8e02ec" @@ -915,15 +1027,20 @@ uuid = "04da0e3b-1cad-4b2c-a963-fc1602baf1af" version = "0.3.0" [[deps.URIs]] -git-tree-sha1 = "67db6cc7b3821e19ebe75791a9dd19c9b1188f2b" +git-tree-sha1 = "cbbebadbcc76c5ca1cc4b4f3b0614b3e603b5000" uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.5.1" +version = "1.5.2" [[deps.UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" version = "1.11.0" +[[deps.UnPack]] +git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.2" + [[deps.Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" version = "1.11.0" @@ -1014,9 +1131,9 @@ version = "1.5.1+0" [[deps.YAML]] deps = ["Base64", "Dates", "Printf", "StringEncodings"] -git-tree-sha1 = "dea63ff72079443240fbd013ba006bcbc8a9ac00" +git-tree-sha1 = "b46894beba6c05cd185d174654479aaec09ea6b1" uuid = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" -version = "0.4.12" +version = "0.4.13" [[deps.Zlib_jll]] deps = ["Libdl"] @@ -1042,9 +1159,9 @@ version = "5.11.0+0" [[deps.libpng_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "055a96774f383318750a1a5e10fd4151f04c29c5" +git-tree-sha1 = "068dfe202b0a05b8332f1e8e6b4080684b9c7700" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.46+0" +version = "1.6.47+0" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] diff --git a/docs/Project.toml b/docs/Project.toml index 48345484..c325337c 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -4,4 +4,6 @@ CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244" Krylov = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" +PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5" +PlotlyDocumenter = "9b90f1cd-2639-4507-8b17-2fbe371ceceb" TypeTree = "04da0e3b-1cad-4b2c-a963-fc1602baf1af" diff --git a/docs/src/geometry/flat.md b/docs/src/geometry/flat.md index e69de29b..76bc3389 100644 --- a/docs/src/geometry/flat.md +++ b/docs/src/geometry/flat.md @@ -0,0 +1,14 @@ +# Geometry + +```@setup geo +import PlotlyBase +import PlotlyDocumenter +``` + +```@example geo +using CompScienceMeshes +Γ = meshsphere(radius=1.0, h=0.35) +pt = CompScienceMeshes.patch(Γ) +pl = PlotlyBase.Plot(pt) +PlotlyDocumenter.to_documenter(pl) # hide +``` diff --git a/docs/src/manual/usage.md b/docs/src/manual/usage.md index 625a2150..a32ed0dc 100644 --- a/docs/src/manual/usage.md +++ b/docs/src/manual/usage.md @@ -15,13 +15,18 @@ The available basis functions and corresponding geometry representations, availa The fundamental procedure is exemplified for the electric field integral equation (EFIE); further common steps are discussed afterwards: +```@setup introductory +import PlotlyBase +import PlotlyDocumenter +``` + ```@example introductory using CompScienceMeshes using BEAST # --- 1. basis functions -Γ = meshsphere(1.0, 2.5) # triangulate sphere of radius one -RT = raviartthomas(Γ) # define basis functions +Γ = meshsphere(radius=1.0, h=0.4) # triangulate sphere of radius one +RT = raviartthomas(Γ) # define basis functions # --- 2. operators & excitation 𝑇 = Maxwell3D.singlelayer(wavenumber=2.0) # integral operator @@ -63,11 +68,18 @@ This is shown in the following: ```@example introductory -using Krylov +using Krylov, LinearAlgebra # --- solve linear system iteratively u, ch = Krylov.gmres(T, -e, rtol=1e-5) +fcr, geo = facecurrents(u, RT) +pt = CompScienceMeshes.patch(Γ, norm.(fcr)) +pl = PlotlyBase.Plot(pt) +PlotlyDocumenter.to_documenter(pl) # hide +``` + +```@example introductory # --- post processing: compute scattered electric field at two Cartesian points points = [[3.0, 4.0, 2.0], [3.0, 4.0, 3.0]] EF = potential(MWSingleLayerField3D(gamma=im*2.0), points, u, RT) From 9d36385d3106bb9ff4ee64ba6b7fafad24d09fc3 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 1 Apr 2025 16:11:06 +0200 Subject: [PATCH 469/528] remove docs/Manifest.toml --- docs/Manifest.toml | 1180 -------------------------------------------- 1 file changed, 1180 deletions(-) delete mode 100644 docs/Manifest.toml diff --git a/docs/Manifest.toml b/docs/Manifest.toml deleted file mode 100644 index 464b48f2..00000000 --- a/docs/Manifest.toml +++ /dev/null @@ -1,1180 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.11.3" -manifest_format = "2.0" -project_hash = "53b6c150b595d31c87d8ddd4bda5237b234b4b75" - -[[deps.ANSIColoredPrinters]] -git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" -uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" -version = "0.0.1" - -[[deps.AbstractFFTs]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" -uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" -version = "1.5.0" - - [deps.AbstractFFTs.extensions] - AbstractFFTsChainRulesCoreExt = "ChainRulesCore" - AbstractFFTsTestExt = "Test" - - [deps.AbstractFFTs.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[deps.AbstractTrees]] -git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" -uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" -version = "0.4.5" - -[[deps.ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" -version = "1.1.2" - -[[deps.ArrayLayouts]] -deps = ["FillArrays", "LinearAlgebra"] -git-tree-sha1 = "4e25216b8fea1908a0ce0f5d87368587899f75be" -uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "1.11.1" -weakdeps = ["SparseArrays"] - - [deps.ArrayLayouts.extensions] - ArrayLayoutsSparseArraysExt = "SparseArrays" - -[[deps.Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" -version = "1.11.0" - -[[deps.BEAST]] -deps = ["AbstractTrees", "BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "ConvolutionOperators", "Distributed", "ExtendableSparse", "FFTW", "FastGaussQuadrature", "FillArrays", "Infiltrator", "InteractiveUtils", "IterativeSolvers", "LiftedMaps", "LinearAlgebra", "LinearMaps", "NestedUnitRanges", "Requires", "SauterSchwab3D", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SpecialFunctions", "StaticArrays", "TestItems", "WiltonInts84"] -path = "C:\\Users\\krcools\\.julia\\dev\\BEAST" -uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "2.5.0" - -[[deps.Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" -version = "1.11.0" - -[[deps.BibInternal]] -git-tree-sha1 = "78aa378482bf6f338eef8f2440fb62a75ab1aaa3" -uuid = "2027ae74-3657-4b95-ae00-e2f7d55c3e64" -version = "0.3.6" - -[[deps.BibParser]] -deps = ["BibInternal", "DataStructures", "Dates", "JSONSchema", "YAML"] -git-tree-sha1 = "f24884311dceb5f8eafe11809b6f1d867b489a46" -uuid = "13533e5b-e1c2-4e57-8cef-cac5e52f6474" -version = "0.2.1" - -[[deps.Bibliography]] -deps = ["BibInternal", "BibParser", "DataStructures", "Dates", "FileIO", "YAML"] -git-tree-sha1 = "520c679daed011ce835d9efa7778863aad6687ed" -uuid = "f1be7e48-bf82-45af-a471-ae754a193061" -version = "0.2.20" - -[[deps.BlockArrays]] -deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] -git-tree-sha1 = "9a9610fbe5779636f75229e423e367124034af41" -uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" -version = "0.16.43" - -[[deps.Bzip2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1b96ea4a01afe0ea4090c5c8039690672dd13f2e" -uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.9+0" - -[[deps.Cairo_jll]] -deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "2ac646d71d0d24b44f3f8c84da8c9f4d70fb67df" -uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.18.4+0" - -[[deps.ClusterTrees]] -deps = ["DelimitedFiles", "LinearAlgebra", "Pkg"] -git-tree-sha1 = "4114d60c95974edf9272d88571d14abeed598942" -uuid = "5100927d-02aa-593a-b4f9-7235df19f0db" -version = "0.2.1" - -[[deps.CodecZlib]] -deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "962834c22b66e32aa10f7611c08c8ca4e20749a9" -uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.8" - -[[deps.CollisionDetection]] -deps = ["Compat", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "88a33f2fba4ef1065edd06164c84d8b03ee4d049" -uuid = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" -version = "0.1.6" - -[[deps.ColorSchemes]] -deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] -git-tree-sha1 = "403f2d8e209681fcbd9468a8514efff3ea08452e" -uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.29.0" - -[[deps.ColorTypes]] -deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "c7acce7a7e1078a20a285211dd73cd3941a871d6" -uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.12.0" -weakdeps = ["StyledStrings"] - - [deps.ColorTypes.extensions] - StyledStringsExt = "StyledStrings" - -[[deps.ColorVectorSpace]] -deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] -git-tree-sha1 = "8b3b6f87ce8f65a2b4f857528fd8d70086cd72b1" -uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" -version = "0.11.0" -weakdeps = ["SpecialFunctions"] - - [deps.ColorVectorSpace.extensions] - SpecialFunctionsExt = "SpecialFunctions" - -[[deps.Colors]] -deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] -git-tree-sha1 = "64e15186f0aa277e174aa81798f7eb8598e0157e" -uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.13.0" - -[[deps.Combinatorics]] -git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" -uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" -version = "1.0.2" - -[[deps.CompScienceMeshes]] -deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Permutations", "Requires", "SparseArrays", "StaticArrays", "TestItems"] -path = "C:\\Users\\krcools\\.julia\\dev\\CompScienceMeshes" -uuid = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" -version = "0.9.1" - -[[deps.Compat]] -deps = ["TOML", "UUIDs"] -git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.16.0" -weakdeps = ["Dates", "LinearAlgebra"] - - [deps.Compat.extensions] - CompatLinearAlgebraExt = "LinearAlgebra" - -[[deps.CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.1.1+0" - -[[deps.ConvolutionOperators]] -deps = ["LinearAlgebra", "LinearMaps"] -git-tree-sha1 = "ae44e38013c05c7ec59f428b4ea7ad7d34926b63" -uuid = "15927181-a1bb-497c-b745-8dbf505c019d" -version = "0.4.1" - -[[deps.DataStructures]] -deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "4e1fe97fdaed23e9dc21d4d664bea76b65fc50a0" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.22" - -[[deps.Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" -version = "1.11.0" - -[[deps.DelimitedFiles]] -deps = ["Mmap"] -git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" -version = "1.9.1" - -[[deps.Distributed]] -deps = ["Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" -version = "1.11.0" - -[[deps.DocStringExtensions]] -git-tree-sha1 = "e7b7e6f178525d17c720ab9c081e4ef04429f860" -uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.9.4" - -[[deps.Documenter]] -deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] -git-tree-sha1 = "5a1ee886566f2fa9318df1273d8b778b9d42712d" -uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "1.7.0" - -[[deps.DocumenterCitations]] -deps = ["AbstractTrees", "Bibliography", "Dates", "Documenter", "Logging", "Markdown", "MarkdownAST", "OrderedCollections", "Unicode"] -git-tree-sha1 = "ca601b812efd1155a9bdf9c80e7e0428da598a08" -uuid = "daee34ce-89f3-4625-b898-19384cb65244" -version = "1.3.4" - -[[deps.Downloads]] -deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -version = "1.6.0" - -[[deps.Expat_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "d55dffd9ae73ff72f1c0482454dcf2ec6c6c4a63" -uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.6.5+0" - -[[deps.ExtendableSparse]] -deps = ["DocStringExtensions", "ILUZero", "LinearAlgebra", "Printf", "SparseArrays", "Sparspak", "StaticArrays", "SuiteSparse", "Test"] -git-tree-sha1 = "c04d2c808f0b595dc3be5fa98d107213f81e77fb" -uuid = "95c220a8-a1cf-11e9-0c77-dbfce5f500b3" -version = "1.7.1" - - [deps.ExtendableSparse.extensions] - ExtendableSparseAMGCLWrapExt = "AMGCLWrap" - ExtendableSparseAlgebraicMultigridExt = "AlgebraicMultigrid" - ExtendableSparseIncompleteLUExt = "IncompleteLU" - ExtendableSparseLinearSolveExt = "LinearSolve" - ExtendableSparsePardisoExt = "Pardiso" - - [deps.ExtendableSparse.weakdeps] - AMGCLWrap = "4f76b812-4ba5-496d-b042-d70715554288" - AlgebraicMultigrid = "2169fc97-5a83-5252-b627-83903c6c433c" - IncompleteLU = "40713840-3770-5561-ab4c-a76e7d0d7895" - LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" - Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" - -[[deps.FFTW]] -deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] -git-tree-sha1 = "7de7c78d681078f027389e067864a8d53bd7c3c9" -uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" -version = "1.8.1" - -[[deps.FFTW_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4d81ed14783ec49ce9f2e168208a12ce1815aa25" -uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" -version = "3.3.10+3" - -[[deps.FLTK_jll]] -deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "72a4842f93e734f378cf381dae2ca4542f019d23" -uuid = "4fce6fc7-ba6a-5f4c-898f-77e99806d6f8" -version = "1.3.8+0" - -[[deps.FastGaussQuadrature]] -deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "fd923962364b645f3719855c88f7074413a6ad92" -uuid = "442a2c76-b920-505d-bb47-c5924d526838" -version = "1.0.2" - -[[deps.FileIO]] -deps = ["Pkg", "Requires", "UUIDs"] -git-tree-sha1 = "91e0e5c68d02bcdaae76d3c8ceb4361e8f28d2e9" -uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -version = "1.16.5" - -[[deps.FileWatching]] -uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" -version = "1.11.0" - -[[deps.FillArrays]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a" -uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.13.0" - - [deps.FillArrays.extensions] - FillArraysPDMatsExt = "PDMats" - FillArraysSparseArraysExt = "SparseArrays" - FillArraysStatisticsExt = "Statistics" - - [deps.FillArrays.weakdeps] - PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" - SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[deps.FixedPointNumbers]] -deps = ["Statistics"] -git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" -uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.8.5" - -[[deps.Fontconfig_jll]] -deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] -git-tree-sha1 = "21fac3c77d7b5a9fc03b0ec503aa1a6392c34d2b" -uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.15.0+0" - -[[deps.FreeType2_jll]] -deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "2c5512e11c791d1baed2049c5652441b28fc6a31" -uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.13.4+0" - -[[deps.GLU_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] -git-tree-sha1 = "65af046f4221e27fb79b28b6ca89dd1d12bc5ec7" -uuid = "bd17208b-e95e-5925-bf81-e2f59b3e5c61" -version = "9.0.1+0" - -[[deps.GMP_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" -version = "6.3.0+0" - -[[deps.Gettext_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" -uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" -version = "0.21.0+0" - -[[deps.Git]] -deps = ["Git_jll"] -git-tree-sha1 = "04eff47b1354d702c3a85e8ab23d539bb7d5957e" -uuid = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2" -version = "1.3.1" - -[[deps.Git_jll]] -deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "2f6d6f7e6d6de361865d4394b802c02fc944fc7c" -uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" -version = "2.49.0+0" - -[[deps.Glib_jll]] -deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "fee60557e4f19d0fe5cd169211fdda80e494f4e8" -uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.84.0+0" - -[[deps.GmshTools]] -deps = ["Libdl", "gmsh_jll"] -git-tree-sha1 = "299aa66053646db77f8aa7fafcebe0f9e5c0d1dc" -uuid = "82e2f556-b1bd-5f1a-9576-f93c0da5f0ee" -version = "0.5.2" - -[[deps.GrundmannMoeller]] -deps = ["LinearAlgebra", "StaticArrays", "Test"] -git-tree-sha1 = "e264cf5f081091e4af712a911d3b620567c565e3" -uuid = "36aa67b7-9d79-4e90-bbc0-05abd90a007e" -version = "0.1.2" - -[[deps.HDF5_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "LibCURL_jll", "Libdl", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "OpenSSL_jll", "TOML", "Zlib_jll", "libaec_jll"] -git-tree-sha1 = "82a471768b513dc39e471540fdadc84ff80ff997" -uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" -version = "1.14.3+3" - -[[deps.Hwloc_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "f93a9ce66cd89c9ba7a4695a47fd93b4c6bc59fa" -uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" -version = "2.12.0+0" - -[[deps.HypertextLiteral]] -deps = ["Tricks"] -git-tree-sha1 = "7134810b1afce04bbc1045ca1985fbe81ce17653" -uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2" -version = "0.9.5" - -[[deps.ILUZero]] -deps = ["LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "b007cfc7f9bee9a958992d2301e9c5b63f332a90" -uuid = "88f59080-6952-5380-9ea5-54057fb9a43f" -version = "0.2.0" - -[[deps.IOCapture]] -deps = ["Logging", "Random"] -git-tree-sha1 = "b6d6bfdd7ce25b0f9b2f6b3dd56b2673a66c8770" -uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" -version = "0.2.5" - -[[deps.Infiltrator]] -deps = ["InteractiveUtils", "Markdown", "REPL", "UUIDs"] -git-tree-sha1 = "139299acf639f1bbfecd3058f116e91e212d359a" -uuid = "5903a43b-9cc3-4c30-8d17-598619ec4e9b" -version = "1.8.6" - -[[deps.IntelOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] -git-tree-sha1 = "0f14a5456bdc6b9731a5682f439a672750a09e48" -uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2025.0.4+0" - -[[deps.InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -version = "1.11.0" - -[[deps.IrrationalConstants]] -git-tree-sha1 = "e2222959fbc6c19554dc15174c81bf7bf3aa691c" -uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.2.4" - -[[deps.IterativeSolvers]] -deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] -git-tree-sha1 = "59545b0a2b27208b0650df0a46b8e3019f85055b" -uuid = "42fd0dbc-a981-5370-80f2-aaf504508153" -version = "0.9.4" - -[[deps.JLLWrappers]] -deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "a007feb38b422fbdab534406aeca1b86823cb4d6" -uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.7.0" - -[[deps.JSON]] -deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.4" - -[[deps.JSON3]] -deps = ["Dates", "Mmap", "Parsers", "PrecompileTools", "StructTypes", "UUIDs"] -git-tree-sha1 = "196b41e5a854b387d99e5ede2de3fcb4d0422aae" -uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" -version = "1.14.2" - - [deps.JSON3.extensions] - JSON3ArrowExt = ["ArrowTypes"] - - [deps.JSON3.weakdeps] - ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" - -[[deps.JSONSchema]] -deps = ["Downloads", "JSON", "JSON3", "URIs"] -git-tree-sha1 = "243f1cdb476835d7c249deb9f29ad6b7827da7d3" -uuid = "7d188eb4-7ad8-530c-ae41-71a32a6d4692" -version = "1.4.1" - -[[deps.JpegTurbo_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "eac1206917768cb54957c65a615460d87b455fc1" -uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.1.1+0" - -[[deps.Krylov]] -deps = ["LinearAlgebra", "Printf", "SparseArrays"] -git-tree-sha1 = "4f20a2df85a9e5d55c9e84634bbf808ed038cabd" -uuid = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" -version = "0.9.8" - -[[deps.LLVMOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "78211fb6cbc872f77cad3fc0b6cf647d923f4929" -uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" -version = "18.1.7+0" - -[[deps.LZO_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1c602b1127f4751facb671441ca72715cc95938a" -uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.3+0" - -[[deps.LaTeXStrings]] -git-tree-sha1 = "dda21b8cbd6a6c40d9d02a73230f9d70fed6918c" -uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.4.0" - -[[deps.LazilyInitializedFields]] -git-tree-sha1 = "0f2da712350b020bc3957f269c9caad516383ee0" -uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf" -version = "1.3.0" - -[[deps.LazyArtifacts]] -deps = ["Artifacts", "Pkg"] -uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" -version = "1.11.0" - -[[deps.LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" -version = "0.6.4" - -[[deps.LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "8.6.0+0" - -[[deps.LibGit2]] -deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" -version = "1.11.0" - -[[deps.LibGit2_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] -uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" -version = "1.7.2+0" - -[[deps.LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "MbedTLS_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" -version = "1.11.0+1" - -[[deps.Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -version = "1.11.0" - -[[deps.Libffi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "c8da7e6a91781c41a863611c7e966098d783c57a" -uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.4.7+0" - -[[deps.Libgcrypt_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] -git-tree-sha1 = "8be878062e0ffa2c3f67bb58a595375eda5de80b" -uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.11.0+0" - -[[deps.Libglvnd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll", "Xorg_libXext_jll"] -git-tree-sha1 = "ff3b4b9d35de638936a525ecd36e86a8bb919d11" -uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" -version = "1.7.0+0" - -[[deps.Libgpg_error_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "df37206100d39f79b3376afb6b9cee4970041c61" -uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.51.1+0" - -[[deps.Libiconv_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "be484f5c92fad0bd8acfef35fe017900b0b73809" -uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.18.0+0" - -[[deps.Libmount_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "89211ea35d9df5831fca5d33552c02bd33878419" -uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.40.3+0" - -[[deps.Libuuid_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e888ad02ce716b319e6bdb985d2ef300e7089889" -uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.40.3+0" - -[[deps.LiftedMaps]] -deps = ["LinearAlgebra", "LinearMaps"] -git-tree-sha1 = "68c65fe9d32407ddb13eb26ef96fa803d45369cd" -uuid = "d22a30c1-52ac-4762-a8c9-5838452405e0" -version = "0.5.1" - -[[deps.LinearAlgebra]] -deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -version = "1.11.0" - -[[deps.LinearElasticity_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "71e8ee0f9fe0e86a8f8c7f28361e5118eab2f93f" -uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" -version = "5.0.0+0" - -[[deps.LinearMaps]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "7f6be2e4cdaaf558623d93113d6ddade7b916209" -uuid = "7a12625a-238d-50fd-b39a-03d52299707e" -version = "3.11.4" - - [deps.LinearMaps.extensions] - LinearMapsChainRulesCoreExt = "ChainRulesCore" - LinearMapsSparseArraysExt = "SparseArrays" - LinearMapsStatisticsExt = "Statistics" - - [deps.LinearMaps.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[deps.LogExpFunctions]] -deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "13ca9e2586b89836fd20cccf56e57e2b9ae7f38f" -uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.29" - - [deps.LogExpFunctions.extensions] - LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" - LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" - LogExpFunctionsInverseFunctionsExt = "InverseFunctions" - - [deps.LogExpFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[deps.Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" -version = "1.11.0" - -[[deps.METIS_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "2eefa8baa858871ae7770c98c3c2a7e46daba5b4" -uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" -version = "5.1.3+0" - -[[deps.MKL_jll]] -deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] -git-tree-sha1 = "5de60bc6cb3899cd318d80d627560fae2e2d99ae" -uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2025.0.1+1" - -[[deps.MMG_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] -git-tree-sha1 = "70a59df96945782bb0d43b56d0fbfdf1ce2e4729" -uuid = "86086c02-e288-5929-a127-40944b0018b7" -version = "5.6.0+0" - -[[deps.MPICH_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "3aa3210044138a1749dbd350a9ba8680869eb503" -uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" -version = "4.3.0+1" - -[[deps.MPIPreferences]] -deps = ["Libdl", "Preferences"] -git-tree-sha1 = "c105fe467859e7f6e9a852cb15cb4301126fac07" -uuid = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267" -version = "0.1.11" - -[[deps.MPItrampoline_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "ff91ca13c7c472cef700f301c8d752bc2aaff1a8" -uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" -version = "5.5.3+0" - -[[deps.Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" -version = "1.11.0" - -[[deps.MarkdownAST]] -deps = ["AbstractTrees", "Markdown"] -git-tree-sha1 = "465a70f0fc7d443a00dcdc3267a497397b8a3899" -uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" -version = "0.1.2" - -[[deps.MbedTLS_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.6+0" - -[[deps.MicrosoftMPI_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "bc95bf4149bf535c09602e3acdf950d9b4376227" -uuid = "9237b28f-5490-5468-be7b-bb81f5f5e6cf" -version = "10.1.4+3" - -[[deps.Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" -version = "1.11.0" - -[[deps.MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2023.12.12" - -[[deps.NestedUnitRanges]] -deps = ["AbstractTrees", "ArrayLayouts", "BlockArrays"] -git-tree-sha1 = "1cbdce42da2370fee5ef906ef24179f8c070e3b9" -uuid = "032820ab-dc03-4b49-91f4-7d58d4da98b3" -version = "0.2.1" - -[[deps.NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" -version = "1.2.0" - -[[deps.OCCT_jll]] -deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "bef34b68c20cc34475c5cb464ab48555e74f4c61" -uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" -version = "7.7.2+0" - -[[deps.OffsetArrays]] -git-tree-sha1 = "a414039192a155fb38c4599a60110f0018c6ec82" -uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.16.0" - - [deps.OffsetArrays.extensions] - OffsetArraysAdaptExt = "Adapt" - - [deps.OffsetArrays.weakdeps] - Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" - -[[deps.OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.27+1" - -[[deps.OpenLibm_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" - -[[deps.OpenMPI_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "fcd38c48ed4ab9763eb5770cfb786174b548ae08" -uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" -version = "4.1.8+0" - -[[deps.OpenSSL_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "a9697f1d06cc3eb3fb3ad49cc67f2cfabaac31ea" -uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.16+0" - -[[deps.OpenSpecFun_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1346c9208249809840c91b26703912dff463d335" -uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.6+0" - -[[deps.OrderedCollections]] -git-tree-sha1 = "cc4054e898b852042d7b503313f7ad03de99c3dd" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.8.0" - -[[deps.PCRE2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" -version = "10.42.0+1" - -[[deps.PackageExtensionCompat]] -git-tree-sha1 = "fb28e33b8a95c4cee25ce296c817d89cc2e53518" -uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" -version = "1.0.2" -weakdeps = ["Requires", "TOML"] - -[[deps.Parameters]] -deps = ["OrderedCollections", "UnPack"] -git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" -uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" -version = "0.12.3" - -[[deps.Parsers]] -deps = ["Dates", "PrecompileTools", "UUIDs"] -git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" -uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.8.1" - -[[deps.Permutations]] -deps = ["Combinatorics", "LinearAlgebra", "Random"] -git-tree-sha1 = "b1f03a4943c62552a12c7f95965b76c3f91cf5b7" -uuid = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" -version = "0.4.23" - -[[deps.Pixman_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] -git-tree-sha1 = "db76b1ecd5e9715f3d043cec13b2ec93ce015d53" -uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.44.2+0" - -[[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.11.0" -weakdeps = ["REPL"] - - [deps.Pkg.extensions] - REPLExt = "REPL" - -[[deps.PlotlyBase]] -deps = ["ColorSchemes", "Colors", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "LaTeXStrings", "Logging", "Parameters", "Pkg", "REPL", "Requires", "Statistics", "UUIDs"] -git-tree-sha1 = "90af5c9238c1b3b25421f1fdfffd1e8fca7a7133" -uuid = "a03496cd-edff-5a9b-9e67-9cda94a718b5" -version = "0.8.20" - - [deps.PlotlyBase.extensions] - DataFramesExt = "DataFrames" - DistributionsExt = "Distributions" - IJuliaExt = "IJulia" - JSON3Ext = "JSON3" - - [deps.PlotlyBase.weakdeps] - DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" - Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" - IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" - JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" - -[[deps.PlotlyDocumenter]] -deps = ["Downloads", "HypertextLiteral", "PackageExtensionCompat", "Random"] -git-tree-sha1 = "ff52f1faa5eefbbd96ad2f860c1a0e8131a6d76c" -uuid = "9b90f1cd-2639-4507-8b17-2fbe371ceceb" -version = "0.2.0" - - [deps.PlotlyDocumenter.extensions] - DocumenterVitepressPlotsExt = ["DocumenterVitepress", "Documenter"] - PlotlyBaseExt = "PlotlyBase" - PlotlyJSExt = "PlotlyJS" - PlotlyLightExt = "PlotlyLight" - - [deps.PlotlyDocumenter.weakdeps] - Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" - DocumenterVitepress = "4710194d-e776-4893-9690-8d956a29c365" - PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5" - PlotlyJS = "f0f68f2c-4968-5e81-91da-67840de0976a" - PlotlyLight = "ca7969ec-10b3-423e-8d99-40f33abb42bf" - -[[deps.PrecompileTools]] -deps = ["Preferences"] -git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" -uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" -version = "1.2.1" - -[[deps.Preferences]] -deps = ["TOML"] -git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6" -uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.4.3" - -[[deps.Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" -version = "1.11.0" - -[[deps.REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "StyledStrings", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" -version = "1.11.0" - -[[deps.Random]] -deps = ["SHA"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -version = "1.11.0" - -[[deps.RecipesBase]] -deps = ["PrecompileTools"] -git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" -uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "1.3.4" - -[[deps.Reexport]] -git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.2.2" - -[[deps.RegistryInstances]] -deps = ["LazilyInitializedFields", "Pkg", "TOML", "Tar"] -git-tree-sha1 = "ffd19052caf598b8653b99404058fce14828be51" -uuid = "2792f1a3-b283-48e8-9a74-f99dce5104f3" -version = "0.1.0" - -[[deps.Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "62389eeff14780bfe55195b7204c0d8738436d64" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.1" - -[[deps.SCOTCH_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "7110b749766853054ce8a2afaa73325d72d32129" -uuid = "a8d0f55d-b80e-548d-aff6-1a04c175f0f9" -version = "6.1.3+0" - -[[deps.SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" -version = "0.7.0" - -[[deps.SauterSchwab3D]] -deps = ["FastGaussQuadrature", "GrundmannMoeller", "LinearAlgebra", "ShunnHamQuadrature", "StaticArrays"] -git-tree-sha1 = "87242fb25711b1f9eaa45506d8b5e6e0b50f086a" -uuid = "0a13313b-1c00-422e-8263-562364ed9544" -version = "0.1.4" - -[[deps.SauterSchwabQuadrature]] -deps = ["FastGaussQuadrature", "LinearAlgebra", "StaticArrays", "TestItems"] -git-tree-sha1 = "41c2ba71198ffc925603004cb19b78a4d5df9ff0" -uuid = "535c7bfe-2023-5c1d-b712-654ef9d93a38" -version = "2.4.1" - -[[deps.Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" -version = "1.11.0" - -[[deps.SharedArrays]] -deps = ["Distributed", "Mmap", "Random", "Serialization"] -uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" -version = "1.11.0" - -[[deps.ShunnHamQuadrature]] -deps = ["LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "dfa53166b13cd6f352d54c99a24124321ef95282" -uuid = "164309f2-5039-4884-b6c7-6da8aa5c66ad" -version = "0.1.0" - -[[deps.Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" -version = "1.11.0" - -[[deps.SparseArrays]] -deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -version = "1.11.0" - -[[deps.Sparspak]] -deps = ["Libdl", "LinearAlgebra", "Logging", "OffsetArrays", "Printf", "SparseArrays", "Test"] -git-tree-sha1 = "342cf4b449c299d8d1ceaf00b7a49f4fbc7940e7" -uuid = "e56a9233-b9d6-4f03-8d0f-1825330902ac" -version = "0.3.9" - -[[deps.SpecialFunctions]] -deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "64cca0c26b4f31ba18f13f6c12af7c85f478cfde" -uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.5.0" - - [deps.SpecialFunctions.extensions] - SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" - - [deps.SpecialFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - -[[deps.StaticArrays]] -deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "0feb6b9031bd5c51f9072393eb5ab3efd31bf9e4" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.13" - - [deps.StaticArrays.extensions] - StaticArraysChainRulesCoreExt = "ChainRulesCore" - StaticArraysStatisticsExt = "Statistics" - - [deps.StaticArrays.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[deps.StaticArraysCore]] -git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682" -uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.3" - -[[deps.Statistics]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "ae3bb1eb3bba077cd276bc5cfc337cc65c3075c0" -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -version = "1.11.1" -weakdeps = ["SparseArrays"] - - [deps.Statistics.extensions] - SparseArraysExt = ["SparseArrays"] - -[[deps.StringEncodings]] -deps = ["Libiconv_jll"] -git-tree-sha1 = "b765e46ba27ecf6b44faf70df40c57aa3a547dcb" -uuid = "69024149-9ee7-55f6-a4c4-859efe599b68" -version = "0.3.7" - -[[deps.StructTypes]] -deps = ["Dates", "UUIDs"] -git-tree-sha1 = "159331b30e94d7b11379037feeb9b690950cace8" -uuid = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" -version = "1.11.0" - -[[deps.StyledStrings]] -uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" -version = "1.11.0" - -[[deps.SuiteSparse]] -deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] -uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" - -[[deps.SuiteSparse_jll]] -deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] -uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.7.0+0" - -[[deps.TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.3" - -[[deps.Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" -version = "1.10.0" - -[[deps.TensorCore]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" -uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" -version = "0.1.1" - -[[deps.Test]] -deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -version = "1.11.0" - -[[deps.TestItems]] -git-tree-sha1 = "8621ba2637b49748e2dc43ba3d84340be2938022" -uuid = "1c621080-faea-4a02-84b6-bbd5e436b8fe" -version = "0.1.1" - -[[deps.TranscodingStreams]] -git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" -uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.11.3" - -[[deps.Tricks]] -git-tree-sha1 = "6cae795a5a9313bbb4f60683f7263318fc7d1505" -uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" -version = "0.1.10" - -[[deps.TypeTree]] -deps = ["InteractiveUtils"] -git-tree-sha1 = "c1adbb6b9d9374babe8975a456f383a37a8e02ec" -uuid = "04da0e3b-1cad-4b2c-a963-fc1602baf1af" -version = "0.3.0" - -[[deps.URIs]] -git-tree-sha1 = "cbbebadbcc76c5ca1cc4b4f3b0614b3e603b5000" -uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.5.2" - -[[deps.UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" -version = "1.11.0" - -[[deps.UnPack]] -git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" -uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" -version = "1.0.2" - -[[deps.Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" -version = "1.11.0" - -[[deps.WiltonInts84]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "30444f863e76cc609b52d5b8e3e047acd7deaccf" -uuid = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" -version = "0.2.8" - -[[deps.XML2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "b8b243e47228b4a3877f1dd6aee0c5d56db7fcf4" -uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.13.6+1" - -[[deps.XSLT_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"] -git-tree-sha1 = "7d1671acbe47ac88e981868a078bd6b4e27c5191" -uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.42+0" - -[[deps.Xorg_libX11_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] -git-tree-sha1 = "9dafcee1d24c4f024e7edc92603cedba72118283" -uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.8.6+3" - -[[deps.Xorg_libXau_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e9216fdcd8514b7072b43653874fd688e4c6c003" -uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.12+0" - -[[deps.Xorg_libXdmcp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "89799ae67c17caa5b3b5a19b8469eeee474377db" -uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.5+0" - -[[deps.Xorg_libXext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "d7155fea91a4123ef59f42c4afb5ab3b4ca95058" -uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.6+3" - -[[deps.Xorg_libXfixes_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "6fcc21d5aea1a0b7cce6cab3e62246abd1949b86" -uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" -version = "6.0.0+0" - -[[deps.Xorg_libXft_jll]] -deps = ["Fontconfig_jll", "Libdl", "Pkg", "Xorg_libXrender_jll"] -git-tree-sha1 = "754b542cdc1057e0a2f1888ec5414ee17a4ca2a1" -uuid = "2c808117-e144-5220-80d1-69d4eaa9352c" -version = "2.3.3+1" - -[[deps.Xorg_libXinerama_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll"] -git-tree-sha1 = "a1a7eaf6c3b5b05cb903e35e8372049b107ac729" -uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" -version = "1.1.5+0" - -[[deps.Xorg_libXrender_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "a490c6212a0e90d2d55111ac956f7c4fa9c277a6" -uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.11+1" - -[[deps.Xorg_libpthread_stubs_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "c57201109a9e4c0585b208bb408bc41d205ac4e9" -uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" -version = "0.1.2+0" - -[[deps.Xorg_libxcb_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "1a74296303b6524a0472a8cb12d3d87a78eb3612" -uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.17.0+3" - -[[deps.Xorg_xtrans_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "6dba04dbfb72ae3ebe5418ba33d087ba8aa8cb00" -uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.5.1+0" - -[[deps.YAML]] -deps = ["Base64", "Dates", "Printf", "StringEncodings"] -git-tree-sha1 = "b46894beba6c05cd185d174654479aaec09ea6b1" -uuid = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" -version = "0.4.13" - -[[deps.Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.2.13+1" - -[[deps.gmsh_jll]] -deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "1e7fe5c8dbe0e911931a18cdfbd2c7a1e01b68ef" -uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" -version = "4.13.1+0" - -[[deps.libaec_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "f5733a5a9047722470b95a81e1b172383971105c" -uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" -version = "1.1.3+0" - -[[deps.libblastrampoline_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.11.0+0" - -[[deps.libpng_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "068dfe202b0a05b8332f1e8e6b4080684b9c7700" -uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.47+0" - -[[deps.nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.59.0+0" - -[[deps.oneTBB_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "d5a767a3bb77135a99e433afe0eb14cd7f6914c3" -uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" -version = "2022.0.0+0" - -[[deps.p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.4.0+2" From 24186dc6177d4448819b372208768df0f5db4b10 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 1 Apr 2025 23:54:29 +0200 Subject: [PATCH 470/528] integralop can deal with zero sized blocks --- src/integralop.jl | 54 ++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/integralop.jl b/src/integralop.jl index 32621123..ebe1a157 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -69,6 +69,9 @@ Computes the matrix of operator biop wrt the finite element spaces tfs and bfs function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; quadstrat=defaultquadstrat(biop, tfs, bfs)) + numfunctions(tfs) == 0 && return + numfunctions(bfs) == 0 && return + tr = assemblydata(tfs); tr == nothing && return br = assemblydata(bfs); br == nothing && return @@ -98,33 +101,32 @@ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; tfs, test_elements, tad, tcells, bfs, bsis_elements, bad, bcells, qd, zlocal, store; quadstrat=qs) +end - # if CompScienceMeshes.refines(tgeo, bgeo) - # assemblechunk_body_test_refines_trial!(biop, - # tfs, test_elements, tad, tcells, - # bfs, bsis_elements, bad, bcells, - # qd, zlocal, store; quadstrat) - # qs = TestRefinesTrialQStrat(quadstrat) - # # assemblechunk_body!(biop, - # # tfs, test_elements, tad, tcells, - # # bfs, bsis_elements, bad, bcells, - # # qd, zlocal, store; quadstrat=qs) - # elseif CompScienceMeshes.refines(bgeo, tgeo) - # qs = TrialRefinesTestQStrat(quadstrat) - # assemblechunk_body!(biop, - # tfs, test_elements, tad, tcells, - # bfs, bsis_elements, bad, bcells, - # qd, zlocal, store; quadstrat=qs) - # # assemblechunk_body_trial_refines_test!(biop, - # # tfs, test_elements, tad, tcells, - # # bfs, bsis_elements, bad, bcells, - # # qd, zlocal, store; quadstrat) - # else - # assemblechunk_body!(biop, - # tfs, test_elements, tad, tcells, - # bfs, bsis_elements, bad, bcells, - # qd, zlocal, store; quadstrat) - # end +@testitem "assemble!: zero sized block" begin + using CompScienceMeshes + + fn = joinpath(dirname(pathof(BEAST)), "../examples/assets/sphere45.in") + m1 = readmesh(fn) + m2 = m1[Int[]] + + X = BEAST.DirectProductSpace([raviartthomas(m) for m in [m1, m2]]) + T = Maxwell3D.singlelayer(gamma=1.0) + + @hilbertspace j[1:2] + @hilbertspace k[1:2] + a = T[k[1],j[1]] + T[k[1],j[2]] + T[k[2],j[2]] + T[k[2],j[1]] + + A = assemble(a, X, X) + import BEAST.BlockArrays + + n1 = numfunctions(X[1]) + n2 = numfunctions(X[2]) + + @test n2 == 0 + + @test BlockArrays.blocksize(A) == (2,2) + @test BlockArrays.blocksizes(A) == ([n1,n2], [n1,n2]) end From d298ac8216672a2ba7153917c3a57d65519b71b2 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sat, 5 Apr 2025 19:24:58 +0200 Subject: [PATCH 471/528] tutorial in how to implement new quadstrat --- docs/make.jl | 5 +- docs/src/manual/quadrule.md | 194 ++++++++++++++++++ docs/src/manual/quadstrat.md | 2 + src/BEAST.jl | 2 + src/bases/local/gwplocal.jl | 2 +- src/bases/local/rtlocal.jl | 91 +++++++- src/quadrature/rules/momintegrals.jl | 4 +- .../rules/testinbaryrefoftrialqrule.jl | 77 +++++++ src/quadrature/sauterschwabints.jl | 7 +- .../nonconftestbaryrefoftrialqstrat.jl | 46 +++++ 10 files changed, 421 insertions(+), 9 deletions(-) create mode 100644 docs/src/manual/quadrule.md create mode 100644 src/quadrature/rules/testinbaryrefoftrialqrule.jl create mode 100644 src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl diff --git a/docs/make.jl b/docs/make.jl index e3306889..d75c450a 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -21,8 +21,9 @@ makedocs(; "Introduction" => "index.md", "Manual" => Any[ "General Usage"=>"manual/usage.md", - "Defining custom excitations"=>"manual/customexc.md", - "Customising Quadrature Rules" => "manual/quadstrat.md", + "Custom excitations"=>"manual/customexc.md", + "Setting the Quadrature Strategy" => "manual/quadstrat.md", + "Custom Quadrature Rules" => "manual/quadrule.md", "Application Examples"=>Any[ "Time-Harmonic"=>Any[ "EFIE"=>"manual/examplesTH/efie.md", diff --git a/docs/src/manual/quadrule.md b/docs/src/manual/quadrule.md new file mode 100644 index 00000000..425416d4 --- /dev/null +++ b/docs/src/manual/quadrule.md @@ -0,0 +1,194 @@ +# Designing your own quadrature rule and strategy + +In the context of multi-trace solvers, testing and trial functions with logically separate but geometrically coinciding support can interact. In general these support can be equipped with completely indepenent meshes. + +For meshes of flat faceted triangular panels, BEAST.jl defines the `BEAST.NonConformingIntegralOpQStrat` strategy. The constructor of `NonConformingIntegralOpQStrat` takes another quadratue strategy `ctrat` that is fit to deal with pairs of mutually conforming meshes of flat faceted meshes. For a pair comprising a test triangle and a trial triangle, the appropriate quadrature rule is chosen as follows: + +- If the triangles are well-separated, `cstrat` is run on the pair and the resulting quadrature strategy is chosen. +- If the triangles have a single vertex in common, the Sauter-Schwab quadrature rule for common vertices is chosen. +- If the triangles overlap or have edges that overlap, they are both refined so that a geometrically conforming mesh is obtained. For each pair of triangles in this refinement, `cstrat` is run and the resulting quadrature strategy is chosen. + +This quadrature strategy is powerful but requires a lot of geometric processing, resulting in significant increases in matrix assembly times. + +When the user has additional knowledge about the precise geometric constellation of the interacting meshes, a more economic approach can be desirable. It is of course the user's responsibility not to use this economic approach in a context where it is not applicable. + +In this tutorial we assume that the test mesh is conforming to the barycentric refinement of the trial mesh. We propose a quadrature strategy `NonConfTestBaryRefOfTrialQStrat`, parametrised by an underlying quadrature strategy fit for mutually conforming meshes, that implements the following algorithm: + +- If the triangles are well-separated, `cstrat` is run on the pair and the resulting quadrature strategy is chosen. +- If the test and trial triangle share a vertex, the barycentric refinement of the trial triangle is constructed and the decision on the quadrature rule is deferred to `cstrat`. + +Because of the a priori knowledge about the relative constellation of the meshes, the construction in the second case will automatically result in a pair of mutually conforming meshes that can safely be send off to `cstrat`. + +THe first step in defining a new quadrature strategy is the definition of the corresponding type: + +```julia +struct NonConfTestBaryRefOfTrialQStrat{P} <: BEAST.AbstractQuadStrat + conforming_qstrat::P +end +``` + +The semantics of the quadrature strategy are captured by the definition of a pair of methods for the functions **quaddata** and **quadrule**, respectively. The purpose of quaddata is the computation of cache data that can speed up the assembly. Typically, this involves computing and storing triangle normals, and the quadrature points and weights for the various quadrature rules that the quadrature strategy considers. + +Because in the majority of cases, the computation of the interaction is deferred to the conforming quadrature strategy, the computation of the cache is forwarded to its method of quaddata, resulting simply in: + +```julia +function BEAST.quaddata(a, X, Y, test_charts, trial_charts, + quadstrat::NonConfTestBaryRefOfTrialQStrat) + + return quaddata(a, X, Y, test_charts, trial_charts, + quadstrat.conforming_qstrat) +end +``` + +The method of `quadrule` is key to the definition of the quadrature strategy and contains the the actual algorithm in charge of choosing the quadrature rule for any given pair of triangles: + +```julia +function BEAST.quadrule(a, X, Y, i, test_chart, j, trial_chart, qd, + quadstrat::NonConfTestBaryRefOfTrialQStrat) + + nh = BEAST._numhits(test_chart, trial_chart) + nh > 0 && return TestInBaryRefOfTrialQRule(quadstrat.conforming_qstrat) + return BEAST.quadrule(a, X, Y, i, test_chart, j, trial_chart, qd, + quadstrat.conforming_qstrat) +end +``` + +The function body is essentially a one-to-one translation of the quadrature rule selection algorithm above to julia. The object `qd` passed to `quadrule` is the cache computed by quadrule. In our case, it is simply passed on to the underlying conforming quadrature rule in the case of well separated triangles. + +```julia +struct NonConfTestBaryRefOfTrialQStrat{S} + conforming_qstrat::S +end +``` + +The type `TestInBaryRefOfTrialQRule` refers to the quadrature rule that is responsible for the actual computation of the interactions in case of adjacency or overlap. Quadrature rules are implemented by specifying a method for the function `BEAST.momintegrals!`. + +```julia +function BEAST.momintegrals!(out, op, + test_functions, test_cell, test_chart, + trial_functions, trial_cell, trial_chart, + qr::TestInBaryRefOfTrialQRule) + + test_local_space = refspace(test_functions) + trial_local_space = refspace(trial_functions) + + num_tshapes = numfunctions(test_local_space, domain(test_chart)) + num_bshapes = numfunctions(trial_local_space, domain(trial_chart)) + + T = coordtype(test_chart) + z, u, h, t = zero(T), one(T), T(1//2), T(1//3) + + c = CompScienceMeshes.point(T, t, t) + v = ( + CompScienceMeshes.point(T, u, z), + CompScienceMeshes.point(T, z, u), + CompScienceMeshes.point(T, z, z)) + e = ( + CompScienceMeshes.point(T, z, h), + CompScienceMeshes.point(T, h, z), + CompScienceMeshes.point(T, h, h)) + + X = ( + CompScienceMeshes.simplex(v[1], e[3], c), + CompScienceMeshes.simplex(v[2], c, e[3]), + CompScienceMeshes.simplex(v[2], e[1], c), + CompScienceMeshes.simplex(v[3], c, e[1]), + CompScienceMeshes.simplex(v[3], e[2], c), + CompScienceMeshes.simplex(v[1], c, e[2])) + + C = CompScienceMeshes.cartesian( + CompScienceMeshes.neighborhood(trial_chart, c)) + V = CompScienceMeshes.cartesian.(( + CompScienceMeshes.neighborhood(trial_chart, v[1]), + CompScienceMeshes.neighborhood(trial_chart, v[2]), + CompScienceMeshes.neighborhood(trial_chart, v[3]))) + E = CompScienceMeshes.cartesian.(( + CompScienceMeshes.neighborhood(trial_chart, e[1]), + CompScienceMeshes.neighborhood(trial_chart, e[2]), + CompScienceMeshes.neighborhood(trial_chart, e[3]))) + + trial_charts = ( + CompScienceMeshes.simplex(V[1], E[3], C), + CompScienceMeshes.simplex(V[2], C, E[3]), + CompScienceMeshes.simplex(V[2], E[1], C), + CompScienceMeshes.simplex(V[3], C, E[1]), + CompScienceMeshes.simplex(V[3], E[2], C), + CompScienceMeshes.simplex(V[1], C, E[2])) + + quadstrat = qr.conforming_qstrat + qd = BEAST.quaddata(op, test_local_space, trial_local_space, + (test_chart,), trial_charts, quadstrat) + + Q = zeros(T, num_tshapes, num_bshapes) + out1 = zero(out) + for (q,chart) in enumerate(trial_charts) + qr1 = BEAST.quadrule(op, test_local_space, trial_local_space, + 1, test_chart, q ,chart, qd, quadstrat) + + BEAST.restrict!(Q, trial_local_space, trial_chart, chart, X[q]) + + fill!(out1, 0) + BEAST.momintegrals!(out1, op, + test_functions, nothing, test_chart, + trial_functions, nothing, chart, qr1) + + for j in 1:num_bshapes + for i in 1:num_tshapes + for k in 1:size(Q, 2) + out[i,j] += out1[i,k] * Q[j,k] +end end end end end +``` + +The algorithm constructs the charts of the barycentric refinement of the trial chart, which either share a vertex or an edge with the test chart, or completely coincide with the test chart. Because of this, contributions from any of the refinement charts can be computed accuratey by a classic quadrature rule: + +```julia +momintegrals!(outq, op, + test_functions, nothing, test_chart, + trial_functions, nothing, chart, qr) +``` + +This call to `momintegrals!` calculates interactions between the shape functions on the original test chart and the shape functions on one of the six subcharts in the refinement of the trial chart. The corresponding contribution to the interaction with the shape functions on the coarse trial chart can be calculated if we know how the restriction of the coarse shape functions to any of the subcharts can be written as linear combinations of the shape on that subchart. This information is given by + +```julia +BEAST.restrict!(Q, trial_local_space, trial_chart, chart, X[q]) +``` + +For efficiency, the overlap function from the domain of `chart` to the domain of `test_chart` has to be supplied. + +!!! note + The quadrature strategy and related quadrature rules implemented here can be rearded as meta-strategies, and meta-rules, as they defer most of the heavy lifting to underlying strategies and rules for mutually conforming meshes. + + In a *primitive* rule, methods of `momintegrals!` typically contain implementations of numerical quadrature methods. + +To verify correctness of the above strategy, we can compare the results against existing routines that either provide less accurate results or similar results at reduced efficiency. + +```julia +@testitem "NonConfTestBaryRefOfTrialQStrat" begin + using CompScienceMeshes + + fnm = joinpath(dirname(pathof(BEAST)), "../test/assets/sphere45.in") + Γ1 = BEAST.readmesh(fnm) + Γ2 = deepcopy(Γ1) + + X = raviartthomas(Γ1) + Y1 = buffachristiansen(Γ1) + Y = buffachristiansen(Γ2) + + K = Maxwell3D.doublelayer(gamma=1.0) + qs1 = BEAST.DoubleNumWiltonSauterQStrat(2, 3, 6, 7, 5, 5, 4, 3) + qs2 = BEAST.NonConformingIntegralOpQStrat(qs1) + qs3 = BEAST.NonConfTestBaryRefOfTrialQStrat(qs1) + + @time Kyx1 = assemble(K, Y, X; quadstrat=qs1) + @time Kyx2 = assemble(K, Y, X; quadstrat=qs2) + @time Kyx3 = assemble(K, Y, X; quadstrat=qs3) + @time Kyx4 = assemble(K, Y1, X; quadstrat=qs1) + + using LinearAlgebra + @test norm(Kyx1 - Kyx2) < 0.05 + @test norm(Kyx1 - Kyx3) < 0.05 + @test norm(Kyx2 - Kyx3) < 0.002 + @test norm(Kyx2 - Kyx4) < 0.002 + @test norm(Kyx3 - Kyx4) < 1e-12 +end +``` diff --git a/docs/src/manual/quadstrat.md b/docs/src/manual/quadstrat.md index bdd12a1c..1b7322c4 100644 --- a/docs/src/manual/quadstrat.md +++ b/docs/src/manual/quadstrat.md @@ -6,6 +6,8 @@ This page describes how the user can intervene in this system. ## List of implemented quadrature strategies +In BEAST, a clear distinction is made between a **quadrature rule**, which is a numerical algorithm that computes approximate values of a given integral with a given domain (a pair of panels in the case of BEM) and a given integral (comprising testing functions, trial functions, and a fundamental solution in the case of BEM). Different integrands and domains call for different quadrature rules. For a fixed integrand structure, the algorithm that chooses for any given constellation of panels the most appropriate rule is called the **quadrature strategy**. + The algorithm that determines which quadrature rule is used for any given geometric constellation of interacting panels is called the quadrature strategy. The list of strategies currently implemented in BEAST is: ```@example introductory diff --git a/src/BEAST.jl b/src/BEAST.jl index 3159b673..89e26427 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -204,6 +204,7 @@ include("quadrature/commonfaceoverlappingedgeqstrat.jl") include("quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl") include("quadrature/strategies/testrefinestrialqstrat.jl") include("quadrature/strategies/trialrefinestestqstrat.jl") +include("quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl") include("excitation.jl") @@ -223,6 +224,7 @@ include("quadrature/nonconformingoverlapqrule.jl") include("quadrature/nonconformingtouchqrule.jl") include("quadrature/rules/testrefinestrialqrule.jl") include("quadrature/rules/trialrefinestestqrule.jl") +include("quadrature/rules/testinbaryrefoftrialqrule.jl") include("postproc.jl") include("postproc/segcurrents.jl") diff --git a/src/bases/local/gwplocal.jl b/src/bases/local/gwplocal.jl index 3ecc0073..db419bfb 100644 --- a/src/bases/local/gwplocal.jl +++ b/src/bases/local/gwplocal.jl @@ -164,7 +164,7 @@ function interpolate(fields, interpolant::GWPCurlRefSpace{T,Degree}, chart) wher k = (d+2)-i-j u_edge = s[j+1] p_edge = neighborhood(edge, (u_edge,)) - @show cartesian(p_edge) + # @show cartesian(p_edge) t_edge = -tangents(p_edge, 1) vals = fields_edge(u_edge) [dot(t_edge, val) for val in vals] diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index c9350014..d494529c 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -101,6 +101,17 @@ function interpolate(interpolant::RefSpace, chart1, interpolee::RefSpace, chart2 interpolate(fields, interpolant, chart1) end +function interpolate!(out, interpolant::RefSpace, chart1, interpolee::RefSpace, chart2) + function fields(p) + x = cartesian(p) + v = carttobary(chart2, x) + r = neighborhood(chart2, v) + fieldvals = [f.value for f in interpolee(r)] + end + + interpolate!(out, fields, interpolant, chart1) +end + function interpolate(interpolant::RefSpace, chart1, interpolee::RefSpace, chart2, ch1toch2) function fields(p1) @@ -113,6 +124,17 @@ function interpolate(interpolant::RefSpace, chart1, interpolee::RefSpace, chart2 interpolate(fields, interpolant, chart1) end +function interpolate!(out, interpolant::RefSpace, chart1, interpolee::RefSpace, chart2, ch1toch2) + function fields(p1) + u1 = parametric(p1) + u2 = cartesian(ch1toch2, u1) + p2 = neighborhood(chart2, u2) + fieldvals = [f.value for f in interpolee(p2)] + end + + interpolate!(out, fields, interpolant, chart1) +end + function interpolate(fields, interpolant::RTRefSpace, chart) Q = map(faces(chart)) do face @@ -134,14 +156,80 @@ function interpolate(fields, interpolant::RTRefSpace, chart) end +# function interpolate!(out, fields, interpolant::RTRefSpace, chart) +# for (f,face) in zip(axes(out,2), faces(chart)) +# p = center(face) +# x = cartesian(p) +# u = carttobary(chart, x) +# q = neighborhood(chart, u) +# n = normal(q) + +# # minus because in CSM the tangent points towards vertex[1] +# t = -tangents(p,1) +# m = cross(t,n) + +# fieldvals = fields(q) +# out[:,f] = [dot(fv,m) for fv in fieldvals] +# end +# end + +# TODO: remove when new version CompScienceMeshes is released +function subcharts( + c::CompScienceMeshes.Simplex{U,2}, ::Type{Val{1}}) where {U} + + T = coordtype(c) + d = ( + point(T, 1, 0), + point(T, 0, 1), + point(T, 0, 0), + ) + tp = ( + (simplex(c[2], c[3]), simplex(d[2], d[3])), + (simplex(c[3], c[1]), simplex(d[3], d[1])), + (simplex(c[1], c[2]), simplex(d[1], d[2])), + ) + return tp +end + +function interpolate!(out, fields, interpolant::RTRefSpace{T}, chart) where {T} + n_chart = normal(chart) + + for (f,(face, inj)) in zip(axes(out,2), + subcharts(chart, Val{1})) + + # fields_face = trace(face, chart, fields) + u_face = T(1//2) + p_face = neighborhood(face, (u_face,)) + t_face = -tangents(p_face, 1) + m_face = cross(t_face, n_chart) + # vals = fields_face(u_face) + u_chart = cartesian(neighborhood(inj, u_face)) + p_chart = neighborhood(chart, u_chart) + vals = fields(p_chart) + for (g,val) in zip(axes(out, 1), vals) + out[g,f] = dot(m_face, val) + end + # out[:,f] = [dot(m_face, val) for val in vals] + end +end + + function restrict(ϕ::RefSpace, dom1, dom2) interpolate(ϕ, dom2, ϕ, dom1) end +function restrict!(out, ϕ::RefSpace, dom1, dom2) + interpolate!(out, ϕ, dom2, ϕ, dom1) +end + function restrict(ϕ::RefSpace, dom1, dom2, dom2todom1) interpolate(ϕ, dom2, ϕ, dom1, dom2todom1) end +function restrict!(out, ϕ::RefSpace, dom1, dom2, dom2todom1) + interpolate!(out, ϕ, dom2, ϕ, dom1, dom2todom1) +end + const _dof_rtperm_matrix = [ @SMatrix[1 0 0; # 1. {1,2,3} 0 1 0; @@ -167,6 +255,3 @@ const _dof_rtperm_matrix = [ 0 1 0; 1 0 0] ] - - -# Support for zeroth order elements on quadrilaterals diff --git a/src/quadrature/rules/momintegrals.jl b/src/quadrature/rules/momintegrals.jl index 6b749033..a599c539 100644 --- a/src/quadrature/rules/momintegrals.jl +++ b/src/quadrature/rules/momintegrals.jl @@ -1,6 +1,6 @@ function momintegrals!(out, op, - test_functions::Space, test_cellptr, test_chart, - trial_functions::Space, trial_cellptr, trial_chart, + test_functions, test_cellptr, test_chart, + trial_functions, trial_cellptr, trial_chart, quadrule) local_test_space = refspace(test_functions) diff --git a/src/quadrature/rules/testinbaryrefoftrialqrule.jl b/src/quadrature/rules/testinbaryrefoftrialqrule.jl new file mode 100644 index 00000000..eea7a055 --- /dev/null +++ b/src/quadrature/rules/testinbaryrefoftrialqrule.jl @@ -0,0 +1,77 @@ +struct TestInBaryRefOfTrialQRule{S} + conforming_qstrat::S +end + +function BEAST.momintegrals!(out, op, + test_functions, test_cell, test_chart, + trial_functions, trial_cell, trial_chart, + qr::TestInBaryRefOfTrialQRule) + + test_local_space = refspace(test_functions) + trial_local_space = refspace(trial_functions) + + num_tshapes = numfunctions(test_local_space, domain(test_chart)) + num_bshapes = numfunctions(trial_local_space, domain(trial_chart)) + + T = coordtype(test_chart) + z, u, h, t = zero(T), one(T), T(1//2), T(1//3) + + c = CompScienceMeshes.point(T, t, t) + v = ( + CompScienceMeshes.point(T, u, z), + CompScienceMeshes.point(T, z, u), + CompScienceMeshes.point(T, z, z)) + e = ( + CompScienceMeshes.point(T, z, h), + CompScienceMeshes.point(T, h, z), + CompScienceMeshes.point(T, h, h)) + + X = ( + CompScienceMeshes.simplex(v[1], e[3], c), + CompScienceMeshes.simplex(v[2], c, e[3]), + CompScienceMeshes.simplex(v[2], e[1], c), + CompScienceMeshes.simplex(v[3], c, e[1]), + CompScienceMeshes.simplex(v[3], e[2], c), + CompScienceMeshes.simplex(v[1], c, e[2])) + + C = CompScienceMeshes.cartesian( + CompScienceMeshes.neighborhood(trial_chart, c)) + V = CompScienceMeshes.cartesian.(( + CompScienceMeshes.neighborhood(trial_chart, v[1]), + CompScienceMeshes.neighborhood(trial_chart, v[2]), + CompScienceMeshes.neighborhood(trial_chart, v[3]))) + E = CompScienceMeshes.cartesian.(( + CompScienceMeshes.neighborhood(trial_chart, e[1]), + CompScienceMeshes.neighborhood(trial_chart, e[2]), + CompScienceMeshes.neighborhood(trial_chart, e[3]))) + + trial_charts = ( + CompScienceMeshes.simplex(V[1], E[3], C), + CompScienceMeshes.simplex(V[2], C, E[3]), + CompScienceMeshes.simplex(V[2], E[1], C), + CompScienceMeshes.simplex(V[3], C, E[1]), + CompScienceMeshes.simplex(V[3], E[2], C), + CompScienceMeshes.simplex(V[1], C, E[2])) + + quadstrat = qr.conforming_qstrat + qd = BEAST.quaddata(op, test_local_space, trial_local_space, + (test_chart,), trial_charts, quadstrat) + + Q = zeros(T, num_tshapes, num_bshapes) + out1 = zero(out) + for (q,chart) in enumerate(trial_charts) + qr1 = BEAST.quadrule(op, test_local_space, trial_local_space, + 1, test_chart, q ,chart, qd, quadstrat) + + BEAST.restrict!(Q, trial_local_space, trial_chart, chart, X[q]) + + fill!(out1, 0) + BEAST.momintegrals!(out1, op, + test_functions, nothing, test_chart, + trial_functions, nothing, chart, qr1) + + for j in 1:num_bshapes + for i in 1:num_tshapes + for k in 1:size(Q, 2) + out[i,j] += out1[i,k] * Q[j,k] +end end end end end diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index 1d56cdb3..8f89b16c 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -156,7 +156,12 @@ function momintegrals!(op::Operator, igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) igdp = pulledback_integrand(igd, I, test_chart, J, trial_chart) G = SauterSchwabQuadrature.sauterschwab_parameterized(igdp, rule) - out[1:num_tshapes, 1:num_bshapes] .+= G + # out[1:num_tshapes, 1:num_bshapes] .+= G + for j in 1:num_bshapes + for i in 1:num_tshapes + out[i,j] += G[i,j] + end + end nothing end diff --git a/src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl b/src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl new file mode 100644 index 00000000..82ef5656 --- /dev/null +++ b/src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl @@ -0,0 +1,46 @@ +struct NonConfTestBaryRefOfTrialQStrat{P} <: BEAST.AbstractQuadStrat + conforming_qstrat::P +end + +function BEAST.quaddata(a, X, Y, tels, bels, qs::NonConfTestBaryRefOfTrialQStrat) + return BEAST.quaddata(a, X, Y, tels, bels, qs.conforming_qstrat) +end + +function BEAST.quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, + quadstrat::NonConfTestBaryRefOfTrialQStrat) + + # return TestInBaryRefOfTrialQRule(quadstrat.conforming_qstrat) + nh = BEAST._numhits(τ, σ) + nh > 0 && return TestInBaryRefOfTrialQRule(quadstrat.conforming_qstrat) + return BEAST.quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, + quadstrat.conforming_qstrat) +end + +@testitem "NonConfTestBaryRefOfTrialQStrat" begin + using CompScienceMeshes + + fnm = joinpath(dirname(pathof(BEAST)), "../test/assets/sphere45.in") + Γ1 = BEAST.readmesh(fnm) + Γ2 = deepcopy(Γ1) + + X = raviartthomas(Γ1) + Y1 = buffachristiansen(Γ1) + Y = buffachristiansen(Γ2) + + K = Maxwell3D.doublelayer(gamma=1.0) + qs1 = BEAST.DoubleNumWiltonSauterQStrat(2, 3, 6, 7, 5, 5, 4, 3) + qs2 = BEAST.NonConformingIntegralOpQStrat(qs1) + qs3 = BEAST.NonConfTestBaryRefOfTrialQStrat(qs1) + + @time Kyx1 = assemble(K, Y, X; quadstrat=qs1) + @time Kyx2 = assemble(K, Y, X; quadstrat=qs2) + @time Kyx3 = assemble(K, Y, X; quadstrat=qs3) + @time Kyx4 = assemble(K, Y1, X; quadstrat=qs1) + + using LinearAlgebra + @test norm(Kyx1 - Kyx2) < 0.05 + @test norm(Kyx1 - Kyx3) < 0.05 + @test norm(Kyx2 - Kyx3) < 0.002 + @test norm(Kyx2 - Kyx4) < 0.002 + @test norm(Kyx3 - Kyx4) < 1e-12 +end \ No newline at end of file From b177f9fca3d13e6fe3b057d030a4d6c0ad7a730c Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Sat, 5 Apr 2025 19:41:30 +0200 Subject: [PATCH 472/528] fix some errors in quadrule.md --- docs/src/manual/quadrule.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/src/manual/quadrule.md b/docs/src/manual/quadrule.md index 425416d4..9f9c9c99 100644 --- a/docs/src/manual/quadrule.md +++ b/docs/src/manual/quadrule.md @@ -19,7 +19,7 @@ In this tutorial we assume that the test mesh is conforming to the barycentric r Because of the a priori knowledge about the relative constellation of the meshes, the construction in the second case will automatically result in a pair of mutually conforming meshes that can safely be send off to `cstrat`. -THe first step in defining a new quadrature strategy is the definition of the corresponding type: +The first step in defining a new quadrature strategy is the definition of the corresponding type: ```julia struct NonConfTestBaryRefOfTrialQStrat{P} <: BEAST.AbstractQuadStrat @@ -55,8 +55,10 @@ end The function body is essentially a one-to-one translation of the quadrature rule selection algorithm above to julia. The object `qd` passed to `quadrule` is the cache computed by quadrule. In our case, it is simply passed on to the underlying conforming quadrature rule in the case of well separated triangles. +Next, we define a type representing the quadrature rule that is used when test triangle and trial triangle are not well-separated: + ```julia -struct NonConfTestBaryRefOfTrialQStrat{S} +struct TestInBaryRefOfTrialQRule{S} conforming_qstrat::S end ``` @@ -147,13 +149,13 @@ momintegrals!(outq, op, trial_functions, nothing, chart, qr) ``` -This call to `momintegrals!` calculates interactions between the shape functions on the original test chart and the shape functions on one of the six subcharts in the refinement of the trial chart. The corresponding contribution to the interaction with the shape functions on the coarse trial chart can be calculated if we know how the restriction of the coarse shape functions to any of the subcharts can be written as linear combinations of the shape on that subchart. This information is given by +This call to `momintegrals!` calculates interactions between the shape functions on the original test chart and the shape functions on one of the six subcharts in the refinement of the trial chart. The corresponding contribution to the interaction with the shape functions on the coarse trial chart can be calculated if we know how the restriction of the coarse shape functions to any of the subcharts can be written as linear combinations of the shape on that subchart. This information is provided by ```julia BEAST.restrict!(Q, trial_local_space, trial_chart, chart, X[q]) ``` -For efficiency, the overlap function from the domain of `chart` to the domain of `test_chart` has to be supplied. +For efficiency, the overlap function from the domain of `chart` to the domain of `trial_chart` has to be supplied. !!! note The quadrature strategy and related quadrature rules implemented here can be rearded as meta-strategies, and meta-rules, as they defer most of the heavy lifting to underlying strategies and rules for mutually conforming meshes. From b52bd3e4a9f3113b7c6fbdd9039212c6ffc01056 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 8 Apr 2025 13:03:51 +0200 Subject: [PATCH 473/528] set version to 2.6.0 --- Project.toml | 4 +- src/bases/local/rtlocal.jl | 85 +++++++++++-------- .../nonconftestbaryrefoftrialqstrat.jl | 6 +- 3 files changed, 55 insertions(+), 40 deletions(-) diff --git a/Project.toml b/Project.toml index 0587053a..20b42c9a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "2.5.0" +version = "2.6.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" @@ -37,7 +37,7 @@ AbstractTrees = "0.4.4" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 1" CollisionDetection = "0.1.6" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.8.3, 0.9" +CompScienceMeshes = "0.9.3" Compat = "2, 3, 4" ConvolutionOperators = "0.4" ExtendableSparse = "1.4" diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index d494529c..19cd8c7c 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -136,20 +136,29 @@ function interpolate!(out, interpolant::RefSpace, chart1, interpolee::RefSpace, end -function interpolate(fields, interpolant::RTRefSpace, chart) - Q = map(faces(chart)) do face - p = center(face) - x = cartesian(p) - u = carttobary(chart, x) - q = neighborhood(chart, u) - n = normal(q) +function interpolate(fields, interpolant::RTRefSpace{T}, chart) where {T} + Q = map(CompScienceMeshes.subcharts(chart, Val{1})) do (face, inj) + + u_face = T(1//2) + p_face = neighborhood(face, (u_face,)) + t_face = -tangents(p_face, 1) + u_chart = cartesian(neighborhood(inj, u_face)) + p_chart = neighborhood(chart, u_chart) + n_chart = normal(p_chart) + m_face = cross(t_face, n_chart) + + # p = center(face) + # x = cartesian(p) + # u = carttobary(chart, x) + # q = neighborhood(chart, u) + # n = normal(q) # minus because in CSM the tangent points towards vertex[1] - t = -tangents(p,1) - m = cross(t,n) + # t = -tangents(p,1) + # m = cross(t,n) - fieldvals = fields(q) - q = [dot(fv,m) for fv in fieldvals] + fieldvals = fields(p_chart) + q = [dot(fv,m_face) for fv in fieldvals] end return hcat(Q...) @@ -174,44 +183,48 @@ end # end # TODO: remove when new version CompScienceMeshes is released -function subcharts( - c::CompScienceMeshes.Simplex{U,2}, ::Type{Val{1}}) where {U} - - T = coordtype(c) - d = ( - point(T, 1, 0), - point(T, 0, 1), - point(T, 0, 0), - ) - tp = ( - (simplex(c[2], c[3]), simplex(d[2], d[3])), - (simplex(c[3], c[1]), simplex(d[3], d[1])), - (simplex(c[1], c[2]), simplex(d[1], d[2])), - ) - return tp -end +# function subcharts( +# c::CompScienceMeshes.Simplex{U,2}, ::Type{Val{1}}) where {U} + +# T = coordtype(c) +# d = ( +# point(T, 1, 0), +# point(T, 0, 1), +# point(T, 0, 0), +# ) +# v = c.vertices +# E1 = simplex(v[2], v[3]) +# E2 = simplex(v[3], v[1]) +# E3 = simplex(v[1], v[2]) + +# e1 = simplex(d[2], d[3]) +# e2 = simplex(d[3], d[1]) +# e3 = simplex(d[1], d[2]) +# tp = ( +# (E1, e1), +# (E2, e2), +# (E3, e3), +# ) +# return tp +# end function interpolate!(out, fields, interpolant::RTRefSpace{T}, chart) where {T} - n_chart = normal(chart) + # n_chart = normal(chart) for (f,(face, inj)) in zip(axes(out,2), - subcharts(chart, Val{1})) + CompScienceMeshes.subcharts(chart, Val{1})) - # fields_face = trace(face, chart, fields) u_face = T(1//2) p_face = neighborhood(face, (u_face,)) t_face = -tangents(p_face, 1) - m_face = cross(t_face, n_chart) - # vals = fields_face(u_face) u_chart = cartesian(neighborhood(inj, u_face)) p_chart = neighborhood(chart, u_chart) + n_chart = normal(p_chart) + m_face = cross(t_face, n_chart) vals = fields(p_chart) for (g,val) in zip(axes(out, 1), vals) out[g,f] = dot(m_face, val) - end - # out[:,f] = [dot(m_face, val) for val in vals] - end -end +end end end function restrict(ϕ::RefSpace, dom1, dom2) diff --git a/src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl b/src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl index 82ef5656..7b9fc217 100644 --- a/src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl +++ b/src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl @@ -17,7 +17,10 @@ function BEAST.quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, end @testitem "NonConfTestBaryRefOfTrialQStrat" begin - using CompScienceMeshes + using BEAST, Test + using CompScienceMeshes, LinearAlgebra + + @show pathof(CompScienceMeshes) fnm = joinpath(dirname(pathof(BEAST)), "../test/assets/sphere45.in") Γ1 = BEAST.readmesh(fnm) @@ -37,7 +40,6 @@ end @time Kyx3 = assemble(K, Y, X; quadstrat=qs3) @time Kyx4 = assemble(K, Y1, X; quadstrat=qs1) - using LinearAlgebra @test norm(Kyx1 - Kyx2) < 0.05 @test norm(Kyx1 - Kyx3) < 0.05 @test norm(Kyx2 - Kyx3) < 0.002 From 4793f6eaba5fb320813ddf8e43a251e05ed1a3f5 Mon Sep 17 00:00:00 2001 From: krcools Date: Tue, 8 Apr 2025 15:46:59 +0200 Subject: [PATCH 474/528] Update TagBot.yml --- .github/workflows/TagBot.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index 35600114..4bad0ec9 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -4,6 +4,22 @@ on: types: - created workflow_dispatch: + inputs: + lookback: + default: "3" +permissions: + actions: read + checks: read + contents: write + deployments: read + issues: read + discussions: read + packages: read + pages: read + pull-requests: read + repository-projects: read + security-events: read + statuses: read jobs: TagBot: if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' @@ -12,5 +28,6 @@ jobs: - uses: JuliaRegistries/TagBot@v1 with: token: ${{ secrets.GITHUB_TOKEN }} + # Edit the following line to reflect the actual name of the GitHub Secret containing your private key ssh: ${{ secrets.DOCUMENTER_KEY }} - + # ssh: ${{ secrets.NAME_OF_MY_SSH_PRIVATE_KEY_SECRET }} From cee796f4f3108048a3252e3b8b830a9eb283303e Mon Sep 17 00:00:00 2001 From: krcools Date: Tue, 8 Apr 2025 15:52:29 +0200 Subject: [PATCH 475/528] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 9b427a6a..5092e02d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ -[![Docs-stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://krcools.github.io/BEAST.jl/stable/) [![Docs-dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://krcools.github.io/BEAST.jl/dev/) [![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/krcools/BEAST.jl/blob/master/LICENSE) [![CI](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml) From f15e45fefd222597e8e7a89dd55ef7e0d4382a00 Mon Sep 17 00:00:00 2001 From: krcools Date: Fri, 11 Apr 2025 15:48:32 +0200 Subject: [PATCH 476/528] Update TagBot.yml --- .github/workflows/TagBot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index 4bad0ec9..4905fd1d 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -29,5 +29,5 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} # Edit the following line to reflect the actual name of the GitHub Secret containing your private key - ssh: ${{ secrets.DOCUMENTER_KEY }} + ssh: ${{ secrets.DOCUMENTER_SSH_KEY }} # ssh: ${{ secrets.NAME_OF_MY_SSH_PRIVATE_KEY_SECRET }} From a67e27e600d4b04d5991d17711058e6fe460045f Mon Sep 17 00:00:00 2001 From: krcools Date: Fri, 11 Apr 2025 16:12:03 +0200 Subject: [PATCH 477/528] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5092e02d..a6d8a107 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ -[![Docs-dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://krcools.github.io/BEAST.jl/dev/) +[![Docs-stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://krcools.github.io/BEAST.jl/stable/) [![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/krcools/BEAST.jl/blob/master/LICENSE) [![CI](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml) [![codecov.io](http://codecov.io/github/krcools/BEAST.jl/coverage.svg?branch=master)](http://codecov.io/github/krcools/BEAST.jl?branch=master) From f02f99f9b953741475b06376753fab8dae40db47 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 14 Apr 2025 18:01:54 +0200 Subject: [PATCH 478/528] less verbose --- docs/make.jl | 4 +- src/bases/bdmdiv.jl | 3 +- src/bases/global/gwpglobal.jl | 4 +- src/bases/local/laglocal.jl | 27 +++++- src/bases/local/rtlocal.jl | 48 +--------- src/bases/local/rtqlocal.jl | 6 +- src/bases/ncrossbdmspace.jl | 3 +- src/integralop.jl | 88 ++----------------- src/maxwell/timedomain/mwtdops.jl | 8 +- src/quadrature/nonconformingoverlapqrule.jl | 42 +++++---- src/quadrature/nonconformingtouchqrule.jl | 56 ++++++------ .../rules/testinbaryrefoftrialqrule.jl | 21 +++-- src/quadrature/rules/testrefinestrialqrule.jl | 15 +++- .../nonconftestbaryrefoftrialqstrat.jl | 2 +- test/test_dipole.jl | 4 +- test/test_gwp.jl | 4 +- test/test_higher_order_lagrange_functions.jl | 2 +- test/test_mixed_blkassm.jl | 2 +- test/test_variational.jl | 6 +- 19 files changed, 126 insertions(+), 219 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index d75c450a..574404e7 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -86,7 +86,7 @@ makedocs(; deploydocs(; repo="github.com/krcools/BEAST.jl.git", target="build", - push_preview=true, - forcepush=true, + # push_preview=true, + forcepush=false, #devbranch = "feature/docs", ) diff --git a/src/bases/bdmdiv.jl b/src/bases/bdmdiv.jl index 7865825d..89e7d44e 100644 --- a/src/bases/bdmdiv.jl +++ b/src/bases/bdmdiv.jl @@ -18,7 +18,8 @@ end function brezzidouglasmarini(mesh, cellpairs::Array{Int,2}) - @warn "brezzidouglasmarini(mesh, cellpairs) assumes mesh is oriented" + @assert CompScienceMeshes.isoriented(mesh) "brezzidouglasmarini assumes mesh is oriented" + # @warn "brezzidouglasmarini(mesh, cellpairs) assumes mesh is oriented" @assert size(cellpairs,1) == 2 diff --git a/src/bases/global/gwpglobal.jl b/src/bases/global/gwpglobal.jl index f302632f..b0bda1c8 100644 --- a/src/bases/global/gwpglobal.jl +++ b/src/bases/global/gwpglobal.jl @@ -39,7 +39,7 @@ end localspace = BEAST.GWPCurlRefSpace{T,degree}() dofs = BEAST.GWPGlobalEdgeDoFs(degree) A = BEAST.globaldofs(edge_ch, face_ch, localspace, dofs) - display(round.(A, digits=3)) + # display(round.(A, digits=3)) end function globaldofs(edge_ch, face_ch, localspace, dof::GWPGlobalFaceDoFs) @@ -92,7 +92,7 @@ end localspace = BEAST.GWPCurlRefSpace{T,degree}() dofs = BEAST.GWPGlobalFaceDoFs(degree) A = BEAST.globaldofs(edge_ch, face_ch, localspace, dofs) - display(round.(A, digits=3)) + # display(round.(A, digits=3)) end function _addshapes!(fns, cell, gids, lids, β) diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index 0adea811..20af2e98 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -381,8 +381,6 @@ end # fields[i] ≈ sum(Q[j,i] * interpolant[j].value for j in 1:numfunctions(interpolant)) function interpolate(fields, interpolant::LagrangeRefSpace{T,Degree,3}, chart) where {T,Degree} - dim = binomial(2+Degree, Degree) - I = 0:Degree s = range(0,1,length=Degree+1) Is = zip(I,s) @@ -398,10 +396,31 @@ function interpolate(fields, interpolant::LagrangeRefSpace{T,Degree,3}, chart) w idx += 1 end end end - # Q = hcat(vals...) Q = Matrix{T}(undef, length(vals[1]), length(vals)) for i in eachindex(vals) Q[:,i] .= vals[i] end return Q -end \ No newline at end of file +end + + +function interpolate!(out, fields, interpolant::LagrangeRefSpace{T,Degree,3}, chart) where {T,Degree} + Is = zip((0:Degree), range(0,1,length=Degree+1)) + idx = 0 + for (i,ui) in Is + for (j,vj) in Is + for (k,wk) in Is + i + j + k == Degree || continue; idx += 1 + @assert ui + vj + wk ≈ 1 + p = neighborhood(chart, (ui,vj)) + vals = fields(p) + for (g, val) in zip(axes(out, 1), vals) + out[g,idx] = val +end end end end end + +function interpolate!(out, fields, interpolant::LagrangeRefSpace{T,0,3}, chart) where {T} + p = center(chart) + vals = fields(p) + for (g, val) in zip(axes(out, 1), vals) + out[g,1] = val +end end \ No newline at end of file diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index 19cd8c7c..5aca7cff 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -142,7 +142,7 @@ function interpolate(fields, interpolant::RTRefSpace{T}, chart) where {T} u_face = T(1//2) p_face = neighborhood(face, (u_face,)) t_face = -tangents(p_face, 1) - u_chart = cartesian(neighborhood(inj, u_face)) + u_chart = cartesian(inj, u_face) p_chart = neighborhood(chart, u_chart) n_chart = normal(p_chart) m_face = cross(t_face, n_chart) @@ -165,51 +165,7 @@ function interpolate(fields, interpolant::RTRefSpace{T}, chart) where {T} end -# function interpolate!(out, fields, interpolant::RTRefSpace, chart) -# for (f,face) in zip(axes(out,2), faces(chart)) -# p = center(face) -# x = cartesian(p) -# u = carttobary(chart, x) -# q = neighborhood(chart, u) -# n = normal(q) - -# # minus because in CSM the tangent points towards vertex[1] -# t = -tangents(p,1) -# m = cross(t,n) - -# fieldvals = fields(q) -# out[:,f] = [dot(fv,m) for fv in fieldvals] -# end -# end - -# TODO: remove when new version CompScienceMeshes is released -# function subcharts( -# c::CompScienceMeshes.Simplex{U,2}, ::Type{Val{1}}) where {U} - -# T = coordtype(c) -# d = ( -# point(T, 1, 0), -# point(T, 0, 1), -# point(T, 0, 0), -# ) -# v = c.vertices -# E1 = simplex(v[2], v[3]) -# E2 = simplex(v[3], v[1]) -# E3 = simplex(v[1], v[2]) - -# e1 = simplex(d[2], d[3]) -# e2 = simplex(d[3], d[1]) -# e3 = simplex(d[1], d[2]) -# tp = ( -# (E1, e1), -# (E2, e2), -# (E3, e3), -# ) -# return tp -# end - function interpolate!(out, fields, interpolant::RTRefSpace{T}, chart) where {T} - # n_chart = normal(chart) for (f,(face, inj)) in zip(axes(out,2), CompScienceMeshes.subcharts(chart, Val{1})) @@ -217,7 +173,7 @@ function interpolate!(out, fields, interpolant::RTRefSpace{T}, chart) where {T} u_face = T(1//2) p_face = neighborhood(face, (u_face,)) t_face = -tangents(p_face, 1) - u_chart = cartesian(neighborhood(inj, u_face)) + u_chart = cartesian(inj, u_face) p_chart = neighborhood(chart, u_chart) n_chart = normal(p_chart) m_face = cross(t_face, n_chart) diff --git a/src/bases/local/rtqlocal.jl b/src/bases/local/rtqlocal.jl index f1472d52..cf6e13ea 100644 --- a/src/bases/local/rtqlocal.jl +++ b/src/bases/local/rtqlocal.jl @@ -63,8 +63,8 @@ end val1 = f(p)[1] val2 = sum(Q[1,i] * ϕ.value for (i,ϕ) in zip(axes(Q,2), rtq(p))) - @show val1 - @show val2 + # @show val1 + # @show val2 @test val1 ≈ val2 end @@ -113,7 +113,7 @@ end point(0,1,0)] chart1 = CompScienceMeshes.Quadrilateral(vertices...) for I in BEAST._vrtperm_matrix_rtq - @show I + # @show I chart2 = CompScienceMeshes.Quadrilateral( vertices[I[1]], vertices[I[2]], diff --git a/src/bases/ncrossbdmspace.jl b/src/bases/ncrossbdmspace.jl index c5ed0364..77f5aba0 100644 --- a/src/bases/ncrossbdmspace.jl +++ b/src/bases/ncrossbdmspace.jl @@ -17,7 +17,8 @@ end function ncrossbdm(mesh, cellpairs::Array{Int,2}) - @warn "brezzidouglasmarini(mesh, cellpairs) assumes mesh is oriented" + @assert CompScienceMeshes.isoriented(mesh) "brezzidouglasmarini assumes mesh is oriented" + # @warn "brezzidouglasmarini(mesh, cellpairs) assumes mesh is oriented" @assert size(cellpairs,1) == 2 diff --git a/src/integralop.jl b/src/integralop.jl index ebe1a157..83228a23 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -138,8 +138,10 @@ function assemblechunk_body!(biop, test_shapes = refspace(test_space) trial_shapes = refspace(trial_space) + + verbose = (length(test_elements) > 256) myid = Threads.threadid() - myid == 1 && print("dots out of 10: ") + verbose && myid == 1 && print("dots out of 10: ") todo, done, pctg = length(test_elements), 0, 0 for (p,(tcell,tptr)) in enumerate(zip(test_elements, test_cell_ptrs)) for (q,(bcell,bptr)) in enumerate(zip(trial_elements, trial_cell_ptrs)) @@ -162,94 +164,14 @@ function assemblechunk_body!(biop, done += 1 new_pctg = round(Int, done / todo * 100) if new_pctg > pctg + 9 - myid == 1 && print(".") + verbose && myid == 1 && print(".") pctg = new_pctg end end - myid == 1 && println("") + verbose && myid == 1 && println("") end -# function assemblechunk_body_test_refines_trial!(biop, -# test_functions, test_charts, test_assembly_data, test_cells, -# trial_functions, trial_charts, trial_assembly_data, trial_cells, -# qd, zlocal, store; quadstrat) - -# test_shapes = refspace(test_functions) -# trial_shapes = refspace(trial_functions) - -# myid = Threads.threadid() -# myid == 1 && print("dots out of 10: ") -# todo, done, pctg = length(test_charts), 0, 0 -# for (p,(tcell,tchart)) in enumerate(zip(test_cells, test_charts)) -# for (q,(bcell,bchart)) in enumerate(zip(trial_cells, trial_charts)) -# fill!(zlocal, 0) -# qrule = quadrule(biop, test_shapes, trial_shapes, p, tchart, q, bchart, qd, quadstrat) -# # @show ("1", qrule) -# momintegrals_test_refines_trial!(zlocal, biop, -# test_functions, tcell, tchart, -# trial_functions, bcell, bchart, -# qrule, quadstrat) - -# I = length(test_assembly_data[p]) -# J = length(trial_assembly_data[q]) -# for j in 1 : J, i in 1 : I -# zij = zlocal[i,j] -# for (n,b) in trial_assembly_data[q][j] -# zb = zij*b -# for (m,a) in test_assembly_data[p][i] -# store(a*zb, m, n) -# end end end end - -# done += 1 -# new_pctg = round(Int, done / todo * 100) -# if new_pctg > pctg + 9 -# myid == 1 && print(".") -# pctg = new_pctg -# end end -# myid == 1 && println("") -# end - - -# function assemblechunk_body_trial_refines_test!(biop, -# test_functions, test_charts, test_assembly_data, test_cells, -# trial_functions, trial_charts, trial_assembly_data, trial_cells, -# qd, zlocal, store; quadstrat) - -# test_shapes = refspace(test_functions) -# trial_shapes = refspace(trial_functions) - -# myid = Threads.threadid() -# myid == 1 && print("dots out of 10: ") -# todo, done, pctg = length(test_charts), 0, 0 -# for (p,(tcell,tchart)) in enumerate(zip(test_cells, test_charts)) -# for (q,(bcell,bchart)) in enumerate(zip(trial_cells, trial_charts)) - -# fill!(zlocal, 0) -# qrule = quadrule(biop, test_shapes, trial_shapes, p, tchart, q, bchart, qd, quadstrat) -# momintegrals_trial_refines_test!(zlocal, biop, -# test_functions, tcell, tchart, -# trial_functions, bcell, bchart, -# qrule, quadstrat) - -# I = length(test_assembly_data[p]) -# J = length(trial_assembly_data[q]) -# for j in 1 : J, i in 1 : I -# zij = zlocal[i,j] -# for (n,b) in trial_assembly_data[q][j] -# zb = zij*b -# for (m,a) in test_assembly_data[p][i] -# store(a*zb, m, n) -# end end end end - -# done += 1 -# new_pctg = round(Int, done / todo * 100) -# if new_pctg > pctg + 9 -# myid == 1 && print(".") -# pctg = new_pctg -# end end -# myid == 1 && println("") -# end diff --git a/src/maxwell/timedomain/mwtdops.jl b/src/maxwell/timedomain/mwtdops.jl index 65b56344..dbc73aa1 100644 --- a/src/maxwell/timedomain/mwtdops.jl +++ b/src/maxwell/timedomain/mwtdops.jl @@ -14,7 +14,7 @@ mutable struct MWSingleLayerTDIO{T} <: RetardedPotential{T} end function Base.:*(a::Number, op::MWSingleLayerTDIO) - @info "scalar product a * op (SL)" + # @info "scalar product a * op (SL)" MWSingleLayerTDIO( op.speed_of_light, a * op.ws_weight, @@ -30,7 +30,7 @@ mutable struct MWDoubleLayerTDIO{T} <: RetardedPotential{T} end function Base.:*(a::Number, op::MWDoubleLayerTDIO) - @info "scalar product a * op (DL)" + # @info "scalar product a * op (DL)" MWDoubleLayerTDIO( op.speed_of_light, a * op.weight, @@ -44,7 +44,7 @@ mutable struct MWDoubleLayerTransposedTDIO{T} <: RetardedPotential{T} end function Base.:*(a::Number, op::MWDoubleLayerTransposedTDIO) - @info "scalar product a * op (DL)" + # @info "scalar product a * op (DL)" MWDoubleLayerTransposedTDIO( op.speed_of_light, a * op.weight, @@ -120,7 +120,7 @@ function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, Y, S = spatialbasis(W), temporalbasis(W) splits = [round(Int,s) for s in range(0, stop=numfunctions(Y), length=P+1)] - @info "Starting assembly with $P threads:" + # @info "Starting assembly with $P threads:" Threads.@threads for i in 1:P lo, hi = splits[i]+1, splits[i+1] lo <= hi || continue diff --git a/src/quadrature/nonconformingoverlapqrule.jl b/src/quadrature/nonconformingoverlapqrule.jl index 12c563b0..c93dd267 100644 --- a/src/quadrature/nonconformingoverlapqrule.jl +++ b/src/quadrature/nonconformingoverlapqrule.jl @@ -8,8 +8,11 @@ function momintegrals!(op, test_chart::CompScienceMeshes.Simplex, basis_chart::CompScienceMeshes.Simplex, out, qrule::NonConformingOverlapQRule) + num_tshapes = numfunctions(test_local_space, domain(test_chart)) + num_bshapes = numfunctions(basis_local_space, domain(basis_chart)) + test_charts, tclps = CompScienceMeshes.intersection_keep_clippings(test_chart, basis_chart) - _, bclps = CompScienceMeshes.intersection_keep_clippings(basis_chart, test_chart) + _, bclps = CompScienceMeshes.intersection_keep_clippings(basis_chart, test_chart) bsis_charts = copy(test_charts) for tclp in tclps append!(test_charts, tclp) end @@ -26,32 +29,36 @@ function momintegrals!(op, isempty(test_charts) && return isempty(bsis_charts) && return - # @assert volume(test_chart) ≈ sum(volume.(test_charts)) - # if volume(basis_chart) ≈ sum(volume.(bsis_charts)) else - # @show volume(basis_chart) - # @show sum(volume.(bsis_charts)) - # error() - # end - # test_local_space = refspace(test_functions) - # basis_local_space = refspace(basis_functions) + test_overlaps = map(test_charts) do tchart + simplex( + carttobary(test_chart, tchart.vertices[1]), + carttobary(test_chart, tchart.vertices[2]), + carttobary(test_chart, tchart.vertices[3])) + end + + trial_overlaps = map(bsis_charts) do bchart + simplex( + carttobary(basis_chart, bchart.vertices[1]), + carttobary(basis_chart, bchart.vertices[2]), + carttobary(basis_chart, bchart.vertices[3])) + end qstrat = CommonFaceOverlappingEdgeQStrat(qrule.conforming_qstrat) qdata = quaddata(op, test_local_space, basis_local_space, test_charts, bsis_charts, qstrat) + zlocal = zero(out) + P = zeros(T, num_tshapes, num_tshapes) + Q = zeros(T, num_bshapes, num_bshapes) for (p,tchart) in enumerate(test_charts) + restrict!(P, test_local_space, test_chart, tchart, test_overlaps[p]) for (q,bchart) in enumerate(bsis_charts) + restrict!(Q, basis_local_space, basis_chart, bchart, trial_overlaps[q]) + qrule = quadrule(op, test_local_space, basis_local_space, p, tchart, q, bchart, qdata, qstrat) - # @show qrule - - P = restrict(test_local_space, test_chart, tchart) - Q = restrict(basis_local_space, basis_chart, bchart) - zlocal = zero(out) - # momintegrals!(zlocal, op, - # test_local_space, nothing, tchart, - # basis_local_space, nothing, bchart, qrule) + fill!(zlocal, 0) momintegrals!(op, test_local_space, basis_local_space, tchart, bchart, zlocal, qrule) @@ -60,5 +67,4 @@ function momintegrals!(op, for k in axes(P,2) for l in axes(Q,2) out[i,j] += P[i,k] * zlocal[k,l] * Q[j,l] - # out .+= P * zlocal * Q' end end end end end end end \ No newline at end of file diff --git a/src/quadrature/nonconformingtouchqrule.jl b/src/quadrature/nonconformingtouchqrule.jl index cbe08aa3..e2b63866 100644 --- a/src/quadrature/nonconformingtouchqrule.jl +++ b/src/quadrature/nonconformingtouchqrule.jl @@ -9,8 +9,8 @@ function momintegrals!(op, τ::CompScienceMeshes.Simplex, σ::CompScienceMeshes.Simplex, out, qrule::NonConformingTouchQRule) - # test_locspace = refspace(test_functions) - # bsis_locspace = refspace(bsis_functions) + num_tshapes = numfunctions(test_locspace, domain(τ)) + num_bshapes = numfunctions(bsis_locspace, domain(σ)) T = coordtype(τ) P = eltype(τ.vertices) @@ -25,50 +25,44 @@ function momintegrals!(op, isempty(τs) && return isempty(σs) && return - # test conformity - # for a in τs - # for b in σs - # if !_test_conformity(a, b) - # @infiltrate - # end - # end - # end - - # volume(σ) ≈ sum(volume.(σs)) || @infiltrate - - # if volume(τ) ≈ sum(volume.(τs)) else - # @show volume(τ) - # @show sum(volume.(τs)) - # error() - # end - # @assert volume(σ) ≈ sum(volume.(σs)) - @assert all(volume.(τs) .> 1e3 * eps(T) * (volume(τ))) @assert all(volume.(σs) .> 1e3 * eps(T) * (volume(σ))) + test_overlaps = map(τs) do tchart + simplex( + carttobary(τ, tchart.vertices[1]), + carttobary(τ, tchart.vertices[2]), + carttobary(τ, tchart.vertices[3])) + end + + trial_overlaps = map(σs) do bchart + simplex( + carttobary(σ, bchart.vertices[1]), + carttobary(σ, bchart.vertices[2]), + carttobary(σ, bchart.vertices[3])) + end + qstrat = qrule.conforming_qstrat qdata = quaddata(op, test_locspace, bsis_locspace, τs, σs, qstrat) - any(volume.(τs) .< 1e-13) && @infiltrate - any(volume.(σs) .< 1e-13) && @infiltrate + @assert !any(volume.(τs) .< 1e-13) + @assert !any(volume.(σs) .< 1e-13) + zlocal = zero(out) + P = zeros(T, num_tshapes, num_tshapes) + Q = zeros(T, num_bshapes, num_bshapes) for (p,tchart) in enumerate(τs) + restrict!(P, test_locspace, τ, tchart, test_overlaps[p]) for (q,bchart) in enumerate(σs) + restrict!(Q, bsis_locspace, σ, bchart, trial_overlaps[q]) + qrule = quadrule(op, test_locspace, bsis_locspace, p, tchart, q, bchart, qdata, qstrat) - # @show qrule - - P = restrict(test_locspace, τ, tchart) - Q = restrict(bsis_locspace, σ, bchart) - zlocal = zero(out) - # momintegrals!(zlocal, op, - # test_locspace, nothing, tchart, - # bsis_locspace, nothing, bchart, qrule) + fill!(zlocal, 0) momintegrals!(op, test_locspace, bsis_locspace, tchart, bchart, zlocal, qrule) - # out .+= P * zlocal * Q' for i in axes(P,1) for j in axes(Q,1) for k in axes(P,2) diff --git a/src/quadrature/rules/testinbaryrefoftrialqrule.jl b/src/quadrature/rules/testinbaryrefoftrialqrule.jl index eea7a055..6f5239b7 100644 --- a/src/quadrature/rules/testinbaryrefoftrialqrule.jl +++ b/src/quadrature/rules/testinbaryrefoftrialqrule.jl @@ -34,16 +34,15 @@ function BEAST.momintegrals!(out, op, CompScienceMeshes.simplex(v[3], e[2], c), CompScienceMeshes.simplex(v[1], c, e[2])) - C = CompScienceMeshes.cartesian( - CompScienceMeshes.neighborhood(trial_chart, c)) - V = CompScienceMeshes.cartesian.(( - CompScienceMeshes.neighborhood(trial_chart, v[1]), - CompScienceMeshes.neighborhood(trial_chart, v[2]), - CompScienceMeshes.neighborhood(trial_chart, v[3]))) - E = CompScienceMeshes.cartesian.(( - CompScienceMeshes.neighborhood(trial_chart, e[1]), - CompScienceMeshes.neighborhood(trial_chart, e[2]), - CompScienceMeshes.neighborhood(trial_chart, e[3]))) + C = CompScienceMeshes.cartesian(trial_chart, c) + V = ( + CompScienceMeshes.cartesian(trial_chart, v[1]), + CompScienceMeshes.cartesian(trial_chart, v[2]), + CompScienceMeshes.cartesian(trial_chart, v[3])) + E = ( + CompScienceMeshes.cartesian(trial_chart, e[1]), + CompScienceMeshes.cartesian(trial_chart, e[2]), + CompScienceMeshes.cartesian(trial_chart, e[3])) trial_charts = ( CompScienceMeshes.simplex(V[1], E[3], C), @@ -57,7 +56,7 @@ function BEAST.momintegrals!(out, op, qd = BEAST.quaddata(op, test_local_space, trial_local_space, (test_chart,), trial_charts, quadstrat) - Q = zeros(T, num_tshapes, num_bshapes) + Q = zeros(T, num_tshapes, num_tshapes) out1 = zero(out) for (q,chart) in enumerate(trial_charts) qr1 = BEAST.quadrule(op, test_local_space, trial_local_space, diff --git a/src/quadrature/rules/testrefinestrialqrule.jl b/src/quadrature/rules/testrefinestrialqrule.jl index e2d86b99..baf388f7 100644 --- a/src/quadrature/rules/testrefinestrialqrule.jl +++ b/src/quadrature/rules/testrefinestrialqrule.jl @@ -22,17 +22,26 @@ function momintegrals!(out, op, parent_mesh = CompScienceMeshes.parent(test_mesh) trial_charts = [chart(test_mesh, p) for p in CompScienceMeshes.children(parent_mesh, trial_cell)] + trial_overlaps = map(trial_charts) do chart + simplex( + carttobary(trial_chart, chart.vertices[1]), + carttobary(trial_chart, chart.vertices[2]), + carttobary(trial_chart, chart.vertices[3])) + end + quadstrat = qr.conforming_qstrat qd = quaddata(op, test_local_space, trial_local_space, [test_chart], trial_charts, quadstrat) + zlocal = zero(out) + Q = zeros(coordtype(trial_chart), num_bshapes, num_bshapes) for (q,chart) in enumerate(trial_charts) + restrict!(Q, trial_local_space, trial_chart, chart, trial_overlaps[q]) + qr = quadrule(op, test_local_space, trial_local_space, 1, test_chart, q ,chart, qd, quadstrat) - - Q = restrict(trial_local_space, trial_chart, chart) - zlocal = zero(out) + fill!(zlocal, 0) momintegrals!(zlocal, op, test_functions, nothing, test_chart, trial_functions, nothing, chart, qr) diff --git a/src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl b/src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl index 7b9fc217..51c1394d 100644 --- a/src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl +++ b/src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl @@ -20,7 +20,7 @@ end using BEAST, Test using CompScienceMeshes, LinearAlgebra - @show pathof(CompScienceMeshes) + # @show pathof(CompScienceMeshes) fnm = joinpath(dirname(pathof(BEAST)), "../test/assets/sphere45.in") Γ1 = BEAST.readmesh(fnm) diff --git a/test/test_dipole.jl b/test/test_dipole.jl index 02af52ea..34b27699 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -83,8 +83,8 @@ for U in [Float32,Float64] nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(𝓚), pts, j_BCMFIE, X) ff_E_BCMFIE = potential(MWFarField3D(𝓣), pts, j_BCMFIE, X) - @show length(pts) - @show norm.(nf_E_BCMFIE - E.(pts)) ./ norm.(E.(pts)) + # @show length(pts) + # @show norm.(nf_E_BCMFIE - E.(pts)) ./ norm.(E.(pts)) @test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 diff --git a/test/test_gwp.jl b/test/test_gwp.jl index ca2210e8..f6bb6cad 100644 --- a/test/test_gwp.jl +++ b/test/test_gwp.jl @@ -29,7 +29,7 @@ end nf = numfunctions(ϕ, dom) @test coeffs ≈ Matrix{T}(I,nf,nf) atol=sqrt(eps(T)) - display(round.(coeffs, digits=3)) + # display(round.(coeffs, digits=3)) end @@ -53,7 +53,7 @@ end nf1 = numfunctions(ψ, domain(supp)) nf2 = numfunctions(ϕ, domain(supp)) @test size(coeffs) == (nf1, nf2) - display(round.(coeffs, digits=3)) + # display(round.(coeffs, digits=3)) pts = [ point(T, 0.3, 0.1), diff --git a/test/test_higher_order_lagrange_functions.jl b/test/test_higher_order_lagrange_functions.jl index 5f6bab8d..aec7f292 100644 --- a/test/test_higher_order_lagrange_functions.jl +++ b/test/test_higher_order_lagrange_functions.jl @@ -187,7 +187,7 @@ end val2 = sum(Q[j,i] * b.value for (i,b) in enumerate(basis)) @test val1≈val2 atol=1e-8 end - println() + # println() end end diff --git a/test/test_mixed_blkassm.jl b/test/test_mixed_blkassm.jl index d49d485c..21fbabb4 100644 --- a/test/test_mixed_blkassm.jl +++ b/test/test_mixed_blkassm.jl @@ -43,7 +43,7 @@ for T in [Float32, Float64] local X = raviartthomas(Γ) local Y = buffachristiansen(Γ) - println("Number of RWG functions: ", numfunctions(X)) + # println("Number of RWG functions: ", numfunctions(X)) T_blockassembler = hassemble(𝓣, X, X) T_standardassembler = assemble(𝓣, X, X) diff --git a/test/test_variational.jl b/test/test_variational.jl index eee2b42e..7b053d67 100644 --- a/test/test_variational.jl +++ b/test/test_variational.jl @@ -45,9 +45,9 @@ dbf = BEAST.discretise(bf, j=>X, k=>X) dlf = BEAST.discretise(lf, j=>X) space_mappings = (j=>X, k=>X,) -for sm in space_mappings - @show sm -end +# for sm in space_mappings +# @show sm +# end M = assemble(dbf) A = Matrix(M) From 51f8e0d3b606c88d1a8a5a8f282b4d73a16301db Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 15 Apr 2025 12:02:25 +0200 Subject: [PATCH 479/528] Remove some more output --- src/localop.jl | 2 +- test/runtests.jl | 2 +- test/test_ndlcd_restrict.jl | 4 ++-- test/test_quadstrat_as_fn.jl | 4 ++-- test/untested/test_mot.jl | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/localop.jl b/src/localop.jl index a788bb17..b272973e 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -145,7 +145,7 @@ end function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, store; quadstrat=defaultquadstrat(biop, tfs, bfs)) - println("Using 'refines' algorithm for local assembly:") + # println("Using 'refines' algorithm for local assembly:") tgeo = geometry(tfs) bgeo = geometry(bfs) diff --git a/test/runtests.jl b/test/runtests.jl index fde4a5bc..0698da38 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -84,7 +84,7 @@ include("test_gridfunction.jl") include("test_hh_lsvie.jl") using TestItemRunner -@run_package_tests filter=ti->!(:example in ti.tags) +@run_package_tests filter=ti->!(:example in ti.tags) verbose=true try diff --git a/test/test_ndlcd_restrict.jl b/test/test_ndlcd_restrict.jl index fc88e1e0..27aad40b 100644 --- a/test/test_ndlcd_restrict.jl +++ b/test/test_ndlcd_restrict.jl @@ -31,8 +31,8 @@ for T in [Float32, Float64] for j in axes(Q,1) x = Fp[j].value y = sum(Q[j,i]*fp[i].value for i in axes(Q,2)) - @show x - @show y + # @show x + # @show y @test x ≈ y end end \ No newline at end of file diff --git a/test/test_quadstrat_as_fn.jl b/test/test_quadstrat_as_fn.jl index 0d056b8e..fc180c6d 100644 --- a/test/test_quadstrat_as_fn.jl +++ b/test/test_quadstrat_as_fn.jl @@ -5,7 +5,7 @@ pd = dirname(pathof(BEAST)) fn = joinpath(pd, "../test/assets/sphere45.in") Γ = CompScienceMeshes.readmesh(fn) - @show length(Γ) + # @show length(Γ) X = raviartthomas(Γ) @show numfunctions(X) @@ -31,7 +31,7 @@ end pd = dirname(pathof(BEAST)) fn = joinpath(pd, "../test/assets/sphere45.in") Γ = CompScienceMeshes.readmesh(fn) - @show length(Γ) + # @show length(Γ) X = raviartthomas(Γ) @show numfunctions(X) diff --git a/test/untested/test_mot.jl b/test/untested/test_mot.jl index 847a15e8..929fc87a 100644 --- a/test/untested/test_mot.jl +++ b/test/untested/test_mot.jl @@ -18,7 +18,7 @@ W0 = inv(Z0) x = BEAST.mot(W0,Z,B,Nt) n = 20 -@show x[n] +# @show x[n] @show exp(-n*Δt) @show norm(x[n]-exp(-n*Δt)) From c63c16c11fd119d7534b04a3ceb55e68ce4e3d43 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 15 Apr 2025 17:08:26 +0200 Subject: [PATCH 480/528] increased use of testitems --- test/runtests.jl | 6 +- test/test_hh3dexc.jl | 22 ++-- test/test_hh3dtd_exc.jl | 60 +++++----- test/test_interpolate_and_restrict.jl | 2 +- test/test_local_storage.jl | 34 +++--- test/test_mixed_blkassm.jl | 99 +++++++++-------- test/test_mult.jl | 45 ++++---- test/test_ncrossbdm.jl | 75 ++++++------- test/test_ndlcd_restrict.jl | 60 +++++----- test/test_restrict.jl | 153 ++++++++++++-------------- 10 files changed, 274 insertions(+), 282 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 0698da38..de70814f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,6 +4,8 @@ using StaticArrays module PkgTests +using TestItemRunner + using Distributed using LinearAlgebra using SparseArrays @@ -60,7 +62,7 @@ include("test_nitschehh3d.jl") include("test_curlcurlgreen.jl") include("test_hh3dtd_exc.jl") -include("test_hh3dexc.jl") +# include("test_hh3dexc.jl") include("test_hh3d_nearfield.jl") include("test_tdassembly.jl") include("test_tdhhdbl.jl") @@ -83,7 +85,7 @@ include("test_gridfunction.jl") include("test_hh_lsvie.jl") -using TestItemRunner + @run_package_tests filter=ti->!(:example in ti.tags) verbose=true diff --git a/test/test_hh3dexc.jl b/test/test_hh3dexc.jl index cb11981c..7aa78119 100644 --- a/test/test_hh3dexc.jl +++ b/test/test_hh3dexc.jl @@ -1,10 +1,12 @@ +@testitem "excitation: HH3D" begin using CompScienceMeshes -using BEAST -using Test +# using BEAST +# using Test for T in [Float32, Float64] - sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in"), T=T) - numcells(sphere) + # sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in"), T=T) + sphere = readmesh(joinpath(dirname(pathof(BEAST)),"../test","assets","sphere5.in"), T=T) + # numcells(sphere) local κ = T(2π) direction = point(T,0,0,1) @@ -24,27 +26,29 @@ for T in [Float32, Float64] γnlp = dot(BEAST.NormalVector(), gradlp) - import BEAST.∂n + # import BEAST.∂n p = ∂n(f) local s = chart(sphere,first(sphere)) local c = neighborhood(s, T.([1,1]/3)) r = cartesian(c) - local n = normal(s) + local nr = normal(s) w1 = p(c) - w2 = -im*κ*dot(direction, n)*f(r) + w2 = -im*κ*dot(direction, nr)*f(r) w1 ≈ w2 local N = BEAST.HH3DHyperSingularFDBIO(im*κ) local X = BEAST.lagrangec0d1(sphere) - numfunctions(X) + # numfunctions(X) - BEAST.quadinfo(N, X, X) + # BEAST.quadinfo(N, X, X) Nxx = assemble(N, X, X) @test size(Nxx) == (numfunctions(X), numfunctions(X)) +end + end \ No newline at end of file diff --git a/test/test_hh3dtd_exc.jl b/test/test_hh3dtd_exc.jl index a400dad6..75432cbe 100644 --- a/test/test_hh3dtd_exc.jl +++ b/test/test_hh3dtd_exc.jl @@ -1,35 +1,37 @@ -using BEAST -using CompScienceMeshes -using Test -for T in [Float32, Float64] - dir = point(T,0,0,1) - local width = T(1.0) - delay = T(1.5) - scaling = T(1.0) - sig = creategaussian(width, delay, scaling) +@testitem "excitation: TD Helmholtz 3D" begin + # using BEAST + using CompScienceMeshes + # using Test + for T in [Float32, Float64] + dir = point(T,0,0,1) + width = T(1.0) + delay = T(1.5) + scaling = T(1.0) + sig = creategaussian(width, delay, scaling) - pw = BEAST.planewave(dir, T(1.0), sig) + pw = BEAST.planewave(dir, T(1.0), sig) - CompScienceMeshes.cartesian(p::typeof(dir)) = p - CompScienceMeshes.cartesian(p::Number) = p - local x = point(T,1,0,0) - local t = T(1.0) - @test pw(x,t) ≈ sig(t-dot(dir,x)) + CompScienceMeshes.cartesian(p::typeof(dir)) = p + CompScienceMeshes.cartesian(p::Number) = p + x = point(T,1,0,0) + t = T(1.0) + @test pw(x,t) ≈ sig(t-dot(dir,x)) - pw2 = BEAST.gradient(pw) - @test pw2.direction ≈ dir - @test pw2.polarisation ≈ -dir - @test pw2.speedoflight ≈ pw.speed_of_light + pw2 = BEAST.gradient(pw) + @test pw2.direction ≈ dir + @test pw2.polarisation ≈ -dir + @test pw2.speedoflight ≈ pw.speed_of_light - dsig = derive(sig) - @test pw2(x,t) ≈ -dir*dsig(t-dot(dir,x)) + dsig = derive(sig) + @test pw2(x,t) ≈ -dir*dsig(t-dot(dir,x)) - trc = dot(BEAST.n,pw2) - ch = simplex( - point(T,0,0,0), - point(T,1,0,0), - point(T,0,1,0)) - ctr = center(ch) - val = trc(ctr,t) - @test val ≈ -dsig(t-dot(dir,x)) + trc = dot(BEAST.n,pw2) + ch = simplex( + point(T,0,0,0), + point(T,1,0,0), + point(T,0,1,0)) + ctr = center(ch) + val = trc(ctr,t) + @test val ≈ -dsig(t-dot(dir,x)) + end end \ No newline at end of file diff --git a/test/test_interpolate_and_restrict.jl b/test/test_interpolate_and_restrict.jl index 369a61af..9671c0e6 100644 --- a/test/test_interpolate_and_restrict.jl +++ b/test/test_interpolate_and_restrict.jl @@ -28,7 +28,7 @@ vals = [f.value for f in X(ctr)] itpol = sum(w*val for (w,val) in zip(Q3,vals)) @test itpol ≈ constant_vector_field -using TestItems +# using TestItems @testitem "restrict RT0" begin using CompScienceMeshes diff --git a/test/test_local_storage.jl b/test/test_local_storage.jl index 7e186ae1..a5cd8bb5 100644 --- a/test/test_local_storage.jl +++ b/test/test_local_storage.jl @@ -1,22 +1,22 @@ -using Test -using BEAST -using CompScienceMeshes -using SparseArrays -for T in [Float32, Float64] - local fn = joinpath(@__DIR__, "assets/sphere5.in") - local m = readmesh(fn, T=T) +@testitem "storage: local operators" begin + using CompScienceMeshes + using SparseArrays + for T in [Float32, Float64] + fn = joinpath(@__DIR__, "assets/sphere5.in") + m = readmesh(fn, T=T) - Id = BEAST.Identity() - local X = BEAST.raviartthomas(m) + Id = BEAST.Identity() + X = BEAST.raviartthomas(m) - Z1 = assemble(Id, X, X, storage_policy=Val{:densestorage}) - Z2 = assemble(Id, X, X, storage_policy=Val{:bandedstorage}) - Z3 = assemble(Id, X, X, storage_policy=Val{:sparsedicts}) + Z1 = assemble(Id, X, X, storage_policy=Val{:densestorage}) + Z2 = assemble(Id, X, X, storage_policy=Val{:bandedstorage}) + Z3 = assemble(Id, X, X, storage_policy=Val{:sparsedicts}) - @test Z1 isa DenseMatrix - @test Z2 isa SparseMatrixCSC - @test Z3 isa SparseMatrixCSC + @test Z1 isa DenseMatrix + @test Z2 isa SparseMatrixCSC + @test Z3 isa SparseMatrixCSC - @test Z1 ≈ Z2 atol=1e-8 - @test Z1 ≈ Z3 atol=1e-8 + @test Z1 ≈ Z2 atol=1e-8 + @test Z1 ≈ Z3 atol=1e-8 + end end \ No newline at end of file diff --git a/test/test_mixed_blkassm.jl b/test/test_mixed_blkassm.jl index 21fbabb4..f1ddcdf4 100644 --- a/test/test_mixed_blkassm.jl +++ b/test/test_mixed_blkassm.jl @@ -1,68 +1,71 @@ -# test resolutuion of #66 +@testitem "Issue #66: block assembly dbl-layer" begin + # test resolutuion of #66 -using BEAST -using Test -using CompScienceMeshes -using LinearAlgebra + # using BEAST + # using Test + using CompScienceMeshes + using LinearAlgebra -function hassemble(operator::BEAST.AbstractOperator, - test_functions, - trial_functions) + function hassemble2(operator::BEAST.AbstractOperator, + test_functions, + trial_functions) - blkasm = BEAST.blockassembler(operator, test_functions, trial_functions) + blkasm = BEAST.blockassembler(operator, test_functions, trial_functions) - function assembler(Z, tdata, sdata) - store(v,m,n) = (Z[m,n] += v) - blkasm(tdata,sdata,store) - end + function assembler2(Z, tdata, sdata) + store(v,m,n) = (Z[m,n] += v) + blkasm(tdata,sdata,store) + end - mat = zeros(scalartype(operator), - numfunctions(test_functions), - numfunctions(trial_functions)) + mat = zeros(scalartype(operator), + numfunctions(test_functions), + numfunctions(trial_functions)) - assembler(mat, 1:numfunctions(test_functions), 1:numfunctions(trial_functions)) - return mat -end + assembler2(mat, 1:numfunctions(test_functions), 1:numfunctions(trial_functions)) + return mat + end -for T in [Float32, Float64] - c = T(3e8) - μ = T(4π * 1e-7) - ε = T(1/(μ*c^2)) - local f = T(1e8) - λ = T(c/f) - k = T(2π/λ) - local ω = T(k*c) - η = T(sqrt(μ/ε)) + for T in [Float32, Float64] + c = T(3e8) + μ = T(4π * 1e-7) + ε = T(1/(μ*c^2)) + f = T(1e8) + λ = T(c/f) + k = T(2π/λ) + ω = T(k*c) + η = T(sqrt(μ/ε)) - a = T(1) - local Γ = CompScienceMeshes.meshcuboid(a,a,a,T(0.2)) + a = T(1) + Γ = CompScienceMeshes.meshcuboid(a,a,a,T(0.2)) - 𝓣 = Maxwell3D.singlelayer(wavenumber=k) - 𝓚 = Maxwell3D.doublelayer(wavenumber=k) + 𝓣 = Maxwell3D.singlelayer(wavenumber=k) + 𝓚 = Maxwell3D.doublelayer(wavenumber=k) - local X = raviartthomas(Γ) - local Y = buffachristiansen(Γ) + X = raviartthomas(Γ) + Y = buffachristiansen(Γ) - # println("Number of RWG functions: ", numfunctions(X)) + # println("Number of RWG functions: ", numfunctions(X)) - T_blockassembler = hassemble(𝓣, X, X) - T_standardassembler = assemble(𝓣, X, X) + T_blockassembler = hassemble2(𝓣, X, X) + T_standardassembler = assemble(𝓣, X, X) - @test norm(T_blockassembler - T_standardassembler)/norm(T_standardassembler) ≈ 0.0 atol=100*eps(T) + @test norm(T_blockassembler - T_standardassembler)/norm(T_standardassembler) ≈ 0.0 atol=100*eps(T) - T_bc_blockassembler = hassemble(𝓣, Y, Y) - T_bc_standardassembler = assemble(𝓣, Y, Y) + T_bc_blockassembler = hassemble2(𝓣, Y, Y) + T_bc_standardassembler = assemble(𝓣, Y, Y) - @test norm(T_bc_blockassembler - T_bc_standardassembler)/norm(T_bc_standardassembler) ≈ 0.0 atol=100*eps(T) + @test norm(T_bc_blockassembler - T_bc_standardassembler)/norm(T_bc_standardassembler) ≈ 0.0 atol=100*eps(T) - K_mix_blockassembler = hassemble(𝓚,Y,X) - K_mix_standardassembler = assemble(𝓚,Y,X) + K_mix_blockassembler = hassemble2(𝓚,Y,X) + K_mix_standardassembler = assemble(𝓚,Y,X) - T_mix_blockassembler = hassemble(𝓣, Y, X) - T_mix_standardassembler = assemble(𝓣, Y, X) + T_mix_blockassembler = hassemble2(𝓣, Y, X) + T_mix_standardassembler = assemble(𝓣, Y, X) - if T==Float64 - @test norm(K_mix_blockassembler - K_mix_standardassembler)/norm(K_mix_standardassembler) ≈ 0.0 atol=100*eps(T) - @test norm(T_mix_blockassembler - T_mix_standardassembler)/norm(T_mix_standardassembler) ≈ 0.0 atol=100*eps(T) + if T==Float64 + @test norm(K_mix_blockassembler - K_mix_standardassembler)/norm(K_mix_standardassembler) ≈ 0.0 atol=100*eps(T) + @test norm(T_mix_blockassembler - T_mix_standardassembler)/norm(T_mix_standardassembler) ≈ 0.0 atol=100*eps(T) + @info "Tests executed!" + end end end \ No newline at end of file diff --git a/test/test_mult.jl b/test/test_mult.jl index 4c5399df..420b30ae 100644 --- a/test/test_mult.jl +++ b/test/test_mult.jl @@ -1,31 +1,30 @@ -using CompScienceMeshes -using BEAST +@testitem "loops have div zero" begin + using CompScienceMeshes + using LinearAlgebra -using Test -using LinearAlgebra + for T in [Float32, Float64] + faces = meshrectangle(T(1.0), T(1.0), T(0.5), 3) -for T in [Float32, Float64] - faces = meshrectangle(T(1.0), T(1.0), T(0.5), 3) + bnd = boundary(faces) + edges = submesh(!in(bnd), skeleton(faces,1)) - local bnd = boundary(faces) - local edges = submesh(!in(bnd), skeleton(faces,1)) + bnd_nodes = skeleton(bnd, 0) + @test length(bnd_nodes) == 8 + nodes = submesh(!in(bnd_nodes), skeleton(faces,0)) + @test length(nodes) == 1 - local bnd_nodes = skeleton(bnd, 0) - @test length(bnd_nodes) == 8 - local nodes = submesh(!in(bnd_nodes), skeleton(faces,0)) - @test length(nodes) == 1 + Conn = connectivity(nodes, edges, sign) - Conn = connectivity(nodes, edges, sign) + X = raviartthomas(faces, cellpairs(faces,edges)) + @test numfunctions(X) == 8 - local X = raviartthomas(faces, cellpairs(faces,edges)) - @test numfunctions(X) == 8 - - divX = divergence(X) - Id = BEAST.Identity() - DD = Matrix(assemble(Id, divX, divX)) - @test rank(DD) == 7 - L = divX * Conn - for sh in L.fns[1] - @test isapprox(sh.coeff, 0, atol=1e-8) + divX = divergence(X) + Id = BEAST.Identity() + DD = Matrix(assemble(Id, divX, divX)) + @test rank(DD) == 7 + L = divX * Conn + for sh in L.fns[1] + @test isapprox(sh.coeff, 0, atol=1e-8) + end end end \ No newline at end of file diff --git a/test/test_ncrossbdm.jl b/test/test_ncrossbdm.jl index 753c0845..1df7ec8a 100644 --- a/test/test_ncrossbdm.jl +++ b/test/test_ncrossbdm.jl @@ -1,47 +1,40 @@ -using Test -using BEAST -using CompScienceMeshes -#testing local value in the center of a triangle -for T in [Float64] - for j in [1,2,3,4,5,6] - local o, x, y, z = euclidianbasis(3,T) - triang = simplex(x,y,o) - ctr = center(triang) - n = normal(triang) - oref = BEAST.NCrossBDMRefSpace{T}() - oref2= BEAST.BDMRefSpace{T}() - oshp=BEAST.Shape(1,j,1.0) - oshp2=BEAST.Shape(1,j,1.0) - - o1 = oref(ctr)[oshp.refid].value * oshp.coeff - o2=n × oref2(ctr)[oshp2.refid].value * oshp2.coeff - @test o1 ≈ o2 +@testitem "local basis: nxBDM and BDM consistency" begin + using CompScienceMeshes + #testing local value in the center of a triangle + for T in [Float64] + for j in [1,2,3,4,5,6] + o, x, y, z = euclidianbasis(3,T) + triang = simplex(x,y,o) + ctr = center(triang) + n = normal(triang) + oref = BEAST.NCrossBDMRefSpace{T}() + oref2= BEAST.BDMRefSpace{T}() + oshp=BEAST.Shape(1,j,1.0) + oshp2=BEAST.Shape(1,j,1.0) + + o1 = oref(ctr)[oshp.refid].value * oshp.coeff + o2=n × oref2(ctr)[oshp2.refid].value * oshp2.coeff + @test o1 ≈ o2 + end end -end - -#testing their connectivity on a mesh - -m=meshsphere(radius=1.0, h=1.0) -nodes = skeleton(m,0) -edges = skeleton(m,1) - -Z = BEAST.brezzidouglasmarini(m) -NZ = BEAST.ncrossbdm(m) -for i=(1,11,36,52,111) - for j=(1,2) - @test Z.fns[i][j].coeff ≈ NZ.fns[i][j].coeff - @test Z.fns[i][j].refid ≈ NZ.fns[i][j].refid - @test Z.fns[i][j].cellid ≈ NZ.fns[i][j].cellid + m=meshsphere(radius=1.0, h=1.0) + Z = BEAST.brezzidouglasmarini(m) + NZ = BEAST.ncrossbdm(m) + for i=(1,11,36,52,111) + for j=(1,2) + @test Z.fns[i][j].coeff ≈ NZ.fns[i][j].coeff + @test Z.fns[i][j].refid ≈ NZ.fns[i][j].refid + @test Z.fns[i][j].cellid ≈ NZ.fns[i][j].cellid + end end -end -#testing the values on a mesh through Gram matrices + #testing the values on a mesh through Gram matrices + Id = BEAST.Identity() + qs = BEAST.SingleNumQStrat(8) -Id = BEAST.Identity() -qs = BEAST.SingleNumQStrat(8) - -Gzz= assemble(Id,Z,Z,quadstrat=qs) -Gnznz= assemble(Id,NZ,NZ,quadstrat=qs) -@test Gzz ≈ Gnznz + Gzz= assemble(Id,Z,Z,quadstrat=qs) + Gnznz= assemble(Id,NZ,NZ,quadstrat=qs) + @test Gzz ≈ Gnznz +end diff --git a/test/test_ndlcd_restrict.jl b/test/test_ndlcd_restrict.jl index 27aad40b..6b7d306e 100644 --- a/test/test_ndlcd_restrict.jl +++ b/test/test_ndlcd_restrict.jl @@ -1,38 +1,38 @@ -using CompScienceMeshes -using BEAST -using Test -using LinearAlgebra +@testitem "restrict for 3D Nedelec-curl" begin + using CompScienceMeshes + using LinearAlgebra -for T in [Float32, Float64] - local o, x, y, z = CompScienceMeshes.euclidianbasis(3,T) - tet = simplex(x,y,z,o) + for T in [Float32, Float64] + o, x, y, z = CompScienceMeshes.euclidianbasis(3,T) + tet = simplex(x,y,z,o) - rs = BEAST.NDLCDRefSpace{T}() - Q = BEAST.restrict(rs, tet, tet) - @test Q ≈ Matrix(T(1.0)LinearAlgebra.I, 4, 4) + rs = BEAST.NDLCDRefSpace{T}() + Q = BEAST.restrict(rs, tet, tet) + @test Q ≈ Matrix(T(1.0)LinearAlgebra.I, 4, 4) - rs = BEAST.NDLCCRefSpace{T}() - Q = BEAST.restrict(rs, tet, tet) - @test Q ≈ Matrix(T(1.0)LinearAlgebra.I, 6, 6) + rs = BEAST.NDLCCRefSpace{T}() + Q = BEAST.restrict(rs, tet, tet) + @test Q ≈ Matrix(T(1.0)LinearAlgebra.I, 6, 6) - c = cartesian(center(tet)) - smalltet = simplex(x,y,z,c) - p_smalltet = center(smalltet) - p_tet = neighborhood(tet, carttobary(tet, cartesian(p_smalltet))) - @show cartesian(p_smalltet) - @show cartesian(p_tet) - @assert cartesian(p_tet) ≈ cartesian(p_smalltet) - @assert volume(tet) / volume(smalltet) ≈ 4 + c = cartesian(center(tet)) + smalltet = simplex(x,y,z,c) + p_smalltet = center(smalltet) + p_tet = neighborhood(tet, carttobary(tet, cartesian(p_smalltet))) + # @show cartesian(p_smalltet) + # @show cartesian(p_tet) + @assert cartesian(p_tet) ≈ cartesian(p_smalltet) + @assert volume(tet) / volume(smalltet) ≈ 4 - Q = BEAST.restrict(rs, tet, smalltet) - Fp = rs(p_tet) - fp = rs(p_smalltet) + Q = BEAST.restrict(rs, tet, smalltet) + Fp = rs(p_tet) + fp = rs(p_smalltet) - for j in axes(Q,1) - x = Fp[j].value - y = sum(Q[j,i]*fp[i].value for i in axes(Q,2)) - # @show x - # @show y - @test x ≈ y + for j in axes(Q,1) + x = Fp[j].value + y = sum(Q[j,i]*fp[i].value for i in axes(Q,2)) + # @show x + # @show y + @test x ≈ y + end end end \ No newline at end of file diff --git a/test/test_restrict.jl b/test/test_restrict.jl index e06a6a58..69d03137 100644 --- a/test/test_restrict.jl +++ b/test/test_restrict.jl @@ -1,84 +1,73 @@ -using Test - -using CompScienceMeshes -using BEAST - -const e0 = point(0.0,0.0,0.0) -const e1 = point(1.0,0.0,0.0) -const e2 = point(0.0,1.0,0.0) -const e3 = point(0.0,0.0,1.0) - -for T in [Float32, Float64] - - - p = simplex( - [ - point(T,0.0,0.0,0.0), - point(T,1.0,0.0,0.0) - ], - Val{1} - ) - - q = simplex( - [ - point(T,0.0,0.0,0.0), - point(T,0.5,0.0,0.0) - ], - Val{1} - ) - - f = BEAST.LagrangeRefSpace{T,1,2,2}() - local x = neighborhood(p, T.([0.0])) - v = f(x) - - @test v[1].value == 0 - @test v[2].value == 1 - - @test v[1].derivative == -1 - @test v[2].derivative == +1 - - Q = restrict(f, p, q) - @test Q == [ - T(1.0) T(0.5) - T(0.0) T(0.5)] - - - # Test restriction of RT elements - # ni, no = 6, 7; - # ui = transpose([CompScienceMeshes.triangleGaussA[ni] CompScienceMeshes.triangleGaussB[ni] ]); - # uo = transpose([CompScienceMeshes.triangleGaussA[no] CompScienceMeshes.triangleGaussB[no] ]); - # wi = triangleGaussW[ni]; - # wo = triangleGaussW[no]; - # universe = Universe(1.0, ui, wi, uo, wo); - - p = simplex([T.(2*e0),T.(2*e1),T.(2*e2)], Val{2}) - x = neighborhood(p,T.([0.5, 0.5])) - ϕ = BEAST.RTRefSpace{T}() - v = ϕ(x) - - Q = restrict(ϕ, p, p) - if T==Float64 - @test Q == Matrix(LinearAlgebra.I, 3, 3) - - q = simplex([T.(e0+e1), T.(2*e1), T.(e1+e2)], Val{2}) - Q = restrict(ϕ, p, q) - @test Q == [ - 2 -1 0 - 0 1 0 - 0 -1 2] // 4 - end - - # Test restriction of Nedelec elements - ψ = BEAST.NDRefSpace{T}() - Q = restrict(ψ, p, p) - if T==Float64 - @test Q == Matrix(LinearAlgebra.I, 3, 3) - - q = simplex([T.(e0+e1), T.(2*e1), T.(e1+e2)], Val{2}) - Q = restrict(ψ, p, q) - @test Q == [ - 2 -1 0 - 0 1 0 - 0 -1 2] // 4 +@testitem "restrict" begin + using LinearAlgebra + using CompScienceMeshes + + const e0 = point(0.0,0.0,0.0) + const e1 = point(1.0,0.0,0.0) + const e2 = point(0.0,1.0,0.0) + const e3 = point(0.0,0.0,1.0) + + for T in [Float32, Float64] + p = simplex( + [ + point(T,0.0,0.0,0.0), + point(T,1.0,0.0,0.0) + ], + Val{1} + ) + + q = simplex( + [ + point(T,0.0,0.0,0.0), + point(T,0.5,0.0,0.0) + ], + Val{1} + ) + + f = BEAST.LagrangeRefSpace{T,1,2,2}() + x = neighborhood(p, T.([0.0])) + v = f(x) + + @test v[1].value == 0 + @test v[2].value == 1 + + @test v[1].derivative == -1 + @test v[2].derivative == +1 + + Q = restrict(f, p, q) + @test Q == [ + T(1.0) T(0.5) + T(0.0) T(0.5)] + + p = simplex([T.(2*e0),T.(2*e1),T.(2*e2)], Val{2}) + x = neighborhood(p,T.([0.5, 0.5])) + ϕ = BEAST.RTRefSpace{T}() + v = ϕ(x) + + Q = restrict(ϕ, p, p) + if T==Float64 + @test Q == Matrix(LinearAlgebra.I, 3, 3) + + q = simplex([T.(e0+e1), T.(2*e1), T.(e1+e2)], Val{2}) + Q = restrict(ϕ, p, q) + @test Q == [ + 2 -1 0 + 0 1 0 + 0 -1 2] // 4 + end + + # Test restriction of Nedelec elements + ψ = BEAST.NDRefSpace{T}() + Q = restrict(ψ, p, p) + if T==Float64 + @test Q == Matrix(LinearAlgebra.I, 3, 3) + + q = simplex([T.(e0+e1), T.(2*e1), T.(e1+e2)], Val{2}) + Q = restrict(ψ, p, q) + @test Q == [ + 2 -1 0 + 0 1 0 + 0 -1 2] // 4 + end end end \ No newline at end of file From ab8c195da22f4613e9f0ceaf518b19c21988e1b1 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 16 Apr 2025 12:13:43 +0200 Subject: [PATCH 481/528] Mysterious test failure fixed by removing test_hh3dexc from inclusions in runtests --- test/runtests.jl | 2 +- test/test_hh3dexc.jl | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index de70814f..c279533f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -47,7 +47,7 @@ include("test_local_storage.jl") include("test_embedding.jl") include("test_assemblerow.jl") -include("test_mixed_blkassm.jl") +# include("test_mixed_blkassm.jl") include("test_local_assembly.jl") include("test_assemble_refinements.jl") diff --git a/test/test_hh3dexc.jl b/test/test_hh3dexc.jl index 7aa78119..cd67ef35 100644 --- a/test/test_hh3dexc.jl +++ b/test/test_hh3dexc.jl @@ -8,9 +8,9 @@ for T in [Float32, Float64] sphere = readmesh(joinpath(dirname(pathof(BEAST)),"../test","assets","sphere5.in"), T=T) # numcells(sphere) - local κ = T(2π) + κ = T(2π) direction = point(T,0,0,1) - local f = BEAST.HH3DPlaneWave(direction, im*κ, T(1.0)) + f = BEAST.HH3DPlaneWave(direction, im*κ, T(1.0)) v1 = f(point(T,0,0,0)) v2 = f(point(T,0,0,0.5)) @@ -29,19 +29,19 @@ for T in [Float32, Float64] # import BEAST.∂n p = ∂n(f) - local s = chart(sphere,first(sphere)) - local c = neighborhood(s, T.([1,1]/3)) + s = chart(sphere,first(sphere)) + c = neighborhood(s, T.([1,1]/3)) r = cartesian(c) - local nr = normal(s) + nr = normal(s) w1 = p(c) w2 = -im*κ*dot(direction, nr)*f(r) w1 ≈ w2 - local N = BEAST.HH3DHyperSingularFDBIO(im*κ) - local X = BEAST.lagrangec0d1(sphere) + N = BEAST.HH3DHyperSingularFDBIO(im*κ) + X = BEAST.lagrangec0d1(sphere) # numfunctions(X) From 2171fa709b1ef750b8f9526113988d732f9b82f2 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Wed, 16 Apr 2025 18:07:14 +0200 Subject: [PATCH 482/528] Assembly into sparse for finite range integral ops --- src/BEAST.jl | 3 + src/maxwell/mwops.jl | 65 ------------------- src/maxwell/qlmwops.jl | 64 ++++++++++++++++++ src/operators/quasilocalops.jl | 115 +++++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 65 deletions(-) create mode 100644 src/maxwell/qlmwops.jl create mode 100644 src/operators/quasilocalops.jl diff --git a/src/BEAST.jl b/src/BEAST.jl index 89e26427..0aefb159 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -210,6 +210,7 @@ include("quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl") include("excitation.jl") include("gridfunction.jl") include("localop.jl") +include("operators/quasilocalops.jl") include("multiplicativeop.jl") include("identityop.jl") include("integralop.jl") @@ -246,6 +247,8 @@ include("quadrature/rules/timedomain/excitation/singlequad2qrule.jl") # Support for Maxwell equations include("maxwell/mwexc.jl") include("maxwell/mwops.jl") +include("maxwell/qlmwops.jl") + include("maxwell/nxdbllayer.jl") include("maxwell/wiltonints.jl") include("maxwell/nitsche.jl") diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index dbac76e5..66fe0666 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -80,71 +80,6 @@ MWDoubleLayer3D(gamma) = MWDoubleLayer3D(1.0, gamma) # For legacy purposes regularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DReg(op.alpha, op.gamma) singularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DSng(op.alpha, op.gamma) - - -# function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::BDMRefSpace, i, τ, j, σ, qd, -# qs::DoubleNumWiltonSauterQStrat) - -# hits = 0 -# dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) -# dmin2 = floatmax(eltype(eltype(τ.vertices))) -# for t in τ.vertices -# for s in σ.vertices -# d2 = LinearAlgebra.norm_sqr(t-s) -# dmin2 = min(dmin2, d2) -# hits += (d2 < dtol) -# end -# end - -# hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) -# hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) -# hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - -# h2 = volume(σ) -# xtol2 = 0.2 * 0.2 -# k2 = abs2(gamma(op)) -# return DoubleQuadRule( -# qd.tpoints[1,i], -# qd.bpoints[1,j],) -# end - - -# function qrdf(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd) -# # defines coincidence of points -# dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) - -# # decides on whether to use singularity extraction -# xtol = 0.2 - -# k = norm(gamma(op)) - -# hits = 0 -# xmin = xtol -# for t in τ.vertices -# for s in σ.vertices -# d = norm(t-s) -# xmin = min(xmin, k*d) -# if d < dtol -# hits +=1 -# break -# end -# end -# end - -# xmin < xtol && return WiltonSERule( -# qd.tpoints[1,i], -# DoubleQuadRule( -# qd.tpoints[2,i], -# qd.bpoints[2,j], -# ), -# ) -# return DoubleQuadRule( -# qd.tpoints[1,i], -# qd.bpoints[1,j], -# ) - -# end - ################################################################################ # # Kernel definitions diff --git a/src/maxwell/qlmwops.jl b/src/maxwell/qlmwops.jl new file mode 100644 index 00000000..e8767a26 --- /dev/null +++ b/src/maxwell/qlmwops.jl @@ -0,0 +1,64 @@ +struct QuasiLocalSingleLayerMW{R<:Real} <: QuasiLocalOperator + gamma::R + alpha::R + beta::R + range::R +end + +scalartype(op::QuasiLocalSingleLayerMW{T}) where {T} = T +defaultquadstrat(op::QuasiLocalSingleLayerMW, + tfs::RTRefSpace, bfs::RTRefSpace) = DoubleNumSauterQstrat(4,5,5,5,4,3) + +oprange(op::QuasiLocalSingleLayerMW) = op.range + +QuasiLocalSingleLayerMW(gamma) = QuasiLocalSingleLayerMW(gamma, -gamma, -1/(gamma), 12/gamma) + +function (igd::Integrand{<:QuasiLocalSingleLayerMW})(x,y,f,g) + α = igd.operator.alpha + β = igd.operator.beta + γ = igd.operator.gamma + + r = cartesian(x) - cartesian(y) + R = norm(r) + iR = 1 / R + green = exp(-γ*R)*(i4pi*iR) + + αG = α * green + βG = β * green + + _integrands(f,g) do fi,gj + αG * dot(fi.value, gj.value) + βG * dot(fi.divergence, gj.divergence) + end +end + +@testitem "quasi-local singlelayer MW" begin + using CompScienceMeshes, LinearAlgebra + using SparseArrays + + fn = joinpath(dirname(pathof(BEAST)),"../test","assets","sphere45.in") + Γ1 = readmesh(fn) + Γ2 = CompScienceMeshes.translate(Γ1, point(0,0,3.0)) + # Γ = meshsphere(radius=1.0, h=0.075) + + δ = 0.025 + γ = 1/δ + t1 = BEAST.QuasiLocalSingleLayerMW(γ, -γ, -δ, 12*δ) + t2 = BEAST.MWSingleLayer3D(γ) + X1 = raviartthomas(Γ1) + X2 = raviartthomas(Γ2) + + @time Z11 = assemble(t1, X1, X1) + @time W11 = assemble(t2, X1, X1) + @time Z12 = assemble(t1, X1, X2) + @time W12 = assemble(t2, X1, X2) + # @show norm(Z11-W11) + # @show norm(W11) + # @show norm(Z12-W12) + # @show norm(W12) + # @show norm(Z12) + # @show length(nonzeros(Z11)) / length(Z11) + + @test norm(Z11-W11) < 1e-5 + @test norm(Z12-W12) < 1e-19 + @test norm(Z12) == 0 +end \ No newline at end of file diff --git a/src/operators/quasilocalops.jl b/src/operators/quasilocalops.jl new file mode 100644 index 00000000..3680e4f4 --- /dev/null +++ b/src/operators/quasilocalops.jl @@ -0,0 +1,115 @@ +abstract type QuasiLocalOperator <: IntegralOperator end + +function allocatestorage(op::QuasiLocalOperator, + test_functions, trial_functions, + storage::Type{Val{:bandedstorage}}) + + T = scalartype(op, test_functions, trial_functions) + + nt = Threads.nthreads() + M = Vector{Vector{Int}}(undef, nt) + N = Vector{Vector{Int}}(undef, nt) + V = Vector{Vector{T}}(undef, nt) + for i in 1:nt + M[i] = Vector{Int}() + N[i] = Vector{Int}() + V[i] = Vector{T}() + end + # M = Int[] + # N = Int[] + # V = T[] + + # @show M + + function storeq2(v,m,n) + tid = Threads.threadid() + push!(M[tid],m) + # @show length(M) + push!(N[tid],n) + push!(V[tid],v) + end + + function freeze() + nrows = numfunctions(test_functions) + ncols = numfunctions(trial_functions) + Mall = reduce(vcat, M) + Nall = reduce(vcat, N) + Vall = reduce(vcat, V) + return sparse(Mall,Nall,Vall, nrows, ncols) + end + + return freeze, storeq2 +end + + +function assemblechunk!(op::QuasiLocalOperator, tfs::Space, bfs::Space, store321; + quadstrat=defaultquadstrat(op, tfs, bfs)) + + tr = assemblydata(tfs); tr == nothing && return + br = assemblydata(bfs); br == nothing && return + + T = scalartype(op, tfs, bfs) + tol = sqrt(eps(real(T))) + + trefs = refspace(tfs) + brefs = refspace(bfs) + + tgeo = geometry(tfs) + bgeo = geometry(bfs) + + num_trefs = numfunctions(trefs, domain(chart(tgeo, first(tgeo)))) + num_brefs = numfunctions(brefs, domain(chart(bgeo, first(bgeo)))) + + tels, tad, tact = tr + bels, bad, bact = br + # @show size(tad.data) + # @show size(bad.data) + + @assert length(tels) == size(tad.data, 3) + @assert length(bels) == size(bad.data, 3) + + qd = quaddata(op, trefs, brefs, tels, bels, quadstrat) + zlocal = zeros(T, num_trefs, num_brefs) + tree = elementstree(bels) + + δ = oprange(op) + tid = Threads.threadid() + + tid == 1 && print("dots out of 10: ") + todo, done, pctg = length(tels), 0, 0 + for (p,(tcell,tptr)) in enumerate(zip(tels, tact)) + + tc, ts = boundingbox(tcell.vertices) + pred = (c,s) -> boxesoverlap(c,s,tc,ts + δ/2) + + for box in boxes(tree, pred) + for q in box + # q is the index into bels: the active basis charts + bcell = bels[q] + bptr = bact[q] + @assert q <= length(bels) + @assert q <= size(bad.data, 3) + + fill!(zlocal, 0) + qrule = quadrule(op, trefs, brefs, p, tcell, q, bcell, qd, quadstrat) + momintegrals!(zlocal, op, + tfs, tptr, tcell, + bfs, bptr, bcell, qrule) + + for j in 1 : length(bad[q]) + for i in 1 : length(tad[p]) + zij = zlocal[i,j] + for (n,b) in bad[q][j] + zb = zij*b + for (m,a) in tad[p][i] + store321(a*zb, m, n) + end end end end end end + + done += 1 + new_pctg = round(Int, done / todo * 100) + if new_pctg > pctg + 9 + tid == 1 && print(".") + pctg = new_pctg + end end + tid == 1 && println("") +end \ No newline at end of file From c41693dba58b89e6dd1ccf8a5d238939c99b1ed2 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 17 Apr 2025 19:00:44 +0200 Subject: [PATCH 483/528] allow custom nominator in lossy single layer --- src/maxwell/qlmwops.jl | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/maxwell/qlmwops.jl b/src/maxwell/qlmwops.jl index e8767a26..f294e507 100644 --- a/src/maxwell/qlmwops.jl +++ b/src/maxwell/qlmwops.jl @@ -1,8 +1,9 @@ -struct QuasiLocalSingleLayerMW{R<:Real} <: QuasiLocalOperator +struct QuasiLocalSingleLayerMW{R<:Real,F} <: QuasiLocalOperator gamma::R alpha::R beta::R range::R + nominator::F end scalartype(op::QuasiLocalSingleLayerMW{T}) where {T} = T @@ -11,17 +12,36 @@ defaultquadstrat(op::QuasiLocalSingleLayerMW, oprange(op::QuasiLocalSingleLayerMW) = op.range -QuasiLocalSingleLayerMW(gamma) = QuasiLocalSingleLayerMW(gamma, -gamma, -1/(gamma), 12/gamma) + +struct ExponentialNominator{R} + gamma::R +end +function (f::ExponentialNominator)(x,y) + exp(-f.gamma * norm(x-y)) +end + +function QuasiLocalSingleLayerMW(gamma, range) + f = ExponentialNominator(gamma) + QuasiLocalSingleLayerMW(gamma, -gamma, -1/(gamma), range, f) +end + +function QuasiLocalSingleLayerMW(gamma, range, f) + QuasiLocalSingleLayerMW(gamma, -gamma, -1/(gamma), range, f) +end function (igd::Integrand{<:QuasiLocalSingleLayerMW})(x,y,f,g) α = igd.operator.alpha β = igd.operator.beta γ = igd.operator.gamma + F = igd.operator.nominator - r = cartesian(x) - cartesian(y) + cx = cartesian(x) + cy = cartesian(y) + r = cx - cy R = norm(r) - iR = 1 / R - green = exp(-γ*R)*(i4pi*iR) + # iR = 1 / R + # green = exp(-γ*R) / (4π*R) + green = F(cx,cy) / (4π*R) αG = α * green βG = β * green @@ -42,7 +62,7 @@ end δ = 0.025 γ = 1/δ - t1 = BEAST.QuasiLocalSingleLayerMW(γ, -γ, -δ, 12*δ) + t1 = BEAST.QuasiLocalSingleLayerMW(γ, 12*δ) t2 = BEAST.MWSingleLayer3D(γ) X1 = raviartthomas(Γ1) X2 = raviartthomas(Γ2) @@ -56,9 +76,10 @@ end # @show norm(Z12-W12) # @show norm(W12) # @show norm(Z12) - # @show length(nonzeros(Z11)) / length(Z11) + @show length(nonzeros(Z11)) / length(Z11) @test norm(Z11-W11) < 1e-5 @test norm(Z12-W12) < 1e-19 @test norm(Z12) == 0 + @test length(nonzeros(Z11)) / length(Z11) < 0.86 end \ No newline at end of file From 49d8a19386eaf1c71f4ece95c297f732183997dc Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 24 Apr 2025 14:54:24 +0200 Subject: [PATCH 484/528] Print compression rate when assembling quasilocalop --- src/maxwell/qlmwops.jl | 3 +++ src/operators/quasilocalops.jl | 4 +++- src/quadrature/sauterschwabints.jl | 8 +------- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/maxwell/qlmwops.jl b/src/maxwell/qlmwops.jl index f294e507..b0bee435 100644 --- a/src/maxwell/qlmwops.jl +++ b/src/maxwell/qlmwops.jl @@ -67,6 +67,9 @@ end X1 = raviartthomas(Γ1) X2 = raviartthomas(Γ2) + qs = BEAST.defaultquadstrat(t1, X1, X1) + @show qs + @time Z11 = assemble(t1, X1, X1) @time W11 = assemble(t2, X1, X1) @time Z12 = assemble(t1, X1, X2) diff --git a/src/operators/quasilocalops.jl b/src/operators/quasilocalops.jl index 3680e4f4..69e04cf0 100644 --- a/src/operators/quasilocalops.jl +++ b/src/operators/quasilocalops.jl @@ -35,7 +35,9 @@ function allocatestorage(op::QuasiLocalOperator, Mall = reduce(vcat, M) Nall = reduce(vcat, N) Vall = reduce(vcat, V) - return sparse(Mall,Nall,Vall, nrows, ncols) + S = sparse(Mall,Nall,Vall, nrows, ncols) + @info "Compression rate: $(length(nonzeros(S)) / (nrows*ncols))" + return S end return freeze, storeq2 diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index 8f89b16c..2729e8a0 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -109,12 +109,6 @@ function (igd::Integrand)(x,y,f,g) end -# function CompScienceMeshes.permute_vertices( -# ch::CompScienceMeshes.RefQuadrilateral, I) - -# V = vertices(ch) -# return Quadrilateral(V[I[1]], V[I[2]], V[I[3]], V[I[4]]) -# end struct PulledBackIntegrand{I,C1,C2} igd::I @@ -156,7 +150,7 @@ function momintegrals!(op::Operator, igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) igdp = pulledback_integrand(igd, I, test_chart, J, trial_chart) G = SauterSchwabQuadrature.sauterschwab_parameterized(igdp, rule) - # out[1:num_tshapes, 1:num_bshapes] .+= G + for j in 1:num_bshapes for i in 1:num_tshapes out[i,j] += G[i,j] From 57697034f376e5d69d50d143f33a3ef9d5ebc042 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 24 Apr 2025 17:15:14 +0200 Subject: [PATCH 485/528] multiples of quasi local ops use sparse storage --- src/maxwell/qlmwops.jl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/maxwell/qlmwops.jl b/src/maxwell/qlmwops.jl index b0bee435..844e9324 100644 --- a/src/maxwell/qlmwops.jl +++ b/src/maxwell/qlmwops.jl @@ -11,7 +11,8 @@ defaultquadstrat(op::QuasiLocalSingleLayerMW, tfs::RTRefSpace, bfs::RTRefSpace) = DoubleNumSauterQstrat(4,5,5,5,4,3) oprange(op::QuasiLocalSingleLayerMW) = op.range - +Base.:*(a::T, op::QuasiLocalSingleLayerMW) where {T<:Number} = + QuasiLocalSingleLayerMW(op.gamma, a*op.alpha, a*op.beta, op.range, op.nominator) struct ExponentialNominator{R} gamma::R @@ -85,4 +86,12 @@ end @test norm(Z12-W12) < 1e-19 @test norm(Z12) == 0 @test length(nonzeros(Z11)) / length(Z11) < 0.86 + + @hilbertspace j + @hilbertspace k + Q1 = assemble(t1[k,j], j∈X1, k∈X1) + Q2 = assemble(t1[k,j] + t1[k,j], j∈X1, k∈X1) + + @test Q1.A.lmap isa SparseMatrixCSC + @test Q2.maps[1].A.lmap isa SparseMatrixCSC end \ No newline at end of file From 02711239cdf4741fb2ca1a0cc0e948cac5818144 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 28 Apr 2025 10:50:47 +0200 Subject: [PATCH 486/528] quasilocalops assembly uses bloated octree to increase sparsity --- src/localop.jl | 4 ++-- src/maxwell/qlmwops.jl | 6 ++++++ src/operators/quasilocalops.jl | 12 ++++++------ test/Project.toml | 1 + 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/localop.jl b/src/localop.jl index b272973e..aa402de7 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -237,7 +237,7 @@ function assemble_local_matched!(biop::LocalOperator, tfs::subdBasis, bfs::subdB end end end end -function elementstree(elements) +function elementstree(elements, expansion_ratio=1) nverts = dimension(eltype(elements)) + 1 ncells = length(elements) @@ -264,7 +264,7 @@ function elementstree(elements) end end - return Octree(points, radii) + return Octree(points, radii, expansion_ratio) end diff --git a/src/maxwell/qlmwops.jl b/src/maxwell/qlmwops.jl index 844e9324..87e3d018 100644 --- a/src/maxwell/qlmwops.jl +++ b/src/maxwell/qlmwops.jl @@ -61,6 +61,12 @@ end Γ2 = CompScienceMeshes.translate(Γ1, point(0,0,3.0)) # Γ = meshsphere(radius=1.0, h=0.075) + import Pkg + @show pathof(BEAST.CollisionDetection) + for d in Pkg.project().dependencies + @show d + end + δ = 0.025 γ = 1/δ t1 = BEAST.QuasiLocalSingleLayerMW(γ, 12*δ) diff --git a/src/operators/quasilocalops.jl b/src/operators/quasilocalops.jl index 69e04cf0..ca7565af 100644 --- a/src/operators/quasilocalops.jl +++ b/src/operators/quasilocalops.jl @@ -64,15 +64,13 @@ function assemblechunk!(op::QuasiLocalOperator, tfs::Space, bfs::Space, store321 tels, tad, tact = tr bels, bad, bact = br - # @show size(tad.data) - # @show size(bad.data) @assert length(tels) == size(tad.data, 3) @assert length(bels) == size(bad.data, 3) qd = quaddata(op, trefs, brefs, tels, bels, quadstrat) zlocal = zeros(T, num_trefs, num_brefs) - tree = elementstree(bels) + tree = elementstree(bels, 1.1) δ = oprange(op) tid = Threads.threadid() @@ -82,12 +80,14 @@ function assemblechunk!(op::QuasiLocalOperator, tfs::Space, bfs::Space, store321 for (p,(tcell,tptr)) in enumerate(zip(tels, tact)) tc, ts = boundingbox(tcell.vertices) - pred = (c,s) -> boxesoverlap(c,s,tc,ts + δ/2) + # pred = (c,s) -> boxesoverlap(c,s,tc,ts + δ/2) - for box in boxes(tree, pred) + for box in boxes(tree, tc, ts + δ/2) for q in box - # q is the index into bels: the active basis charts bcell = bels[q] + bc, bs = boundingbox(bcell.vertices) + norm(tc-bc) - 2*(ts+bs)*sqrt(3) > δ && continue + bptr = bact[q] @assert q <= length(bels) @assert q <= size(bad.data, 3) diff --git a/test/Project.toml b/test/Project.toml index c9ab94c0..b84f134f 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,5 +1,6 @@ [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" +CollisionDetection = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" From 2795dc9d349e279981966b9b83fd6a6ac9d71cb3 Mon Sep 17 00:00:00 2001 From: "Simon B. Adrian" Date: Thu, 25 Jul 2024 18:52:15 +0200 Subject: [PATCH 487/528] Align 2D-Helmholtz code and introduce 1D-SauterSchwab First step in refactoring Helmholtz2D matching the structure of Helmholtz3D code: For now limited to providing a module based interface similar to BEAST.Helmholtz3D TODO: - Match operator types to Helmholtz3D case (i.e., SingleLayer -> HH2DSingleLayerFDBIO etc) - Provide alpha (and beta) pre-factor for operators - Use gamma instead of wavenumber WARNING: Unlike 3D case, we have different kernels for gamma = 0 and gamma = - Excitations need to be adapted - Evaluation of potentials is entirely missing Use Ma-Rokhlin-Wandzura and Sauter-Schwab for 2D Integral Operators Sauter and Schwab describe in Example 5.2.3 a technique to remove the singularity from a 2D-Kernel (as a motivation for the 3D surface integral equation technique). However, due to the logarithmic singularity, the higher-order derivatives still contain a singularity. We therefore combine there technique with the Ma-Rokhlin-Wandzura quadrature. Similar ideas have been presented in the Master thesis "2D Electromagnetic field MoM calculations using well-conditioned higher order polynomials" by Denturck under the supervision of Bogaert. --- examples/helmholtz2d.jl | 6 +- src/BEAST.jl | 11 +- src/helmholtz2d/helmholtz2d.jl | 87 +++++ src/helmholtz2d/helmholtzop.jl | 202 ------------ src/helmholtz2d/hh2dexc.jl | 138 ++++++++ src/helmholtz2d/hh2dops.jl | 136 ++++++++ src/quadrature/SauterSchwabQuadrature1D.jl | 14 + src/quadrature/doublesauterschwabint.jl | 183 +++++++++++ src/quadrature/gqlog.jl | 366 +++++++++++++++++++++ src/quadrature/sauterschwabints.jl | 44 ++- test/runtests.jl | 2 + test/test_basis.jl | 8 +- test/test_sauterschwabints1D.jl | 113 +++++++ 13 files changed, 1084 insertions(+), 226 deletions(-) create mode 100644 src/helmholtz2d/helmholtz2d.jl delete mode 100644 src/helmholtz2d/helmholtzop.jl create mode 100644 src/helmholtz2d/hh2dexc.jl create mode 100644 src/helmholtz2d/hh2dops.jl create mode 100644 src/quadrature/SauterSchwabQuadrature1D.jl create mode 100644 src/quadrature/doublesauterschwabint.jl create mode 100644 src/quadrature/gqlog.jl create mode 100644 test/test_sauterschwabints1D.jl diff --git a/examples/helmholtz2d.jl b/examples/helmholtz2d.jl index 8227e108..82987645 100644 --- a/examples/helmholtz2d.jl +++ b/examples/helmholtz2d.jl @@ -4,8 +4,10 @@ h = 2π / 51; Γ = meshcircle(1.0, h) X, Y = lagrangecxd0(Γ), lagrangec0d1(Γ) κ = 1.0 -S, Dᵀ = SingleLayer(κ), DoubleLayerTransposed(κ) -D, N = DoubleLayer(κ), HyperSingular(κ) +S = Helmholtz2D.singlelayer(; wavenumber=κ) +Dᵀ = Helmholtz2D.doublelayer_transposed(; wavenumber = κ) +D = Helmholtz2D.doublelayer(; wavenumber=κ) +N = Helmholtz2D.hypersingular(; wavenumber=κ) I = Identity() # TM scattering diff --git a/src/BEAST.jl b/src/BEAST.jl index 0aefb159..8f0bc40b 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -54,10 +54,6 @@ export TimeBasisDeltaShifted export ntrace export strace export ttrace -export SingleLayer -export DoubleLayer -export DoubleLayerTransposed -export HyperSingular export HH3DSingleLayerTDBIO export HH3DDoubleLayerTDBIO export ∂n, grad @@ -220,6 +216,7 @@ include("interpolation.jl") include("quadrature/rules/momintegrals.jl") include("quadrature/doublenumints.jl") include("quadrature/singularityextractionints.jl") +include("quadrature/SauterSchwabQuadrature1D.jl") include("quadrature/sauterschwabints.jl") include("quadrature/nonconformingoverlapqrule.jl") include("quadrature/nonconformingtouchqrule.jl") @@ -259,8 +256,6 @@ include("maxwell/maxwell.jl") include("maxwell/sourcefield.jl") # Support for the Helmholtz equation -include("helmholtz2d/helmholtzop.jl") - include("helmholtz3d/hh3dexc.jl") include("helmholtz3d/hh3dops.jl") include("helmholtz3d/nitsche.jl") @@ -270,6 +265,10 @@ include("helmholtz3d/hh3d_sauterschwabqr.jl") include("helmholtz3d/helmholtz3d.jl") include("helmholtz3d/wiltonints.jl") +include("helmholtz2d/hh2dexc.jl") +include("helmholtz2d/hh2dops.jl") +include("helmholtz2d/helmholtz2d.jl") + #suport for Volume Integral equation include("volumeintegral/vie.jl") include("volumeintegral/vieexc.jl") diff --git a/src/helmholtz2d/helmholtz2d.jl b/src/helmholtz2d/helmholtz2d.jl new file mode 100644 index 00000000..012e8a54 --- /dev/null +++ b/src/helmholtz2d/helmholtz2d.jl @@ -0,0 +1,87 @@ +module Helmholtz2D + + using ..BEAST + const Mod = BEAST + using LinearAlgebra + + function singlelayer(; + #alpha=nothing, + #gamma=nothing, + wavenumber=nothing + ) + + #alpha, gamma = Mod.operator_parameter_handler(alpha, gamma, wavenumber) + + return Mod.SingleLayer(wavenumber) + end + + function doublelayer(; + #alpha=nothing, + #gamma=nothing, + wavenumber=nothing + ) + + #alpha, gamma = Mod.operator_parameter_handler(alpha, gamma, wavenumber) + + return Mod.DoubleLayer(wavenumber) + end + + function doublelayer_transposed(; + #alpha=nothing, + #gamma=nothing, + wavenumber=nothing + ) + + #alpha, gamma = Mod.operator_parameter_handler(alpha, gamma, wavenumber) + + return Mod.DoubleLayerTransposed(wavenumber) + end + + function hypersingular(; + #alpha=nothing, + #beta=nothing, + #gamma=nothing, + wavenumber=nothing + ) + #= + gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) + + if alpha === nothing + if Mod.isstatic(gamma) #static case + alpha = 0.0 # In the long run, this should probably be rather 'nothing' + else + alpha = gamma^2 + end + + end + + if beta === nothing + if Mod.isstatic(gamma) #static case + beta = one(alpha) + else + beta = one(gamma) + end + end + =# + return Mod.HyperSingular(wavenumber) + end + + function planewave(; + direction=error("direction is a required argument"), + gamma=nothing, + wavenumber=nothing, + amplitude=one(eltype(direction))) + + gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) + + # Note: Unlike for the operators, there seems little benefit in + # explicitly declaring a Laplace-Type excitation. + + Mod.isstatic(gamma) && (gamma = zero(amplitude)) + + return Mod.HH2DPlaneWave(direction, gamma, amplitude) + end + +end + +export Helmholtz2D \ No newline at end of file diff --git a/src/helmholtz2d/helmholtzop.jl b/src/helmholtz2d/helmholtzop.jl deleted file mode 100644 index 61131029..00000000 --- a/src/helmholtz2d/helmholtzop.jl +++ /dev/null @@ -1,202 +0,0 @@ -import SpecialFunctions: hankelh2 - -abstract type HelmholtzOperator2D <: IntegralOperator end -scalartype(::HelmholtzOperator2D) = ComplexF64 - -struct SingleLayer{T} <: HelmholtzOperator2D - wavenumber::T -end - -struct HyperSingular{T} <: HelmholtzOperator2D - wavenumber::T -end - -struct DoubleLayer{T} <: HelmholtzOperator2D - wavenumber::T -end - -struct DoubleLayerTransposed{T} <: HelmholtzOperator2D - wavenumber::T -end - -function cellcellinteractions!(biop::HelmholtzOperator2D, tshs, bshs, tcell, bcell, z) - - regularcellcellinteractions!(biop, tshs, bshs, tcell, bcell, z) - -end - -function testfunc1() - print("test function!") -end - -# defaultquadstrat(op::HelmholtzOperator2D, tfs, bfs) = DoubleNumWiltonSauterQStrat(4,3,4,3,4,4,4,4) -defaultquadstrat(op::HelmholtzOperator2D, tfs, bfs) = DoubleNumQStrat(4,3) - - -function quaddata(op::HelmholtzOperator2D, g::LagrangeRefSpace, f::LagrangeRefSpace, tels, bels, - qs::DoubleNumWiltonSauterQStrat) - - tqd = quadpoints(g, tels, (qs.outer_rule_far,)) - bqd = quadpoints(f, bels, (qs.inner_rule_far,)) - - return (tpoints=tqd, bpoints=bqd) -end - -function quadrule(op::HelmholtzOperator2D, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd, - qs::DoubleNumWiltonSauterQStrat) - - DoubleQuadRule( - qd.tpoints[1,i], - qd.bpoints[1,j] - ) - -end - - -mutable struct KernelValsHelmholtz2D - wavenumber - vect - dist - green - gradgreen - txty -end - - -function kernelvals(biop::HelmholtzOperator2D, tgeo, bgeo) - - k = biop.wavenumber - r = tgeo.cart - bgeo.cart - R = norm(r) - - kr = k * R - hankels = hankelh2.([0 1], kr) - green = -im / 4 * hankels[1] - gradgreen = k * im / 4 * hankels[2] * r / R - - txty = dot(normal(tgeo), normal(bgeo)) - - KernelValsHelmholtz2D(k, r, R, green, gradgreen, txty) -end - - -shapevals(op::HelmholtzOperator2D, ϕ, ts) = shapevals(ValDiff(), ϕ, ts) - - -function integrand(biop::SingleLayer, kerneldata, tvals, - tgeo, bvals, bgeo) - - gx = tvals[1] - fy = bvals[1] - - gx * kerneldata.green * fy -end - - -function integrand(biop::HyperSingular, kernel, tvals, tgeo, - bvals, bgeo) - - gx = tvals[1] - fy = bvals[1] - - dgx = tvals[2] - dfy = bvals[2] - - k = kernel.wavenumber - G = kernel.green - txty = kernel.txty - - (dgx * dfy - k*k * txty * gx * fy) * G -end - -function integrand(biop::DoubleLayer, kernel, fp, mp, fq, mq) - nq = normal(mq) - fp[1] * dot(nq, -kernel.gradgreen) * fq[1] -end - -function integrand(biop::DoubleLayerTransposed, kernel, fp, mp, fq, mq) - np = normal(mp) - fp[1] * dot(np, kernel.gradgreen) * fq[1] -end - -struct PlaneWaveDirichlet{T,P} <: Functional{T} - wavenumber::T - direction::P -end - -scalartype(x::PlaneWaveDirichlet{T}) where {T} = complex(T) - -struct PlaneWaveNeumann{T,P} <: Functional{T} - wavenumber::T - direction::P -end - -scalartype(x::PlaneWaveNeumann{T}) where {T} = complex(T) - -struct ScalarTrace{T,F} <: Functional{T} - field::F -end - -ScalarTrace(f::F) where {F} = ScalarTrace{scalartype(f), F}(f) -ScalarTrace{T}(f::F) where {T,F} = ScalarTrace{T,F}(f) - -strace(f::F) where {F} = ScalarTrace{scalartype(f), F}(f) -strace(f, mesh::Mesh) = ScalarTrace(f) - -@testitem "scalar trace" begin - using CompScienceMeshes - fn = joinpath(dirname(pathof(BEAST)), "../test/assets/sphere45.in") - Γ = readmesh(fn) - X = lagrangecxd0(Γ) - - U = Helmholtz3D.planewave(wavenumber=1.0, direction=ẑ) - u = strace(U) - - ux = assemble(u, X) - @test u isa BEAST.ScalarTrace - @test BEAST.scalartype(u) == ComplexF64 -end - -(s::ScalarTrace)(x) = s.field(cartesian(x)) -integrand(s::ScalarTrace, tx, fx) = dot(tx.value, fx) -scalartype(s::ScalarTrace{T}) where {T} = T - -shapevals(f::Functional, ϕ, ts) = shapevals(ValOnly(), ϕ, ts) - -function (field::PlaneWaveDirichlet)(mp) - - wavenumber = field.wavenumber - direction = field.direction - - cart = cartesian(mp) - exp(-im * wavenumber * dot(direction, cart)) -end - - -function(field::PlaneWaveNeumann)(mp) - - wavenumber = field.wavenumber - direction = field.direction - - cart = cartesian(mp) - norm = normal(mp) - - wave = exp(-im * wavenumber * dot(direction, cart)) - grad = -im * wavenumber * direction * wave - - d = norm[1] * grad[1] - for i in 2:length(norm) d += norm[i] * grad[i] end - return d -end - -function integrand(pw::PlaneWaveDirichlet, sv, fx) - tx = sv[1] - return dot(tx, fx) -end - -function integrand(pw::PlaneWaveNeumann, sv, fx) - tx = sv[1] - d = tx[1] * fx[1] - for i in 2:length(tx) d += tx[i]*fx[i] end - return d -end diff --git a/src/helmholtz2d/hh2dexc.jl b/src/helmholtz2d/hh2dexc.jl new file mode 100644 index 00000000..85e75a2c --- /dev/null +++ b/src/helmholtz2d/hh2dexc.jl @@ -0,0 +1,138 @@ + +struct HH2DPlaneWave{P,K,T} <: Functional{T} + direction::P + gamma::K + amplitude::T +end + +function (f::HH2DPlaneWave)(r) + d = f.direction + γ = f.gamma + a = f.amplitude + return a * exp(-γ*dot(d,r)) +end + +function (f::HH2DPlaneWave)(r::CompScienceMeshes.MeshPointNM) + return f(cartesian(r)) +end + +scalartype(f::HH2DPlaneWave{P,K,T}) where {P,K,T} = promote_type(eltype(P), K, T) + +struct gradHH2DPlaneWave{P,K,T} <: Functional{T} + direction::P + gamma::K + amplitude::T +end + +function (f::gradHH2DPlaneWave)(r) + d = f.direction + γ = f.gamma + a = f.amplitude + + return -γ * d * exp(-γ * dot(d, r)) +end + +function (f::gradHH2DPlaneWave)(mp::CompScienceMeshes.MeshPointNM) + r = cartesian(mp) + return dot(normal(mp), f(r)) +end + +scalartype(f::gradHH2DPlaneWave{P,K,T}) where {P,K,T} = promote_type(eltype(P), K, T) + +struct ScalarTrace{T,F} <: Functional{T} + field::F +end + +ScalarTrace(f::F) where {F} = ScalarTrace{scalartype(f), F}(f) +ScalarTrace{T}(f::F) where {T,F} = ScalarTrace{T,F}(f) + +strace(f::F) where {F} = ScalarTrace{scalartype(f), F}(f) +strace(f, mesh::Mesh) = ScalarTrace(f) + +@testitem "scalar trace" begin + using CompScienceMeshes + fn = joinpath(dirname(pathof(BEAST)), "../test/assets/sphere45.in") + Γ = readmesh(fn) + X = lagrangecxd0(Γ) + + U = Helmholtz3D.planewave(wavenumber=1.0, direction=ẑ) + u = strace(U) + + ux = assemble(u, X) + @test u isa BEAST.ScalarTrace + @test BEAST.scalartype(u) == ComplexF64 +end + +(s::ScalarTrace)(x) = s.field(cartesian(x)) +integrand(s::ScalarTrace, tx, fx) = dot(tx.value, fx) +scalartype(s::ScalarTrace{T}) where {T} = T + +shapevals(f::Functional, ϕ, ts) = shapevals(ValOnly(), ϕ, ts) + +function (f::NormalDerivative{T,F})(manipoint) where {T,F<:HH2DPlaneWave} + d = f.field.direction + γ = f.field.gamma + a = f.field.amplitude + n = normal(manipoint) + r = cartesian(manipoint) + return -γ*a * dot(d,n) * exp(-γ*dot(d,r)) +end + +*(a::Number, m::HH2DPlaneWave) = HH2DPlaneWave(m.direction, m.gamma, a * m.amplitude) +*(a::Number, m::gradHH2DPlaneWave) = gradHH2DPlaneWave(m.direction, m.gamma, a * m.amplitude) + +dot(::NormalVector, m::gradHH2DPlaneWave) = NormalDerivative(HH2DPlaneWave(m.direction, m.gamma, m.amplitude)) + + +struct PlaneWaveDirichlet{T,P} <: Functional{T} + wavenumber::T + direction::P +end + +scalartype(x::PlaneWaveDirichlet{T}) where {T} = complex(T) + +struct PlaneWaveNeumann{T,P} <: Functional{T} + wavenumber::T + direction::P +end + +scalartype(x::PlaneWaveNeumann{T}) where {T} = complex(T) + + +function (field::PlaneWaveDirichlet)(mp) + + wavenumber = field.wavenumber + direction = field.direction + + cart = cartesian(mp) + exp(-im * wavenumber * dot(direction, cart)) +end + + +function(field::PlaneWaveNeumann)(mp) + + wavenumber = field.wavenumber + direction = field.direction + + cart = cartesian(mp) + norm = normal(mp) + + wave = exp(-im * wavenumber * dot(direction, cart)) + grad = -im * wavenumber * direction * wave + + d = norm[1] * grad[1] + for i in 2:length(norm) d += norm[i] * grad[i] end + return d +end + +function integrand(pw::PlaneWaveDirichlet, sv, fx) + tx = sv[1] + return dot(tx, fx) +end + +function integrand(pw::PlaneWaveNeumann, sv, fx) + tx = sv[1] + d = tx[1] * fx[1] + for i in 2:length(tx) d += tx[i]*fx[i] end + return d +end diff --git a/src/helmholtz2d/hh2dops.jl b/src/helmholtz2d/hh2dops.jl new file mode 100644 index 00000000..d8d63d04 --- /dev/null +++ b/src/helmholtz2d/hh2dops.jl @@ -0,0 +1,136 @@ +import SpecialFunctions: hankelh2 + +abstract type HelmholtzOperator2D <: IntegralOperator end +scalartype(::HelmholtzOperator2D) = ComplexF64 + +struct SingleLayer{T} <: HelmholtzOperator2D + wavenumber::T +end + +struct HyperSingular{T} <: HelmholtzOperator2D + wavenumber::T +end + +struct DoubleLayer{T} <: HelmholtzOperator2D + wavenumber::T +end + +struct DoubleLayerTransposed{T} <: HelmholtzOperator2D + wavenumber::T +end + + + +mutable struct KernelValsHelmholtz2D + wavenumber + vect + dist + green + gradgreen + txty +end + + +function kernelvals(biop::HelmholtzOperator2D, tgeo, bgeo) + + k = biop.wavenumber + r = tgeo.cart - bgeo.cart + R = norm(r) + + kr = k * R + hankels = hankelh2.([0 1], kr) + green = -im / 4 * hankels[1] + gradgreen = k * im / 4 * hankels[2] * r / R + + txty = dot(normal(tgeo), normal(bgeo)) + + KernelValsHelmholtz2D(k, r, R, green, gradgreen, txty) +end + + +shapevals(op::HelmholtzOperator2D, ϕ, ts) = shapevals(ValDiff(), ϕ, ts) + + +function integrand(biop::SingleLayer, kerneldata, tvals, + tgeo, bvals, bgeo) + + gx = tvals[1] + fy = bvals[1] + + gx * kerneldata.green * fy +end + +function integrand(biop::HyperSingular, kernel, tvals, tgeo, + bvals, bgeo) + + gx = tvals[1] + fy = bvals[1] + + dgx = tvals[2] + dfy = bvals[2] + + k = kernel.wavenumber + G = kernel.green + txty = kernel.txty + + (dgx * dfy - k*k * txty * gx * fy) * G +end + +function integrand(biop::DoubleLayer, kernel, fp, mp, fq, mq) + nq = normal(mq) + fp[1] * dot(nq, -kernel.gradgreen) * fq[1] +end + +function integrand(biop::DoubleLayerTransposed, kernel, fp, mp, fq, mq) + np = normal(mp) + fp[1] * dot(np, kernel.gradgreen) * fq[1] +end + +function cellcellinteractions!(biop::HelmholtzOperator2D, tshs, bshs, tcell, bcell, z) + + regularcellcellinteractions!(biop, tshs, bshs, tcell, bcell, z) + +end + +defaultquadstrat(op::HelmholtzOperator2D, tfs, bfs) = DoubleNumSauterQstrat(3,3,0,4,10,10) + +function quaddata(op::HelmholtzOperator2D, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_charts, trial_charts, qs::DoubleNumSauterQstrat) + + T = coordtype(test_charts[1]) + + tqd = quadpoints(test_local_space, test_charts, (qs.outer_rule,)) + bqd = quadpoints(trial_local_space, trial_charts, (qs.inner_rule,)) + + leg = ( + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_vert,0,1)), + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_edge,0,1)), + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_face,0,1)), + ) + + mrw = ( + convert.(NTuple{2,T},BEAST.SauterSchwabQuadrature1D._MRWrules(qs.sauter_schwab_common_vert,0,1)), + convert.(NTuple{2,T},BEAST.SauterSchwabQuadrature1D._MRWrules(qs.sauter_schwab_common_edge,0,1)), + convert.(NTuple{2,T},BEAST.SauterSchwabQuadrature1D._MRWrules(qs.sauter_schwab_common_face,0,1)), + ) + + return (tpoints=tqd, bpoints=bqd, gausslegendre=leg, marokhlinwandura=mrw) +end + +function quadrule(op::HelmholtzOperator2D, g::LagrangeRefSpace, f::LagrangeRefSpace, + i, τ::CompScienceMeshes.Simplex{<:Any, 1}, + j, σ::CompScienceMeshes.Simplex{<:Any, 1}, + qd, qs::DoubleNumSauterQstrat) + + hits = _numhits(τ, σ) + @assert hits <= 2 + + hits == 2 && return BEAST.SauterSchwabQuadrature1D.CommonEdge(qd.marokhlinwandura[2], qd.gausslegendre[2]) + hits == 1 && return BEAST.SauterSchwabQuadrature1D.CommonVertex(qd.marokhlinwandura[1], qd.gausslegendre[1]) + + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j], + ) +end \ No newline at end of file diff --git a/src/quadrature/SauterSchwabQuadrature1D.jl b/src/quadrature/SauterSchwabQuadrature1D.jl new file mode 100644 index 00000000..6193eae0 --- /dev/null +++ b/src/quadrature/SauterSchwabQuadrature1D.jl @@ -0,0 +1,14 @@ +module SauterSchwabQuadrature1D + +# -------- exportet parts +# types +export SauterSchwabStrategy1D +export CommonEdge, CommonVertex + +# functions +export sauterschwab_parameterized1D, _MRWrules, reorder + +# -------- included files +include("doublesauterschwabint.jl") + +end \ No newline at end of file diff --git a/src/quadrature/doublesauterschwabint.jl b/src/quadrature/doublesauterschwabint.jl new file mode 100644 index 00000000..ae3a5cc6 --- /dev/null +++ b/src/quadrature/doublesauterschwabint.jl @@ -0,0 +1,183 @@ +# -------- used packages +using FastGaussQuadrature + +using LinearAlgebra +using StaticArrays + +# -------- included files +include("gqlog.jl") + + +abstract type SauterSchwabStrategy1D end + +struct CommonEdge{A} <: SauterSchwabStrategy1D + qpso::A # MRW quadrature for the outer integral + qpsi::A # GL quadrature for the inner integral +end +struct CommonVertex{A} <: SauterSchwabStrategy1D + qpso::A # MRW quadrature for the outer integral + qpsi::A # GL quadrature for the inner integral +end + + + +""" + (::CommonEdge)(f, η, ξ) + +Regularizing coordinate transform for parametrization on the unit line: [0,1] ↦ Γ. +based on Boundary Element Methods by Sauter and Schwab, example, 5.2.3, p.308 + +Common face case. +""" +function (::CommonEdge)(f, η, ξ) + + return (1-ξ) * + ( + f( (1 - η) * (1 - ξ), (1 - η) * (1 - ξ) + ξ ) + + f( 1 - (1 - η) * (1 - ξ), 1 - (1 - η) * (1 - ξ) - ξ) + ) +end + + + +""" + (::CommonVertex)(f, η, ξ) + +Regularizing coordinate transform for parametrization on the unit line: [0,1] ↦ Γ. +based on Boundary Element Methods by Sauter and Schwab, example, 5.2.3, p.308 + +Common vertex case. +""" + + +# We apply the Duffy trick at vertex (0,0) of the two triangles +# created by splitting along the diagonal (0,0)-(1,1) +function (::CommonVertex)(f, η, ξ) + + return ξ * ( + f( ξ, η * ξ) + + f( η * ξ, ξ) + ) +end + + +function _legendre(n, a, b) + x, w = FastGaussQuadrature.gausslegendre(n) + w .*= (b - a) / 2 + x = (x .+ 1) / 2 * (b - a) .+ a + collect(zip(x, w)) +end + +# MRWRules abbreviation of Ma-Rocklin-Wandzura rule +# Ma, J., Rocklin, D., & Wandzura, S. (1996). +#"Generalized Gaussian Quadrature Rules for Systems of Arbitrary Functions." +function _MRWrules(n,a,b) + + x, w = mrwquadrature(n) + return collect(zip(x,w)) +end + +function sauterschwab_parameterized1D(integrand, strategy::SauterSchwabStrategy1D) + return sum(w1 * w2 * strategy(integrand, η, ξ) for (η, w1) in strategy.qpsi, (ξ, w2) in strategy.qpso) +end + +# In the reference domain [0, 1] x [0,1], we assume +# that the singularity is at [0, 0] and then apply the +# Duffy trick at the triangles created by splitting along +# the diagonal (0,0)-(1,1). +# ==> Therefore, the vertices must be mapped that the segments +# with vertices [vt1, vt2] and [vs1, vs2] meet at vertices +# vt2 and vs2 (since the barycentric coordinate ξ = 0 is at vt2/vs2) +function reorder(t, s, strat::CommonVertex) + + T = eltype(t[1]) + tol = 1e3 * eps(T) + # tol = 1e5 * eps(T) + # tol = sqrt(eps(T)) + + # Find the permutation P of t and s that make + # Pt = [P, A1, A2] + # Ps = [P, B1, B2] + I = zeros(Int, 1) + J = zeros(Int, 1) + e = 1 + for i in 1:2 + v = t[i] + for j in 1:2 + w = s[j] + if norm(w - v) < tol + I[e] = i + J[e] = j + e += 1 + break + end + end + e == 2 && break + end + + prepend!(I, setdiff([1, 2], I)) + prepend!(J, setdiff([1, 2], J)) + + K = zeros(Int, 2) + for i in 1:2 + for j in 1:2 + if I[j] == i + K[i] = j + break + end + end + end + + L = zeros(Int, 2) + for i in 1:2 + for j in 1:2 + if J[j] == i + L[i] = j + break + end + end + end + + return I, J, K, L +end + +function reorder(t, s, strat::CommonEdge) + + T = eltype(t[1]) + tol = 1e3 * eps(T) + # tol = 1e5 * eps(T) + # tol = sqrt(eps(T)) + + I = [1, 2] + J = zeros(Int, 2) + v = t[1] + w = s[1] + if norm(w - v) < tol + J[:] = I[:] + else # If first vertices do not coincide -> swap + J[1] = 2 + J[2] = 1 + end + + K = zeros(Int, 2) + for i in 1:2 + for j in 1:2 + if I[j] == i + K[i] = j + break + end + end + end + + L = zeros(Int, 2) + for i in 1:2 + for j in 1:2 + if J[j] == i + L[i] = j + break + end + end + end + + return I, J, K, L +end \ No newline at end of file diff --git a/src/quadrature/gqlog.jl b/src/quadrature/gqlog.jl new file mode 100644 index 00000000..5ef8d2bc --- /dev/null +++ b/src/quadrature/gqlog.jl @@ -0,0 +1,366 @@ +## precision 1e-64 +const gq3logx = [ + big"0.0288116625309518311743284463058892943833785410148091127120685877756728991180956", + big"0.3040637296121376526108623586392372317949520269925648162447492695778139996408323", + big"0.8116692253440781168637051777605030761450290604667706879097761140167527022589782" +] +const gq3logw = [ + big"0.1033307079649286467692515926638152925117601886607103120667018372055221973825252", + big"0.4546365259700987088406911214255950929711433227560482628824643195989125765801328", + big"0.4420327660649726443900572859105896145170964885832414250508338431955652260378603" +] + +const gq4logx = [ + big"0.0118025909978449182649173011095277243792040203690474965703379785154526233875205", + big"0.1428256799774836951368513691763590994938532316254834466840646624072159114529217", + big"0.4892015226545744787190313056994040673995128251953935115454141480106812059217529", + big"0.8786799740691837028076889062651203170983114607803952562798953418334277261264303" +] +const gq4logw = [ + big"0.04339102877841439110189836963212064031500034973926060944228561441031014255089125", + big"0.2404520976594606759784500615666669970425863019151864377534566626947204831710365", + big"0.421403452259775931978815024210724368880313959764404972252008918197462697072954", + big"0.2947534213023490009408365445904879937620993885811479805522488046975066771977948" +] + +const gq5logx = [ + big"0.005652228205080097135927256196733224364461736662521571634310808301956553659434744", + big"0.07343037174265227340615889388832093647469715917834067035684484541883673822337442", + big"0.2849574044625581537145276019260509101628963210662763809477575315819894593888129", + big"0.6194822640847783814068089430513733271561626825848934609549153022972420903233235", + big"0.9157580830046983337846091809279726334110698024540233984987896162377435354971433" +] +const gq5logw = [ + big"0.02104694579185462911900268264212980706242530912584843774923279348541444162086709", + big"0.1307055407444466975910762549921867773164715864654795406970237184043603827930743", + big"0.289702301671314156841590351056571717447074684999630662451019615095549595934383", + big"0.3502203701203987102855468041352946381567577080211603909627150348339178095411739", + big"0.2083248416719858061627839071738170600172707113878809681400088381807577699303166" +] + +const gq6logx = [ + big"0.003025802137546258709729970375256268401779482992828101213315602517070676774568322", + big"0.04097825415595061505346596310891297982215214416543044471948088845952583162746435", + big"0.1708632955268772947251498297861305222272021210798376492086997941491912322656478", + big"0.4132557088447932476664814551636301801016057123765087288754062575075223818171789", + big"0.7090951467906285439500459170837777217505798833565268634341442960650946180757895", + big"0.9382395903771670913550205947159654750322534581742281653799581714380847089429558" +] +const gq6logw = [ + big"0.01135133881727260944049112382835582058174453200264770791905594141446248858716565", + big"0.07524106995491652291735628910920604024498865691978884727033600167691095492167005", + big"0.1887900416154163546095079437717888698034483559720734297158003205881410216356809", + big"0.2858207218272273119866834800845419826732913507876038823492520584906108457473145", + big"0.2844864278914088000451516698444240021352894282576606882526226532705898320941591", + big"0.1543103998937584010008094933616832845612376760602254444929330245592848866881613" +] + +const gq7logx = [ + big"0.001759652118465774280562642849488452238847882376829417161020566844898763296815622", + big"0.02446965071251336742764533734970449814707465617881458711466958936398767171520647", + big"0.1067480568587889541802597810831012639984493524877324427179411407375702636036147", + big"0.2758076412959173830778595120569401032015064743411215315346947545778810161812877", + big"0.5178551421518337161586689619818074537726293041441122507100974625383718453865105", + big"0.7718154853623849002746468694943818398820314642582719680233938471526845409035046", + big"0.9528413405810905589943065885030879911652507982696548514590485932036664026265743" +] +const gq7logw = [ + big"0.006632666319025705117839049890505451545085156065257921564980554577340348816003555", + big"0.04579970797847533412557673481204533079388958290463806525668605988795024448820841", + big"0.1238402080713181945504895649219393280879395309587789720141373794746235307041714", + big"0.2121019260238119301079148754555698567157309653785676929348714013330178452209891", + big"0.2613906456720077256465806068585123980383539106322107830995996640959468257240063", + big"0.2316361802909093843188155261040439969529839487681492485634359818123016339590676", + big"0.1185986656444517261327836419573836378660169052923973165662889588188190507751178" +] + +const gq8logx = [ + big"0.001090693941921822894165111895272961656889761270364157490698764319877404204397833", + big"0.01544065354637409035654803129696733338088233302185817085956683953435628581173319", + big"0.06943486210070215589606820183704622774352513760912341739401993180500014904198664", + big"0.1874432442554370314741850139770783912914836031762547096874553060230836873430399", + big"0.3733044213430930068774224196130946427983769812240799839889190905932509629106671", + big"0.6004940136993972160075727034500910768028300567728462727056609455469481882732827", + big"0.8168773397346666264599684042762922851882693053719296062937312826985616566562561", + big"0.9628397592694479679767293938117621595514008471737935702073567287533849959502929" +] +const gq8logw = [ + big"0.004124301185198343019856732777906332094734815195995821999647282928812400963714979", + big"0.02927037967468729611607963459474011124474094870767491048230999412065760132430261", + big"0.08311406745317000356959934495432356781311685459112761469028864096298503979443943", + big"0.1537216703422877404504605696868084750732921159913022489119351065174839736994873", + big"0.2134976095222608553772321005742849287594848638665672219708479691269085320689126", + big"0.2318702724435750456389973716207802644106398233930637097216879048919245919447895", + big"0.1905362390403677390075584696403609750435838347240190689367511770476690710464044", + big"0.09386546033845297682021577615079534556040674353024940328653192440354873593922193" +] + +const gq9logx = [ + big"0.0007110732887084292371271948114411427130596929286827033747571630375829895092934655", + big"0.01019453302625490675705263439721390757082070374909770360626863228804971969139422", + big"0.0468338672211245120800478827726438577871775553713352006780256391811070482319059", + big"0.1303678313651315279092725116746256616748483487812351497491905645734524393839669", + big"0.2704472571889117534115792396210086109828026312327005462427877165292572019618002", + big"0.4583194570951279555704788423104835576655393832207886739103153471011494009470139", + big"0.6654446330703511931141011568840343222343257021433467228651963799007886473741206", + big"0.8501167298492689862135218078117680443898748048201621390257943239647520356529031", + big"0.9699770448705806690488601014922724532077813279649453870772921791520938067334167" +] +const gq9logw = [ + big"0.002694891149020972537650818274669972064785863224366740658735088194859850376821306", + big"0.01949806475263514725048677490738025162488067888132757835094210266913341165923162", + big"0.05727368794913121851622494268319581220230302710787939286951364778040103463322235", + big"0.1115510143487581908445290090392732768292549693822402575676675123047847211910685", + big"0.1671748768632405941331694750536953632851337573319718468823265216730223402765603", + big"0.2036971186947111327284495789202735996350952027805427819858864230265916190717751", + big"0.2033824531641998599297042266271605769330084459619524205450522923748533235284438", + big"0.158655279830106274450813985467734586755046487348205019646993658645688017119264", + big"0.07607261324819660960897118902661656067049156798151396149288275333032421965258767" +] +const gq10logx = [ + big"0.0004829617106896294943183149272749175233248985914823525942928633132005676508123314", + big"0.006988629214315765291321415052330852703903885292050311462565292717699814705239717", + big"0.03261139659467762873636756333311119502903377871063522373995097575113617928003805", + big"0.09282575738916595754304180899809018017197868009727626181054204984045825798259768", + big"0.1983272568954037952451448876788239049325686339457225672360989187271669627070847", + big"0.3488801429793531934308727052079965952821380621199977107169557491389991483084391", + big"0.5304405557879560773593628930481504753741482469114746733537471295142940779164014", + big"0.7167646485116550851271524596080857897065882439999679965244791572396532440846242", + big"0.8752345575062335681899918401828609991184087254011484223626515130124980427858389", + big"0.9752456986843928703116413484567293450966284004578761398160192959831174182935528" +] +const gq10logw = [ + big"0.001833400073789844974343039244187088756939297229916101905004850946833688773766269", + big"0.01345312234599178938388397441120775272856711649003550514202802923403524514255926", + big"0.04049719431695833328218443450460775397747621201836680039657708236714280143083774", + big"0.08182236965890360616068411173063619462800676003125110086271887123013027996979842", + big"0.1291923427701375391426228447040414123310685703067390418217018174652510377316056", + big"0.1695453195472587471761867424266567039339458883985308141716254981178276286005089", + big"0.1891002165329956092241637321518972638740135835067914498094639132865254218185757", + big"0.1779657539614705508518683516871686460340560486233669980423658137698553596240428", + big"0.1337247706154615197642626403147324065464076855087306464470729665894779967882703", + big"0.06286551017703246003980012882486477718951883788627154140144115699614809281187774" +] + +const gq15logx = [ + big"0.0001057845484586292753493864924668380916251024074531216298515114307885975888182837077025", + big"0.007595218903207091883247444376284730435089898982134959893543137589162227513216067560734", + big"0.001566243836167816956655385777415432235256871358416825366132255779407797797063564074336", + big"0.0228310673939862320908871357311429238465611023426736927457085695758783454249166053234", + big"0.05238863015682000512815362893330259140205201964625249898277863454897904936380636336242", + big"0.1007586852012129667391004051136416390637516216000338429059475799900456473867694200564", + big"0.1707407688499432897166276448099318000084410205203915118544771821000765281005023511881", + big"0.2625912061189931044996158999566953474829984421658717933965928855192943179193219106092", + big"0.3735365051845580413656463497279392875704241296651853711319349601068756502026863034781", + big"0.4977463584145334387874316512677272094693748259201281232961985452805697138926236954657", + big"0.6267890313923734128862401704231215953332762536281211672089523277856861188842261626273", + big"0.7505161034614075810996828048439310865633264955639212529377408870151103751864345161899", + big"0.8582553352078605177984423047477266972702530005265449789606259388659161604670935837999", + big"0.9401412912123457712018243306013409987706249783593107354789051443834065655521367132188", + big"0.98840159598634178326441972546720013231209699249441888675085570941504454528126530438" +] +const gq15logw = [ + big"0.0004032177246484613108113868855719067131609139354902442780939481796966138315560952884750", + big"0.009784212118766145151161398889593401651383575047217047223872276721858634524108733830682", + big"0.003062978434787002192079338214803518648777190553257027589956117925535847239417067776433", + big"0.02155875222558125941055846177671850985290937096257277641281484956583132046253300979456", + big"0.03832306737088916459447923899346820916960051106457997764057575699361858087669673468580", + big"0.05889819902630037803354721437087112558399556998861012518316858072147312154129159956634", + big"0.08111702993925953014418613733560273596656944057632845507111787372353568675899055124403", + big"0.1021221019720686409827399402219384580411590819905097066492730049234426900752992643841", + big"0.1187890590304012903122863318580292804774040165420944681986724151227161900089339293267", + big"0.1282103164466939209716269453809265385177800805573983908408790332666576381949672776871", + big"0.1281633274170931946155990062722928672897994926781647759252725389773093642921684420955", + big"0.1174894658884916604705425997823953678879687624841131431788200137454728360609792520454", + big"0.09632301856959041974222565383861801802356754266825087678627376540093734939016915901427", + big"0.06613453983189341688459160887643830449298566566458033351875146195781511683399415051504", + big"0.0296207140035355151835647373027317576829387852868326515024583627740990099088947327451" +] + +const gq20logx =[ + big"3.52330453033399471667816984505212163385372867701241177054557432008560084854639227897305e-05", + big"0.00052609398251740622931185201230712336983446921505852417585026623624467681675527629213", + big"0.00258751954058139354982343346427624186380724620758427946400293435944533268347785475355", + big"0.00793447194838036690898675737551375268645848500751865408409873748481145848080538488209", + big"0.01868288813744561783653938764750025119630809002162018788255377919833387462704672359943", + big"0.03709767336975036321140295948415363410628316780781877636363314115648013425635225927340", + big"0.06531248867402127000203627397232005971094419594710515781581286966321679280710510277392", + big"0.10504850471155061286810146844991137213753086149525569391876624641826242856000857964041", + big"0.15735969181900194255764110855237616685370902165441514721181535970287673096005782068619", + big"0.22243006276745466795344010160142143129919223811088898974943778369604881916260678361773", + big"0.29944376565409990140139180056840606405847048366147916785308499946161590450020290372508", + big"0.38654244694388192611269692971909327833983859776432003583652778494397228807150436302099", + big"0.48087645382678963009875046547745208340402413105861821236744947129359489183930838809025", + big"0.57874793220550687780118957035777358141664078156681562597114176771293610958660152141124", + big"0.67583547584003749409689635673268871053832441601541226337158644402514605525463511344023", + big"0.76748246087256435235881593088168817030085019655264020473919169064531201055566527849563", + big"0.84902525397032003391717514950021749147719817851968628768372316422346888625639398588588", + big"0.91613370324166446526561782425398464194563663350874014749925948312513509742697660053806", + big"0.96513542790025568797284412200174800997093368204723690145127197708961367148386513479096", + big"0.99330353645695419477749224434889710143097251086524695494463434414747362227484890290069" +] +const gq20logw =[ + big"0.0001344996764677571785755869109998153753077125501697493791309017346370516233712556344", + big"0.0010347769229506137001964267002086113327273066783579727965914510659856637214605457741", + big"0.0033772636772332014304071807493684205732945358218422159622340103282499537881211088281", + big"0.0076735561935946429061968738147092460135554371267072145698890068024955734275871246431", + big"0.0142054962855419692230156082678233228034489481480776486190695719837525785785047977667", + big"0.0229844384632086133642926604803330825551668634385287894069098453272948919261594660325", + big"0.0337363605577136353630349538167293653140706844323453207110012764976530438528535058147", + big"0.0459147630734521805763787261700834101052513301612066180095957478645178171817632110455", + big"0.0587404799428039977139522416532948172566068384302970423568064199808158670985424149117", + big"0.0712650131611019951597126355449221280821946137970396226433000650095010279558501379770", + big"0.0824518089775831713766154049650768079393967900663760427250713849268578291550998231446", + big"0.0912682015163873671456134433980263438610572775942202418862625342739119798024940831677", + big"0.0967797159091613537245599424606049227671293198402166937066405170578512391472371710119", + big"0.0982381433400897170264204215678838446666990091981590951692574443184601626076371233194", + big"0.0951553030540296616805586810238431737748294263531686413205589409221933865160968370927", + big"0.0873556504104573967653727605061775985247662198036079972745116768635241054129859568447", + big"0.0750027772122717350558059607955531490075827440767261036945605828913202042466050580026", + big"0.0585972958082336954531999669843095642303012092555129152937077146462168365372613268424", + big"0.0389472505496114365437373129779855044193052969187765645674151605884742219863536023733", + big"0.0171372052681058586123532112120668713973084363086635099074857469162865654340154497718" +] + +const gq25logx =[ + big"1.488052056467244909625638598196564951876569065220877785868029725929257175702073e-05", + big"0.0002230911595769679150052824827061963553887308057912589297421430508322235593830149", + big"0.001104643649055818355966527940649085000685595160923981048336572605217909595781463", + big"0.003419469468885920108435532061507438449748930344249701792199240567760744981134533", + big"0.008150529295033886863001737760922833583972167939553544027949623676022634980540248", + big"0.01642893749479774357536914607010297463429235120115467227534160504792104112918616", + big"0.02944598355986495611729043551183351592537620021435726249691258773257858495243842", + big"0.04835756970793361992206318289677079171377067665053611124161178808395997974547869", + big"0.07418709391973235055161345105314559978006226850379538594909956584122958662110417", + big"0.1077329558835263957555273771944780860183523493640476006201185800123101693565726", + big"0.1494866382580873514029205773718203880809421017595584426091740310673042986930094", + big"0.1995667309592877334892849598527591611240819852994166926207587305100971208280098", + big"0.2576733558313253022937358067504523798314306274156044714730893469651697969374821", + big"0.3230662664067204776334892632593698903208493483364823778280717807027167910904648", + big"0.3945685120691864972423465462055174654475617953335967500929296325180965705827934", + big"0.4705960495534080911589738896473523490798329385681206138773790559940837999359339", + big"0.5492121464331800113338053478110828306174234808821422519671406282623117939769409", + big"0.62820394224342956742721788713433694930593440087226148756627291757420870460847", + big"0.705177201069315684009216075313764796668874317071394637193611443315058812652506", + big"0.7776641844158138725454459327579467862496790765378705168736570171130565114296634", + big"0.8432387621385732265330937708707768036353565992240126265713341165914802212256237", + big"0.8996324161061799578911370633997479446760397229530673482338851736387075804002855", + big"0.9448447334057146581716256758822538016902245031938619437645914717774015185865162", + big"0.9772425752266930964071101829574726836904699310716772379925450956228825958567725", + big"0.9956472154564406578393873465081762532052103442281190457464841657872264187993479" +] +const gq25logw =[ + big"5.68460660251698016879030856022417630001430609016622367404055301532420517518146e-05", + big"0.0004399975857693375973647244464678378656571747820875928868053402781833492973839444", + big"0.001450718904759957538678349120904065177343596226567549479016314001060341544670339", + big"0.003344018738173782930390142077859263786746242661488414521712207604655464684262988", + big"0.006308099547359185301394009297771731168039890753375951467515248755245728133502821", + big"0.01044887231035341693833085280730910931287694687370918949624926858979554546942906", + big"0.01577950366313618961381321505098910773186227729332837668009224396751136533141598", + big"0.02221579084737618976616974555580489592306146268420586418029213097641676732770199", + big"0.02957770241410115563354566497367172700221328972120378713034720074800286876842385", + big"0.03759704560718378026508260437372317265973035980591719381931293743645095154963036", + big"0.04593085159498192065795356091614634903045149175052029457054511209195320135850725", + big"0.05417972366570000073562979252398890641461333672904032950920405925127192012041869", + big"0.0619100915223072560034657428054791826935387118547807898511465447238853319719781", + big"0.06867907489284756060553676062557690702482539043342506486021238240403629718273571", + big"0.0740604961651022732248948486171917260340764931698174907073336916833564924542006", + big"0.07767050451276309473099166537844979688969030762299763172471541920363489078274423", + big"0.07919128776745480042852947234337603958242405415397338299700888106674317554728525", + big"0.0783914525088150148888334078694416997243579916181124400848147761113332917972084", + big"0.07514184161385324278653409971527606906237843712486869148047723092914634697649294", + big"0.06942582121576333636781033240187019134337158605139855903585929269150790978196787", + big"0.06134339190482063881362517452637817009569584955196664117532098895052422109096163", + big"0.05110885129800288147090741101894360205572976775703651201802794222866948111927817", + big"0.03904218956400919229641780441420384985429514672241964476559246224509873932540361", + big"0.02555547136263279771626117616472806907229536248219411749824155386571674026297952", + big"0.01115035472670782388615153988884628873172468911466282782341636466564633607871119" +] +const gq30logx = [ + big"7.3237974427259711014121960070261558027540416963138812613431263278327151190959804366e-06", + big"0.0001100447004577745007716984769879706584453254088480184618404891401583368805852606", + big"0.0005469183261839665800863499163609522643981329961208508247165296601075876520669445", + big"0.0017018575191016396996609998210211063372476794957637728051826599621512487361999522", + big"0.0040838636097143723718644912310460035773312700037827241100616448033337453278004756", + big"0.0083000411768823359286287204715113149951924068604220901716433344126671711618772075", + big"0.0150229781560799918407734738092689183169334797613696585255554437053296826559583688", + big"0.0249539236157545452623839559701354241297597712533297138906598587000838054214649009", + big"0.0387833861710629296537643873484755707882131324051356611114889508499039466807898273", + big"0.0571508984811763829294398275011739764422515015438295843301931490904109469284692630", + big"0.0806057414726551613156465122418320971118831782536207674741123918423202714028978615", + big"0.1095703942345179343611158071642564640247389773976704867002267074489662838861495058", + big"0.1443083730015008037356197789175449537749814887445783639406919211150498752312441063", + big"0.1848979494275319495770725900200967399299860317548604528456354192477147513110879917", + big"0.2312130015482551721163548462811451176341279422817911572631877314792390476096369565", + big"0.3394354811908526513550841586470308321278005121866896994848949413776526679876480729", + big"0.2829119601972077387080427035003151356127360118392446697437361910056006346697600011", + big"0.4000131131094501820250769897649776296380125851532828677483580844712844326518780426", + big"0.4636788569393062849191467490065869972502641315496346884536143223006897517940708194", + big"0.5292951427410362462923319470735170564284332778195315985758918131361310822398350604", + big"0.5955843953256450027825382220533229626567086395897337474045253978208215527348048846", + big"0.6611670403969151929139247690126765162243640607544294092814316869342595350201284609", + big"0.7246045281760327657790707990935709795002999206671464914538818667060410225576321978", + big"0.7844457347349415400509678017888811719495951273900367011694108038702325222630460459", + big"0.8392749514786630154000514251848497883439805962605613432967984751367075590980896528", + big"0.8877595976173431134701092007511246347018682591436474515354898615262600610351937045", + big"0.9286957959632275789862107959484967949713679877492330402104285186684398776724010128", + big"0.9610500591874097103222899330483108727864338896073734995030477023581225096095722360", + big"0.9839957035212889808179890593218683763658708185435852305227846725586056753688294546", + big"0.9969459586797630509861337066494387438585030158185224196851893029505421506025964264" +] +const gq30logw = [ + big"2.79892154309545405739723510489380069210793611655050550747711543479704102561861919685e-05", + big"0.00021736552650254116285124642012621259181832733064311295409672178173804747259446376", + big"0.00072070358653438901040194053761631875159806889598654616182904411596462505061716433", + big"0.00167446096505498902233281391415105911504274990708724661497346440039733067657261145", + big"0.00319128240641146441610066277996670825343132418947802492373263356129383541125471007", + big"0.00535378831352933473172631190251374346030494065364313342637259357587998203452787619", + big"0.00820962136808195471589891530215803621769361461546980563318888002909952552567724036", + big"0.01176802921308481152797096394244181125451658226184469697172509738148802153652282010", + big"0.01599814350489140149763603068428629666775039563032076452547616160487744529727224182", + big"0.02082904109362429386427713970365819913806790570243416235608770390517636839882038207", + big"0.02615159766130931255371398669267371160770236587219917317217286765695263056664607358", + big"0.03182206826945638096504346131337724704287534948009793135273588003504229344353078382", + big"0.03766725599980671697189382643523378842658467493644965516029104462279299436170505501", + big"0.04349106226322339672911771677370940528027127799845281803511333713210191659662041518", + big"0.04908215322840304030099174860840581446598241617157948889097724877574668651907173348", + big"0.05869594429097691387579357598711458627497606185797316466625163785697740760494015714", + big"0.05422242866126261587110150982953097239633598880605386130322259535988549142217642663", + big"0.06229791811493648011168304384832523168380265713682130944354943903684490153886454541", + big"0.06484344570723305525372721814737560442230745015510585776187183540111537894524832593", + big"0.06617555985125437859534613656176695554584622877929469097064774571451332612064623233", + big"0.06617229527720047913131104667640576429008335550213045345021005286440129176246678245", + big"0.06475245892292092403406452472768098104967651874004644271092850354821405524611080741", + big"0.06187985834995463849925309457706242097748883881928765598589247454949234397734149989", + big"0.05756580366344204424412375601782622950190169249457352156861756311335070073919719047", + big"0.05186976919246567846117295123649254581339360407011283296332312580588896440891173441", + big"0.04489817849556722268070380739886932332497986016922684846199394709407370500832867311", + big"0.03680136250036949402188921872896857566077811836438319898471514629234808804665804404", + big"0.02776887037741359260389115885664022791979235167927439041544240586106164189739881477", + big"0.01802387378416074345440757639679616037798750110594698516423528647063618940758975519", + big"0.00782767019549675715100064364777713048008869931291672091525079129829684057243125330" +] + +const gqlogx = [gq3logx, gq4logx, gq5logx, gq6logx, gq7logx, gq8logx, gq9logx, gq10logx, gq15logx, gq20logx, gq25logx, gq30logx] +const gqlogw = [gq3logw, gq4logw, gq5logw, gq6logw, gq7logw, gq8logw, gq9logw, gq10logw, gq15logw, gq20logw, gq25logw, gq30logw] + +mutable struct GQExceptionDegree <: Exception +end + +Base.showerror(io::IO, e::GQExceptionDegree) = print( + io, + "Only quadratures of degree 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25 and 30 available." +) + +function mrwquadrature(n::Int) + rules = [15, 20, 25, 30] + if n >= 3 && n <= 10 + return Float64.(gqlogx[n-2]), Float64.(gqlogw[n-2]) + else + if n in rules + gqlogx[8+Int((n-10)/5)], gqlogw[8+Int((n-10)/5)] + else + throw(GQExceptionDegree()) + end + end +end \ No newline at end of file diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index 2729e8a0..1688ab08 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -62,6 +62,7 @@ function _krondot_gen(a::Type{U}, b::Type{V}) where {U<:SVector{N}, V<:SVector{M end return ex end + @generated function _krondot(a::SVector{N}, b::SVector{M}) where {M,N} ex = _krondot_gen(a,b) return ex @@ -77,15 +78,13 @@ function _integrands_gen(::Type{U}, ::Type{V}) where {U<:SVector{N}, V<:SVector{ end return ex end + @generated function _integrands(f, a::SVector{N}, b::SVector{M}) where {M,N} ex = _integrands_gen(a,b) # println(ex) return ex end - - - function _integrands_leg_gen(f::Type{U}, g::Type{V}) where {U<:SVector{N}, V<:SVector{M}} where {M,N} ex = :(SMatrix{N,M}(())) for m in 1:M @@ -105,11 +104,10 @@ function (igd::Integrand)(x,y,f,g) op = igd.operator kervals = kernelvals(op, x, y) - _integrands_leg(op, kervals, f, x, g, y) + return _integrands_leg(op, kervals, f, x, g, y) end - struct PulledBackIntegrand{I,C1,C2} igd::I chart1::C1 @@ -135,21 +133,44 @@ function pulledback_integrand(igd, PulledBackIntegrand(igd, ichart1, ichart2) end +function sauterschwab_parameterized(igdp, rule::SauterSchwabStrategy) + return SauterSchwabQuadrature.sauterschwab_parameterized(igdp, rule) +end + +function sauterschwab_parameterized(igdp, rule::SauterSchwabQuadrature1D.SauterSchwabStrategy1D) + return SauterSchwabQuadrature1D.sauterschwab_parameterized1D(igdp, rule) +end + +function sauterschwab_reorder(test_vertices, trial_vertices, rule::SauterSchwabStrategy) + I, J, _, _ = SauterSchwabQuadrature.reorder(test_vertices, trial_vertices, rule) + + return I, J +end + +function sauterschwab_reorder(test_vertices, trial_vertices, rule::SauterSchwabQuadrature1D.SauterSchwabStrategy1D) + I, J, _, _ = SauterSchwabQuadrature1D.reorder(test_vertices, trial_vertices, rule) + + return I, J +end + function momintegrals!(op::Operator, test_local_space, trial_local_space, test_chart, trial_chart, - out, rule::SauterSchwabStrategy) + out, rule::Union{SauterSchwabStrategy,SauterSchwabQuadrature1D.SauterSchwabStrategy1D}) - I, J, _, _ = SauterSchwabQuadrature.reorder( + I, J = sauterschwab_reorder( vertices(test_chart), - vertices(trial_chart), rule) + vertices(trial_chart), + rule + ) num_tshapes = numfunctions(test_local_space, domain(test_chart)) num_bshapes = numfunctions(trial_local_space, domain(trial_chart)) igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) igdp = pulledback_integrand(igd, I, test_chart, J, trial_chart) - G = SauterSchwabQuadrature.sauterschwab_parameterized(igdp, rule) + + G = sauterschwab_parameterized(igdp, rule) for j in 1:num_bshapes for i in 1:num_tshapes @@ -158,7 +179,4 @@ function momintegrals!(op::Operator, end nothing -end - - - +end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index c279533f..52622073 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -53,6 +53,8 @@ include("test_assemble_refinements.jl") include("test_dipole.jl") +include("test_sauterschwabints1D.jl") + include("test_wiltonints.jl") include("test_sauterschwabints.jl") include("test_hh3dints.jl") diff --git a/test/test_basis.jl b/test/test_basis.jl index 64a7d0ef..cebb14e1 100644 --- a/test/test_basis.jl +++ b/test/test_basis.jl @@ -13,9 +13,9 @@ for T in [Float32, Float64] X = lagrangec0d1(Γ) @test numvertices(Γ)-2 == numfunctions(X) - hypersingular = HyperSingular(κ) + hypersingular = Helmholtz2D.hypersingular(; wavenumber=κ) identityop = Identity() - doublelayer = DoubleLayer(κ) + doublelayer = Helmholtz2D.doublelayer(; wavenumber=κ) @show BEAST.defaultquadstrat(hypersingular, X, X) # @show @which BEAST.defaultquadstrat(hypersingular, X, X) @@ -26,7 +26,9 @@ for T in [Float32, Float64] @test size(I) == (numfunctions(X), numfunctions(X)) @test rank(I) == numfunctions(X) - @time e = assemble(PlaneWaveNeumann(κ, point(0.0, 1.0)), X) + E = Helmholtz2D.planewave(wavenumber=κ, direction=point(1.0,0.0)) + @time e = assemble(BEAST.NormalDerivative(E), X) + #e = assemble(PlaneWaveNeumann(κ, point(0.0, 1.0)), X) @test length(e) == numfunctions(X) x1 = N \ e; diff --git a/test/test_sauterschwabints1D.jl b/test/test_sauterschwabints1D.jl new file mode 100644 index 00000000..0cdb2427 --- /dev/null +++ b/test/test_sauterschwabints1D.jl @@ -0,0 +1,113 @@ +using Test +using LinearAlgebra + +using BEAST, CompScienceMeshes, StaticArrays + + +# Float32 not working since hankelh2 returns always F64 +for T in [Float64] + T = Float64 + Γto = meshsegment(T(1.0), T(0.5)) + Γt = Γto + Γs = Γt + + Xt = lagrangecxd0(Γt) + Xs = lagrangecxd0(Γs) + + λ = 10 + k = 2π/λ + + ops = [ + Helmholtz2D.singlelayer(; wavenumber=k) + ] + + refstrat = BEAST.DoubleNumSauterQstrat(3,3,0,4,30,30) + + for op in ops + Sref = assemble(op, Xt, Xs; quadstrat=refstrat) + ref = Sref[1, 1] + + n = 25 + + #Sgl = assemble(op, Xt, Xs; quadstrat=BEAST.DoubleNumQStrat(n, n+1)) + Sss = assemble(op, Xt, Xs; quadstrat=BEAST.DoubleNumSauterQstrat(3,3,0,4,n,n)) + + #gl = Sgl[1, 1] + ss = Sss[1, 1] + + #glrel = norm(gl - ref) / norm(ref) + ssrel = norm(ss - ref) / norm(ref) + + @test ssrel < 1e-14 + end + + Xt = lagrangec0d1(Γt) + Xs = lagrangec0d1(Γs) + + ops = [ + Helmholtz2D.singlelayer(; wavenumber=k) + Helmholtz2D.hypersingular(; wavenumber=k) + ] + 𝒮 = + + refstrat = BEAST.DoubleNumSauterQstrat(3,3,0,4,30,30) + + for op in ops + Sref = assemble(op, Xt, Xs; quadstrat=refstrat) + ref = Sref[1, 1] + + n = 25 + + #Sgl = assemble(op, Xt, Xs; quadstrat=BEAST.DoubleNumQStrat(n, n+1)) + Sss = assemble(op, Xt, Xs; quadstrat=BEAST.DoubleNumSauterQstrat(3,3,0,4,n,n)) + + #gl = Sgl[1, 1] + ss = Sss[1, 1] + + #glrel = norm(gl - ref) / norm(ref) + ssrel = norm(ss - ref) / norm(ref) + + @test ssrel < 1e-14 + end + + ## Test accuracy of vertex integrations + + T = Float64 + Γto = meshsegment(T(1.0), T(1.0)) + Γt = Γto + Γs = translate(Γto, SVector(1.0, 0.0)) + + Γs = Mesh([SVector(0.0, 0.0), SVector(0.0, 1.0)], [SVector(1, 2)]) + Xt = lagrangecxd0(Γt) + Xs = lagrangecxd0(Γs) + + λ = 10 + k = 2π/λ + + ops = [ + Helmholtz2D.singlelayer(; wavenumber=k) + Helmholtz2D.doublelayer(; wavenumber=k) + Helmholtz2D.doublelayer_transposed(; wavenumber=k) + ] + 𝒮 = + + refstrat = BEAST.DoubleNumSauterQstrat(3,3,0,4,30,30) + + for op in ops + Sref = assemble(op, Xt, Xs; quadstrat=refstrat) + ref = Sref[1, 1] + + n = 25 + + #Sgl = assemble(op, Xt, Xs; quadstrat=BEAST.DoubleNumQStrat(n, n+1)) + Sss = assemble(op, Xt, Xs; quadstrat=BEAST.DoubleNumSauterQstrat(3,3,0,4,n,n)) + + #gl = Sgl[1, 1] + ss = Sss[1, 1] + + #glrel = norm(gl - ref) / norm(ref) + ssrel = norm(ss - ref) / norm(ref) + + @test ssrel < 1e-14 + end +end \ No newline at end of file From ae9cbe94ee8e9fd4fac0c41596c5b8d3253257d9 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 28 Apr 2025 11:20:24 +0200 Subject: [PATCH 488/528] fix type issue in Octree construction --- src/localop.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/localop.jl b/src/localop.jl index aa402de7..d80616a0 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -264,7 +264,7 @@ function elementstree(elements, expansion_ratio=1) end end - return Octree(points, radii, expansion_ratio) + return Octree(points, radii, T(expansion_ratio)) end From 9f4bfc5fe3c7dca09efb85ceb27d7c647f8a6fda Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 28 Apr 2025 16:34:44 +0200 Subject: [PATCH 489/528] chechdocs=:none --- CHANGELOG.md | 1 + Project.toml | 4 ++-- docs/make.jl | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..8fb5c93c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- PR #157: Improved support for 2D Helmholtz problems diff --git a/Project.toml b/Project.toml index 20b42c9a..0dadd15a 100644 --- a/Project.toml +++ b/Project.toml @@ -35,9 +35,9 @@ WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] AbstractTrees = "0.4.4" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 1" -CollisionDetection = "0.1.6" +CollisionDetection = "0.1.7" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.9.3" +CompScienceMeshes = "0.9.4" Compat = "2, 3, 4" ConvolutionOperators = "0.4" ExtendableSparse = "1.4" diff --git a/docs/make.jl b/docs/make.jl index 574404e7..0b40e9c7 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -8,6 +8,7 @@ makedocs(; modules=[BEAST], authors="Kristof Cools and contributors", sitename="BEAST.jl", + checkdocs=:none, format=Documenter.HTML(; prettyurls=get(ENV, "CI", "false") == "true", canonical="https://krcools.github.io/BEAST.jl", From ef9f4d40881c61ca3b80ecb39965a4fe346bc2f7 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 28 Apr 2025 17:06:16 +0200 Subject: [PATCH 490/528] set version to 2.7.0 --- CHANGELOG.md | 4 ++++ Project.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fb5c93c..df1b56db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,5 @@ +# Changelog + +## New in 2.7.0 + - PR #157: Improved support for 2D Helmholtz problems diff --git a/Project.toml b/Project.toml index 0dadd15a..db138be8 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "2.6.0" +version = "2.7.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From 66c05eac979303224f7890104e4395e49522f06a Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 1 May 2025 12:35:14 +0200 Subject: [PATCH 491/528] single colorbar in bilinear.md --- docs/src/manual/bilinear.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/src/manual/bilinear.md b/docs/src/manual/bilinear.md index 8019b164..13cdef33 100644 --- a/docs/src/manual/bilinear.md +++ b/docs/src/manual/bilinear.md @@ -68,10 +68,18 @@ typeof(uX) The solution of this iterative process *remembers* its block structure. This makes it easy to select the electric and magnetic components: ```@example transmission +import PlotlyBase +import PlotlyDocumenter # hide using LinearAlgebra -println(norm(uX[m])) -println(norm(uX[j])) -nothing # hide + +fcrm, geom = facecurrents(uX[m], RT) +fcrj, geoj = facecurrents(uX[j], RT) + +ptm = CompScienceMeshes.patch(geom, norm.(fcrm); caxis=(0,1.2) , showscale=false) +ptj= CompScienceMeshes.patch(geoj, norm.(fcrj); caxis=(0,1.2)) + +pl = [ PlotlyBase.Plot(ptm) PlotlyBase.Plot(ptj) ] +PlotlyDocumenter.to_documenter(pl) # hide ``` ## Calderon preconditioning for the PMCHWT @@ -119,6 +127,7 @@ PSXx = BEAST.GMRESSolver(PAXx) u1, ch1 = solve(SXX, bx) u2, ch2 = solve(PSXx, PbX) +using LinearAlgebra norm(u1-u2), ch1.iters, ch2.iters ``` From 6c8dd38f84b3b25ada681be13d808b0b1805734e Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Tue, 6 May 2025 17:13:27 +0200 Subject: [PATCH 492/528] docs example custom operator --- docs/make.jl | 1 + docs/src/manual/customop.md | 58 +++++++++++++++++++++++++++++++++++++ examples/efie.jl | 4 +-- 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 docs/src/manual/customop.md diff --git a/docs/make.jl b/docs/make.jl index 0b40e9c7..f542cc1e 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -25,6 +25,7 @@ makedocs(; "Custom excitations"=>"manual/customexc.md", "Setting the Quadrature Strategy" => "manual/quadstrat.md", "Custom Quadrature Rules" => "manual/quadrule.md", + "Custom Operators" => "manual/customop.md", "Application Examples"=>Any[ "Time-Harmonic"=>Any[ "EFIE"=>"manual/examplesTH/efie.md", diff --git a/docs/src/manual/customop.md b/docs/src/manual/customop.md new file mode 100644 index 00000000..dbe6d2e6 --- /dev/null +++ b/docs/src/manual/customop.md @@ -0,0 +1,58 @@ +# Custom Operators + +BEAST.jl provides the single and double layer operators for the 3D Laplace operator, the 2D and 3D Helmholtz equations, and the 3D Maxwell equations. Nevertheless there are situations where the user may want to define their own integral operator. BEAST.jl makes this process as easy as possible. + +Defining a new integral operator can be as simple as defining a type representing the operator, a method for the evaluation of the integral operator's integrand, and a method for `BEAST.scalartype` so the framework can determine the most appropriate storage type. + +```@example customop +using LinearAlgebra +using CompScienceMeshes +using BEAST + +struct BellCurveOp{T} <: BEAST.IntegralOperator + width::T +end +``` + +By default, assembly of new operators is dealt using the `DoubleNumSauterQstrat` quadrature strategy. This strategy selects either a simplex tensorial quadrature rule when triangles are well-separated, or a bespoke Sauter-Schwab rule when triangles have vertices in common. These rules are to a large extend independent of the integrand. When defining operators exhibiting highly irregular integrands, or when using panels for which the mentioned quadrature rules are not applicable, additional implementation may be required. + +At the heart of the operator defintion lies the routine that allows evaluation of the integrand. This should be provided in the following format: + +```@example customop +function (igd::BEAST.Integrand{<:BellCurveOp})(p,q,f,g) + α = igd.operator.width + + x = CompScienceMeshes.cartesian(p) + y = CompScienceMeshes.cartesian(q) + R = LinearAlgebra.norm(x-y) + + BEAST._integrands(f,g) do fi,gj + dot(fi.value, gj.value) * exp(-(R/α)^2) + end +end +``` + +Note that `(p,q)` are neighborhoods (see CompScienceMeshes.jl). The convenience function `BEAST._integrands` populates a `StaticArrays.SMatrix` with all posible combinations of the operator integrans with trial and test functions without allocating memory. + +Finally, a method declaring the scalar type used by the operator needs to be defined so that BEAST.jl can provide the most efficient storage for any combination of real or complex values operators and finite element spaces. + +```@example customop +BEAST.scalartype(op::BellCurveOp{T}) where {T} = T +``` + +```@example customop +Γ = CompScienceMeshes.meshsphere(radius=1.0, h=0.3) +X = BEAST.raviartthomas(Γ) + +op = BellCurveOp(2.0) +Z = assemble(op, X, X) + +sv = svdvals(Z) +import PlotlyBase +t1 = PlotlyBase.scatter(y=sv) +plt = PlotlyBase.Plot([t1]) +import PlotlyDocumenter # hide +PlotlyDocumenter.to_documenter(plt) #hide +``` + +Being a Hilbert-Schmidt operator, the quickly decaying spectrum is to be expected. \ No newline at end of file diff --git a/examples/efie.jl b/examples/efie.jl index 89df72d6..aeb5223c 100644 --- a/examples/efie.jl +++ b/examples/efie.jl @@ -1,8 +1,8 @@ using CompScienceMeshes using BEAST -Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -# Γ = meshsphere(radius=1.0, h=0.1) +# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +Γ = meshsphere(radius=1.0, h=0.1) @show length(Γ) X = raviartthomas(Γ) From 7ba60851dcedab9e1557d6252c8277604ae10e00 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Wed, 7 May 2025 15:22:24 +0200 Subject: [PATCH 493/528] small fixes itsolvers and higher-order lagrange basis --- src/bases/lagrange.jl | 4 ++-- src/solvers/itsolver.jl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bases/lagrange.jl b/src/bases/lagrange.jl index c0bef513..f56a7a63 100644 --- a/src/bases/lagrange.jl +++ b/src/bases/lagrange.jl @@ -1072,12 +1072,12 @@ end function localindices(dof::_LagrangeGlobalNodesDoFs, chart::CompScienceMeshes.Simplex, localspace::LagrangeRefSpace{<:Real,2}, i) - return [i] + return [1,3,6][[i]] end function localindices(dof::_LagrangeGlobalEdgeDoFs, chart::CompScienceMeshes.Simplex, localspace::LagrangeRefSpace{<:Real,2}, i) - return [3+i] + return [5,4,2][[i]] end function localindices(dof::_LagrangeGlobalFaceDoFs, chart::CompScienceMeshes.Simplex, diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index 1334b58b..8464f487 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -39,7 +39,7 @@ function GMRESSolver(op::L; m, n = size(op) @assert m == n - maxiter == 0 && (maxiter = div(n, 5)) + maxiter == 0 && (maxiter = n) restart == 0 && (restart = n) P = typeof(Pl) @@ -92,7 +92,7 @@ function LinearAlgebra.mul!(y::AbstractVecOrMat, solver::GMRESSolver, x::Abstrac end LinearAlgebra.adjoint(A::GMRESSolver) = GMRESSolver(adjoint(A.linear_operator); maxiter=A.maxiter, restart=A.restart, abstol=A.abstol, reltol=A.reltol, verbose=A.verbose, left_preconditioner=A.left_preconditioner) -LinearAlgebra.transpose(A::GMRESSolver) = GMRESSolver(transpose(A.linear_operator), A.maxiter, A.restart, A.abstol, A.reltol, A.verbose) +LinearAlgebra.transpose(A::GMRESSolver) = GMRESSolver(transpose(A.linear_operator); maxiter=A.maxiter, restart=A.restart, abstol=A.abstol, reltol=A.reltol, verbose=A.verbose, left_preconditioner=A.left_preconditioner) From a562a458f6312ee358df26d465ba45d9d38dae8b Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Wed, 7 May 2025 15:37:01 +0200 Subject: [PATCH 494/528] low frequency double layer and planewave --- src/bases/local/gwplocal.jl | 3 +-- src/identityop.jl | 4 ++-- src/maxwell/maxwell.jl | 8 ++++++++ src/maxwell/mwexc.jl | 40 +++++++++++++++++++++++++++++++++++++ src/maxwell/mwops.jl | 4 ++-- src/postproc/farfield.jl | 2 +- src/solvers/itsolver.jl | 30 +++++++++++++++++++++------- 7 files changed, 77 insertions(+), 14 deletions(-) diff --git a/src/bases/local/gwplocal.jl b/src/bases/local/gwplocal.jl index 3ecc0073..a24e162f 100644 --- a/src/bases/local/gwplocal.jl +++ b/src/bases/local/gwplocal.jl @@ -164,7 +164,6 @@ function interpolate(fields, interpolant::GWPCurlRefSpace{T,Degree}, chart) wher k = (d+2)-i-j u_edge = s[j+1] p_edge = neighborhood(edge, (u_edge,)) - @show cartesian(p_edge) t_edge = -tangents(p_edge, 1) vals = fields_edge(u_edge) [dot(t_edge, val) for val in vals] @@ -195,7 +194,7 @@ function interpolate(fields, interpolant::GWPCurlRefSpace{T,Degree}, chart) wher end Q = hcat(Q1,Q2,Q3) - if d > 1 + if d > 0 S = ((i,j,d+2-i-j) for i in 1:d+1 for j in 1:d+1 if d+2-i-j > 0) for (i,j,k) in S p_chart = neighborhood(chart, (s[i+1],s[j+1])) diff --git a/src/identityop.jl b/src/identityop.jl index df8e0f49..883fa945 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -22,7 +22,7 @@ function integrand(op::Curl, kernel, x, g, f) scalartype(op::Curl) = Union{} -defaultquadstrat(::LocalOperator, ::GWPDivRefSpace{<:Real,D1},::LagrangeRefSpace{T,D2,D3}) where {T,D1,D2,D3} = SingleNumQStrat(7) +defaultquadstrat(::LocalOperator, ::GWPDivRefSpace{<:Real,D1},::LagrangeRefSpace{T,D2,D3}) where {T,D1,D2,D3} = SingleNumQStrat(9) function quaddata(op::LocalOperator, g::GWPDivRefSpace, f::LagrangeRefSpace{T,D2,D1} where {T,D1,D2} , tels, bels, qs::SingleNumQStrat) u, w = trgauss(qs.quad_rule) @@ -118,7 +118,7 @@ end defaultquadstrat(::LocalOperator, ::GWPDivRefSpace{<:Real,D1}, - ::GWPDivRefSpace{<:Real,D2}) where {D1,D2} = SingleNumQStrat(7) + ::GWPDivRefSpace{<:Real,D2}) where {D1,D2} = SingleNumQStrat(9) function quaddata(op::LocalOperator, g::GWPDivRefSpace, f::GWPDivRefSpace, diff --git a/src/maxwell/maxwell.jl b/src/maxwell/maxwell.jl index ea515750..79fa275c 100644 --- a/src/maxwell/maxwell.jl +++ b/src/maxwell/maxwell.jl @@ -74,6 +74,14 @@ module Maxwell3D amplitude = one(real(typeof(wavenumber)))) = Mod.PlaneWaveMW(direction, polarization, wavenumber*im, amplitude) + + planewaveExtractedKernel(; + direction = error("missing arguement `direction`"), + polarization = error("missing arguement `polarization`"), + wavenumber = error("missing arguement `wavenumber`"), + amplitude = one(real(typeof(wavenumber)))) = + Mod.PlaneWaveExtractedKernelMW(direction, polarization, wavenumber*im, amplitude) + farfield(; wavenumber = error("missing argument: `wavenumber`")) = Mod.MWFarField3D(wavenumber=wavenumber) diff --git a/src/maxwell/mwexc.jl b/src/maxwell/mwexc.jl index 6361c5e5..7e98b862 100644 --- a/src/maxwell/mwexc.jl +++ b/src/maxwell/mwexc.jl @@ -53,6 +53,45 @@ end *(a::Number, e::PlaneWaveMW) = PlaneWaveMW(e.direction, e.polarisation, e.gamma, a*e.amplitude) + +mutable struct PlaneWaveExtractedKernelMW{T,P} + direction::P + polarisation::P + gamma::T + amplitude::T +end + +function PlaneWaveExtractedKernelMW(d,p,γ,a = 1) + T = promote_type(eltype(d), eltype(p), typeof(γ), typeof(a)) + P = similar_type(typeof(d), T) + PlaneWaveExtractedKernelMW{T,P}(d,p,γ,a) +end + +scalartype(x::PlaneWaveExtractedKernelMW{T,P}) where {T,P} = promote_type(T, eltype(P)) + + +function (e::PlaneWaveExtractedKernelMW)(x) + γ = e.gamma + d = e.direction + u = e.polarisation + a = e.amplitude + a * expm1(-γ * dot(d, x)) * u +end + +function curl(field::PlaneWaveExtractedKernelMW) + γ = field.gamma + d = field.direction + u = field.polarisation + a = field.amplitude + v = d × u + b = -a * γ + PlaneWaveExtractedKernelMW(d, v, γ, b) +end + +*(a::Number, e::PlaneWaveExtractedKernelMW) = PlaneWaveExtractedKernelMW(e.direction, e.polarisation, e.gamma, a*e.amplitude) + + + abstract type Dipole end mutable struct DipoleMW{T,P} <: Dipole @@ -160,6 +199,7 @@ scalartype(t::TangTraceMW) = scalartype(t.field) cross(::NormalVector, p::Function) = CrossTraceMW(p) cross(::NormalVector, p::PlaneWaveMW) = CrossTraceMW(p) +cross(::NormalVector, p::PlaneWaveExtractedKernelMW) = CrossTraceMW(p) cross(::NormalVector, p::Dipole) = CrossTraceMW(p) cross(t::CrossTraceMW, ::NormalVector) = TangTraceMW(t.field) diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index e228e4fd..4ef2b022 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -81,7 +81,7 @@ regularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DReg(op.alpha, op.gamma) singularpart(op::MWDoubleLayer3D) = MWDoubleLayer3DSng(op.alpha, op.gamma) -struct MWDoubleLayer3DLoop{T,K} <: MaxwellOperator3DReg{T,K} +struct MWDoubleLayer3DLoop{T,K} <: MaxwellOperator3D{T,K} alpha::T gamma::K end @@ -246,7 +246,7 @@ function (igd::Integrand{<:MWDoubleLayer3DLoop})(x,y,f,g) R = norm(r) γR = γ*R iR = 1/R - gradgreen = -im*exp(-γR)*( im*γR + im*expm1(γR)) * iR^3/(4pi) * r + gradgreen = -(γ*exp(-γR)*iR+expm1(-γR)*iR^2)*iR/(4π)*r # -(expm1(γR)*(1+γR)+γR)*iR^3/(4pi) * r #-im*exp(-γR)*( im*γR + im*expm1(γR)) * iR^3/(4pi) * r fvalue = getvalue(f) gvalue = getvalue(g) diff --git a/src/postproc/farfield.jl b/src/postproc/farfield.jl index d7af91f8..4d8a7482 100644 --- a/src/postproc/farfield.jl +++ b/src/postproc/farfield.jl @@ -1,6 +1,6 @@ abstract type FarField end -defaultquadstrat(op::FarField, basis) = SingleNumQStrat(3) +defaultquadstrat(op::FarField, basis) = SingleNumQStrat(10) defaultquadstrat(op::FarField, basis::DirectProductSpace) = defaultquadstrat(op, basis.factors[1]) quaddata(op::FarField,rs,els,qs::SingleNumQStrat) = quadpoints(rs,els,(qs.quad_rule,)) diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index 8464f487..b798981e 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -2,14 +2,15 @@ import IterativeSolvers -struct GMRESSolver{T,L,R,P} <: LinearMap{T} +struct GMRESSolver{T,L,R,PL,PR} <: LinearMap{T} linear_operator::L maxiter::Int restart::Int abstol::R reltol::R verbose::Bool - left_preconditioner::P + left_preconditioner::PL + right_preconditioner::PR end @@ -18,7 +19,9 @@ Base.axes(A::GMRESSolver) = reverse(axes(A.linear_operator)) function GMRESSolver(op::L; left_preconditioner = nothing, + right_preconditioner = nothing, Pl = nothing, + Pr = nothing, maxiter=0, restart=0, abstol::R = zero(real(eltype(op))), @@ -36,15 +39,27 @@ function GMRESSolver(op::L; end @assert Pl != nothing + if right_preconditioner == nothing + Pr == nothing && (Pr = IterativeSolvers.Identity()) + else + if Pr == nothing + Pr = BEAST.Preconditioner(right_preconditioner) + else + error("Either supply Pl or left_preconditioner, not both.") + end + end + @assert Pr != nothing + m, n = size(op) @assert m == n maxiter == 0 && (maxiter = n) restart == 0 && (restart = n) - P = typeof(Pl) + PL = typeof(Pl) + PR = typeof(Pr) T = eltype(op) - GMRESSolver{T,L,R,P}(op, maxiter, restart, abstol, reltol, verbose, Pl) + GMRESSolver{T,L,R,PL,PR}(op, maxiter, restart, abstol, reltol, verbose, Pl, Pr) end @@ -68,7 +83,8 @@ function solve!(x, solver::GMRESSolver, b; abstol=solver.abstol, reltol=solver.r reltol=reltol, abstol=abstol, verbose=solver.verbose, - Pl=solver.left_preconditioner) + Pl=solver.left_preconditioner, + Pr=solver.right_preconditioner) return x, ch end @@ -91,8 +107,8 @@ function LinearAlgebra.mul!(y::AbstractVecOrMat, solver::GMRESSolver, x::Abstrac return y end -LinearAlgebra.adjoint(A::GMRESSolver) = GMRESSolver(adjoint(A.linear_operator); maxiter=A.maxiter, restart=A.restart, abstol=A.abstol, reltol=A.reltol, verbose=A.verbose, left_preconditioner=A.left_preconditioner) -LinearAlgebra.transpose(A::GMRESSolver) = GMRESSolver(transpose(A.linear_operator); maxiter=A.maxiter, restart=A.restart, abstol=A.abstol, reltol=A.reltol, verbose=A.verbose, left_preconditioner=A.left_preconditioner) +LinearAlgebra.adjoint(A::GMRESSolver) = GMRESSolver(adjoint(A.linear_operator); maxiter=A.maxiter, restart=A.restart, abstol=A.abstol, reltol=A.reltol, verbose=A.verbose, Pl=A.right_preconditioner, Pr=A.left_preconditioner) +LinearAlgebra.transpose(A::GMRESSolver) = GMRESSolver(transpose(A.linear_operator); maxiter=A.maxiter, restart=A.restart, abstol=A.abstol, reltol=A.reltol, verbose=A.verbose, Pl=A.right_preconditioner, Pr=A.left_preconditioner) From 5aa36e6f406c9030c7dd6c9445d2a570d47b7096 Mon Sep 17 00:00:00 2001 From: Cedric Muenger Date: Fri, 9 May 2025 10:55:52 +0200 Subject: [PATCH 495/528] resolve merge conflicts after fetching upstream --- .github/workflows/CI.yml | 32 +- .github/workflows/Documentation.yml | 29 + .github/workflows/TagBot.yml | 21 +- .gitignore | 5 +- CHANGELOG.md | 5 + Project.toml | 10 +- README.md | 88 +- docs/Manifest.toml | 969 ------------------ docs/Project.toml | 5 + docs/make.jl | 99 +- docs/mkdocs.yml | 18 - docs/src/apiref.md | 8 + docs/src/assets/currentREADME.png | Bin 0 -> 201421 bytes docs/src/assets/currentRealREADME.png | Bin 0 -> 158101 bytes docs/src/assets/logo-dark.svg | 474 +++++++++ docs/src/assets/logo.svg | 474 +++++++++ docs/src/assets/logo_README.svg | 486 +++++++++ docs/src/assets/logo_README_white.svg | 300 ++++++ docs/src/assets/postproc.md | 25 - docs/src/bases/brezzidouglasmarini.md | 2 + docs/src/bases/buffachristiansen.md | 20 + docs/src/bases/gragliawiltonpeterson.md | 4 + docs/src/bases/overview.md | 9 + docs/src/bases/raviartthomas.md | 28 + docs/src/contributing.md | 60 ++ docs/src/excitations/dipole.md | 15 + docs/src/excitations/linearpotential.md | 11 + docs/src/excitations/monopole.md | 11 + docs/src/excitations/planewave.md | 42 + docs/src/geometry/flat.md | 14 + docs/src/index.md | 111 +- docs/src/{ => internals}/assemble.md | 0 docs/src/internals/overview.md | 69 ++ docs/src/{ => internals}/quadstrat.md | 5 +- docs/src/manual/bilinear.md | 134 +++ docs/src/manual/customexc.md | 36 + docs/src/manual/customop.md | 58 ++ docs/src/{ => manual/examplesTD}/tdefie.md | 14 +- docs/src/manual/examplesTH/efie.md | 11 + docs/src/manual/examplesTH/mfie.md | 4 + docs/src/manual/quadrule.md | 196 ++++ docs/src/manual/quadstrat.md | 121 +++ docs/src/manual/usage.md | 114 +++ docs/src/operators/helmholtz.md | 0 docs/src/operators/identity.md | 54 + docs/src/operators/maxwelldoublelayer.md | 76 ++ docs/src/operators/maxwelldoublelayer_td.md | 4 + docs/src/operators/maxwellsinglelayer.md | 92 ++ docs/src/operators/maxwellsinglelayerVIE.md | 4 + docs/src/operators/maxwellsinglelayer_td.md | 14 + docs/src/operators/overview.md | 11 + docs/src/references.md | 5 + docs/src/refs.bib | 31 + docs/src/tutorial.md | 74 -- examples/assets/sphere45.in | 323 ++++++ examples/calderon.jl | 41 +- examples/customexcitation.jl | 41 + examples/disabled/tdhh3d_neumann.jl | 26 +- examples/discontinuous_pairing.jl | 81 ++ examples/efie.jl | 4 +- examples/efie_convergence_cuboid.jl | 99 +- examples/efie_convergence_screen.jl | 104 ++ examples/efie_convergence_sphere.jl | 133 +++ examples/efie_graded.jl | 44 + examples/helmholtz2d.jl | 6 +- src/BEAST.jl | 24 +- src/bases/bdmdiv.jl | 3 +- src/bases/global/gwpglobal.jl | 4 +- src/bases/local/bdmlocal.jl | 2 + src/bases/local/laglocal.jl | 28 +- src/bases/local/ncrossbdmlocal.jl | 2 + src/bases/local/rtlocal.jl | 82 +- src/bases/local/rtqlocal.jl | 6 +- src/bases/ncrossbdmspace.jl | 3 +- src/bases/rtqspace.jl | 4 +- src/excitation.jl | 69 +- src/helmholtz2d/helmholtz2d.jl | 87 ++ src/helmholtz2d/helmholtzop.jl | 187 ---- src/helmholtz2d/hh2dexc.jl | 138 +++ src/helmholtz2d/hh2dops.jl | 136 +++ src/helmholtz3d/hh3dexc.jl | 4 +- src/helmholtz3d/timedomain/tdhh3dexc.jl | 3 +- src/identityop.jl | 11 + src/integralop.jl | 142 +-- src/localop.jl | 44 +- src/maxwell/farfield.jl | 24 +- src/maxwell/maxwell.jl | 42 +- src/maxwell/mwexc.jl | 52 +- src/maxwell/mwops.jl | 67 -- src/maxwell/qlmwops.jl | 103 ++ src/maxwell/sourcefield.jl | 10 +- src/maxwell/timedomain/mwtdexc.jl | 4 + src/maxwell/timedomain/mwtdops.jl | 10 +- src/operator.jl | 48 +- src/operators/quasilocalops.jl | 117 +++ src/postproc.jl | 6 + src/postproc/segcurrents.jl | 12 +- src/quadrature/SauterSchwabQuadrature1D.jl | 14 + .../commonfaceoverlappingedgeqstrat.jl | 2 +- src/quadrature/doublenumqstrat.jl | 5 + src/quadrature/doublenumsauterqstrat.jl | 2 +- .../doublenumwiltonbogaertqstrat.jl | 2 +- src/quadrature/doublenumwiltonsauterqstrat.jl | 11 + src/quadrature/doublesauterschwabint.jl | 183 ++++ src/quadrature/gqlog.jl | 366 +++++++ .../nonconformingintegralopqstrat.jl | 2 +- src/quadrature/nonconformingoverlapqrule.jl | 42 +- src/quadrature/nonconformingtouchqrule.jl | 85 +- src/quadrature/quadstrats.jl | 24 +- src/quadrature/rules/momintegrals.jl | 4 +- .../rules/testinbaryrefoftrialqrule.jl | 76 ++ src/quadrature/rules/testrefinestrialqrule.jl | 15 +- .../timedomain/excitation/multiquadqrule.jl | 22 + .../timedomain/excitation/singlequad2qrule.jl | 42 + src/quadrature/sauterschwabints.jl | 55 +- .../selfsauterdnumotherwiseqstrat.jl | 2 +- .../cfcvsautercewiltonpdnumqstrat.jl | 2 +- .../nonconftestbaryrefoftrialqstrat.jl | 48 + src/quadrature/strategies/quadstrat.jl | 9 + .../strategies/testrefinestrialqstrat.jl | 2 +- .../excitation/numspacenumtimeqstrat.jl | 91 ++ .../strategies/trialrefinestestqstrat.jl | 2 +- src/solvers/solver.jl | 16 +- src/timedomain/tdexcitation.jl | 140 +-- src/timedomain/tdintegralop.jl | 5 +- src/utils/variational.jl | 5 + src/utils/zeromap.jl | 4 - src/volumeintegral/sauterschwab_ints.jl | 19 +- src/volumeintegral/vieexc.jl | 22 +- test/Manifest.toml | 671 ------------ test/Project.toml | 1 + test/runtests.jl | 16 +- test/test_assemble_refinements.jl | 6 + test/test_basis.jl | 8 +- test/test_dipole.jl | 5 + test/test_gridfunction.jl | 6 +- test/test_gwp.jl | 4 +- test/test_hh3dexc.jl | 34 +- test/test_hh3dtd_exc.jl | 60 +- test/test_hh_lsvie.jl | 18 +- test/test_higher_order_lagrange_functions.jl | 2 +- test/test_interpolate_and_restrict.jl | 2 +- test/test_local_storage.jl | 34 +- test/test_mixed_blkassm.jl | 99 +- test/test_mult.jl | 45 +- test/test_ncrossbdm.jl | 75 +- test/test_ndlcd_restrict.jl | 60 +- test/test_nonconf_quadrules.jl | 42 + test/test_overlapping_edge_remeshing.jl | 32 + test/test_quadstrat_as_fn.jl | 47 + test/test_restrict.jl | 153 ++- test/test_sauterschwabints1D.jl | 113 ++ test/test_tdmwdbl.jl | 4 +- test/test_variational.jl | 6 +- test/untested/test_mot.jl | 2 +- 155 files changed, 6695 insertions(+), 3047 deletions(-) create mode 100644 .github/workflows/Documentation.yml create mode 100644 CHANGELOG.md delete mode 100644 docs/Manifest.toml delete mode 100644 docs/mkdocs.yml create mode 100644 docs/src/apiref.md create mode 100644 docs/src/assets/currentREADME.png create mode 100644 docs/src/assets/currentRealREADME.png create mode 100644 docs/src/assets/logo-dark.svg create mode 100644 docs/src/assets/logo.svg create mode 100644 docs/src/assets/logo_README.svg create mode 100644 docs/src/assets/logo_README_white.svg delete mode 100644 docs/src/assets/postproc.md create mode 100644 docs/src/bases/brezzidouglasmarini.md create mode 100644 docs/src/bases/buffachristiansen.md create mode 100644 docs/src/bases/gragliawiltonpeterson.md create mode 100644 docs/src/bases/overview.md create mode 100644 docs/src/bases/raviartthomas.md create mode 100644 docs/src/contributing.md create mode 100644 docs/src/excitations/dipole.md create mode 100644 docs/src/excitations/linearpotential.md create mode 100644 docs/src/excitations/monopole.md create mode 100644 docs/src/excitations/planewave.md create mode 100644 docs/src/geometry/flat.md rename docs/src/{ => internals}/assemble.md (100%) create mode 100644 docs/src/internals/overview.md rename docs/src/{ => internals}/quadstrat.md (97%) create mode 100644 docs/src/manual/bilinear.md create mode 100644 docs/src/manual/customexc.md create mode 100644 docs/src/manual/customop.md rename docs/src/{ => manual/examplesTD}/tdefie.md (95%) create mode 100644 docs/src/manual/examplesTH/efie.md create mode 100644 docs/src/manual/examplesTH/mfie.md create mode 100644 docs/src/manual/quadrule.md create mode 100644 docs/src/manual/quadstrat.md create mode 100644 docs/src/manual/usage.md create mode 100644 docs/src/operators/helmholtz.md create mode 100644 docs/src/operators/identity.md create mode 100644 docs/src/operators/maxwelldoublelayer.md create mode 100644 docs/src/operators/maxwelldoublelayer_td.md create mode 100644 docs/src/operators/maxwellsinglelayer.md create mode 100644 docs/src/operators/maxwellsinglelayerVIE.md create mode 100644 docs/src/operators/maxwellsinglelayer_td.md create mode 100644 docs/src/operators/overview.md create mode 100644 docs/src/references.md create mode 100644 docs/src/refs.bib delete mode 100644 docs/src/tutorial.md create mode 100644 examples/assets/sphere45.in create mode 100644 examples/customexcitation.jl create mode 100644 examples/discontinuous_pairing.jl create mode 100644 examples/efie_convergence_screen.jl create mode 100644 examples/efie_convergence_sphere.jl create mode 100644 examples/efie_graded.jl create mode 100644 src/helmholtz2d/helmholtz2d.jl delete mode 100644 src/helmholtz2d/helmholtzop.jl create mode 100644 src/helmholtz2d/hh2dexc.jl create mode 100644 src/helmholtz2d/hh2dops.jl create mode 100644 src/maxwell/qlmwops.jl create mode 100644 src/operators/quasilocalops.jl create mode 100644 src/quadrature/SauterSchwabQuadrature1D.jl create mode 100644 src/quadrature/doublesauterschwabint.jl create mode 100644 src/quadrature/gqlog.jl create mode 100644 src/quadrature/rules/testinbaryrefoftrialqrule.jl create mode 100644 src/quadrature/rules/timedomain/excitation/multiquadqrule.jl create mode 100644 src/quadrature/rules/timedomain/excitation/singlequad2qrule.jl create mode 100644 src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl create mode 100644 src/quadrature/strategies/quadstrat.jl create mode 100644 src/quadrature/strategies/timedomain/excitation/numspacenumtimeqstrat.jl delete mode 100644 test/Manifest.toml create mode 100644 test/test_nonconf_quadrules.jl create mode 100644 test/test_overlapping_edge_remeshing.jl create mode 100644 test/test_quadstrat_as_fn.jl create mode 100644 test/test_sauterschwabints1D.jl diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 0331687a..245016c4 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: version: - - '1.6' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'. + - '1.10' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'. - '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia. os: - ubuntu-latest @@ -27,7 +27,7 @@ jobs: with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - - uses: actions/cache@v1 + - uses: actions/cache@v4 env: cache-name: cache-artifacts with: @@ -40,28 +40,8 @@ jobs: - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v1 + - uses: codecov/codecov-action@v4 with: - file: lcov.info - docs: - name: Documentation - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: julia-actions/setup-julia@v1 - with: - version: '1' - - run: | - julia --project=docs -e ' - using Pkg - Pkg.develop(PackageSpec(path=pwd())) - Pkg.instantiate()' - - run: | - julia --project=docs -e ' - using Documenter: doctest - using BEAST - doctest(BEAST)' # change MYPACKAGE to the name of your package - - run: julia --project=docs docs/make.jl - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} \ No newline at end of file + files: lcov.info + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml new file mode 100644 index 00000000..fda05bab --- /dev/null +++ b/.github/workflows/Documentation.yml @@ -0,0 +1,29 @@ +name: Documentation + +on: + push: + branches: + - master # update to match your development branch (master, main, dev, trunk, ...) + tags: '*' + pull_request: + branches: + - master + +jobs: + build: + permissions: + contents: write + statuses: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v1 + with: + version: '1' + - name: Install dependencies + run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' + - name: Build and deploy + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # If authenticating with GitHub Actions token + DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # If authenticating with SSH deploy key + run: julia --project=docs/ docs/make.jl \ No newline at end of file diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index 35600114..4905fd1d 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -4,6 +4,22 @@ on: types: - created workflow_dispatch: + inputs: + lookback: + default: "3" +permissions: + actions: read + checks: read + contents: write + deployments: read + issues: read + discussions: read + packages: read + pages: read + pull-requests: read + repository-projects: read + security-events: read + statuses: read jobs: TagBot: if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' @@ -12,5 +28,6 @@ jobs: - uses: JuliaRegistries/TagBot@v1 with: token: ${{ secrets.GITHUB_TOKEN }} - ssh: ${{ secrets.DOCUMENTER_KEY }} - + # Edit the following line to reflect the actual name of the GitHub Secret containing your private key + ssh: ${{ secrets.DOCUMENTER_SSH_KEY }} + # ssh: ${{ secrets.NAME_OF_MY_SSH_PRIVATE_KEY_SECRET }} diff --git a/.gitignore b/.gitignore index b879feb5..8a679b85 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,8 @@ docs/site/ temp/ dev/ profile/ -/Manifest.toml /envs/ +/Manifest.toml +/test/Manifest.toml +/docs/Manifest.toml +/data/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..df1b56db --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## New in 2.7.0 + +- PR #157: Improved support for 2D Helmholtz problems diff --git a/Project.toml b/Project.toml index 1b31d623..db138be8 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "BEAST" uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "2.5.0" +version = "2.7.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" @@ -35,9 +35,9 @@ WiltonInts84 = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" [compat] AbstractTrees = "0.4.4" BlockArrays = "0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 1" -CollisionDetection = "0.1.6" +CollisionDetection = "0.1.7" Combinatorics = "0.7, 1" -CompScienceMeshes = "0.8.3" +CompScienceMeshes = "0.9.4" Compat = "2, 3, 4" ConvolutionOperators = "0.4" ExtendableSparse = "1.4" @@ -55,5 +55,5 @@ SauterSchwabQuadrature = "2.4.0" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" TestItems = "0.1.1, 1" -WiltonInts84 = "0.2.5" -julia = "1.6" +WiltonInts84 = "0.2.8" +julia = "1.10" diff --git a/README.md b/README.md index 79cbc5e2..a6d8a107 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,62 @@ -# BEAST -Boundary Element Analysis and Simulation Toolkit + + + + + + +[![Docs-stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://krcools.github.io/BEAST.jl/stable/) +[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/krcools/BEAST.jl/blob/master/LICENSE) [![CI](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/krcools/BEAST.jl/actions/workflows/CI.yml) [![codecov.io](http://codecov.io/github/krcools/BEAST.jl/coverage.svg?branch=master)](http://codecov.io/github/krcools/BEAST.jl?branch=master) -[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://krcools.github.io/BEAST.jl/dev/) [![DOI](https://zenodo.org/badge/87720391.svg)](https://zenodo.org/badge/latestdoi/87720391) + ## Introduction -This package contains common basis functions and assembly routines for the implementation of -boundary element methods. Examples are included for the 2D and 3D Helmholtz equations and for -the 3D Maxwell equations. +This Julia package, the *boundary element analysis and simulation toolkit (BEAST)*, provides routines to convert integral and differential equations to linear systems of equations +via the boundary element method (BEM) and the finite element method (FEM). +To this end, the (Petrov-) **Galerkin method** is employed. -Support for the space-time Galerkin based solution of time domain integral equations is in -place for the 3D Helmholtz and Maxwell equations. +Currently, the focus is on equations encountered in **classical electromagnetism**, where frequency and time domain equations are covered. +Several operators, basis functions, and geometry representations are implemented. -## Installation -Installing `BEAST` is done by entering the package manager (enter `]` at the julia REPL) and issuing: +## Documentation -``` -pkg>add BEAST -``` +- Documentation for the [latest stable version](https://krcools.github.io/BEAST.jl/stable/). +- Documentation for the [development version](https://krcools.github.io/BEAST.jl/dev/). -To run the examples, the following steps are required in addition: - -``` -pkg> add CompScienceMeshes # For the creation of scatterer geometries -pkg> add Plots # For visualising the results -pkg> add GR # Other Plots compatible back-ends can be chosen -``` - -Examples can be run by: - -``` -julia>using BEAST -julia>d = dirname(pathof(BEAST)) -julia>include(joinpath(d,"../examples/efie.jl")) -``` ## Hello World -To solve scattering of a time harmonic electromagnetic plane wave by a perfectly conducting -sphere: +To solve scattering of a time-harmonic electromagnetic plane wave by a perfectly conducting sphere: ```julia -using CompScienceMeshes, BEAST +using CompScienceMeshes +using BEAST -Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -X = raviartthomas(Γ) +# --- basis functions +Γ = meshsphere(1.0, 2.5) # triangulate sphere of radius one +RT = raviartthomas(Γ) # define basis functions -t = Maxwell3D.singlelayer(wavenumber=1.0) -E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=1.0) -e = (n × E) × n +# --- operators & excitation +𝑇 = Maxwell3D.singlelayer(wavenumber=2.0) # integral operator +𝐸 = Maxwell3D.planewave(direction=x̂, polarization=ẑ, wavenumber=2.0) # excitation +𝑒 = (n × 𝐸) × n # tangential part -@hilbertspace j -@hilbertspace k -efie = @discretise t[k,j]==e[k] j∈X k∈X -u = gmres(efie) -``` -![](output.png) +# --- compute the RHS and system matrix +e = assemble(𝑒, RT) # assemble RHS +T = assemble(𝑇, RT, RT) # assemble system matrix -## Features +# --- solve +u = T \ -e -- General framework allowing to easily add support for more kernels, finite element spaces, and excitations. -- Assembly routines that take in symbolic representations of the defining bilinear form. Support for block systems and finite element spaces defined in terms of direct products or tensor products of atomic spaces. -- LU and iterative solution of the resulting system. -- Computation of secondary quantities of interest such as the near field and the limiting far field. -- Support for space-time Galerkin and convolution quadrature approaches to the solution of time domain boundary integral equations. -- Implementation of Lagrange zeroth and first order space, Raviart-Thomas, Brezzi-Douglas-Marini, and Buffa-Christianssen vector elemenents. +# ... post processing ... +``` +

+ +                  + +

diff --git a/docs/Manifest.toml b/docs/Manifest.toml deleted file mode 100644 index 2f4406a9..00000000 --- a/docs/Manifest.toml +++ /dev/null @@ -1,969 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -[[ANSIColoredPrinters]] -git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" -uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" -version = "0.0.1" - -[[AbstractFFTs]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" -uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" -version = "1.5.0" - - [AbstractFFTs.extensions] - AbstractFFTsChainRulesCoreExt = "ChainRulesCore" - AbstractFFTsTestExt = "Test" - - [AbstractFFTs.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[AbstractTrees]] -git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" -uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" -version = "0.4.5" - -[[ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" -version = "1.1.2" - -[[ArrayLayouts]] -deps = ["FillArrays", "LinearAlgebra"] -git-tree-sha1 = "0dd7edaff278e346eb0ca07a7e75c9438408a3ce" -uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "1.10.3" -weakdeps = ["SparseArrays"] - - [ArrayLayouts.extensions] - ArrayLayoutsSparseArraysExt = "SparseArrays" - -[[Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" -version = "1.11.0" - -[[BEAST]] -deps = ["AbstractTrees", "BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "ConvolutionOperators", "Distributed", "ExtendableSparse", "FFTW", "FastGaussQuadrature", "FillArrays", "Infiltrator", "InteractiveUtils", "IterativeSolvers", "LiftedMaps", "LinearAlgebra", "LinearMaps", "NestedUnitRanges", "Requires", "SauterSchwab3D", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SpecialFunctions", "StaticArrays", "TestItems", "WiltonInts84"] -path = ".." -uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "2.5.0" - -[[Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" -version = "1.11.0" - -[[BlockArrays]] -deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] -git-tree-sha1 = "9a9610fbe5779636f75229e423e367124034af41" -uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" -version = "0.16.43" - -[[Bzip2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9e2a6b69137e6969bab0152632dcb3bc108c8bdd" -uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+1" - -[[Cairo_jll]] -deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "009060c9a6168704143100f36ab08f06c2af4642" -uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.18.2+1" - -[[ClusterTrees]] -deps = ["DelimitedFiles", "LinearAlgebra", "Pkg"] -git-tree-sha1 = "4114d60c95974edf9272d88571d14abeed598942" -uuid = "5100927d-02aa-593a-b4f9-7235df19f0db" -version = "0.2.1" - -[[CodecZlib]] -deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "bce6804e5e6044c6daab27bb533d1295e4a2e759" -uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.6" - -[[CollisionDetection]] -deps = ["Compat", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "88a33f2fba4ef1065edd06164c84d8b03ee4d049" -uuid = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" -version = "0.1.6" - -[[Combinatorics]] -git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" -uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" -version = "1.0.2" - -[[CompScienceMeshes]] -deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Permutations", "Requires", "SparseArrays", "StaticArrays", "TestItems"] -git-tree-sha1 = "6bb7cd3831707872fe404e01d156e1b149fcfa3a" -uuid = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" -version = "0.8.3" - -[[Compat]] -deps = ["TOML", "UUIDs"] -git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.16.0" -weakdeps = ["Dates", "LinearAlgebra"] - - [Compat.extensions] - CompatLinearAlgebraExt = "LinearAlgebra" - -[[CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.1.1+0" - -[[ConvolutionOperators]] -deps = ["LinearAlgebra", "LinearMaps"] -git-tree-sha1 = "ae44e38013c05c7ec59f428b4ea7ad7d34926b63" -uuid = "15927181-a1bb-497c-b745-8dbf505c019d" -version = "0.4.1" - -[[DataStructures]] -deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "1d0a14036acb104d9e89698bd408f63ab58cdc82" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.20" - -[[Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" -version = "1.11.0" - -[[DelimitedFiles]] -deps = ["Mmap"] -git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" -version = "1.9.1" - -[[Distributed]] -deps = ["Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" -version = "1.11.0" - -[[DocStringExtensions]] -deps = ["LibGit2"] -git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" -uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.9.3" - -[[Documenter]] -deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] -git-tree-sha1 = "5a1ee886566f2fa9318df1273d8b778b9d42712d" -uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "1.7.0" - -[[Downloads]] -deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -version = "1.6.0" - -[[Expat_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7" -uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.6.2+0" - -[[ExtendableSparse]] -deps = ["DocStringExtensions", "ILUZero", "LinearAlgebra", "Printf", "SparseArrays", "Sparspak", "StaticArrays", "SuiteSparse", "Test"] -git-tree-sha1 = "3dc0ead7baa71735e03be4379d812dd8167e904a" -uuid = "95c220a8-a1cf-11e9-0c77-dbfce5f500b3" -version = "1.5.2" - - [ExtendableSparse.extensions] - ExtendableSparseAMGCLWrapExt = "AMGCLWrap" - ExtendableSparseAlgebraicMultigridExt = "AlgebraicMultigrid" - ExtendableSparseIncompleteLUExt = "IncompleteLU" - ExtendableSparsePardisoExt = "Pardiso" - - [ExtendableSparse.weakdeps] - AMGCLWrap = "4f76b812-4ba5-496d-b042-d70715554288" - AlgebraicMultigrid = "2169fc97-5a83-5252-b627-83903c6c433c" - IncompleteLU = "40713840-3770-5561-ab4c-a76e7d0d7895" - Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" - -[[FFTW]] -deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] -git-tree-sha1 = "4820348781ae578893311153d69049a93d05f39d" -uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" -version = "1.8.0" - -[[FFTW_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4d81ed14783ec49ce9f2e168208a12ce1815aa25" -uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" -version = "3.3.10+1" - -[[FLTK_jll]] -deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "72a4842f93e734f378cf381dae2ca4542f019d23" -uuid = "4fce6fc7-ba6a-5f4c-898f-77e99806d6f8" -version = "1.3.8+0" - -[[FastGaussQuadrature]] -deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "fd923962364b645f3719855c88f7074413a6ad92" -uuid = "442a2c76-b920-505d-bb47-c5924d526838" -version = "1.0.2" - -[[FileWatching]] -uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" -version = "1.11.0" - -[[FillArrays]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a" -uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.13.0" - - [FillArrays.extensions] - FillArraysPDMatsExt = "PDMats" - FillArraysSparseArraysExt = "SparseArrays" - FillArraysStatisticsExt = "Statistics" - - [FillArrays.weakdeps] - PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" - SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[Fontconfig_jll]] -deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] -git-tree-sha1 = "db16beca600632c95fc8aca29890d83788dd8b23" -uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.96+0" - -[[FreeType2_jll]] -deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc" -uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.13.2+0" - -[[GLU_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] -git-tree-sha1 = "65af046f4221e27fb79b28b6ca89dd1d12bc5ec7" -uuid = "bd17208b-e95e-5925-bf81-e2f59b3e5c61" -version = "9.0.1+0" - -[[GMP_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" -version = "6.3.0+0" - -[[Gettext_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" -uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" -version = "0.21.0+0" - -[[Git]] -deps = ["Git_jll"] -git-tree-sha1 = "04eff47b1354d702c3a85e8ab23d539bb7d5957e" -uuid = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2" -version = "1.3.1" - -[[Git_jll]] -deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "ea372033d09e4552a04fd38361cd019f9003f4f4" -uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" -version = "2.46.2+0" - -[[Glib_jll]] -deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "674ff0db93fffcd11a3573986e550d66cd4fd71f" -uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.80.5+0" - -[[GmshTools]] -deps = ["Libdl", "gmsh_jll"] -git-tree-sha1 = "299aa66053646db77f8aa7fafcebe0f9e5c0d1dc" -uuid = "82e2f556-b1bd-5f1a-9576-f93c0da5f0ee" -version = "0.5.2" - -[[GrundmannMoeller]] -deps = ["LinearAlgebra", "StaticArrays", "Test"] -git-tree-sha1 = "e264cf5f081091e4af712a911d3b620567c565e3" -uuid = "36aa67b7-9d79-4e90-bbc0-05abd90a007e" -version = "0.1.2" - -[[HDF5_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "LibCURL_jll", "Libdl", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "OpenSSL_jll", "TOML", "Zlib_jll", "libaec_jll"] -git-tree-sha1 = "82a471768b513dc39e471540fdadc84ff80ff997" -uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" -version = "1.14.3+3" - -[[Hwloc_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "dd3b49277ec2bb2c6b94eb1604d4d0616016f7a6" -uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" -version = "2.11.2+0" - -[[ILUZero]] -deps = ["LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "b007cfc7f9bee9a958992d2301e9c5b63f332a90" -uuid = "88f59080-6952-5380-9ea5-54057fb9a43f" -version = "0.2.0" - -[[IOCapture]] -deps = ["Logging", "Random"] -git-tree-sha1 = "b6d6bfdd7ce25b0f9b2f6b3dd56b2673a66c8770" -uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" -version = "0.2.5" - -[[Infiltrator]] -deps = ["InteractiveUtils", "Markdown", "REPL", "UUIDs"] -git-tree-sha1 = "38298a8eabe09e49e6f60927c9e1ca3481688ba0" -uuid = "5903a43b-9cc3-4c30-8d17-598619ec4e9b" -version = "1.8.3" - -[[IntelOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] -git-tree-sha1 = "10bd689145d2c3b2a9844005d01087cc1194e79e" -uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2024.2.1+0" - -[[InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -version = "1.11.0" - -[[IrrationalConstants]] -git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" -uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.2.2" - -[[IterativeSolvers]] -deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] -git-tree-sha1 = "59545b0a2b27208b0650df0a46b8e3019f85055b" -uuid = "42fd0dbc-a981-5370-80f2-aaf504508153" -version = "0.9.4" - -[[JLLWrappers]] -deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "be3dc50a92e5a386872a493a10050136d4703f9b" -uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.6.1" - -[[JSON]] -deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.4" - -[[JpegTurbo_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c" -uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.0.4+0" - -[[LLVMOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "78211fb6cbc872f77cad3fc0b6cf647d923f4929" -uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" -version = "18.1.7+0" - -[[LZO_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "854a9c268c43b77b0a27f22d7fab8d33cdb3a731" -uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.2+1" - -[[LazilyInitializedFields]] -git-tree-sha1 = "8f7f3cabab0fd1800699663533b6d5cb3fc0e612" -uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf" -version = "1.2.2" - -[[LazyArtifacts]] -deps = ["Artifacts", "Pkg"] -uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" -version = "1.11.0" - -[[LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" -version = "0.6.4" - -[[LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "8.6.0+0" - -[[LibGit2]] -deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" -version = "1.11.0" - -[[LibGit2_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] -uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" -version = "1.7.2+0" - -[[LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "MbedTLS_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" -version = "1.11.0+1" - -[[Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -version = "1.11.0" - -[[Libffi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" -uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.2.2+1" - -[[Libgcrypt_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] -git-tree-sha1 = "9fd170c4bbfd8b935fdc5f8b7aa33532c991a673" -uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.11+0" - -[[Libglvnd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] -git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" -uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" -version = "1.6.0+0" - -[[Libgpg_error_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "fbb1f2bef882392312feb1ede3615ddc1e9b99ed" -uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.49.0+0" - -[[Libiconv_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" -uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.17.0+0" - -[[Libmount_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "0c4f9c4f1a50d8f35048fa0532dabbadf702f81e" -uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.40.1+0" - -[[Libuuid_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807" -uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.40.1+0" - -[[LiftedMaps]] -deps = ["LinearAlgebra", "LinearMaps"] -git-tree-sha1 = "68c65fe9d32407ddb13eb26ef96fa803d45369cd" -uuid = "d22a30c1-52ac-4762-a8c9-5838452405e0" -version = "0.5.1" - -[[LinearAlgebra]] -deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -version = "1.11.0" - -[[LinearElasticity_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "71e8ee0f9fe0e86a8f8c7f28361e5118eab2f93f" -uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" -version = "5.0.0+0" - -[[LinearMaps]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "ee79c3208e55786de58f8dcccca098ced79f743f" -uuid = "7a12625a-238d-50fd-b39a-03d52299707e" -version = "3.11.3" - - [LinearMaps.extensions] - LinearMapsChainRulesCoreExt = "ChainRulesCore" - LinearMapsSparseArraysExt = "SparseArrays" - LinearMapsStatisticsExt = "Statistics" - - [LinearMaps.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[LogExpFunctions]] -deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" -uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.28" - - [LogExpFunctions.extensions] - LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" - LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" - LogExpFunctionsInverseFunctionsExt = "InverseFunctions" - - [LogExpFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" -version = "1.11.0" - -[[METIS_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "1fd0a97409e418b78c53fac671cf4622efdf0f21" -uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" -version = "5.1.2+0" - -[[MKL_jll]] -deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] -git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f" -uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2024.2.0+0" - -[[MMG_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] -git-tree-sha1 = "70a59df96945782bb0d43b56d0fbfdf1ce2e4729" -uuid = "86086c02-e288-5929-a127-40944b0018b7" -version = "5.6.0+0" - -[[MPICH_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "7715e65c47ba3941c502bffb7f266a41a7f54423" -uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" -version = "4.2.3+0" - -[[MPIPreferences]] -deps = ["Libdl", "Preferences"] -git-tree-sha1 = "c105fe467859e7f6e9a852cb15cb4301126fac07" -uuid = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267" -version = "0.1.11" - -[[MPItrampoline_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "70e830dab5d0775183c99fc75e4c24c614ed7142" -uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" -version = "5.5.1+0" - -[[Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" -version = "1.11.0" - -[[MarkdownAST]] -deps = ["AbstractTrees", "Markdown"] -git-tree-sha1 = "465a70f0fc7d443a00dcdc3267a497397b8a3899" -uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" -version = "0.1.2" - -[[MbedTLS_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.6+0" - -[[MicrosoftMPI_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f12a29c4400ba812841c6ace3f4efbb6dbb3ba01" -uuid = "9237b28f-5490-5468-be7b-bb81f5f5e6cf" -version = "10.1.4+2" - -[[Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" -version = "1.11.0" - -[[MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2023.12.12" - -[[NestedUnitRanges]] -deps = ["AbstractTrees", "ArrayLayouts", "BlockArrays"] -git-tree-sha1 = "1cbdce42da2370fee5ef906ef24179f8c070e3b9" -uuid = "032820ab-dc03-4b49-91f4-7d58d4da98b3" -version = "0.2.1" - -[[NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" -version = "1.2.0" - -[[OCCT_jll]] -deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "bef34b68c20cc34475c5cb464ab48555e74f4c61" -uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" -version = "7.7.2+0" - -[[OffsetArrays]] -git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e" -uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.14.1" - - [OffsetArrays.extensions] - OffsetArraysAdaptExt = "Adapt" - - [OffsetArrays.weakdeps] - Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" - -[[OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.27+1" - -[[OpenLibm_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" - -[[OpenMPI_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "e25c1778a98e34219a00455d6e4384e017ea9762" -uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" -version = "4.1.6+0" - -[[OpenSSL_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10" -uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.15+1" - -[[OpenSpecFun_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" -uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.5+0" - -[[OrderedCollections]] -git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.6.3" - -[[PCRE2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" -version = "10.42.0+1" - -[[Parsers]] -deps = ["Dates", "PrecompileTools", "UUIDs"] -git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" -uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.8.1" - -[[Permutations]] -deps = ["Combinatorics", "LinearAlgebra", "Random"] -git-tree-sha1 = "f92b0a7b722b1ecfd5c0d77a7eda24b4eea5c56a" -uuid = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" -version = "0.4.22" - -[[Pixman_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] -git-tree-sha1 = "35621f10a7531bc8fa58f74610b1bfb70a3cfc6b" -uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.43.4+0" - -[[Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.11.0" -weakdeps = ["REPL"] - - [Pkg.extensions] - REPLExt = "REPL" - -[[PrecompileTools]] -deps = ["Preferences"] -git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" -uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" -version = "1.2.1" - -[[Preferences]] -deps = ["TOML"] -git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6" -uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.4.3" - -[[Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" -version = "1.11.0" - -[[REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "StyledStrings", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" -version = "1.11.0" - -[[Random]] -deps = ["SHA"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -version = "1.11.0" - -[[RecipesBase]] -deps = ["PrecompileTools"] -git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" -uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "1.3.4" - -[[Reexport]] -git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.2.2" - -[[RegistryInstances]] -deps = ["LazilyInitializedFields", "Pkg", "TOML", "Tar"] -git-tree-sha1 = "ffd19052caf598b8653b99404058fce14828be51" -uuid = "2792f1a3-b283-48e8-9a74-f99dce5104f3" -version = "0.1.0" - -[[Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.0" - -[[SCOTCH_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "7110b749766853054ce8a2afaa73325d72d32129" -uuid = "a8d0f55d-b80e-548d-aff6-1a04c175f0f9" -version = "6.1.3+0" - -[[SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" -version = "0.7.0" - -[[SauterSchwab3D]] -deps = ["FastGaussQuadrature", "GrundmannMoeller", "LinearAlgebra", "ShunnHamQuadrature", "StaticArrays"] -git-tree-sha1 = "87242fb25711b1f9eaa45506d8b5e6e0b50f086a" -uuid = "0a13313b-1c00-422e-8263-562364ed9544" -version = "0.1.4" - -[[SauterSchwabQuadrature]] -deps = ["FastGaussQuadrature", "LinearAlgebra", "StaticArrays", "TestItems"] -git-tree-sha1 = "b98948c567cbe250d774d01a07833b7a329ec511" -uuid = "535c7bfe-2023-5c1d-b712-654ef9d93a38" -version = "2.4.0" - -[[Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" -version = "1.11.0" - -[[SharedArrays]] -deps = ["Distributed", "Mmap", "Random", "Serialization"] -uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" -version = "1.11.0" - -[[ShunnHamQuadrature]] -deps = ["LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "dfa53166b13cd6f352d54c99a24124321ef95282" -uuid = "164309f2-5039-4884-b6c7-6da8aa5c66ad" -version = "0.1.0" - -[[Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" -version = "1.11.0" - -[[SparseArrays]] -deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -version = "1.11.0" - -[[Sparspak]] -deps = ["Libdl", "LinearAlgebra", "Logging", "OffsetArrays", "Printf", "SparseArrays", "Test"] -git-tree-sha1 = "342cf4b449c299d8d1ceaf00b7a49f4fbc7940e7" -uuid = "e56a9233-b9d6-4f03-8d0f-1825330902ac" -version = "0.3.9" - -[[SpecialFunctions]] -deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" -uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.4.0" - - [SpecialFunctions.extensions] - SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" - - [SpecialFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - -[[StaticArrays]] -deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "eeafab08ae20c62c44c8399ccb9354a04b80db50" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.7" - - [StaticArrays.extensions] - StaticArraysChainRulesCoreExt = "ChainRulesCore" - StaticArraysStatisticsExt = "Statistics" - - [StaticArrays.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[StaticArraysCore]] -git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682" -uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.3" - -[[StyledStrings]] -uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" -version = "1.11.0" - -[[SuiteSparse]] -deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] -uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" - -[[SuiteSparse_jll]] -deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] -uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.7.0+0" - -[[TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.3" - -[[Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" -version = "1.10.0" - -[[Test]] -deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -version = "1.11.0" - -[[TestItems]] -git-tree-sha1 = "8621ba2637b49748e2dc43ba3d84340be2938022" -uuid = "1c621080-faea-4a02-84b6-bbd5e436b8fe" -version = "0.1.1" - -[[TranscodingStreams]] -git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" -uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.11.3" - -[[UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" -version = "1.11.0" - -[[Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" -version = "1.11.0" - -[[WiltonInts84]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "9d61cac63c100e936194e5db8613e33572fd2bb8" -uuid = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" -version = "0.2.5" - -[[XML2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "1165b0443d0eca63ac1e32b8c0eb69ed2f4f8127" -uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.13.3+0" - -[[XSLT_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"] -git-tree-sha1 = "a54ee957f4c86b526460a720dbc882fa5edcbefc" -uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.41+0" - -[[Xorg_libX11_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] -git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" -uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.8.6+0" - -[[Xorg_libXau_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" -uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.11+0" - -[[Xorg_libXdmcp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" -uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.4+0" - -[[Xorg_libXext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "d2d1a5c49fae4ba39983f63de6afcbea47194e85" -uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.6+0" - -[[Xorg_libXfixes_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" -uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" -version = "5.0.3+4" - -[[Xorg_libXft_jll]] -deps = ["Fontconfig_jll", "Libdl", "Pkg", "Xorg_libXrender_jll"] -git-tree-sha1 = "754b542cdc1057e0a2f1888ec5414ee17a4ca2a1" -uuid = "2c808117-e144-5220-80d1-69d4eaa9352c" -version = "2.3.3+1" - -[[Xorg_libXinerama_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] -git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" -uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" -version = "1.1.4+4" - -[[Xorg_libXrender_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "47e45cd78224c53109495b3e324df0c37bb61fbe" -uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.11+0" - -[[Xorg_libpthread_stubs_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9" -uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" -version = "0.1.1+0" - -[[Xorg_libxcb_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "bcd466676fef0878338c61e655629fa7bbc69d8e" -uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.17.0+0" - -[[Xorg_xtrans_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77" -uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.5.0+0" - -[[Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.2.13+1" - -[[gmsh_jll]] -deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "1e7fe5c8dbe0e911931a18cdfbd2c7a1e01b68ef" -uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" -version = "4.13.1+0" - -[[libaec_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "46bf7be2917b59b761247be3f317ddf75e50e997" -uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" -version = "1.1.2+0" - -[[libblastrampoline_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.11.0+0" - -[[libpng_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "b70c870239dc3d7bc094eb2d6be9b73d27bef280" -uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.44+0" - -[[nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.59.0+0" - -[[oneTBB_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "7d0ea0f4895ef2f5cb83645fa689e52cb55cf493" -uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" -version = "2021.12.0+0" - -[[p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.4.0+2" diff --git a/docs/Project.toml b/docs/Project.toml index ad968743..c325337c 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -2,3 +2,8 @@ BEAST = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244" +Krylov = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" +PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5" +PlotlyDocumenter = "9b90f1cd-2639-4507-8b17-2fbe371ceceb" +TypeTree = "04da0e3b-1cad-4b2c-a963-fc1602baf1af" diff --git a/docs/make.jl b/docs/make.jl index 640dbb5c..f542cc1e 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,11 +1,94 @@ -using Documenter, BEAST +using Documenter +using DocumenterCitations +using BEAST -makedocs( - # clean=false, - sitename="BEAST Documentation") +bib = CitationBibliography(joinpath(@__DIR__, "src", "refs.bib"); style=:alpha) -deploydocs( - # deps = Deps.pip("mkdocs", "python-markdown-math"), - repo = "github.com/krcools/BEAST.jl.git", - # julia = "1.0", +makedocs(; + modules=[BEAST], + authors="Kristof Cools and contributors", + sitename="BEAST.jl", + checkdocs=:none, + format=Documenter.HTML(; + prettyurls=get(ENV, "CI", "false") == "true", + canonical="https://krcools.github.io/BEAST.jl", + edit_link="master", + assets=String[], + collapselevel=1, + sidebar_sitename=true, + ), + plugins=[bib], + pages=[ + "Introduction" => "index.md", + "Manual" => Any[ + "General Usage"=>"manual/usage.md", + "Custom excitations"=>"manual/customexc.md", + "Setting the Quadrature Strategy" => "manual/quadstrat.md", + "Custom Quadrature Rules" => "manual/quadrule.md", + "Custom Operators" => "manual/customop.md", + "Application Examples"=>Any[ + "Time-Harmonic"=>Any[ + "EFIE"=>"manual/examplesTH/efie.md", + "MFIE"=>"manual/examplesTH/mfie.md", + ], + "Time-Domain"=>Any["EFIE"=>"manual/examplesTD/tdefie.md"], + ], + "System of Equations and Bilinear Forms" => "manual/bilinear.md", + ], + "Operators & Excitations" => Any[ + "Overview"=>"operators/overview.md", + "Local Operators"=>Any["Identiy"=>"operators/identity.md",], + "Boundary Integral Operators"=>Any[ + "Helmholtz"=>"operators/helmholtz.md", + "Maxwell Single Layer"=>Any[ + "Time Harmonic"=>"operators/maxwellsinglelayer.md", + "Time Domain"=>"operators/maxwellsinglelayer_td.md", + ], + "Maxwell Double Layer"=>Any[ + "Time Harmonic"=>"operators/maxwelldoublelayer.md", + "Time Domain"=>"operators/maxwelldoublelayer_td.md", + ], + ], + "Volume Integral Operators"=>Any[ + "Maxwell Single Layer"=>"operators/maxwellsinglelayerVIE.md", + "Maxwell Double Layer"=>Any[], + ], + "Excitations"=>Any[ + "Plane Wave"=>"excitations/planewave.md", + "Dipole"=>"excitations/dipole.md", + "Monopole"=>"excitations/monopole.md", + "Linear Potential"=>"excitations/linearpotential.md", + ], + ], + "Basis Functions" => Any[ + "Overview"=>"bases/overview.md", + "Spatial"=>Any[ + "Raviart Thomas"=>"bases/raviartthomas.md", + "Buffa Christiansen"=>"bases/buffachristiansen.md", + "Brezzi-Douglas-Marini"=>"bases/brezzidouglasmarini.md", + "Graglia-Wilton-Peterson"=>"bases/gragliawiltonpeterson.md", + ], + "Temporal"=>Any[], + ], + "Geometry Representations" => Any["Flat"=>"geometry/flat.md", "Curvilinear"=>Any[]], + "____________________________________" => Any[], + "Internals" => Any[ + "Overview"=>"internals/overview.md", + "The Matrix Assemble Routine"=>"internals/assemble.md", + "Quadrature"=>"internals/quadstrat.md", + "Parametric Domain"=>Any[], + "Multithreading"=>Any[], + ], + "Contributing" => "contributing.md", + "References" => "references.md", + "API Reference" => "apiref.md", + ], +) + +deploydocs(; + repo="github.com/krcools/BEAST.jl.git", + target="build", + # push_preview=true, + forcepush=false, + #devbranch = "feature/docs", ) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml deleted file mode 100644 index 9fae846d..00000000 --- a/docs/mkdocs.yml +++ /dev/null @@ -1,18 +0,0 @@ -site_name: BEAST.jl -site_author: krcools -site_description: Documentation for BEAST.jl -repo_url: https://github.com/krcools/BEAST.jl - -markdown_extensions: - - mdx_math - -extra_javascript: - - https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML - - assets/mathjaxhelper.js - -docs_dir: build -pages: - - Home: index.md - - Tutorial: tutorial.md - - Assembly: assemble.md - - Quadrature Strategies: quadstrat.md diff --git a/docs/src/apiref.md b/docs/src/apiref.md new file mode 100644 index 00000000..9b37de9f --- /dev/null +++ b/docs/src/apiref.md @@ -0,0 +1,8 @@ + +# API Reference + + + +```@autodocs +Modules = [BEAST,VIE,Maxwell3D,TDMaxwell3D,BEAST.Variational] +``` \ No newline at end of file diff --git a/docs/src/assets/currentREADME.png b/docs/src/assets/currentREADME.png new file mode 100644 index 0000000000000000000000000000000000000000..659f50319743c34a11e893658c5c138b68c75185 GIT binary patch literal 201421 zcmeEtbyQqU(LU4!RK?Z`mTkzoS8VCdpwnLuhwX=K9 zp6{I9|K6Fob9=h0>vsRTs=BJXCsJ8a1|5YM1quoZT~1c=0~8cYJmiW%LV)C8Cu8+O zZj+{3a%OUJ^iW8U9}r4tI4EdHDjo)s{5u^1Ny9+HLcv1v!ywlyNcx55rv@{OphqCxjY0?_Vu?;UwWHiVfisVkse^EGHoW zaB#9Wv$QdVg7V1nO%#&t6(#LAR4ppN!xo6(Hz`$gJ4YSV38hk_s~4}pph@);OA344 z->>4p#Cr?Ya&>T=o9$M|f)f$VBFnkt zO_0W{w!(M=PY5ZGqBkuit!&~=60%2JwfF6<7s00X+Wr%1`@3UZyNh-11_qjk#kg4I@Jce`FKO9Ka7;L^+uII#s^v z6(JwKav;j^dGK@hncdjKPDmzh40JY2!y_9CoLivuSVL#LZL1^9ZEhe#y^Z&A@!U{rm9|wY9KFb5T6N^ zh%kzv2S0>?t*HwT;9+ZH=gjXRMD>R+KP3H<%|ZqEL*imBM5P6hBMEyaQvfG3Co?OP zl!v7oJC!gBK+ws=jQ@kA^xrHXzl5kPTwEOZSyK7J&@sKoi202+exLDfT0bY0ljqP1sgs7+><$!;*&(=Xf;h*g7 zoc~4vf(MHS(1C@GnU%%XmgV1TIJ-!>L74pAp#NhHXEjd;QKWn@|U~Xya@TV38?Ej#2u{8S^ zSpU(smy$o-`S*rEs{fPke^CFU?|+CPR0<0GlJ+3im*&Yy3Q@g0pWnnDWNE_xC&|WX z%w=i{#x6nsFO*^Oyj6IR1^5oSm}^&<b}Se@b44g&)!x2(Z8xIfXFzlMm?&zl4)1(8bntA@;s9%%^ zAPAv{47seOGvxW6f3p74C{pcynNhDX6$U--0U32+{T=y z|H<9i-ps`v=wvEp4#5$E6+}G$%uoQjKLhplKdId|S>ahk82zlTq(0!5iK0P5Du`TR(q& z=wp6%T4`Dx{93p6_4^c`26raJMXE}p4^&*Q%?YJ2dRgTag~*DX3tJOaHlI8Kq@nep z$^(jD<(?gfYPuGZ%v$6Hcx~UjPDuRUU)pDM7nG0!&w7)5Ef44JTJj-X8op5~>vR+O zq_36<^m|&|QLlgTk>8J_t~~xI@wmxq(GKATvVMqR+K(wnl@t2E0Sc!W?iF4mq3p%C*#5%Cbz~u zoKE98T40fZ%NYF?m{&o6*RA!))7!(|@GB|^*}Tr8JgA)0uYxfHUlcyIFdB|CR#uso z7!6C6YbzgcJ~MNPaZ|HhTybz5Gm{!IO%OYs8BtqQt=mB{Iy59JI5IRe#5p4Awb=1d zAIM(Uo6?J9TN!Yy7 zJcO=>yfiBxM5bsf6_^zCVsskgOqQ+-D#ks4eGoLREi0a~v5`*lG9ir?cefFgzAD&; zWQ-O3-k!d5!602^?6di?G;WD%)ZIj|M(i>=|1PcYHF~ zv$0Ct->+s(F#y)4?Z0Cl`@VHcpE1`}qp~uobyZa?Zd;^03pgp|FWwhJ6=#FZ=RE)- zNy4!{4zN?Ex;$$?$6`hCo@TYA~_^bX^M=*(jbRn;dmywdwJDWmpnM1={=Ag%O-;XubqZH-Eu>@qLlb0W^Vj933eOjhAinOXx~Q6@vI7*UMaSEYwFNhqhV zoBO7O>r3n4=ie|h%PkB1lf83+v3GO7yMNm=omNVf!jPnEpN;ZY^a6t8a$Z-h46s10Ed!=6t+d& zwz?~S{NwNisE}4s%)8Es?CN0qcDRy~%?p@s{Pfjg3Ndt;770k$WQR03csHJ(1;%X= z-Od|Gs`q|pXPUZxkd~I-QjwN6Rxvs!Ay7L1Il`EvJ@rtwfJC?CiR2z#p6aX|y|lwb zO<1;4jHW1M>DNmCxk!?u&Q$F;jaA?)ELa$oiuL2Gd+PCQn+NwjmP!gz-F_2l-l|1I zBr1KFB=8eW8iv?J{TBBwAx+G9uQ3AtOu?|X4fdW!pX=f@hzz}Fy#9W@(=#D>{&Zuo zYOSB-`bWa7+}}e@VadqMxEP<)+9$%CyV<(9Lyu3bO0(F^iOYxtXY>{ko+OIKLdR6H z#x%HCqmcDWZ%yGVJ}{C7avFW!+k&GKhOLGcm=+XhJY#6W(|{TjN<0{p&jbBW-fmE# zDEN4@&mt>maekud+5vYQ1=@?;@5kmfc0W9=vA>^zElaK}Ksli=I)lYVy2)EWaB~B< z({ioAPmxDpYHGd-)}I8I5XaTa?ZcC!ay>GP*1+*{plhb|4V`sM4)+q%qexE~H8mzJ zW1Q>^8oCE8U!NIC*p~?A{%r{#96*krI!6t6LaTF3T->mwRFq**Qh=x%Qe;iC+@+<5 z8Ku(w;#e4>Ln?<18LYWgYL&!Ns!}+X%HdBT0j7K`3IQ6wy=3A8Oby{$dSJ@dQF-Tz z6T%|R0ga54S~)z^$P!YOr__g(UXGz1&9p-mjvw6MWL&vJGrTQwNGUO$;+q6X!cEf{2sot}qP37%}_o=Qp{GT%H8oj9lKj zQfzOV@aedmQ#&vurTP^yHOgJg$YX*;S1wU8c$viQkT1%9&pdcUn3)~s`r?l1MPEf4 zE9njfXlT8fRT2tn-bF8B2!@zLUN`~33T?(DEZZc=SXW-cPwty%`h>XGljC9r-WDupt2ODm)obHnrx>0c|1gDqdpRYBwVf9`zhI)H zN9oEx2pzf!F{UJqM8Er3Ue#|5=1k~GFL>Lf$tk63R;=7@Y*--tYV`xFRGmfl<#W7y zNT2U1TDLN(S1(q2P$^3Ad|)y?^c1vI=P*;xcgLIDbg96hlY(l?VB&7DzN}R<{QU8c z1t_*ZUZnOT4xWsN10zr7phUrNtBA_t`~su= zCk>B9ZYV@)qchQC%&&>zW#;{J?`z*1*ZxFExP+DIO!$I0NG~bv1xE!ErbxZTCu~+w z^Q*)5u)~dujXK(voPP8?ii_GGAyN;Nbin%vCE{nqbLRhpYo) z-`q1O2~H#??WgONr(^LGOM`7cWd(lZ=;sFIG4Cm-qSEof;==2~S5V7#REJcjsF-p+ zdD@dA2;^0dkGvhrD9aW-iBso^o*TVH5%d{&H1|d{TF0{a5g0P~yv#zn3P{)gVzoB+ zMevbBMmGq=A?p>mO)NWrP=iB~AyEs0b6a7iVRNV1F11i%>?!i}@O;IbEJ`%lhnN-@ zaT~Dd_N@%%^3(6HFx2|Urwg6Y&p)n@JqJ_9F4|uIHK%XW-jMlqKYhbl-(6SC!qfa# zqM~u}`a!@6`_d3D=jpVXj1GCTuS1~&Trq?r{@u&Yy7QXnDOQ}Qo% zzjG$KTr8gkovtQPv5w!U2K1INjP1vML_(H4B`G&dwjqeKv#&ABsd9G5QOME0**0OW zG}7WeS2p!?m|-5nIYs?mC*3RGBgt6Q zPd|9_^6lD=fp|pcU_r{kTFQD`(1CqVX@qUHCtZ^n_VXEJ!o>r>g85OQ1NUP(@_B~2sRcTw#ZE@sU?%_5Jb)uq0c!^ zV2-PZi#YJsy$6b`0-Ib;QUP~yva}-Z2Y|Cr34a-N&-$G;@7(&%i{vw>^>(Gg(&XYL zJz**4PI2a3aTPV2Wh(Nj({(e*$Sy2v#!8vvPygI{y(6gj21gk{URIjVfF`6u7!y4L zWTdN#hwqnLdtbX*KmUfq0a$fst1bm#&xH z-8GEw^|0(Y&3^p(o2Xif9}>*^bU1#!#`g(9pDF^;<3{C(OQ+vB7RamneEu zbk|ewu>^LKLHOXHCCt$wIOKCoOkZKlw+iSOC8x7aGFTfmCb)&2i$ir*jqI-X7J8M; zr$2fZW_6!289#46WDQys!oc#!lqNtm*o0RBBoU7%_7~`hN1J(RHJHycp^V!l^+O|= zQtrGxVenCF+A&{k?y=gWoJ$4DG@g)PHp#`IdCi(^4>G_+!Sn^aiOy^6`^HDYV-qXEdo)(?@FmAFq+k56*D#OyioN}yV zqP~TMu9em3=CaZDmIK7tRg}w$8kJyScrVhipz~O)GRuZji~OZIq}bgyUa(tvu9pfE z5+N3&C@12k;6dDS#=K@Rjg|QhZv@Sgzo&*xrs;Y_sqE>Ap&@S-w&5dhAm-voz^%^F}G&atqc z7Fy`CZk_~9x1kW729Wq^*l130vmGqInUkOAf*y)Sb$CF@%EIosf|0{orF#WbNnTm0 z*NKg>Kc#m4Aq}-=E_2%zSm81Fvqo@Y_|F{oV6?kg$g*JgFdE+ctUHl^NM8(0B>g&x z&G$TCg*|kctNxI4N&a=-4axyDQW{L0-waY*rRenH1bFtl2K6y|f_C_W z$3--p?b#$3BYZEu?hL+RW{P}s;PMr(+icNMndO~e`cS>!%t$9q(N z$FzHL;V5z>qbnL^_jqN}NcGBJtr=rNHO^2CmuecGWx89#(e>%SEVVLVtXt{@h$6%X zm8}cI>Y|m=Bpk7wwi8BCv8t#bev{^F7qN-rS4O>)Y)$xR$LKOF2{zv+z}h2^KdFku z$l4tVWknki?x^!5^UunsWq$;Ctl*KUDcIoEU(}Zen+DiMaFyAPp4U_Qnc*Q5zqe_5 zXvD=MGj+rjXpI3965usrZ=@%R2qm zz2E}*M`rtst>E0&qTN?xZvM73$2L2 zChs2%Bgg|z8QqS%D2(wEZshnAsFESAeF2F}v%9C~HBT#0wm!yBsje1O50U zyc_wA1D?$sSd5h3cDq))IrMg@x~o#N*V?UW-Iuv|V@t^R*r&s6Jjyou5Jmf8q#I2? ze+0}sFKyileCs3l+fbKRF$gBXKv<2@Y%?GAXMJkFGcNLXik+genj+mUKUE$y*8tPP zwcDCoscBAIEW2(5ngtpYs(Z`nkRse8P;Lz@fdcn z{P_*D^!}WL34;S?O1IWq+f;xzaxFps#P6ygSc;JGxGMqHk3_dSK(OoH5zu4RZDDHt zcgO06R`e4-_FXh0`^kyUvTq)lC<13P0~$M6?RBP3abAdQSYs{L>hL>@G?$ugr--YJ z+6wYxQlAGlF#Oa$kB-jMWM$8niW+bQyZ?c5xYlm52y=1WNaT2~dno_yfx`T+fqXGf z;lHL`OOYF&N0s{-!w3F&QcvhQ3u8=o6vAuT@+mFMn5#*Pop;pMwB_Aen6Y*d@-b=w z#qg4y(&_|;sTGmYgk6L#?TE}p7hPQcgf(G0fp5X zYp90r>A_(9SO!faQ}Mj1q2~1gRMKpg^}vceXTm*}t}S>r@y6smdFmfOZjPWopo;EG zBlYWgeRGw!k?KZ!jbx$AHj@gDiQysi8n%6=Mbp#Y_q4)J%=IK3_c)0@B1nWplZVLu zH-oTLs~HvZu!)qZbK7liq0+x`H7~zWW)9^^LcaVMV-qSG!n{hg=#8Z~(f!PC;4hPc zeyDflq8zDgNUnr&@19{NnSsx5wAnz>M;n~u<8R9=U=p)|Q|@?uf{>o? zx6-2MdIL)Hq3nr?x1QG;;{|@-?>0l0h9E0L1L(0Vc{WGIP5yP;-%7gz6%Xix^f~I^ z`-tlGkQE$qwL;yQPLTsZXi#7|T^qPD{@Hwx zdF{IhU?Tlbgi^id^Xt3zJ!)=iIO3@vuCp~}e`Z7Xm9KIQ02_-oSKLxz??ik%8$QU` z#O3qyJ()#_bT1iAR61>%V01=xCwVQcXamw(aReX9(m3u%0L=!$v?Mn3j-%^4T7ZPG=2!W> zjSDAdNsVr7QA~+5;q%`p7BKOtzZ_`xWxT)Jpyl zrf(Ho@v(8sxxX((e*FxdVE^?*qU;`8Gg8RL)XL7z!7)P`8weouWUEyCp%)&jVd*GQ zI7Lbg=?LddPli&{Bjs=*gX#8 zr%p|jxDM<^X3|xx^lx3cB5-xW4kcw+j0zE@Wmt5E?}AV*Cy4?>Fa(i>Xt2W9VDk{( z6Oetn$V${Xe1-K(7Dh{|CfX=-%a~}(dlaeysvj@S7dK!NZ2xg=R>aSW4H6}ksDE!@^>K-aZF3S-c637SGB zwk3(OoXVZDsxi?cN=%^)NK=4mpk?GQ+)IK>G8gKdLQg19AE;%R?X5Axo>dTk!-$|x zq5vj2Whw1`>aMq7WlU3ymMb=tp}SZ4Ggqg_`os`$19wRlo>9E(D{!;O@4WBhot3o^aj4xR>nOXEd|VVpVy z9gKt8L)+Zo)*6-M!_$!>*y#8q^w_1qaK__wkI@KK$3OxmUOr9zP#dH{r%P6}Qv&MC z<6RdG(P`~ud2@ihdM}u#q2+7-LL&-~P2e#%iafXIYp6ly^Ozmb2asnWke#Gzk~w)kX_N1jo3jQ{N` zU@AbIy(2UGnn?T|xZLZ2=W0XiY7!4fIZvASqX$g>I$4(Bn(VoVk~BC}%JD4?G9ErM z-dpQP_+7VT3NHsHmWFV>zCPv3@>5xE9}-i^;ZulKCXLvY0QL)BDm^E zozl9_xW|ILqZNiWsbB&`w;^m)95Yf`v~8}+bK;gBG#V%iUlXstMGH&zuynshh5hzr zJLOkz%z%i;#anXH%-6W;LsIBS$>na~(|RKp2~d~mB4-_4aUVG2fQ}7GKeO*0lXD3v zE!mkb`XjfhZXlf-Yar3-0E#QnTC8fAyt2oz;t;Jn&fx5R2w&>Wbmd!bp-sZ+KETpA z7FN{x33{$snYBne>L6hjpLi*&QcaC)X~JtQJ%E;M!=?M_>Vp7R*FjL#m~rcx17isT z6;!iz-qoW(d0f3^Jxa5`=!WB%bAE#x!7mz%@}4{ROo9)-g7utTi!QWsy}iV{2IUF6 zpP6_wgJ+s;)l-O9r(reG)e)9?N6U)gqu(Y(e0o`)y`z-eJ}|wH@3`m0 zj8VjjpvTR!fTMd&iEE8ED4JdGhH5$02_`4S2{x|k?K9fFVG%J{rKr{OVC0rIsWUrL zrwePT2}b_XwS;Gy?B_qxa~!Cp+rt#+ZbDG4YlTz>@PW2#$R=w8c%Ox59fF02ChW? zjgOU%6uJMiNE>hT6{5^Ek`wo8|GAo>vpjc&xS5T|NXX$d>SXQa))y^Hnpau^H`_Mm6{joBo9Im?i@n8Yj7}XMW^uf`P($38*RM zFdAhN$;FZN1^LABrkr!I)SPz3Rq)$Z$smj>!A3WL0uE$$QeD=;V)!0PbXtWf#o}=J zGF5#aGhQlqUUo9zRFA9biqgW1J7skV&1XT8U^_rk2Te_^XO|y}XMF1}Yo6P~zeF2! z!}fGi+?cX{08GnvA87W?NwjHDT4zFBj)Z#D9>)F64^Fls!|XJMiZ`qo3ashXcUYz2 zz!~d#7j%%|iJJV-S<>|~I!m}sN2Q`>QMec&%e)(t zn7=4ydX@NRRZfJ}^I#+uD%ln?y7u~r7-(yUw^l#l+itf3V=**P%}hqG?sK2%dJLiEL{s^2A%u< z<>L#5ZO|n?<4g;$ULu@E#7i25s@RN*c0jGR{}961Z0vL?igtB#i@qd{9(j4t+no7 z6(^?d#t544j(+%T&aV#^uQ7W*CI8VpBT zB-?9^L`!^X)Kv1aFZ44=@m^>JBmgjM&uW67hMs+TjR&^QLB&2ntX%-(jlFzGxjCy9 zLSG9jQqTQ$bfxZ+*v_Yk`>hg`MSRk1N9z#-OkV!&xlgOcvyQQWWa_fjW3ztH=@uXI zn9>~BkQtuLO1JJz5Z!ZGwI|mD*c#CVW?|tm@g7X10qln6Z*sJh_JwkKszB+sgwUV@grh zQ>CR?ry+evlJ}Bz&UP(N<4n5pg@t9JVm_4~KAt9flB%w3y3fgY>(FGaaKu+1W@qHZ zjuzi_ep5gEBI=~ZncmssfLo=m3|^eUgk^h{wSkZO0dOjhy`TSr(roA8Ld8g0NIex; zd1UhB8RWwZwKz>053l;gLM`VWEJ*R$GCDV$bMn*3N@4|0OhvNXKx?nDVvn_3w9?yc zLqskr8YC7afE|$$Ns(M%v=M=zW-}u5`e>^g0a0Z7rL>I~Ri!$>8ao}yvs1s_FTqZG zq?aIP^H_3`=XTkkz1G$`5nPB}~Ep_?rI`DfzT6dekSme~lSpd6c z$T=SW^Bs|$a81&>$IXww)%Q5NH;LfKy8k9Jc@{r)RfJ1(AvT~BE+5WiPR)mX`|7z% z9GeC;&#I<-igA^1`8F>3p2uu&j{800$ZWrUK~EDx1)r9u6(lxez?Di9SjA27+q5VL zUIZ#Sb(5!nbhYDbOUw_Y!p!0eDr!oey-{U41G01w7r4vCJ6?If-6~&z7{eMJCWg*{ zZ_)P%ZD>=_%lzo@DK~%}lFN+UwQc;dn_Ah$bU!UWQ@Up?jQT6_tLiiTh*b{ytl*E! z?JBB7$luGIO*z$AKLZ~jrk>^Tuz}si1rpfQa$PpU9M!)EbIO=6_Zy^@_UQy)r3Ux>sWtVxYo06e$aOj8s-Ts#7P*Q zY8hJ?+ekybPC!Rbz=%(PBrsi!t!Am!VWgv&whiAX6AR)h1HI$vKKoQ}9Ka{4reBG4 zSXCrccAZNM$9q1lB7SXTwXzTB!$La=7WMFb&KL1P$jJ_zw+X@|4LY^BzyGGx-4krA zJl|hfBY5adfG3NhW+&e@LHhO3I^S!tl&F=QRMXH+CoObMVm81<+^Itevdu8I9Gd~= z!t)|!_ztJ$@u^4Sc(&Yc?tf-Zbok@bzS*B+5S@I2p50IB zg*U7z4Bc?810{p^cgemL+J(D!?8AG3`U^ZBuCcU;QKv{~U)z;mG32h4$w5H3@EcT4`js~JuB2pn}C-u_q`mtqNW=)z` zuE8L_#DD{Te%#?p&YuMY)3kgM5_MDwfL* zKr#8}OA*76x@dC4FrBpB@kd;7@_FIhK@OB7BI_)sVFc}-pVrJfNURU%HH|Lx;LLH_)^s-fmvO`HD z9x-VGV}-w8gMPrrRdgptVaL6#A>z_-70Ai6De)#X5@QT!z1(Cs`;*g(|U_6hOST- z*JQ}@1v!b7=LT+8PFNMvbxO{NXt*TdQ}1ym?DPmSCo#E92qQyHUe70lx+0DvWtcT@ zq%CYZGozE%gbcs_%>}dsgUvJ!$>*MCM)@K*+|>6K<4vZTm=E8Dr<5>6we_Asie;Wl zM|dBXLrpRW4K>#lGqipg+7+SPt>?Z{m^0XFMA2S0X~zf<Yl*V54&gS=9pnU&W|`LBO(!v6}vT?Y!s(K^?!V=rSaHIJ{lPgM`dj&xNEiJfQG`% z0*8~v0iY$X065$ZWHz}@Xn2mNKwJ#Z`oi;|N5sMSpBp>yHu@Fu15;8Szu6hT8##a) zp+4J6xWQEIY#LkHCQo6K#>Wq zhfmQT&&dYf{v5L=NL5romBer7sT-)>MpL4=h9C3_kcZo(t*$b7I}q77jQh`jw`N0FaCzZ83+lDfucVE9{qW@#dFhJ$)9x@`Uvd8%>cN)6MtsJtruVLBj(8o&D7pEDp z4!?-Ebc191hd-#ZE${4VV#e*_ayXm{C_odjet_UWlR{>H78!k2E5cl^SH1pvFGR5M zfFkm027B2X;ZenKt6Zugxtg8S0enoSHoE>gY`hX^!!s z7fX*8=&6!3ikf~PDB6b%w>$zM>(b*N@SnDTVEDknxEcY-VkX$S?kxt-b)=XN6yG3p z%2`OOAueh^EXBdn7_IQNT^QmHJ=aT+P#yGzTcreFDk&Z(7K%+@jhLd9?kC|iCAq;2 zpT6M`PS!Y1$A`}~e2RYFSzDsnIJU9`7AJ05PzE`Ych|%9Mwh6A>OaFa=kzCYvD8PB z3u_7@%nmH$wIC-w;sb1o(pyM5((}*R?JX5T603Q)jJOV)i z9%_OOFpca8M}1U+JZBhk>kzx}y+XVJRc1fD%u9Xfu2=cz6ju){AXA-87utl091>rU z(-1hJ(9ypS=1BtgLfjC{1XD>G@d7ICOs}zpU7MKhN+D$zPdikypIw$|@;H;6ZsWUm zJ+yw2JbKjgBQH8|hdmaoNpm9N@kB|UB^X`w_SB-S3GkcKfc^?oP2Dlbxl0Ao{gyOw zUB?_p4rOVe#-ZgDKQ^>NZehGdX zX=e-Xs;qg^JFJJ?ZHlngBZ$7-iA>l1jD3bX)GmN-0#(%Oy!I^GT%ZTwQLG{+yRD(y zLJ!fJZHSj4J>4wb!-+2`d?oO5*T6a~$t_I%u0c8KEbv<)xEb)MM)n9*OHSSnQ;VN8 zXvQi0{2?Mb(OMhBKcHYQ-*X*3VYi9PlE-QQ;Wjk0&wFjl)Ln8MZgYWGK0-**k$BtW2#V)S2IX~wZ;wo=TaP=gF>h540C|1Xd~ z68jTKD%=8zxJp>}vcfTq6ZSSL=;(AtA5;H$>69JqZHDS$q)I&q!MfYmY;c~iPPQ(iDY1E)p@<3KTi+z?i0noK`aBEig3I4$dl)!&!kUy9HZ$-o2<>f zUE-JGM8VK=%jEE}b)m+AwGWM>8Liu~64j;?<1mkIwxvN&V+YO!$RFk&qKnz%uBC^@ z8GEz%$#A$;s1KS2Tn0gWf4#R|>)H6rU$J)tK>CbJ!6q%I1!=tB$swMX3%8~Hapl)2 zs5VoclX7k9uV_O$k~G|rVqB?vn>hr~x9*X4!p~q#uw|2G&hkry?$^YgqPY0HITh|{ zVU{A>B75DD+6UJz^~> zfbT}n67e1j_K640{+mkcdK;4tK>gEloJ5231H1%9-YW7ZsiqsB1bG8JZF$my$*Y0i z370TKPR5i6Ojji<0(8Qx{y(K}in5QOYh#9zNmsmKXi^sH)OMUZ@5RXcxuo$YUJ9z|(i`0|`T2Sg-!4xWKv~*4Y zxtWItMT>zatYXm~A&GyEG21>#Tx{0@THLM{W%5p-g|F!EkU}RQ`EDQD(RQkG4w+0k z-dM1k67Kdj*}@c{FZk!0uQe_3GFT4N!3E}GGGa{hi(_b+8^&P6dKdn3cwwDasXP?P zQk$x`9{D>7sEc>emEFTZ1Lg*&)Y_1xO0kE+s;blr9v&_KH?8FRMPwXtMv@ z%JP@=dop3!Pq8*YJVqXp!Vwl`X&ART!jF&-(OI%>0!6M2QAtcZAUh8y7gLBDztIbO z*A;UBlXm{W`phf1nm+Co24W(9>;h-m*hM<;Q&5xskSwXs@A2DgLXp+apCsubjQa3!uD^gyc9lt6 zk&)}Gt^zr$iznKEEJF*hU$@Gqceux2SZA5!!NNnzl0(HckVSbtZr*RQZ7`5>8M+Z~ zAJDP%20>TKq&Q@-yD7kJ%WjCJ z(Z^=B4g2YktY~|E+iT9Q^lLcq4W_?)F(2t8)HL$2&sL>YI`c`VP+WHHh`kL=tk})d zY!oox1ZAPqh2a`nh9XKwmU-?lM)zAFdAKZ(NwkA$Elg~caf>VsEMJ0*LYU*kcON*oP# zD1vh|jWK>FEV>9gsi#4Ww$7wIg_RUa&oxMe!iGV9P{_(?6t8D~0Z*RqsCJm_S@O$F zKZT-QbCJ~e{ct;|@SJ!nF^!&pO3?4-Mu-OguJY~Ct`s+g_GcEXtz*MSb!qM)Gnq|>FsFc)5EN&I>Xw$!Q!H$h(U1D)A?7nq#zv4?FI z$jXxjDr=QB%8&S+J+sg}Lez^1c*&L<4?(aYb^`fR+cW_SpuV;69Tp0Tmj!QEzR z1&8@;G^$Vx$wxb&*#%5;Bc0(HB8I_{|KNl>gWjk=wD(ooI3R&|mj`%>-a60ED;c$! z8ZQBnb3jb4hwZ?rEX1d3{5lmQ^yJ^WX>GJn-pr)voZIvBj0*M)5s!3tZp5uDCA#w!Nek=YN6c8h{aa&{AYx>^k z;>#>Kpi-H(>{uiS?+8~ji!`K6%EJzZhD38W*#+A5YtVg*x@>^ttG)4gPvuvv1c6^X z*43S8uBTLO{D=(hTgZHll0@EAB>V}YQsE3+ezZ10TaB-!l_NQpVZHBv2`yuu3_fD!3MeFyoC^lE9~J0Yy+j1QT$((v zCT92@ce)7~r4Mc2PvjkfOX;|S3lIexU}ZZUY*2=-0zk9d$Sx@WBGH(+Ip|6kmG`L^ zu25!&XSzkv+Rasy&~=`nEFFaVKW#!(8H7`ZZ4idsHX*j8&@aK8#dA46-t1(e4c^aK zGig3tQ*Q&_Y2zKPoy$!hpzs{ASmYo_D{; z8ig+^5fvW~VbfBi0H|IVRC1w4Equh#YU(AeqhxW5c3MZ=vTyp+4Mw#jX;A6HexSzH zt3!+lB)F@0u+h+e`SU=44N2Vi;I97iW|>HDLo`-LN6TV!c2gHv{f{^VxR7bDGLu8U zIXk~mM`WzeID7x|n>C7+2ps1*KW}}3v(MY1sb;`nYS5S%QAE$2E?~2kyKdbtZft7~ zlRXhTS*H3NX~*2NVv=sQ<%-%G_3oNzLQvg8F-Wo)h|~JX5OxT% z)=kmHh&jS3-7?N;bo6_KItw64N=PI!}SPqIwtTJqPZQny}ZMBWO{t{kE%*q)b+ zpDRQ{zjGUE8UUEpha}NOt+9!Y=-M$j)6hiM#62IVRMfn3SsX@5s&;WG{Aqh(v!@YW zbmqxh6lj?=@KL`=D`Ba>3J-gD zQq12<9H=R7pWgw(%_Y=lhdp>Bn$O*8`|WN`Y9IHGqyrdvK;E{sZUSM)m%%>!%(o}H z4?ij>0VMJ$r;xxwmYk&re#3g7>YLs@HhcHe0M9$bO(%sUe|{DyZPie{8Hm_a*Z(h? zzA`Mz@9TQ#M!I7N=?3W-kQhO_8B#*JI|mRDsX+w^>28MZ5(%ZHJEgn(9e>aJ|323> zbMHB4pS9OoXWfWpqk{G)dO!e(hsF{Po6(Fn=`jTp^CQy?ur_VehbRbJ{zf!h-_hC4 z?~k>em)kTUt3;(#p5{ziL2^7O6HGL5)jbzTagHt}_J(|`A0%ipps_=Sw(Q}D>88I2 zQDR6Km!HYK?9m>dYBNJt#Ajo?wiOS`0#=oA18mSnM!Gg9u5lB#HqY(IzHFq}z+(8WY1HNv=gseV=sP$0Ko>$g2%uK=1;$-a5U#)xe;4zgEqdgZM$(Wle@eJ zZtUaNR^a#Wjx(;2HzNH`I>-qFvw9-afnU~kIeim(cX;!qDa>4Gya+?9#y9E85RPa zo&z#K2-H#vbQiJlR?V12z}3dk+)aSe_Cb#Cby(QTni(S>Biw#JktI2 zu5{m8I=N6sVE1O|(euvpRed(s>so^_-)^!6Ndov=e7-3Cs5!K+~jFZuEtxYsQpl!3&z_^#?ee zE^NLeWZ;p@6@)F~U`n+s8ThC$3PtEkHZCa#NBCMxEOYnqglVrda2-rCxBU_RIhfNb zV%s7#7d=9C-XB(lVT!zB6j2F>p10lNK&@s}U-q~6PQQ*Y8tVPFX;jY=RJ-4smIkg6 zF}82kVP$c&E52j!-G22Ba_t7^gyBNQ>@dyy-Xbu>Z|&luBZhqAwR4y7MrXVB!FFtz z^tAjn%0NEK!!Bdsp)piW7F5Z@i0Epu1FXEcLD#uEQR6#LNA#V0d7J<90`%PNKrCM8 zoKglK1-=4W)MJ-O24c5PesXhgqc*!e!?2e5?}Xc<3EH&2*AKZ=d7Jb%eN4HlxKdP3 zI;Xl03A@2pPzQ$${)cjs2$%#_GutK;kg9HaU8R_(FX|lE%^mb^0bv2bf0K`Ma&j9v zJP@~PKpu<0{lu`2d$Fs?LAo7=`+3`!h<7CA zR9{QbVg|M({pa&NHoR@FOResWmjUVBZtBR+2ZXk$n0+}#YP;6%Z-6z0*t_UT`{q@) zpDQsGt6VQI!Mv{JKXd2y!|GO^^v8{LpH}8k^YumVkgX(Khx&jS&}LZ@?yz8oPq~F2 z`vvM6LZ9}%V`nPFD=~oIl#`TJ)X@>jUqER5)wsb(^UIs1qNR`q=Z~Ixv>ApkK&G5} zntta!D?-nbdD!xbo}s^NK|sOW*Ns@u!I&4E=Jzz7%eb&X@g-aYAw-twZ`9qixS4R# z3v}tK&12hT^yV77xxU8hDLn1#{G#&umFo7%%ItHp|>f z9>4IPR--jVF6mgAlmkPeBizs@)26h z2n{{CZ+Q^B0SVmc>xd0TTist4%gDh_3(dA_S-cal z<_JVtWv3G%i78Z@oRGM#O?h+KzyBO)0x*Pl8S{d?Su7w0mL=q%>EGP#Cn^g7V#ofk zstx`8ZkK>m#|fEwLg4S)=k?CPvsrKKxf>(C>HPaD^_=oe!+H9jvBj4@R9Y6#e8p53 z!aLs~Typ&^m@{v=zq02dy!2mL6}xM8_o>XS%wJqDUn4LkVo1}6u(13+Yq7W<|Us|9n`{eeQoQs3QEK2s4104E~(Z-6^|< zP~1>(D#aVZz`Q8dl2T$#`V22BUQp>DI}-*!fzMYQC=)LcmOg0U1`oyV zS>v(OHX4xDjubZI)$Jl0kF~Jn=ZyYF1jHhR>wJbhP*mfx5uN1o`PkMtbTS$HB6y9N zf<@&%Z}XnI&CL6Qg}Plx93a*7gE_9hPxxO@hxLy)yH~1S|B$$!d5#*Wu zuS6x&hUY&@9le{I$f2ONzvg3n;lelzlgjl7m!F8@+K9yt&+v;B!8N!T+^f*MYtHU8 zehxDZL2mee4#83jLA!bg*x5aKhWa{HdjCEk`BnRV*545}=@2lu+%P9Y7S#2R=|EA^ z2hEeAK6iMrP4{Vfm8-Vuf>(5v=%J3Sdj{$A9Qlwf_^=rJ$}_D?=QcGgMmSKCwZT+i z;w8auJ+`5JjdzTh{899ekU5A)pvZ0ZYUCSM;1_AdpCTxmhSucQwlbPT&-+wIoSwm{ z%enTcSPu}2-ivi`v9AhRVpO@&f( z9aDsvL9R`iN|?0iuzsXFkd3a77hORPvS-*?K(SCt@|}a0F#gJt`slunon!DJv)9JC z_3%~T`j_K(z6SAq$q4l;@mFte+hcO;6ZN?u=g$26mk!b~gV{Xoa#)xW$|}KtIqZKG*(8341DgdvO!aRU zFqRKrRB`tpe1njPJX#GnqqrUybdDGy%UVPU>1bQbM3~l?2!D~ljm=(q3Z2W|Yx=vm zU5h>EJq1T%oN3?N`TVdCtVWp}xm?(;lHs|NzEgcI|IciP0pmfG2T&sc$PzQyp6%Rx zHXFE*bB=jlm_o7o%=4u6e4F-y^Ac&dT)L7@;<6Mr@^_yjG}A1m-@V5KIdO|)(-+AR zZ>yJdvNMq6m8HN~bLw!qh{v@Q{b%S`L`gIUZ*IyT3mcz=2OTpb@CbDv1p#ueIos_> ziKuWE97^NrT^9)mT_2w30*?9KjGUR?SB^CY_`5HsE@i0xYMtO>vUpjcH=#T))GFb|s=jS-`FQF* z>RDV`S$afje%D;tVgG7xcUSp1VCMK*a+F%HqMOj%8Oe00%Q?%I)Pl98(eU+5kA?54 zGn_lB98|3DSjP4P9VCv_Z)DCD#R^z(Az1j;d58{l;3!K+1GlagM@ZTdzWB+Lb>jn4 z5ZU#p*&eV_d*5B=an?fy$j6(4t9?|?dgr^Psq)dWWY0j1eY;x(Z364mxXukTS?V?(-889Ar=;}}NXMwHiA9RjQhunpOIkaG-@JEqVFge4m`I+;Z~j?@j~Sq+y7Wv`vW^sd7atjI6zEnkZ28BOYo>g_Beipc^6)hG8+sm{?8a4S(OUc%u`^rz$JRoGL<$}}geXN_~|WI>f6o`jLn)If19s%{)U2-VjO3^Xp51Z>|rsliJe-@5b03P26%>TE{% z6W75&_YFtQ1GVjn#QcQAAU2K2>_SQ0QvRL+brMH@;@%Dk(eU~q|Ew;1T|EWRL0732 zU`NjKd}wU|39Vzfk}8X~{KDb%hYHmS6HLJ66eMu-^7OnKwe^wX8wU16zmd%Iun+MB zInmA=N}?9umnsY2aTHp}fTI$qbk}$r!+HtIU{3eS_Ge!T>!rXq(;TD|T+eVaMoV-DJH&POIlLC+vf#1a zA0?i?bfcY8!yk(hj=(UNI;5I^wH^GJnri*nxN=QN_t1?r?TwyYi)4P~Uxwp7P5ALt{QpXr7e*_Zhy)yX2Q1~MaJww0~JjUMbirHX1M&v6LK9F!g z$Q|JLlWIq$D-|6k29YpL22S+({k{gdV@`YB%nJpYG)L!6sNG8#gn>El=fM$QM`-Qa z|0|K2t5F)l<_PR(s1YZk!N8?Bmp!En0jMKrAp3+Bw2+Ace&mV*Y|E9nkMA zHqzWdiJXtPj-f2MR`aflT{I0TpBCX{|5QI-uia%SW`qj!?uH9c&Cgb7(QaV(>ZDwE zQ}Sws5H(1tgGSDm$4t08JH4$V&TQX~0J#CX(`E4$0B}7@7!!zkWnp^5_fxA&<{N1oQZV zN5f`@svEZtNOx+S;F?~qnZu1~h>Jb>MBN&kR>qlMs+cdMA`k}#C@u5$a=Q)$S zDO(I0_?!@tzJsomG8|&(kV4d}Nl@-H* zgG?MdGXR{cny>+JUoI3Y6zW#t%vSJvfg|tpjeKJ2huF`ixfDAE6ja#+b8`G+F@KNc z^dG$dyAp7@(f9FlcM`X--t{DgCL83*Pi?je$W^Gpl#JM(#V~`&5?1-+<+$r9wvn?b zQu2t%>l-SmXX!WKZ@V#9=a;R^e|YlkeppHb8^7}oRtWi{O21*({uRp%y+5Z~gC?$v zKkI{b0;Vt5_G{;Ms-B>}Ra+}f34l^b$`9&v`9knF#=vGnyRj#=d3Km=aV+Gk!d=p< zKV^%gAYl#|oG=3^b#ow{EqKSd&gA?EG|u+RyI!+*!gWSA#n}I67n}&V@95ZCYH6rz ze1UB{G|P6gYZ_sqR2nQ)$mN>%jK!$cQW8|c@zt(rW5mgkv$ciNw;V+&4P!ug3U1#B z!GujH>95^Rv=g4?bY3#vmyLgAn1B$NQ>x(%;BJCQxg(^>g1%l~&=Bse`(shjHf`7d z>@X9kEh!qD4z{nO58bJxZ=kbOPf}9}fA@1K4qmP_e+isHUH*gC@@%T8MqL(NOMBT& zuGJO(6zY(}i!A-7D=$a+RH*ka*OeRRcl?j}>-aKS%pX5$Tk)J0&S#nbH$`qSw(O|;ZR;4W=MH!D^!)j;bj6NhV9E2u+NpR*EAPiFbb+9TZlvGVF1aTVLz(I;A+iehu;P z<>J%G`3eki)Kla5pi%~;goYj@kWmSOQ3Nwh!KVOXH-m8@kyDH=^bp2!=So;@5rF*f zVR*g+v~D20Jfh!SvJucIWv3qT_96rt_AH4MZx&p`4ne*X?O(EUVsA=BDz9PwR> z890FrY=^9U#dw!)>0r(raT(W%g!ym-=aVbnpr@Ro_63|k~MCd~)BTb6s zk4k4OkT{|QfP7ltI+Radfg*-h3+?H@l$Oif?3F&t*WJHl1>)07mQ%vs zwWWwNvISWw9bl*L<%tK2)9Kd(SmBAzBMm6SBpZQT&b5+*0`l{bN_=(j3Iur%s*GDQ zd<-v{iWF`TLJ;KGP!Vkg&cpqfB_R8%YwdXv{80vn++bGFNP)?F`caxKSib zxs#tcmH6?rWy!IJCoMRGyMz+QVo5#lrL#w)i3pzwU(XK zeTHy6wBOpnUFygqvPK?B40aeYb|>X|1lj{L77LZEd3B@uuj zWzv;5oyG?K8axpHk*}71y6@mQUYE`)$;(6po>B($+Cxmz4o(L~_w$&*-zNqrFy>mY z54{|s`|n9dEoZW2-K@B*{VHpyPt8Y+6t-xh%O|7H-e9jq8eR;$QfJmGLV7csW|{he!KniJuAcSc!k%$ARv*a1LgdgnmR#R;gPoEql= ziR=Aa4S2qe=s=OaYDr_ts2o8cWUDsKU?dHD?%dphWhDB)4w5Hb!0&5l6WuWXkyZdY>Q&77x{;kJ?BLEfT|0(Z2l76Q8j8Z4 zhfiOjf*b!GmvvYOU-iH#?t{$xt^jn`Q)u>WD0hL!7>WZ5!Lb(xEOOdw$x6FQj;b_) zn%pf8+s?{tM$YObC=A+9ZryZE@U{X`j0VMP0(=Ziv{xNx$HxvRR@8J0Zqr?eYmVrl z_ztc@TGWSOd3&Md;3w>XD@8d@-wUdy98``8PKPEWoNG+JVK41YFHrckE>lZp38c>OAR_DrB*KS$Khihi)#;zELcSv?X1+A*bN|;MsxU=a$g^=FYo;FmJ$;vDvHpXz zKjp>niW4f#W_70?oKuEq=t%jSbQHsgY(!8?eHk~>1aK5y&f;A`&-_(f7AO@_v`Uk| z*9HGcVCX!$f&HM+ zrmxWA3X9G_J;`^|%4a`8f|3C~W220V?skHt9BuNdqX}=-Fw!@UGt36b=B0HeU(D+- zjLMpo^4y_C4V}~_#Xnk&8lRdNKrMiN8-Tk5`m#waa<;U|n^9pIFq%`$ES0pmA=Ju= zF2BIb1P7KR+>jJx692?^=OiL`DrAf007Za`|EmNydcQ_werDtaHWyt3y3!CpD||?~ zeSQ{`<~3>77bCvMAf}H($Vx8ZEjWU$^THtIBPg6uR2*%<=Ft8}y2nmnp;Ab6?!kN8 zQ;CMD-sCRJF95@Z%zkbVVmd|~c>-*G5{R78ja-VygFYDu9*kUy+0j9}z61dG+XjfW zBXhFcdO82YcuWi3#Lmtu4zlM4+jP=9)Qau>DsGygADB%MO%{jk;DDx`Z zJMqqH_s!D4IyZ_HKT7Hp$Imf-(c}ObBYAF6^4hpVtwv*_$r1tZrBBxm!Vn0sD(iy( z=8f-1+_!Nd=-^)(JTGGy;o{BP669`QrV6wCLSWaZ(Wj;}H)~UxN3G!qNU(+m9*@|K z#IU3`>h&P!C8kJaYTptnzwRjTMVR#Zg9NWv&l*zG)-Habcu2z6nuXgauD-+$A9-Ud6|5b+9~vY<Y}9h;Wd0UEc{`*mlkN$~?sXHxztWhPQ(#HR&y5iV5 zgSVYWwLX00gK6QGHF8KoC`wZp%K$5?A<_C13W1YY?t{G+oPm{Y=zH4EgILG!vs1b# z?~Fi`)IMRI(Rv>?c*K_}kn)!#5t}1(g|z`9UcM62X44U~y}FX=f(L2|Q^FvaQ=Z~q zIb?7%P1&MufoZ+*?|dTq0-3D!5H#vy5_)ebQd1t+6OU~;+1uvKdVriiit>m)^S%sF zgBwqMFoO*(#~MApf~qrx+{0MQi&2hFI|o#O}2g`YsQ_*Qo9c5p~{S45L(~ zRxc>N&rkif6QvE0V2+hvop(E7=Ogj6UW1bXhY#$FvJt%sf(26z&BKE_>Uj;t-o8;q zjW%19U2o&NJyO+5y&2$Vj3U|$LUd=f#A~~Xb_VJ$1eRKwo%QB&(1tc>FldWeKo;;M z0ET7ada{NFo1g|FIz+EJW8nRD_|A1q;*o@hM#yh<1nVPi1x|4+mxvDgia^Vdt?T(6 zf`YN50>%L+a)R(Q4k8Xr#{(Zq(CF#?*t&L4WnM18XHNMKn_c4TGMT4JM1s%jHwbiB zH?M&Um>leht|ge*4j}J z1QHhFo*)o{oInQH&6iR))Qv^d(w-4)Gt?IDb))f}mR_vmwpBV#J3xTB#i*7MM(bLR z%PQQU{d@joT1b;Y|8uT6BE1tSKXuQ5a+qX{BGj@BNvpjAWp{w(k&K5%{@AnnKn*47 zOuzpd=DmBvp}qczEovfS(4WP*rF{pX3^_5O$9P|BC2>_F;3lZxlJ)pnAobmWhoBXs zxFEU8W`+OlRn*#@mbI57dc2Hzxm+RfDppT}{uIjQZ!v@4IZwmar2qkfleNoo*O{e| z`F|qUlV_jLi)W3HVYBDl6KD%(X6zvpvHIdQkrFiU;;F;cs6KmKbrY5!?kU@H^)LQ` zRXT62emlSL|6Isn#njrOWnCVZQw{BJ$&vsJoQ_O??~M78t@L5x&o5DDCMnKLVlh|| zZJ~QTE)6$uqEsp&sdSd@SE=O^e22r;U%k+ebb>)LEQq?bq0v%MvrI12nCSOc2|PjQ zK^&-S0W{~()6o!ynE&!Ud&B)9uO9cLlh&7IJBnJ@*rWoF5N_axB@;kW$(zkwB(0@MdgP9wgqt zC1fB9Uk$z7uDF+BjiF|`1CBx{RpEU(?PHCtcY+gT z)kawu+GC_zY2UIxzfLaY#;-zd^_QoHiKD;iS+f@HzU0 zH@I$W6QRYv2#_5KU2YNLhOA=t)W}r5P-H@`JG{t|%CGk4BQ8J98=lci8NMUL`qLr?Jx_{fpBK&{d=O;af8NFIsz$ED;1LJpODi^TNg0M zS21Bc9FiR*TW8A@vOg(ITSvQk&$(%;b=|1{YGph0J<=Y^v$!~_UeEdx{CPD{5#C75 zj3EeSaz#B+R`B*E7tN#x7D+@&UB+1CZ|(8EBz=`%K;mykpfk6G^oL{sZJcQ7W~Nn? zL1r{Q#@vEDR=pXzh~MSxpx1mcpP|;|_a!otz$>vvf_+jt zZ~;e_e`@d{xt+5+J4G$c&1FMMPG&x8-WF_J48C7^@=Iq!^bT#5c>Ul9r?*u@p&ZeP zvt}(De`2QJ4^W4VxVd@ga>UxK9U~7$Ruqx`T$$!%F;fR9@|Hy@62$!;r4g!ce8v14 zx;Td-t6u-><_w$02ztw$&)uXM-}pYPVFdvM7KnlE89?GDza{pfmZV&* zn)yL$-ljoUJbPm_r9R0=Oq%cXulS!UHUEaVeY`9af8&Cq@ljZRwPve-c2Bvm&JK7o^Z3=rNfmKp& zf5#CfCZio`QMYS(MZO1c-sQ8IkjGPD&=Y1Z_kG92>J7Y6pk17|3trBwhz_(wimw;G?Avk4 z757Kx^9?=Gxy;oN+*aQ0?*NSN<{fhItQOE(fe_6RF7VF*GqMaGz&VY~-aeQT!SmpDBMAkWmXhV1Q z?X5rB+lfj_9S7HA=&VLp*Y+N?)O0u^!VM~0aN*c9U^@F(LS|j9)E8Wz5>lt9;N)sI4=-uA0>BdnL}1{Au*d zYw<_d^R4OlKCc8J3Xj62jK;}V*@s9{s|VManNc!ropKz`Om?ft1@sTgfQan*L_ z;6mBeS)(pq5W5lJ*LExHnGD!Prwg~*Nz$YIoSXtHqDcBn`864LB|vCtu*gQ)lVInA zgEQFz*$2bQyaKuPe5zIj1l{~g5)6qDumI}JD?+6af#N<&SahoIPKCiTOkHl?1!7R# zTXQ~pGr#h*eQF)^RwO0Y9 zP(Q}c^%SsGJoqtC5oBc;@pdF!23tykntbx>%p8yCi9RW**|BKH)DN)VcA4j-o_^~; zYlvqmWq)S21|rRm9n^qKgW&%gV1v#Fay0|5A5 zUsg9;0{3+WGRtA4E?NV`*Ap4$t@ly+vHYcC%PUl#+ycK&r4S zg(T9fKgw#vA4i=`m3_*<=LcpfBN1gy`+sax2zHmI9&nO%RkPwWfBnzui?9A7{#4Op!;)m@gS~mS9U^kv zoyufTIn{US*(qnFGe_t~UvIHWTU%+?)PC!)%E~yh*_i+r@!8MEcJ`JcC<&x6TTG(4 z&L8@LpSlSaZ&@gC>#t^c7qKbBUv?}ZTXA5hxb0uxNKKdK6Cze59W_$E_S87uKXIlW zaC>yFppRALHhc&(^x&7C3iN8_u(IB#t3^<|!;}4S0hI{iQJY=_aOMD0F+#B#LHUzD zz*zDAWaX43sF{cPjg5IVh62yYR8@Pz+FGUX!uKs(eMqO)D(1V}uAO3NpONbYs`+So zb_x;H4ufXYPrKbd#Th|K%7}+}og5NP)Ak93~4?lE0 z=8xaV49tn^eF}RQac%C_>=E6)O7YeAunywvyPsvNT?EBWK`d2acal5d***}N>~E0k zl!&wp5yxF^LqDgR4Ey=pfoRD);V-<}mAz`;3As`Cj=IAL^pKZe#VI5Y9P<%BP+wFr zYzq-ZlbrgdxdfjRs7v40L!8?$i4Qpr8qRLMkpAI3ze@`7Bi7#cNMEJc3mef)Trt3i zmz_PYUB&6zjZ%2ZBNd&VpF}hh8dn`8F--D!IrYK6gREl>)#iJ9{YG%8r#71<>$^uP))CdoAC@RD$KTw1QXfx!Y%R^I*9zq# z|0RUn{*xl#so~V{6VBzh(^uU=xakg7XdwQg7*Y&vg;YpAwv}M1<Y{2e$KeswfxF;JHzY=N)32g_b=I6GHUT^ocejCx^{v=~0G)%AOPEv~|kLmU&>-xz+pygQ3&;1&vQ9 zA+iPBAgI{vqNUVipaDi!-L&zF&mHb-RErb8Pt;ZnQVs(Hu5k5bF1;KhZrCl+CWM9Sq($Z;CCP0u>ewIjTr{73oUQl*hZ^LUj2 zVqJSs)Jg|-b%}RZW!K+anaa!&ToN!Vg4Sod`gs`6^ij`l1N-6^VyexNqf_-cJ51tF zudVSwnIVb7B)~HZ$0+(zrZ#K^thjO{T zS`Ei_q`Y!vwW=<6BJY5;&u^Np3WjH1Z`WyrzQxa`zh+!oNDBnmaa+!!{5xrU4>Wyd zjF2+LP{>~h(*^Wpp{vKu@5qPhy!}q_c)B#k!j-L*<~fMZyeP|so2vTfw~a#AB` zQ8bc$CoS1Ao>CH#4Iw5bsiU^+#}sApx-eTU zkfp9kkmS*J!K;vrUj}G&_KA(oCJg0zkPT@x9vUJ@t3^@EdGk?x)KB zN&ENN7Q7#O_7ZELP{)G3lw+MY2l#MrJB-M zo#z^Q!0$Z}Xe8Tvz7s0}CXKu$CZMQPM$>KB@h)?4-ltP76~! zNHwWflGNXxIxro+dBbLD8CMKt@=Py--A+7^;_|*(860yq9MF8lv8MXDg(%kk_l19) zmI#7a$4>k0(1R(SCwmgGmS$3c0Y==J<9N}byv9`Its`mm8<}xN3S35)?JR}zVJdGjvi#P$8{|zKtyVEBbt`#!s^^H>DdW z9RUIkoA^9hv_cCJrR-a65-2je9B<@r^#3HsfboRs8RtyasS{@DOVC#(grNkTWgL0{ z^i_q%o8JFZ>5}t!8CT(JZ($#9`x%0bgs7<7C+UQeHxLn&RdnXWz}IxXOCRJXS-)m) zxXDE6g}r@wI4a51P2)mW%R^Cv0&^}cqyD+Z^}Q8y&yWL}-1@yeL^d47?>$GpwU<{6 zV4`BLse`xY*HlLNaV?L>aj@`k=BS<-(>}roY5LhY8e0r@r@L_M^##LlZULSI*!7G% zP~@<#yIA#V0GxGGTUbyyOTd%zurM(A^uh+3Fziee(y)NsH?ZVh^2q>?o!3-9quB_b zJy0Q+My}?~lqMo) z=Gkoz!2Io}q@6FwCyXTQ+aooAU9~DN@Lr)${5qS%l+i+_lxln5XKC?2#_Q(i#aVgq zPoHATy!jzcm;WP*nLTOQl zkncKyD_i?qb=iJ)5v<7a*6j)p?c5u zcG2V?(Q$|KmEM4V5Yk9u-udA6KwxQfn)UM*GF4Qi%SfLv!esUmVHGGpM9w$a8n3LU zt)LJ$LS#d{K3Sz!p0Qs3u|l)ZXSDohKEPi3L!c{b4Qq7BCZ@d=<;$k{1G`GzQbN$t zQ)Y2p19b&5xR|Hd&^YQkq`$>WeQB}i2*gv&X4ahYpqW0nXuQ)R%?UG?Wt*oS zxhQM#yTrb?F!GW_Z{YQ3A7bFKKiSsX&+ugrO;X-&Xjm?D(>8U;&_BA>4?&r15NfMv z-|5l^;(0W4MT|`3);yY2pw_KaxLyDKvf*Ho|O@g~8c6kM++l@nXT&#Lrp5NP`n; zf?HG%*@}s}wP4j2>7Nrs(Yr2#4Kf}1&GiewZl=Z?2WHcb2xWuCxzlC*eXZzll!N%Z zq1R%Q^e8gu?szf0Zc^5JAcn;y*lzBNELLTnV)Ciy@M<(qcBFn9^1$y7;3wtp9}pVO z(h?a7D=?V24fY|u(*-Xk(rs_XJeZO$^Fw}|+fDBH1hlN@S;83~R+L3TV22J{l0cR3 zvl7)3SC7V>(9dMZzNa``Mga-9MIO<;xL&#^R8$O@U;Gn>z_lWTg1-gtlIWW<3#?*| zXHtsk9LQ*7++@m`MU>_o3GuT+&(X7M5}hb%I5|u3SnYU?>ro{C78w~SXFqKPvH8$} zt~qQMT7DY&XuHi$Akk4j9ge%WTzGmS$JtuChAQTGppu!ndePaJl{w)hw-jSn93a^> zw%oark;SC*vaVugzZt_;YBO|24Vaqs8Or$K$g?<~UQ~#^P~0QFQbN?q*7E?l7rRW) zD>or~DY&O}-YVh0G+MYDB<>_lLB{O=UME=*o5aKw+}xP=r&>GOX=-8o)JuC0EtM0L zYWaE&(XzJRq!t&O5M zs8SvBfc;4z%}4@tWp#0o?&G&MPj0hLUqyc2JDEGrkKc?%s<7KFuzA%8Pd=O$2e&N~ zlyP#>_#dJ2wqGoHqj@6+v^qCQ*VW|LwXB1l@gG@Mo@zWb>d=({3W)g0=f|vf3zS4A zKEEwcVI`!Z#R8-Ftk6sx+vlAV&(B#)>ce*gO3DZL+?%p*tAPPMep6&EgKx zxT43MP7oj5((RmqfE-37*55@yZ5Fl}_zCB^QASW92lOKnGQ_Jr5CShY!cYlA&BJ~> z1IW*7kVFWk!r_wv438@Y48D@XcO97Tmlykoc=7n3lclG5cYzXPj9dhb6vr>Tz3uv*J?20gZ6d?Hv@!8>o+%gN~W~lph@V?G-m>NNC zLu@FF%Fz7oQ$$y~>ue_VQ#JN*H!Y&j8K-6jIWz+ze0k?04)cpfnO^x}?P`-JO4Wux z+yRYvG|-Y#kprK<_HH5Zwz4ddI;OrN(Kp8D(jvPvgPIwIs^dFa8Xnc7_stuuu>QqI z2uowxtwLr-Ncp^|Dx@V`^#H@6P!ZfuFT{lmbbA9S@PNbRUyMwFL_3Pfx(`p7{RtWC z%K^u2Tk#Uk2)D+U@njd1K9nfBF|+$^7kM0SSr_*GCfo%7X(}2A-TrSQU9>VJt2@_S zQ2z2agfe@h8S2v2n7pMhC%zuY?9^GU;DA{m_U)W1b(QM|U7baEWe4K*6-dN@dI$Z{ zWrOEh9;_?fSkF08nmdsnPcXew%REL(3Af=U4O$7^0AbJClO}$ortkR>yo9adI`{?} z{d3WqR&g$JtR&N5LnfdD>dVs=i-M1%`)ebd(h1_^MXQ$?Qvu%37~7|n3t7b(Bom(2 z(xLhii-@UQCwl2^&f7e&&03e$2B}S&CSBR;P(g%A>b@=6u+i)LM%MofCcD4kiwd>CeHp_Pm|${wdAn;V&dE9Z1P_@sFge zoGjEYWSiDAMQW?xXNKO$4bvF8B_?^$=ZHVUt;{&Zf|#E9Y0frI`)==M7SMzcsw83W z{{tC8=DyU@w0`vmU(M;*67=m9PePU8hy>AL*m{jMIIgAyG`@6Z^O13=)AG@A`_d36 zUT70+Lyq_(e45QvG z+r4Ra`BHQ$1p$Xjk)Tv0AZz$#Z37m=a^v%oMCEC3fq$C5fJeK2i(KFSv$9X#^&Bh*IPfJ{PBhmj z+%7!21<2Z5$Fl*DfzR(onXG?+ZZ$vxM51fF=RN<*O&Qo(uA!UQ?E=pGkMyZxbR*Vc^l001BWNkl|8|zA6)5N>EHdp;S|*hCm4m5cG5lIIM(4$pJS#^?8^-EUK+nQttabI)O*00BJ{c ztc;_H5VxJ!+O+C;HUaYO@Ax`f#-@oV@UC8n7`g4)5=5?nG!HD7z_I~AHiAbpXx!M> ze$cNM|9*5Wz|AiW*i#`Z;jtWZ>jEN;RLENOu?&!JAA;?b(tAbWj1fR89_Ze^X=zsi zvz5UoPfse@oI1c*#2th z8ngz-N}GCPBsI6I0k$m-vbr`)Atw@C^q_~-8@3w*WQ|>IWKr8z9nApJ$gZ{njQ~Di zzaq9~(EuQw+SNwDSP72hc>M$nMVh}(oWi3WfNTjk7R7|5XMv;uMhp_~B^9JFD5GZ= z8q4wb2jEx*+qD`Z&06$00Z6lk;!b)y+B!wP0gB#10-;`JxBMwj9(gkZa$3M*2;>&` zO#J2tD#n5q0A$obw*tEWX;*cu45vLH%~Z##a}!cXk2Sw~z?QLT-u{lSZ>o@10BIC! z0wkqbW1!5e)dOTBV62-Lt_MoLGnZ9)+cTr?0n*IIjs{?BkMZYJw(>DkA1eW}(!MSO z7cT~9a90>Z1 z!ErSOpZWoO<2lIGtOwc?sVcG)`|tprD~E~0S-ju{{h`ZBNx%mWL+YDvw= zU|3!)mFI^X$h~JD9049U1&NB&tfXcMOnnRQ@{+XXbrWCz|Kj5K_o z-2xh`2uBj!bkjHR`Zh24oDMH%V0Qp~c0SqiG(4wD%QZR8#qfvG?m3O zWxDCnAsvDFHCWG}=|y76B_g37Bs>(03Qrz*j>CuVZx_(CmB+IaQ%lqO)gQQl)A40d zAQLWCq2(2r9DwK7VP;KKkbUS4o0Vkci04Ebp4F7`XltN4ZeQB8zSq`f$>r*AoH_+E z8i1>IA|F{WwWzDchemd_(Exhc0NQh_pu4zytn6x=3Z#|lXr(Y3MJ+r6ggo%ZU1Ix6 zfuofIX$u_7wzGwrXlp>I5f+pYkRq+Sj~+5nm_GPmH9S^|q(@a#_ z*j9xyWd*V5A0Z#wMbSSh{@XWT`)|E3{yG3UBzTW1wyliEZii)qzU<8?kWm-HoRtFU zMs?g6H*MxW+EE=h#@r_C>u@B&FaF|J2uF;^WycDn*|>ff*1rY*o_cf4(F5(z*Z-`c zjcuenS}BlQd&InD79A@5#ukv`FlUh-;t2|P?A}@Z^OOdMgYdbBA(kyEkY2WrT!KgS zvAleoul)NTaP772HL%<8*y5{2D^53`5umW*>$9cXJ@`k{>|sP z>-cL_B;x=ejf!NP0;3f;HVcrgfX7OJGz7;Mfnptg+D>k6J)!tCL6uf6(HJy$1*_Pq`&(SJc0g&rofcz;@1mz9D&^UNp4A=Tqpc@FX4Q=d309jc`V^la} z1d#9Uhtx}uTeSwr%Ho+yfP6@YJ0IJ8tLkIx&|ZQ8KVIJ&ci;Ud3=g;M>oz>L3LKZF z^f!L!3Z7k03u(M>ClH3&egTig0!*$6)v@xxc85{l|5^%K5&d&NGzLd7ERv}NM@=iR zvND6FHSVL4({qmcZP-;EJ7Qi2=fZTveXJ+XaO~wDK-h==3;QALU@1YTs$&g6`i^~r z>pQ2I@GZIqM!WFn1|VAnj+Fo@hkz95iPnOm9dO+E9($Vq{D&G$Tvp0svk+;us*KY} zbwRQZ*1K|a99?DZMi0sD<7efZRDxq=Y?I)o7O0TrLK({kOosp&Z89CO79g8Z9qj_7 z6VhOuf@Y3d79S(FcHh2MEuY@Gc^B>>*2qPoy6=>{4 zJLj=MoD3hl<@?j6C&jLvkn4u&LCAPT?#4_U=3{kTb$ybb!KaJF9ywg6V98$8x((a~N9%jv!JzPp#R@fowH6KHCi= zM-j>J7Ws)SgGURdmwe)Nw!jhKi8=f5SeXy01qLXF%;}=hsxp?48iLhvnAs~ab*aRO zYbEB-msr^*k&DRmJ&`5)qRjdQA=j(kSjADan;IIzo$>3{ZntSGpHm(bYIgtJ) zR|qWUKo0c6@({duachpHSrc))xsHa%Mgt{VtXhG_ER6CTt%U$sD8TtHU_5azXeCik zl_6gc?YGvKA(apSso0$Pr8wy4nXsIy@%F(umvyFjbS+4tSaA<@9yHE5$aSoI<&qE@0Dq@gp{D#&y>MPH-iH=Zv!J4&<#TvcAB94Z zVo~PFC!gb`mmX;6KiUYneF)6sf#)C8zy4xHHu=d5U}_lhs%VHd8-p|Px=#9v?4i#$ zf+BkuRmV}yWSwZxP;K0}S8{2?JjacbC&>hZodg4&Ajv3-N-)%oEL)EZddN%hy6u+G zxa;L^b{*-VBfd!uCr>?5{e7lZ#mkl~CtFunn+;xvR+0Yf8KnR9GKBY$_4dA^*FZ84 z-#Q|;XEUDdu72%14#n^*vyW1vwjNE?nmlCFDhs=S4)4+dx*mNWU5~sECB0zyp8hB7 zFB+T+!O{*m@>+=l?-s8kua#Kb1u27tHarOp&w7C!pPI$9UU)^X%a{(g7sS?0%sc33 zF3Zpz6x&xKGCBC(L3mt-jw$f1x8`#@J$c@>rCNpdJ~GtQXDZ1eO0w7x9If&k&Ej+x zPB~n#Ab(1{DkD@O3?}1n@@28LJw7xL?yu@h^R<1`4EbE#10gTmcma4+NE-~Y9ex<= zhRe6XTld1{=R)c&kjn^gG@jvL?rG!o8+(1^Zhkd-=cZ$}9uxgv3k8Ko9y!iKKRB$@ zj<)jXHfC1O=x@LESBXnuwBY5iSn&v42FFaH&W=`O4@1!%cohEz;DwY9QpGqLnxX1w z1dagl_@Yqx%)l|!jUuZ^l0>m&YfF-h>hV*sP#}x1*@@m0Y^XF=f0qU3=IUo0X?g{A z_FFd=>yjKNYDteTB7OD<(w8QY&SYSq6MptW2#rGQ1ZX+e=hlwuSgSy;reQ~S>r}^Q zyWvb=^Hm!|C1BINAm`5BbuQUag7Y8+o%IfM0*(NOmta2Wau7?+;+y?0g^pcyDx_7F zb*c;2#$aZj#N^cyQ~M>Bw@akDq1fQNcbv!)Jt}5)C9Bc-T$0R?mt41}CFjM=vaXD5 z6AI+Z--d*P>(;IU*{t#yOT*-n#6n6!ei^Pdc)ze!*JlB>@?opVruU86c~e7} z+!@eKp6ay%te%4CX#INF!2-qI+ffeAB4rB{i+Oy$FskAq6zt&s?>x%WPk-x{Pk!>R z+6tuAn9x212H?0dr@!svZ{?t}pRC#q3n`e1ZGz)mTx{oKRV{ZE*~4JRb`;rzB3ECq zX)VQkc1j#{ML`m z*buCaL9!Q8P3FaKy?dRGWA(C7Vj{rQ&-am!s!e^q9eCWhe_d2!SN3*v5{+^o-R3;5 zCgDs>B9<_gHy!-e;(UT|Ch%| zz1*6@CbRrUCwY#hU}iJ_u`<`O?m?rJ`)DZsMMOTNRY8qW)wNkXx)$K(7e|=O(Kz=p z;Dw-9JeepmQn83`r%zf(9(vbc_F>4IxC{B5D4>~mP5nx5$2>@}Jf2TKh7`-7C?5Lz zwj;|bidw|yOLE_Rf5GTzZ<~?Y@Ms?#-|_LAcrNraBs7o6WjwMdl6ot_F_RbYXb6tR zcj0{YG-)k`(%Z>++WJw>s5Or)$@l{usLJN&8-b%4Ko%YT$|?v9#@mFl#p08P@K!~2+$pdOSZLstiR&AaU$Q{Nns_3 zl<*=d2bW-P+Y|0kT|SZ5AtW0g*=0sG!EI>e?*kvMN7!V1lLNzfyl2N@BavLTDL;8LanpZ&LVgi;+7v^GOGrw^u2?ebV90lYVA67+DNfcvj zlXhA^LFdvWldE-VV<6axB3BP?MT;uzS_@mhW&*80WNA+ueV5}W9yYblHzJmS;ja3A zUFXOUkPLxQ`uqvxTVFtWctKdxM$s2R|JYj~R0Ft*7do~(vap*0N2o8L=}T3^qLmHZ zEI2}IEo{j}{#v8LnTMKK`6OF}CuW=9ue}5C^Lz5_A5i(SH%7K&m_neVrDH_Lu`Hc0 zn(7uCaV!lV2#O5~YO;tXS^^;zI!9q`S~Lqc+A%E; z>(db3vT0#^bjT>^;SlnvRixE4#bOShFNC6aP*oo%CTFR2_f6f$Hoaq{oHm>rWn4VA{qu(Yddqt?EzQ5~&pX;WaTw5ttw&b|WKEIe)u zaDfPK*_Ohuw07NRD{yS30x7u^SFEh-hnk$&SJLq354#7(D@Mh(>UPEcpw-o@Xc@iPlkuv?R(nFqe)DyF6eLj^ha1@Li`|}6#%)I6bj@* zLOfy@G+2m(rq_X^mFie1=9ybLN->imn~f6+^%_=1SGMLcQycB5j#VdFR*6e-cC3#R z)uXnLiCK}1`QvdB>F5IN9spT~^QHfvz4s22%}_@1Ji@ zFg~yy-hBhSWEBim>B6VtgFGH9#S<^qvg*P!e^^JjEL(DBN)oZ6t;Vk+Vl{<;kDj!rWXR`J9h*CP1-Zjy;d3 z_0kP?1kw$4bVDB-QbLb8S1DjkQ@@x)FS$&St5acwywV8%23A&Lgj@wZh=z^@O10` zv4+&qY8U?<(i0sOj}LA6jP2iV9sMi9iPMC=@GI9r*xUa2b4wlDA&^1`gl=`a!UG+2 zC4TI2lR(;8mlwA7!cD`qgwf7EdFeV1ELA0=2|@5a&=!G=oXxZT+s)OEX@)?yD;^sW zNT+s$ov^&x8fCPz4gL1Csa*nTrH;#DGB5E*&knPY?+i-`{@cw63*BV;J?OIi78?wu z2?o=If@wnG6n%XQbaf>Ng;RvXDLkHnYXX`0Hz=HT|M}d|M<)vDlsHoya(<8Z?;qxo zF$IKmAiWN#A4bwWEwTBTl|{8KfsE>~(c%GeB9KMP{j?Iu5`6>^5;;77_Y_iIXCS&B zNm9^tl~j6;Z+`Q4xa+Q+Ki5k?7mtb(3RNu-2t=1Nsaq9~#h^fPSVC8TbF<5>6SuCu zgGiv?6?JTwBx5HoBn59If>3}=ru71Cwn;K|zp%6Wx*6f~^4J`Ml!Fr@QBb-#qB%_GC=Zs#{? zRY0l{n0lA_T0&Nb)b6>mOM!^V(4mhhb<0xL;Hk4~=;M?jcKAr=gV!W$ffqh+De3QR4IqH0B3;aDjkcOyv(iNq|5 z;%`eh>Ix)>C5pk)qfO=D+zS13OCOu1j6V66+t9e+EIlG0oHT*WS#vA6V-+T_3D!qF-qACO?R z4hO#o@j7G8>jSUX6o)<^gu)OAl>Y2D#bZ7DN0&?>+Ub?3xcLH*wW)jF|1!QOtHr!J|9|6Sk3wXZrEPc_nc)cME zLq|7KbVWRT@8OU9TtJrh{1^KTc=uQTCzpi)Xsb*&wQeBhb%aw3Xjb8v(@+eB{_yIi zg=5{*uv%bZ{se-UXfD(cY4>U}maVr+9ZT*EW*9b>2Gq5klV14u&oFI813q}sm03Fn` zus2dE1j_=^Di#e9bkTe*|F(zMQj`^k&Xsp!HDjWd-VNO|UQUX}QK+k$cVtehAnoNx{-`tIr0 z+}AgBApT4XIn&3csAE|urZIbI+awh0)1~|2qN|&rgXB_c>D_SajQQVBuWj#sJe_cn z5^AI&|J|lCv4i8Csgo2smu2(cOtxJMvSQv!K~l;9DNjEM&njkv+gbE&AnrBO<8yL^ zx2(UHZU1s**3zS=s_s=A0@C9W{2d>6@4k+|%-wtL>dvvNJIC>*=GC%4&M9vUaqKhEhKWE7A$LU^Mo9^HsYuWVk;(8sbvEK`8MUxV;|_X6?_8&ekX*jPX= z4cS++uoaLov2?O+K|qclgpIF+r~#Q3S9$RbyOF;1963FUPxK>-GCpq?2OdAeV~-s- z?z(H&i=IBdXm7w|e9^e&?>^3WatYKm2updyyg~2J5_IX^NQOC6QluDHCK*okq3E@% zl$APW3rXhV7n>4}#vk4fA#+km@dACH8+8{pF6(M>xZvMJ!n=x;r=OHEKtZbSwfKbg-?>LW zfRu*8?}C)>?AR;2tgEdR0lBWvKy>-f+{8cHCCSvYR@~RtSVvo^&_?`YjqnSkxcN2z ziZ@_R_#ILB-Kww|HzHEgVIf%&nblA@f?02KxVt1A-vZ?VC5^1ob-lc5ItoZT*Y)yz zsCHr7xvrNdS_aNM3dL-#>$)s5*W3+D-H=<^&&6b(;J=(?ptw?ELf4aoBMJ}y+|NW|zuMya4DnT2TrQrNZ(k0?l}T7lWvd19$4bfaxtotNFKDEPDCI^yX$|oU2kkZcFsIUVQU9fio#G*UQ|f zf5Q}8N*|ZHZs%D48#p@fW)2O#ohMiQ8cz?ulVkmF;B?PxxYTt!v%&4ey{pN}-DqOt z%%zWO4=#_9%mxTt1hLqe?=gA-PM&q3h%yv4ktb^f001BWNklpPGB~Wd?tX_oFB!qj-UY6zG@0C$^^bbSX!ks(;cV#&z|Osyl+jrF9MQkrR@i-Y@G=Alw ze=sLTW)L*!esm5oqvBU06~giI%ux!GN&9kInw*xdI)Rnr8(qdi^#n2g=ykAjReb@_ zZt4va_4IY?l{S+9=i|Wonkob4N*u4z_ z=_=l_1p(P}JlFEumWATj9+lH~6gl#m0;lgNGPXx0Ga#YOX!t&Sg4|N@ij0rf8nx8C zlLGSe1s8MHrGWIb=qSh)%^1jv^*I9b548Th%JGkl%=^5%7>jcJW2*wv8%VM1wjbI4 zzp5p$DG?d;+l$6}ML)8IDqKf`;7LGlZ)jIDyF|=+k zo{K5ur2<*0KysBxreDNp^u!vtt7{hBLW)(r+ZkIphGd9lsUY#n4GTy2_3ykIhP&zt z#@=sUX7ID4gbvQ*nX+GqvV9^;+boNN3e0FQBtr`U>+3K66W;tn)BmoX3%gzFcyI$u zbgx`iyXBeES{MZ}-nr*yG$;qusta4#N<(*7GcN4UPwXVCw-Z}da$$?TAgmlF-MFkZ z1#)47WS_zOdX1Sa8WTG;7S`${26b{_1GQ@35>$iGkvL*n#k+5k{5`8Mf=Wl{T-jw^ z^GrM7!mj7CuG#2axUjqY2A&olUv&@c*)nrc9i)OmA<{gz zsdiUa+u9He2O0=i8+W!WLQ!3beRSLR-Y}?WAxR!M3Je1d4gh=Cb;ZhQy0OLWfEU#R zedF~9gro}eOvp@cP+_=E0B2$bG!;BSNI0E%GY|xdl^OSiE{VP)=0QRfB_zp9EH=;c z&!6S=>HRNC_w|d4JX+n=Bz5|}H;t6h5Jib#uy0v7UYvfOVzE}7XhxBFbqYyTn9Gd0 zD#ha{99>xa{(iEdJn?lxW7=4@ndaiM#2x)S|N1-{8iLT;aBxE%>k&vN>ez@d+F1p4 zTvo^iJe6yjK(<01s|n=4pKfo=R#U};bzE>|;n-=bM;+U;2Gp@x0=Zc9HkT+2| zxwXj2U2Qcd*L5QjDQ$$FFd(=WjsR&p&qf^heCe_|ygIwCD^|{B0-5O3Nc1)KqIt3c zH-9^Tm{d)XXm^z@)DLPMqO49r-^H50zT7vDDySt05+vXs;J|^C?Av$bMM)oDG~#h= zVZr!?FaHIiA>cU?ruSh}I0DOkrpv-nRr3(A--HT2l}m5XDR`V~98^%pox?2&N7NKr z=22pATg33Ytasv2n#b05MB&5Np?`QM>aHPDAG;T$$Nz#oj|u^4Db3?Q8E;xJTB&2U z&w-k@ARwEGb#$_F4M;cCu^FN0#>O*>eJwPZQQb&DwjA&1sG29xwQsZf%p{3n zd$;rAYMo4%Vc&b#h50U4mJsQ*fP9PIxJZ6G0@97kx)}l4u6WE9;fdogvqvB}1#+(a zdst6Ex^-FCtba|!Kh|?sTkT}byqXfS8zJ&e@ckFF?{%}5cG_+n;vgHkuB!y3s3n=& z?EG)yi775!eufh#zt4%2-$(h-Gw#;-@Q`!)p4_}`jj{ap9xhKEHuVAE^K}slMR?!) z{^mvLzJAfTt$(=I^I-9t)5s4m5*{-Ui{`T~yQ?dNBiKi7cX{9gdok7oVRaA=+LwY6 zG{dBgzx@h$AM{k!bvtZSeZ_E9rSc zH~1B_wNaEOnhP>(mO56P+7%zFpd-9`H@+pEfiIk;=i6hBF6l-#cfAKSQ0E@0K+*sg zB{SS+Si7->f|s5B@RIXJjaYPYKR0Vm)UjRnb+gpbwfov8!`hA2b76mTb`zI#?Wccl z45YZNdyfCQVU*YO%=6{deZ-7@Teow&)^(uP4cRFd_RYq#yhr~>ck{Rf7j`o)Ydeux z?ZWoR5NYH5x|9i#D|*NlJS3kj@QNiEdKw0vgYGjBngTfsf(C|S7N~bE_GE)~p#qOh zuOwW^W4-vE+}zSZvXJ^I=1Mht`LrD3WB8>=hxtlMR=kIa#Cy5aYqzh zyUu*A*r`^cr?nmZXgm6mPR?C%;b)Ujf9)yxd+Ug?Tc$Ff}z zHuDbkzwwXj-i0jcFZlS^)`4{Cg(Z-UYCsCBT7UnWFWc9FY=u1bdGg#CS>&bN^K1{r z*%+{Kt4#vCPQvNUQ0!bhqIWFgJT*!La_O&MhBnZeqH;qW?FgjXI*`q}tnCP-wGL#n z)Nv`}T$fWXz`s5X_w6-9*yL#lPD9@@%Tnuud=#>MkX!?qLCDrgnyxM&ZdVk%sm-_K zzOL84(2cv=jy~>aqT7|8fXqYYfBTe`L>Qlo=TT0a@q3&_DWAtIwE6m`=Y7+c+G#&*@gHscisJ8Z<@S33kY~UVMNiK6_VsZ@dlZg zJGmkp?Zji32Yzjj`D!gD7R$pYpEqC2H#FqO_`?Rm(TO@*g`@0&VAV=C?|~;BHb|#} z;7+2^Mmz!ti&=sn--oc+nEtgQ9<_l0#m(KQt3$}=VsLzt)#1&^V(Ump^$Seg=|lCq ztt>3Stv>VBfjVv-{dDW%$=ge^`+AlJ?;$pDC*FmlL{I!JYqhhyb8nugud`#^ZLDLn z0@Ja zbc6ZgZ&^SdEWWIz&*ip|xKm;9X00qB2P1}4w2&*oSRqV(spX~ozP-(rj?MO(LNd9= z{O^3pE8ty%b9ngUlS8B@d-449Ep2~yT^1a>&r}x!cWZVHA6ZxLo}-J=%au7qA4DJ&AY9?*WL5C6aR7C zoQx&vCSBJ>oI7nevA9}J*HR|~+f!I#^O?^gY3_riir!x0{kJo{;cbj<|1G9A{VK7+ zyT}F21yW2+;63>jVp)ynrs0NRcc*%{w%TQ&d^7IrP9(`Vy0H5|=WX%zL`t;(gq^w5RF4DYBuL?(;`nV zA~cA5Yt@B)kA8qx8IQK~x!kz0+i_V}n@0xS5aO|3E~r_#j05A!ZtJrlol$h`#S8!c;-C$LW)Ra z2vJm!M1@2$#&ge|=k)0pNAd7$+HJl6XGe`U{NhJ!A*&>NnOQtnzeb}~I67I>3%|V! z;mc<*-u%+W!qJX8RtrY}#bM@O7q4|+TZLmI(&%RM=<$>O$UUwJq#Mdui-eQr1*m#~ ziP&jYN4GXjAYHqwtF4?sQ$--xU-&YfLd+FejQ8J0vio|9-ueZCgQH(v{_j{0KKV2F z&~0>tKsJ+M{X)C19T)dioqMmFFT-<+$XwAJV|Vw=aCh&lvkSV}H6*dVN|h90>)Eym zq?pwZv+fGmx8lOKtL@m-+-=~(Zr5d9Z9c`Is0k>N;hHYGwM-r!KGHmatk@`XW!JYs zQY-N4*FVGKFEiIFAuAy+C`hwju}mHplM@uRDwma&I#vru+4)vU*=d5_ucNKM6(iW!9Cgf2!1VWP zg`?uINhk&kTM}prMVL#PN6kj0u@QBwB9KlAG>xdEHV`2HlA#L9SYwASLwZHH>xR}6 zfozy0(*%KZN|JFRi`mddbh&~S)osaA}#;f|L*%D0BtrXh+?E4ez`BB`aac`MG=KN4- zE!@I8k;Qj8TbAhE!?pcx&i_h=YnL5oykZllgH&b z+ya4gvUJqIBWZLG$9XP21d4$XQPGC;6t|}-+!QB&=Mwps#whMcp^fA)f*MFTl1gj) z!tSAQHts#x)@N))AN2&Jz5<3r)P^iNhtS)R$f5?_E;N6pVE)bxXEgSA$;^pOtlf!Q zL6I<4g%PKc4G5%wK3Mud^$27x4tdMoTTLL9;EJFO$5ser#D{Qrj7&C0INXmc`w;}t z^&*!pO}!|RXRcZDxbLy&jR#(DPaQQ33iawdxgn0_Nnc3Neg13Y0srV{Nr(Rs!`IW8 zI;ttN9a46}X60*8>D8|x-7!b-DGyoAL-Duwnkz+zt=R+`b#;j1=5Dls2A|(T9V-{5 z0<+0Wtm@iwWvQbLf%NW7F*5zF6+$?0Crg9(xFU;gsiW0SFPPWX-WPsu{O?SlX;hxa zD1wmzJzWxsF))&#XUxAhY=A?gSLYW-mzWs)b^xHQr6@;qGu(_B7dq2qJ_&nt# z8LKVj=243d)X`2jI#5Tkkf7(t-;yt^_;n0l59yaU2*|Q{v~yE0|1O(E8N11?=6DT1 z;=5}*~Ex63r*H|K3pn36)7S|MVTWINn$&4n@t^ zu0Z4WKX~55`Pot;V+RCeIbp`mUbP#BTaW5tW*A2Iz|1O$t6n}keG_r5{o?e}tOqe` z5ZtG+YWEo;t78NQ6K%cc@9wD-kZy#dl`vK(&(vK*6Sc?)4be?Y)LXctzFU9s8EeVr3b$*2J0R&1XHFoI?1zNr(gfD63cR zMbol)JR%-%lJ9-*W7l-@%r)t@esW%X@Z3}90kJ|j>S2Z0TQ;DbnXWxjDY7$hv{}r= zLW1sNpGPXDu*SR#Y8GYoAjN@OK(1QcHKV?81p7pp>PbqLmuinQofYt=E)wgzsmUWl z6Ga6b5@_raU|iX35>D2yCY>#&8oIA*+7Py2ky(L(vl1gmWoGg*=I(hX*)_X6HVz|) z{wGp;s_x0X)dZT=lkI&rC-=3H>im2Z6f-t`b{51jD4aAe^)l!Srfb{C11(lD^6o6n zNfrFFkQ)S}O`nRR%i4AoZ7z|o!TY1T^27foTf zDhfMRn=a^^H^SB-80j@z1^Qg-=@SzQ*@CcqKoy1d_S|Z>wbiEM?YXb>ewbPd*&z5t zjXi-`j%5bOw>jvHphooJO=K?#Og%Tm_>&vC@aPU^kF6##)`PA}D1id9S9SfKSq*Nr zB&Ss;$kZKYHpw%M#O8des%2pvlW&A@0PY(u^8IOrl9jPTM5f6*+-Hp1d5994O)l$cZgK)H=j$$D}&#})V6%u4^HasD>i2uar zYN?}|t}h(HIZ?(U9Pid1L(y{-@;UtRvjh`Oq&}O*L+Da4jvSWQ^@zgAVVTIRIR&)E4B8hCK`hgeaj485 zLYY0(@aM`2G_fW+VRWhuVRZ+ZcBrGEI9~v{0;E&0l*4RH)+?bZ@mNV=SRdWl{S`3$~sTbdzX^QGU)E^NbJ z`ys_?hkf>En(4+aTht+zrrd!hYt!Xy)kX7l=Sqc#nVHj+hpnc`?B9R*nr>Zq&5_45 zM)bj%6EhXmQIB|7de=^jzS1eAHwfqEVWBiQqJDV)Ui%owD(YAkj$*;&{!W!P#Oxee zbQ^}hbYdyyVEmhng`?u^f*vsDxWjmofRJT2V=On52_(*v?70qIa&hDnnr~$y#!et# zdWhW6EzMEJ7O3O$!cr%JrdjG}C5&A&BHnc6Ms++`N*&D=;rTJRVWhbgvjytdDuL{D z|7t6NbVD7>6tVhhWD9Y#q=%-1M>1Vo9ocNRfLJwUIzb@+eDZcOdXr#Ht!Pspb_#(ZCa0QO27TiSVL9?wigDGK~aeCJ%Z88MKrE z15-RkgN^67;4}Mb1B7IgK%PoUjAuo6l(?_9HC$jq9_sV>gR_){3(YHegDBve)1F79D%&1{PFvrIAXm1{y)Gd zji0i3jJ<6e#dTo_Sr+uubMTqRp(g;pY3UqOhdO%V7l@pDXjwP{w&Zbn@$>bN-Sul| zVi(LjQeQZN*-=LJ+S10C=!Y0!C2{3M)nO-8T=+Lo(-fnd(e-{9 zTi<$#k_NGkwvhEsMwHlV|`w_)7D&Me0~D*3rq_#6Wgh zJeq?TI01sXA`m-ixdO~ATE>-N#=^BLAf;~bT~~*cwHO0AUkvcNQ*UcS4LQd^ddD(E z_sw8e*mxCat?ah>ZzJAC+O936L>@Wj{E%9SfvgwvXth>~h-ZTElh>NBR% ziC3l>W*f{!A-Blj=x3OW>~!%YJdVQg<@#gzSy@DqHizn9!gOD!Bb(d4mu1l?E*>@K zM_>Rw5CQULhUwXj%?n6}4C{uvrkEsKOfDzTIAvJNc?d3vL}mn6g3(bpg6X>c)Q?&g zkWLxajVzXfuN;BjxaEZtjt6c4e+G8XKw$C8%*}XpIFSL1>GlPr(xB8)*@fM#fIO5Q zZtn}%sRh|itfS*0HIXIq!2{&}Y&U;ZxSgb~FsFt}f-%^!-}4&It=!uX47{;30y3Vd zKL|8k??@7jtq^RX2BfHiZu4=v6r+z zi`|^~&Qv&ju zaa%tVR~}qAIEhxuXqF`}dc;Hgm$qPdCGb?vcK^aj))`+S6WxmH?{cJ$WjD2)nW_1a zdd(rCKKQ?O!F`(wtoEe1n2V6B7ff%HC}Sra0cJBSyR0h?NA=v-E~uk4dl1j5&!e3G zXN2tR3P>5c3ByyGfRYZ;1jM_V>#|W**ReSPxspJ$u*X9&Brem%{__&UqY?w>B_gx6 zF6l-#=W%c$w&=pPtLxYlg~`IE9_Y2vtnDVr9Jm244_96QFMSro07!9KPFkr)EaEoe z!YJKhpJI~8R?O?-OxB7E`*SldS?Ma%^2%!O!gkYD#!*Ch%vPL*ut26?Bt0yUTq9Bp z31mY$>faya%-*$JmU@_1gJg71N1x>%2v4w?m9}xB2Ryxy8Ex-5b z>uRCXtPA_pUi;;*)dU(Tr4W9+vU+|a_qA(#yP!B|COs(&O^~KK zwIA(7rEZw>zsIQ%M;_#t+W)Vz8{0OK#?EzZL7r9s7eVO07q+A8dimi4W5A#W*^BjW zfSQHyDnNi@9(2vTdL)Fi8CX~ubE=vr7&L!xFc{+e`HPH>UR$*vuZe`2N1r-k95{N4 ze8L>rUQZY;tg!Ts?dV}o4Rw_AF@hJq4M6Yw{<_4`8sF%aI!1l)p5i5kM)#8H2d-zW@Lr07*naR6hTWrYWQ6@F(y;@@71TKe0?3 zmluG*0D5qs>K7GZWc-R#$K{Er7Z^*8QV4)ltW0?6nii4st*H-N>R3%6zxvb*LLII4 zl_RwYG|f`S12@!Ob;p4O+6JJ*F6($5Dpe+sS_^_z>TBX>n@pmq=_=DMfh;A_xalgR zOHd35WP1b>BO*(iMdo)(%v>)qzf)pqvq)k@B-bOLmO3M;J^@1nV>|`_&loJIS7e>F zlE-BMzs9`Uv89x|ZRkHs5{;~aT4$cLl0Z{U9hdK~?8hs&9!JTlIr4+^%ojBN{MnUt zGP?~H-VEa(F<-OW+|$QK)Ujf(%GFPxG3t~Ia!jNd`LrtnSx%x6XB#A_Wej*y!#0Xf zw_KSeNH3VevHEwuV>Luq4r{qwibBEMyLDaR$3K3KpZw(cYbtr>njnwowCIERL-VL5 z94CeY#QyJ{E6E)e>L})8bRYf<-Nsx=aK*@m)}ng5E2v{V;aHD4mW5+EZmqmm_(YBC z!iyY9_SOsYo+ z9hVoLssx%w!qJH+y0JT-tXvI@2#AT!5J)HLSWO^R-K-JU6m{Ha(o)xlB$ouTsS-WS z5Xhx2Jktd{<5@J1`FT_yLyz4N8O?5Yu7TDbnLsd(e+6UR4D@0PtfE;rckk!dExdsIW>Lt)f zn?R_OFz!eo>(NImft*PpT$IXJ&6k;l&;W>41H>=F@MeUA zW481WG%jDB;`Hgqt{M7x)sx5lPaH9hKXim#s<2EQ6Ax@!5snt>DCT2C5B()VrjpeU zUiuEQMI=i3cwOPxh&Yyo<6_kTB`O!}31q3GOxaCsO^m65VJ=R_!{@(&QiwZ}M(?v9 z!~5*VYAB=aX+K=25%I`)!%QaOPiAvmG+fQL+WU?uRi6f zgij}^%_?s5+=sOEL0$YV1EvTj$Ib_nF2Q%J@^tooCLxJ3UH*ZVs3WT?_mXd%sQ*v0+hFc(=4D4CWfA(q&3%k= z{G(l{l@V+F?^`Y9WB3)++t*?Qlv=SEP|Rf6;V=wGpu6;?23}d4Rc$pvgYl=pu0!3H zHL$h6iawe@=kbI&aNy)MRl4V@CXZuNbH=?Nco21R#kFg!>mhTu#g9^=j%DE}=3_NE z5TcexDa1&1??O@&bf5T#y28=UWnC>Ct<+kJ` zI#vrvxABisAIJl(eXd3X@}py~XzQN#%Nbr4xy(EIj`04$XZXFjF3vBl-@#qcr z!qE&AuE73rY@&9N>L7vqU&6!n@2dv_o0=z(Q!$B&rRKJwM)a|nc*j-==8|V#dK{valhBDi)~x-}N*Za6kCY{INmOS_$M(7(oLu8o=oGLBwaeIuxmP!CCEru5O53bbil#8uAHUo;`YD9dE0aTsbdh+#;&QT3$MC6tLd-kwhQXK_ z_1$~^n{5-vhCeSC_&?OTheVR5Tfm6;SbEQHGeM?$vspTW zvUH}t*zzm>Jsal@=Pw6p&w? zhosh-hdEhi#VQNP|92Jiwzk~Yb6GnHNVg??+$PI73CJr|!UteF22(NnKG;$ddVG^_ ziA}yG&gHrt$)l@#TXJ77rd^a>SnphRWvnRhY44D4Kc3!Hg$ zrOQmk0}Dbf4bz8V?d>2}ZT?#a&Fi#Tp(T@9T8@46czpc&um1%T6Z6LE)qPiW>7J|B zZJo#tJUG|mrI5^H1U+QlaXos(TSFbie2lK6pGVM4%wSixugeSX->b7GtZ`Gn&Y@Yz zIgBrBqD(#Fn02u)yd|^5ISOdFo5T|?Y0ma2ymzOLF6Y?WljFHrGYO*^t0>Ro#;Ox& zoVv+4xuxsb=shoAmAkkeQvgAS1J5-*>l`<;AWDIUu5z39a$*7(+6oS{0>HbAh z{qG>t`$qDiThP2C7@|W!ed+|vJY*&S3NW7$*xC#I9V*+mcHG_S&`JVLxtmNY)+m9d z9hY^rRlBgQE^9mY^ILjP@bbt-w)z+8mYeH;_}>%1y7C9F2;2NHn(K@U``AQ-9lt#n zb|aT{&3$dch22cDjO}=_abdUXwyqG64t=mMZ8m#bHzOeJ+}Cbg))lnJt^4|pqsxgh zcIMW7?f8tBpzkE4ax2L*Zdc)gYWBbO%76;e^&m8Ht_jI7BKPvWb9=#Kz}h|->4D)c zQ#jg$7HJ-VH78{P8HI(uI&K^d7&gG8Ky(CR<7Stcnnk?`x?1bL7H2>of~|uH&y7K@ zh^DKA!+q$wiXccVEG%&B*kw+izPkHfUzKj_eUBeDe)`xE7NQcFSB{W=^$2>4IO5=!9H61vf71ZZCXrFMRMi7%FL! zE+4#Y*A=9WRTu)zq=bP>6Z!UqRRq9y1xi}@z zG%5_<3V|Fv0EcIXcw6LXUH5a#>s5Yl?O&D&WVRpE}MOu6FGIhkV3JZ``;ps_%n^zf4 zm$qq|I<`n4+o6ut0t()$4XCfo32A41v^uRcJ|b43#f{)7cQ? z!fsTTaV)`@h(93^NqSl8&Y?*TbIt(DpGQ)_ABClhwdBz%Aj88e<$Lr}Pd521#}6A%95_OLj!?&v4WAO|`^eLzls6L}`cQLJv60O`;`qN7v+zF- zw4OlI6m@KpKz4#US~r*K1e#{4qgH_IqM0~3`3#)@5uE%t^L6qWNR~p!lBLpYL;F?4 z%oSL(?8sxM2;^LoWw4qgkgbwOHw1Dn+1c&qg(Q$li{}e#0*#bXkW#Ik0v(HoyQGd~ zYc_cXK;Bma*&2&`8!B1U;Mg?i&)1?#Em?Wl(#PH`^1|PN=I(H_G-QqsPeEeGsXjbT zqJ6$f>V^hu6KH&v4O`8@<)5yU?~xC$eDAm43ef=R^h)nnN%9~`9`@}!%>MmXZ}QAl zM;?>vD9%Ij88CD}gPv_jEYfx4f7uF0YbZpQ7v8(adM%S#)j)_j z6$Ni#+muP}3p~e-!ZA+I7m(#S^6T$y?_oEfj@1ORQ3;=RspG0KKjDNzIAIQ?#o`?5 z^OLZ5O;arl8`|Z+t~|0=t!3>H$fl`d^=eV4gioiaqjkfvwlJ*Mj<8GS_7&*cK0WNgbC(aS<-W;1wI1r;f{F(j|Sg75o~^%s^xv$lgk_*ieWo8h?MG zh?qHq5x5D&`UTllFASYAC6A3}?<9xGL%6Rh4bM@0nx9y}$h+`t|ET&qDq=FiHzWVhkZWM_}p) zw!+bRI;s|qN}ao~tsCy{hg;XT9^Ysu93>l<$Gt&#C<&>mNw}D+rH#M3-4u^@Tg@ql zO>%3Gd7WOYGnvEgN#S~VmYcn2*`};y|4o#Lm@<19W%e+|f$Od)b+lT!gikQ3bc8yt z8gpKZ@>-UBE(JN@Ge3skxYykgyphG9b@gM5Su+OmcW-wuHd>~RRzX)P;UlE49CfrK zko{puj6tAOgEun=sTs({%r(0m>#~&o5wj5eF~sf#y#tSPIm^0{wUUKhFU#63fvl5- z-AtCXTLNj#!cOK|7mku>Ad5P(WZ+RWyt0lYnscF`M=7cT!WG!waz*&&;Ud2zIeWDV zUdVgQ*Mir~ATPMx<$kMpBgH>ZY^NK1aioZP}asLH->k~)^{@QlFW8Mtn+ z@mf+%9j*K8bc@3=tHR8`z}k0#+yu86@It>A=JHEOGoL^k{9QA)#Yy~^;vSXKxGNLL zliTNOGq7g?t2ewH|LBx~4J+}Ff!>;%qrd5T=Kt|Yf&+U@P3aAjRbtD9!|%Cg?^Tz1 zeN~aiggX4-g#f4l8PfY8W5H;W2iD$0=<>G^^t>Z=tQL+=)Ny$sScJV3W`{AgpN6f% zsFpaF_ac|;U_QGHh6X=AXVG=N4M0mnl3tOyuv)1QM!rw zqFK}9CF|QC@;<>nxep19I{iec*eYvll5t0Q-_4z zm4EIOfqc5KmCvQ`bN>&TC6JA>tlK1z&1P9QOCV29Drj}$ltsZnkqx|xM$o4c@)d~$ zigX2vgnTLiuZGW~p-4t$Y^>CWs$My7pdu_XpA=Z%@35(ACXEbaAe@9~40`8bF<`Odsv2 zV<+fiz5t;BWJV#h4a6q;Kb%OynG|5;ToK4}+~dl#av+dJ0Y<9EkW`g{Eqf~IV_86k zSAjggLLUL6H;8y~32#xsv* zfb5fDv?Rc$(oob*+W5ZR@QdqBce;~}HrPz-xvz_ZH`Gm(DZ8fc@|_?m)+*wOBs?=^ z-GnoE$DU|SKt9@9td(5?O+D&p6^o4oqm?=)i&b$pi)q-{Zx$(R)vSp!F74!;z|E^0 zyRSPz9j&W|HG!ttZZb7v(qnEuPFJ=73rRDfQtEJ&cG+cJZJiL1pGrMopD5D_0okg{ z+NFT(l*`&qKx#u!I0=(WG6l^*Hw0u!$LG-q`BcJwQy@nCMS@-xzemF(o0PF(2O@Dz zX9=XVqsQ^UkHABL=>hYJ)3(&RTSk=->%`lcyT zw~vwCJxgx$qA4r`s+pLmN(id?y!@UpY0oU&`CQB2sZsKb5C`vqq)GO)F4 zg*I9VW5uL!K^@Cxmwl}V22lHV+ET_>`498P;8X1KjM<7tJL=e$fV{W_=N4NYD`f#0 zj>$-xSVJA%(nVL)(Q0RB+lzl3|9jWeae2(SjDPGDb*vGP3$U0n7ynkpVI!N4m&*5O z?pqkTwZUzmIDm?F1!P5S$4(2#X0ohX5RmO;Sz9fw!Bj!uTvp^rTIBJ#$d|@tZk7zZ zMHQ{667icP(eKevO73T&)n_b3EMVZu2uMXTH<;N3jPy3F5@Tyc1&(YtSnrE-D%(e) zj!Vn%YG_?~ii2tLn-|IKnXRCexsfG$PH!R?n#72SsnULH8=;XS7mK6+l^-wP7!6od6^uKeB+@O5_K^m|qg zci93QY%v*qvakG@3%su9OlyZ^pGt8!MSh2Q(Dv-R1>fi1jPk?VkDX1+GEO?ISIGh-m_vari>kG9aJx&~yu?P~2U=Bd-? z;AzUtLAF-N=U4g3j;w6Fvg|>Yz3kg}gnj!CUsajcR|R=orjBbOP~LsBaoe zYymgrOj{w4jsEJ}QFO6F@b68|tI z^O*^mPh3{`)R@daOv!wGR^s31C4RCb@q9|;Y*u8dAP`ptiVj=ht5uEHsXD;`%r8N@ zlYQHTh|N0RIRaUxj>{stMfdfQZLm-}8${(iWBCYit+k9y@0o6Vr+Sd^QY>Z>V_~EV zYmkp@MLM|walQvZ6Tw$Bedcw{&VB89$m}TN)?~>YeXMQ-U|@Ehf#wM04`*Ph8UWTI zkkxUI)dF(aeQg)_xU8H?@sEoapy;sirDh$;$D9E|+3%R1uG`9IbtFsgRkjZxTM$}Ik^8#_ZB-4Mu55Jyvh zfG}EG3ol%`d%Lv<_V&r-*WSf8`SLPpTrbZy{azPbhzBt{PxxSihN#a?w)SPZ)39|K zZartdb|=7hD}}ASD&6 zE_aGRhWeXWD4QgZZIj2cy*64~1G1egYdiBuE7y@MTv)~XPk9*msgHM`QuxBO#P=5^ zo=u8e$_b>~)M0Ndt~|J#_k!Qoq30Q7kXA zT#5RHj34Rj2+EId#ru`}@qXoglpo)UbkVu0hPCrdGt{ve`e-+=GgX4qErHzSw7feI z$oQc1!ckEJvS9AT&L@cgY`f3?1gaLGkc5RFK~OP!Zimbp*~U7(imelbPFqw}j$>2TxaQBFF7nmCA=pY=WI(JCW zz%viUZrJEjZ#9Z{BwzX*Sr7t14uozH96S)^kv7^w)~R<9UYFNdKX8Iwo{6SNW3_NR z=kjDKrDrJi??Mj_wND+p)3A0CHqStB3jCJir_>7`vjTi!V;KLjCB%GtyI!KOh-$d3 z4%|!z_W0jJ(SbTT1-aHCkYfqBW5gYGtfq_nob1DZ(heLh4 z1kx!{rV|9xRc%Mtlhh{uv6U=qPbu5T?y}nEe%L;QCfx+u5px0Ru>Do8Sg{U|48!hCmtu44ik~*vknt zGlOZwkbn`Yqvs^Wtn@N}4s>nhS{AT5=z9r>l?#SP@e?|^qKZY)towL1(Z~PPis&mmuQN@yewJ>(ztzygPzePdF+rTvY!*cW)l%#(Cd)eyVVz0rbsg zvw2?|}E|vE%h7*~zXwnMr0dGj`%A-mIO?#(A=n?AS^+hvOr* z$B7SGmM=-vNmCaklFj=jo9w$A4K&U|)%@`SjY6Sr6i9l0&r?sco817a>aF+vyx-p? zSD=5ddGB=3w%AP4k##ku)N#-PoWqk#u>Kfqn1omc?optV&HrJKrNz=O{H1yPqZ`$+ zO<}YE-bfSLYQ0fr|JNI-^>!A3SE_l0>r>O>xC*MH-j2*joS2EUsg7>cM7Nfi701eR z5P{6bwmGu1jibZmuBnjq>GJIN8E+>SA^oEL?|ip(XRs4tqXuM;1Pgh03Zn6FYd6;w zDX9(>RhgEzaM4`TzhR07*naROhNoF!en!c&rL*La@w!$Xn;F)} zgv?i09XF<{ylGVI0DI%@*>0gFh+P162IOIoOyF{5!)@cL4mn*N1-Jro4OplvgbsgW z8t+&J?HF(YQ6hl4>bNpcL7N6?Rr6f*f|T1V>*NxQo`s+hcAesqAfh?3^&PDm3nkxDYdnw_Q?1!OE9ZZ_b zP#p%9kPuUqBu@fQx(RYx896|Rtp zYP@GdbBkw84v;DkKHG${nn{Vj6q_^ZHs1H?>2|pjL*Og+S$$ zDAQ0H*B=xAUb$Afpy)WT%g3C0c*f_zGiG2LRmV)xox*7P)SLSwYrRnxe}1E{&%0dp znV?`7|JbSODEC72Ht6|P$owbh|4ZokFqP09M2mmT$DfZfU+hlfg8oU1j}(vsIp?8n zWVYxn{;@xREZ_7C=JAg~#5xX2Ssn<#%zuo3nZN7#43DROhrjFj48I@w3hEbr4OXSP zGwE(M@w*1BK)%fe+^&`a*{%4;Vg()_caDF&yvl~%2Tqa>xm!dUv5sxepd2C|@9pSZ zDwPoDv#^|o(62!F4X)8 zB}X2ng|!Rr6h>2(W+%wz8Qryk7}4{2!VB)V(WXVgJ#7#yGO)eYbquYB z$PS1<2+8+D;vEp(4WTiR>p&r=Kr2uyJwZ`X*?&32J!@3YJ+Pg?alj_RKng)94i%qy z9d1>}m5r6h_(x+$8GmEfJpR#7mi0z?lDoo3_~q#1d@TMi{Ce#ByeaY;TS61`N#a8E zNqKUy5gtpt2OiuGKRyA=0r!KvZ<~3Mdnpd`8mPV0F*rWt(1OIb0x5gj5qOP%v~yXv zEDbK=A79)FB@2Fy$u&lz%N#DOa(u7eJzK0Vha9F>tDvg>R95NDFG6OS*)qJfuF^{} zkXD0yGstfM`K=)B0%;u(F;^$`>SNxecr8iR+3QvcWJ|Gd>=j6=<7m-M3`{j|lA@H! zFV8VMca|$xj*!buQ>~V$l`E9T;z+|`$k^agein-J=IWzT6h*@;a}XbG?yM!Ky>yCr zI7uWFLzY5BB5_o;$b}16uKS+X*JW?(Ctf+Az3t!q3AL@Gpq^Rz`KGkO#&i=XQc-ho zj2P};?L370TE`EEJ5(GkCbotG8M+aqs0+o>qbs0x1VVQ-Vc1M{OfA5`RIAF^PL>AO zc-?l@;3r{sPO7s3lxLBPvy7a7gsE*mi;@l?a5OY14jWti`Hc)ca*R;c-2li}131x! z0@>(+?W8(}RzqkVM7E2sq17H_gXdQMIbW3c{3{VY{>I=yW0i{yL!?I>RUe-IIUn6> z8X4FFJ@y`4FB#U(W4FBzwv+O>QJ!Rb=mM_`on(9H0vqK?*YEeu@cZjGAO(N_%kYt} z!gM~k6WVHXaYEa~Q9J4T<@H(CwxCv5AfMO=#o+UGS`^4asT}z0HsT*wS$NSG9K7or zVp%rCvYanltB0|Z&Qe=6nnF>0G+P*(766p!Kw=|b{aDC=1ODBdkmSw zZ5a3QkI!Cg?=2m#;W(ZT@L}Wb`DFAm;>=sMaaRu-_#6NbdI+`hvo1-xK zcVeam;eL+X4Dkmb@w1Tp1&G}X(OX(oMc2~ZS6aTEDM;KnsIsc3b5BTS7RFq5t@D$C z?NxR3sz45JaaY^Xs{)xHh_IjSsi+Jlj7En;ve-{*3QZx^kv|@+r)CoUpX$f3ZzrKqZvS&#Xl}& z;K&vi&sn%)t3IZGyn#^Ge!ldpJ~mXx=KKL~-AvQnrGK^z->;~XyFh*uNOwbV6sGsN zp9eGzG6{Pxf>D7Sh{EU2xO@F{6xJp~Oud$%IOlwQw6IKNRTR#ZVN*=>5N;bFF_;{$ zU-*#8$x4tIgjxZjgH4y^!~*K%0u`l5IzE6bg^*+!sB-Sy#C5GcUUxB%Pagh>_Qc`m zxi@o;fk=+E(Iqw{a(rmp9GOV1W$s)#iKTj_AjUqXPM*CqcaoJs*PJKNd&Nhah^VH-*v6s^g|V{``SR zAn}i*9)u@9@sD#I3TaL?AcIsM^$O(3lU1IY+sRvakv~lQ!%Cdue~f>bw?$s?q%fLp z)I#^Q|FEGz1`z{!^BNbEhX44-xA{m?Sv4q*Zd*5EAg#L3%=Eka`-b{xswiH_5WV0I zmgDh0WZ8p8q*EOm_UaV8>T~!?8A#m@8wLbZ1*Jaf6-Xrqr}MC-#{n2quwy`cjl>Ep z-M<|*8FBG^*T?R%3Qt=sdXc0!tgNDgf$Xcz?CZNWi}S7KDOQ`W9S)~>>Zw5T7VKhr3)VdlpI?6~Dh@5ODV@IrZHwwsygoxwR zH?>W5v;fCY(itEd@sDy;z^ir?TUWK@vi&m(gr}i@+F4~Z14qe0<_`mD4K7=3FU6s(7`~x3 z7&izjeGAlfS5;AwI*xzzqB^dOH4hosov4mRtZBoo7JV zDUfdC9oxo2eGjYpByL zm7sbUst1I(aH%Bz#LYG~osm-v#Kc4>OAwJ;M`$DGKp*F5FGm1ZgyLB9 zSXRcX;@Gl?RX{lk+Okj^wZnLhaWs8kmZ5eD)C*!9dN!Adc!Qca{HTs*Dr4L0`|ZL zZf+IGxd6lWDoT4v5U*L*N(Gh{VDda1dl_DO9G?27_Uea5Zw28RA1kxX?CV?BwWyDw`a~Itq=p@agT#1Y<$iHxF~R+w$F!ju+jHD zDsRcOwJ>zuiqH)#>J`9JeIP8AkpJl?!2)FY3CCaY3y{X5;C=wIXN~=0Q3yDBSYN^? z*)I+|HF4?!9DfB~*$dA-Dvkqt#m}ZLh~KfYwOi{8brD9NB2a)_NtCgb=sEyMH!kZA z0P<}ec6GU$02#Cy-Pa2qLoz6V1kO z0R4uw#||RB0A!>9tA7CRUJ}sQYH?o5>_7m8gF;8=s=EJSKhp6%?~sClTK5?}RCn-dJTO%NW-f0@NU8ndk3 zsElTI&SEhhWr|TeHVO}Q=8s(b`Hd~{j|Ih<_T9jutpy2?4yvOOAV2?#H{bQ>$<9Aw zBLmyaf>a$F)`)*BRzd1%!e0Xv&D6xn^N?E*s-s)$8xqILLK84RK4%v1Xa$ge^mgxW zu+?WEs7ru!n`M1H0HjfU^y{+Lo4)%@zmJFpedifBS=OfT?sE&I3%DA`ptvqA6 zkJ0vU5-b2R=W<${FNi~`!CR9s;Dc(^WQ5Yaqt?rd5fshhAI;((_3C5AaPu4L<1J0a zQc=4R$(d!x7IhmUQ|Z6;MlPH zw0-W~*P+e>*^CJ^6%*C5V_38RNa?$WI)5D(s-xb%`G*aP$C>ohpyl8X$wP7f;J5z_1S% z-4M&NF_fd{RA+_1?bJsn)v;kM>SN17WjN|qHJRG_E!Iw>3TdZ2Hh^*7utaQ4!EYt~ zT_0t256Wuu-R(-!jNYzP;j&>-PMfHY4S=jw#G$wBt57M!uI2(-sxDJjigkFre$~h8 z1|I+7$?tJn{4}Gz0tMG;kk;)y8tP-H0uhrsb8Rl`8fcTOwEJ@dkdX+1DUO9X@wH`r zpKt%!%8eMZ0LO;fsE0X&>%*=3D0PsO83*1_7VSabv&p;4*ak0i4v^s`{;>m>wGkla zKe!Gxtbr^C4vQ-;YvX-6H-~g?&i@O0P#yK=HUm2d)p2E;Ew9LrMIjx4U;=`*B%&0LY-g(Gnm7NR~03?_yDqyo)_G)|ei- z%4+tWmbrHvKJp#=!_3~_cbtLehdMeZOZCyE>ew*1>SNVnPi&<=nnC1p*%=sHmVy-A z6c&dcE8RP4>Ap4vNMrnCpT)+l0gy)ZQ7yv4i*S4E-z^rWbX+1bulMe~zSPI-BHMcJ z_<%2Q zg8$9HF)TNCXG#&mN%4Ly^RF=$BoE3tbG0#SvN6^QB7<78*$x;x%6J0eNhqF%dsJ9= zM`!nYA`dUSn5$f6V21`Fx~=m;Sj|Fq9If~Qg?HUS{DlRiLN(BFuGsiOrStP>H?9_O z8J?;18{XvS#-F*!!1j}2?Z_I24D8vQd$3CHGz5yz+LA4`9o?Fj3~aYq){ZyTJPX@zmbL9(vEGGdVk$(n=Ds>~O;xGYRI0Ta zWksP}RVY>~ESD=R)G9r(UP4lsP$)tyrZF?K$l=3JTsN85*9APD+mL#AaPE7I zWF%r?Sg%102pI;<&@9LngJA_8O~5f?r?0A@M!=)iAs||rzWk4-1rLxn-sVEZnt|o9);5N2 z?Ag$L>MSI|N8Cg~7c7WY0jDi9~vYciBhgs2?Bp* zDaT9}#sO>&sEzc$wl&XIQ@NDCJEPR9DTTg=@&=7*f^x#oJ2v zj7=lmG4A*S;+fib6tyyCTj2X@o~x zfDB70{U#?|)nHzMZE>M6&YTd7eA@sBLA3yB6Scg27G@-vuYn{XMWa+>DrtIKIh4BY+wOB@LAf6gG%)J~Rab1t@nZ=FymlQ&UCn z;lTjvY7e^Pu15UhmHVOQA^O3W%UW+vZ485OS+`9zeGvU+D1SZ>m-Xl0Y_o|!rI3B> z3d{NKdGlmRBArN(Ed;ZJDwg4?Lva5tNO%wpFYeOVx~9nX#S$l$TIU9(e+i|3iPE+U z;xiZGM6V1Jy|9|-m0{#U>+d(**NRec9qVYcnF9PjKY>59*xMKrWz4KU>0AsvSB63r zMlzyLw&N6^Z+4Ixkl~Xl_*5R|Ej$}Ldq2+M?5Er3s(J=?@_Xlz>mI1kvWH>ZFH?V>d$2oLhD8A!j?Hb=oGk4kT2 z866qBiFI5&2nz=we@eikoel4RRXdvZ=y&fS@q0f)n(b`ia8w;T0m$ljAkU#$4{X1& zj(SKe4?(q8e6JefoMIR2<2u$Ic(h8yDJ$ZLbvXNtZgl^+U)?6 zwh;|@pW+V+-P*cC8r!zi*ndPOXo0ggnb4_ZAtIp=)mkv|o5eD`d=l>6>0w$J5MSRu zmTQB@cJL`t+H#T7mW!=0d1-|3Y#-r;4AHR&wa7V7v5scyqgCJQs*4=JM(m>qU6Hw($7m(F588fBA>prK$ACMTT`-)QAF1L9~9~ z)OnD{OkS*BlBZ3Tl%2tG&0|7{{3Z(SiLuenk z^QwU33fR{(ux}n#U8#<4A<@dxFV&fS018E@D#2bxyYfGa1sF<+WEwwamnahiKt3A; zINAYZK;S4Xa=wDRR3UsmOZfC6;p8uJ=Cfbq^Ig%X_Xx5V~L$q^v!U{d^0CXu_+@p*h@9By5WV}`X~ z^|28PU95;B;V`$9P5L@?0+5Xi?C|9B3P7p}3kh^th41Cz4Pkras+ORzDE#Y2`JRuX|OyGF_*(2?AFau zZM{Zji89)ide@ud;F!>yv?yS%fOZ+AHSJ2L6*!*G`X4Wi1ezIXgt9b<8XgnDbM<5w zu6z&9{v#~B0wo{r=DYum$+<^R?psgz*^9mtXcWb}tR^iMVO>A;*~D{Nv?A;jAnzJd zSvw7JNz_i$LQw64;%dlkgQc5baW`alLVhz8$DrB|wWRo6Hx|1Hnf>)=N``a_a?79w zlo9RJ>af%JN2f#?E0Y!InQ)GQ{QslB#YK5Ta5vT%1NoL4q1Ragl`1vl#WJCp0^y4} zBIC0}Uz;ZO(j>9xCW!67O!VLkkz@0O&t{Pq#13A)t&b-k`fc8r`&L)hj=odSf4n{~ zhG4W`V{%bK326M>u%KiKjwSkqAn+}KdSP=Y)?n$7b?Wa z?7y4xPu8J6bt~HGF_;>FS_mm#fvCHk+~hLs9EMTdvrzgM_tTP7?4#auP^>~K2C~hX z8ts8zlgB0@!TJz9Ycds^#XYug!f0|O{?RmPre*EbU}g^bE*UgBnU>-TktCULI7+FMBbO^&7l||16duoQiak71KF+Er+!19ZpLQitrU29j zp+1{hD?%tG?uD!XHgS$t@TgZDjlW|Dkc}*Bw~C`3JT8|-K(L!$!*P$D2~g_EGrB~X zmIRmy7tq+4I9GubwSFI#+*QnY?y~zt89RX7&M8iW-c9Aw9=P8F{2|&6@l<5Q@ePvm@0LbK>8NX&98%Th0{wh=E~s?p1(H|omfvb3#zI>F$QzJXlKUIUfYItWRvKnsLGBo`I;_y z#wk(8YK=6@yjJVar~do00vVe!Mc>VU(7T}i^o znRZ}oc|aB+mJ%Ph1wIQfybcaGTgSzS5~(l1ic)5L{5aL>wNIS6W_2D99oeJ({=jz# z%QEpE6aRVLT@6i_^~%jupn3^H%l6=C<+65Dth;2VTKIHT8^N(*3&r5C?@IDauTTfo z=RkelI;PPUVw~b7XPw>NeiCJjwgGsD1n)s+)uqqD!mG}4jBag>em}?`h0v!!{zZyBz<=5&F_CcZg7#vui_Lh)mghAL8^*k6qME6WZi|lB zRG9Tx19C8c1ZBG-;(@xX4fcE4U*lc>^J~P*cSplhx7$;v~92>aLb0y-O9tgX&VR>2{b`j!+mWFk0wc? zZy)o&DUDrOMeK?o)Z@wHyJGmD1M|V>D!aJ#b;hm^Ix)bf>T3Buco<{Oae#zf{ z)~fEK*)!R2Ut5klhw?wa)jn96PSl>W3agy zRx&NkqN?IqjK|W1!*TZPdH$N$e7t7L;|({@K3vFOpi-@Id!$LRD=zE0yV|HaHt(jG zfbHtInyHRPaI}kwG%Apxq<3(%QXY+}h@JZAS#@lT&n1vf3wXq5$exrErFk6RYskFj z8vp+ zUw5K9HX+fZgpV)PaTQ40Kz;*AZvpusknaL%2S}?$|2_bzhf4V|^5t2cJ}+_qM$J>7 zSU;*`%QGu9on=s5UDK`ycXua&0Kp+xfB?ZgxJ$_3?ykX|1a}$S-Q9z`ySuxd{k*5X zDu$w_X85u8l3smZeYXfB{31GYIsjPUz}XMWhtTqMpaX)Q0`svTVMiwS=nHrl>SCc! z-!R(+Z4<^}Fwhr9?s!APOSvQsKNq^mnWdL$CFdU<<4(55r63ET?%XL@C10z=EzE~Q znfw-Hw%;z`&NqWP{*(3yeD2_aITpVE4L2-{&L~@8Ns|-xnn=dTzB;E;CX($Ut^-Zt zBl7U4Z07RTJo7hFhT^&>Iw%qD#nxykBnsNyDltDDyx_cF46JJLcfO~h*iS(9(jOii zIbpWtashMX8yo+2j@G3WgL`u#YAG7rFeN%>R!+`=BIR*i<=e{?x@d`L!SYB7pTAmi zA7a@R$xy~BAw?UUkE1_4gxa8s)Y|+YvouDFI}WgU0trK`+!%?p2KLC+kLatu*4jmL zIHt8q`^DSv8{ap$eH)-j1hQNVp@5tEkrSZ~{7$%~ zh*)2DPFiewaUM&s&i#9meX|i%jygZ2J(9PWg*zw^Y%JYiIT7-_a)Fw^0#}`ngJk$Q z$prGp%c|K$Hl%0dr7+qZ$m|r&U{;@C3CqR(d%u78hP>|!1*yXS={EOjP^Q2mu<)ev z%g+`%J9%w<#z={xil>*0pB%zhl{BHOTO$9p_oJbKk#P)h*UrI0Nn6A#LKORC)ng;Y zC9R2Oaqfq}^E=z$P)zYnaWZOgl-kCU>{X-H`VUGYE|%hAlNkTyU^B3HlWWmSI2jXL z9OS~quvpaQ+PoQ&cj;9eSYzrjIJnmPQ|BWbp(Vla^{H3HH0reIN3|nEgkXFGA}axzRiC(6@~c^0UdqPK!A$rVd>dvJJXpn{<+XeSkQ8_ zb&{Hba=OGZw8XI~(-o!KHq2{!>@XSlh5Rzp&uYKgS{3`#le;_gaA*Bcdq%_J{^P&a zGdG|DJ$9EB7-1QiSTaUSySR1Nh)^a)Js*?6?(cN1(7VbC4n&%UjH_TArQsRu(o^n= z*TT3Z5u`HElq&q45O&ZsYm8_LL!}u-T4-ARdul1P2vW>CA}u*iRYZQQyclSw@~j<~ zhO&-)&4=XWblU1wd%KEb*8GnI`Qurg;ef?ba)H#i0)hp zvf}EFHP_^^?wr~N;bkXD9cJOD^Onyn8yZSWtMksx$4W%_HLMMk-HZ`@b&{`V<|&9{ zQ69&JMwxWrCi_~GpMu_##DurZhXMpFf)Lg4$*Ry*5cTtsM2?EHmOw+lXIwe;^X$I@7($C1>^CyBiIhQhz7RDT!K& z;%s-zxs)lG&J94)nQ^`$@KqUS`V`z}z;nqrF)+pt!xys9d7%&E;ZJW^XC`%mtL8-@ zAo!_^K^++L4?j(JO3zBdC!Xw8AiTBgGLg$7Q3$8>sIZQ7^E^{@$VIBXJh`RE1o^^- zA~yN3awbn3$bo*pN)np>%)?cxD7s<_A@OfpnMF1v__@X3T8qK_9@y){^Mo!jU$aU# z*^`S!7j0r=rXQfJh}=<0#)&8U(_%N9ZD9Sd*XvZ=4LHQCbr-C4=KuxjvtNIyZ1iEP z!B+bi-;AGSVYIjE0DhA6%~LzeKR&V5T=C6|gzNLbjAerdZi83-t_d6AEqssLDyWe9 z`QdJ_GFMDDYvi6iTa4;l#-{!-(HRhf*pTkJKrA6+~we9V zUi5g`PFd#V?YlZ(n+|-C7_}ullc|z^(8mCBVa*x?)}Y)}jRASkluaolo(X?xo8r}2 z^Z3K%Aj3ts-okSskKO`Yh5xRWJcwB&EeUXKMCU=sgv`ogg8Gn>6_aFw7I zBfG{7auB;Yc6eV?mO178h!?HAjqv=Wc)s`xC4#9U6YJi6U%gqdwAV*6?olSDx__v}eu6j-7AMYBT(1;MoPAE|D zsgmXeeB)_`8gvaVk_uY3hGpYTi(c;|N97tdLcq_3p#Ocl)K?G+r4 zJxI?TKI0YYHZ8;9`($d2cJI87V89CVSB96Zxk4uV ztV>a-xc?Z3V8Q+f14%s%N-6SRA=@Gmom61^Lyp$>I>DOA`S@A9!MWH(h`pEa5ICj~*oso1%lvED{egTNMvOD&p_r5G zrxXcAKj}KY`|qlOD9cyGsLeiQ_Vq{+*8^D`^Paw7tQdL)4<&7*bq!)tpv^8&^d%E|hCDmHI;u%CncQf(&Ul5XUZ~ zy>;hSp$CPD&@-?}?758#1e#UJNcP0MH~`4^I!Az*$-&|}L#+Gk7tpivI1FW@oQOAP zbw)W`Md#;E_x_5Vc)wVZ@EnNq4|S;z=^LR$)OEg-v zg#5`YbH*ccd=oO^obxRF-&S7;E*_H15o^8MjvBK?kv~1HF~|%d%3Z;dlxhDomh&$& z*o_ItRHrzq?`zUG#;CvlU!sM{#;(M-VR^42d1MND<9V-qqqd;78&{b6xh zFd7IPZno<3k+nooUynTJ-QT&0V4 z_r1}AnJTUa|BRk@-LEh}_)lR3-kkQMQUBZ6O1L%R7y?P`NlQk*T*6E4s_5z@_;B=# zG^r?BPh}u}zccU2G>^ADL`>pEx97y~DJZ+(b+LA(^#5!LGY zt3MOb+=wC&Eue?8tL<^URqg+*+&71ntO#W{_BCx_l?plg5{R63jxB^<*6{B04IsMM z;gO;k!oh9uxyL{$XgwtMX`$%K6d~rj-}VsU^rs08BP|9_4SH#v6oN`uGXc?hN=3C^ zT_y(XrnCo_6H-)fDIvp`#@pwVqUe8+H{{r6MT#<&fuKiKiv1%o3hY03Q%=s~7Cm0N zy7ll9+LPG=D-vTqW0PLGhZgUkd{8o{qUp`q@l4jEuy#qe{c*YWGQX(lG<|w!E*8?j zAERWSw|xrzTc`cvuI;r%a`0zuQ)RMUUKVvHaQkMpa~bbY78;?kaw6yuHWDkEf87@B z;U~5{fBf-3SS`g!6QGBzd?|vHq|(clLQ9U@>mz(_l8mh2!$nwp5_WpO1V~>4KPp=y zrjx8o1I}GP`6nFM02(!7Q+|uW;?2Xb1!}POaO2zxh7t-te2sFlNipD$9yN#@B9Y6Zz9;Yw+_Jsyw(iU{5r6GT* z!u+USy#~!l^2Xwf{?vgEc`tY{&le}29e8+T^BGU_e>%wJ)1?Ej z`{jQHgdE@~p_rg{`)F93Rf(2oXi6eI|BE~(+1}@Sa4B_vgSUpfWi&Y2Y($)#Ojx6y zl0BdiBx$eVVgytr8bCyXt8Lej0S4RRlh0g;lC?UW!>3`2F(5P9I6IUWJ!q;Iy66po zH#W({IwB$X&1$X`0e~3VRh;HfkrPo=rT?GMT1waZQIVO2M*F8-5nU5G-)xFg^xeZ{ zK5#_BxF*tFvaFKM0+_GW!@fo(7fOC zubDChEz=&D_TH%sI`Qs%p3`QTC3@^KW{!%rHMXH<=lHC%u0EdY%5^URQTQDeQ$us< zvbE$ta?(+qIV~F;QaLb@NEUjSf6_yG*2;{yt;)P_0`_?k8p8e5s&^5^P#Qp1IXad?5|W%X0$3bP!=PMC};^o;S+hT&rP z$Z&g!iOHk;$z!`oT3Ww&6Tw`WCRGZQ`t*v0Dur~&oT}i{4c6)%AMP5RY7F_1bc^6Y zmk7kli{olNKXzcGn|H&G=j}=ah*c$6KuzrWK^xuBjD13y+ozuM#PXav={2%hic@pU zqximQ4WR?e7veEiI*>YS#<>r=TKEVGtzKD9sJ#J(?sdBJ>Uj;fI4oY#2&jPzDmb5H zE>m<#%P6d!gV_rEC{xhhUTQ_j@LOCt!HRUtz0RNbKqJ-As%Yjo={{qPD0DWxg3{=H zKcl1I98R5Y)EC*P?ZlTvRTE;4ga}hfXum!}S2fwG$Cw&5vBi4#`rFk;p9;G_^p!jb zabug#)G@Qu%x+MVfiW!SL)q=J2x`CoL>R!!5=4SJVPYe@8sF-H^ZvDOt3MMH%evrXdAAJ(-s^v1KMOHo_+3#TqnP-Ujw`xbr@RpTdr!mSJ0Ly)<1vH12mA>He3h2_uqfu&07U zcw28vTuqSeqI=z@PMdV5|7-R)h*C^3W*4zoYqw>6wcy*$#PO%{-+|4q@DOc!42y9x zJPbuIl9>>>VzDW<-Zcw>e2m}RZ`Tdmw!$E$u zZVOLe`?>=}6iPJZRL)cZE7D&3!{rLja$U_XCl(Ee*mjJw_Qz8AB}Pe+pS!ckqmEp6 z-FT9tJ;h&G*=euaCn@ag-T(CEdH&q?Ft{GirnWNf)Ag4e{}I-muBR%R8J?_C^dU1t zWSJdNF+9A)lcY9KDDzerPcQ9g8|{_2Xpy*THER`ViA1PHyD@VS6&0pvtiF7);;_G* zamYQX4)?}F&C5X>pH7ZnCYfhY0NS^H9VQfg3*y4k{XO*vQLy<%U~j1F@wUzs(a3^g zQ#o7`&n`Q>!$f4EUtfG{u*Rte(|&!wsxb+YoCDRNWb zNrKxxXCG5cXf%j#R*8ieR_AhM=Ge?zpsv+L-^}6kO3)yQF_;$1mL)lIV#E*s8jC1R z{oppvHa-sqU?21za}>J|9-wM2`4ojguW$1@=`8eklG3a$m{$a_LP4axVa3QSgu+UO zb&~FOf{*PjiXx-Ovp$r;I#uo5J$X>ZIN_C_p}uiBG{MVea$x*Q5dI~EVQumHIQiLq z&yZ|qKp_r0P&n%Hvf%B(2gs3LKKgtY6fEYl1{ZmIF(8EyQ(Asnn}jB{T1KPsBNtUe z%#m-ZM*2ALs?X^pSE)W;c4WHO?rg6~qAETR*mUs2@8pu$MK9V1HM#Z`U>W5{zq`Z# z)r5+cOO`O8MCz!8d#hLkoiE3T$ZCFjQ+yj2^lQRaA0|fP2up@063UUi?$Uv&LYr`4 zxLFwrP}`}{f983AoMTTubLIG)+N&VeQ|ZOQ12<&_Wd5NefnyEuy464CSeumhix6~! zvPyrJ5>8?GFq&vk9>Ae2Dv&9X@;U5$e^`=cy#J&g`J69)XW%|TlS~~(Fj3v{A5ot< zkUEq7h#kQ|jUSp6p?HpLSiU1CDqW6)(8(mlV~WX=1LGiyus67};2A=H?H`vfL8r4I zY4?k1$bLx4%Q2O18;B+x=$3o9d?AxIJYV|6t>_xiV(NMIIdie8aM(w7vaP`nN3XfG5A z)&&jV`+DB1bswh$Fzpsg;<)8rf8B}Ilwfsq%52aBZOrQ(?aH_q!XOMPKWlenY7SNB z`>libaTcKloa?m;f$=q?p3#`BZx|cD&0oSUAfpXT+&c zC@6y`^eQ8LO+|tr%hb3f!CRRQ>C?l`+Xhs%y*OzC1s}-wMwxkt@N6tOF<<5V>$;zUn(nNp$7%Y4^R+ZTPqCVXx7?cyj z^SK0vt%+p`2rRLeeK`%u-U-=%L!>+*lZGV`3?Pq7-}1EKQu z>ddSCg2qo`4BS2?q82X@wz(>-79L0PIa2=-)LvxDmeNF;)frxAY=cGLEk!*6C%6Pd$}vk+t{s^Qr?0*ZTC3$h#b)t*jRrk5aHB8nuh?x_ZQ4Zn_J8bb+s)De|QO}Wk;L?ywL4O)^jp6nv#oTmhP8;#cwk} zk%?^ouK?2DKihhR#Jek-=k?rV%l*6N8{R#0pq0t&+epi@81w!`o#(i_M;uvjLr7Pn zc==$9BXBDy#a7xv+3whUQ1kCxLHNN71Ja|OB4#5R6ugl%jOJQ~LUlr1MBsRO8UhW)xEy%kzez(MxDBKLiKLYB34>cx@eoc2EBzSXB-y zDq`#*tyxD^OO56J^MkYT-yrO(f^*;YyZ7v1gQ`*7;MO!%F#QxJ4i3q$g7cp!aSn z+uT$L?ai(1A442d{7yX*A0XSNf*nLmdEZaOCD*s56mKjXK0T~_c<|@*cWbL2POmR|jy_o+@Yqiyen z_UZSFUb(~#60qlt6Kj>^u zVybI8 z%WTj9F>wU1Q=!#KeX<@VQrHl4*?!Ne7))5}ROUlvai`u@C^Yn8LJj7%kv{M2pt@dx z0@$xZMFXb(uxPq`u;m2lpt8{3vP~mwN`m~wp`?Cyp{&h{@!7^PJ`@64xOK3P(ZFXI zZS+RNkQ|i>dR1Xsg2^*i%>K1aO+`m1EvLk~$^39zuwbdQ^WD=fl>+aMf#tITWet>} z+DmyD>5CeUDvm>u;qz;U6>+l??yK0`NyMx}hL)8*B1KE1Ce3ue2qZ;BU}Y8~fFn__JD|iD*_r z&VR@D`knY_d-qe)P)!=$v~n0$Xm89eE1*dV8-(@*x1>6)a=NZ?*?lWYg`RZUw=hqWUa61hjx6mnB!bj^Zr{yVOMj{i~ zHKG1J(RcbUM%ZaTqqq^2$4}h2!cA+V+v1@zQmm0fHZ_#yGgHhO1=)+hXIv^!w(+>4 z1gbVXzAOA=T_RCSg*L=wwh$}IDz1wM?`q9Z?`&(%XwN-V3>bplfoImg&*!4QeT))<(9rj*N?MaEYw*m05Z$%?l|ZKU*Wtt!M6w&Q36{1zxj3 zA5O_?NQ<9;@%xB~oXX&mzhM-EOE@AY zEC{-O{QV;%Y2YrFYc^vmK^Xm9$QoU#e7A;lDZ#59p-hlwYjs$6#5GMTZds=eR6Tlk z*1zAm^tMHMsC<7}LAmguT~EI{7`Lb(pXuf|8bdY@KldrbA^&OC@;;c@EN>}qnaa5M zZ+~jcC6VyfE&g0}VL_y@T8NL1#4evNmhDbu3ei!wK2j-mBdjo^-szC6A{f?UEcO=m z2Oh`>oa=7oL$k)XYjm-~sPI_;qQk)gDv%7F8|-vOV*}qiy)8nU8x+#T*Y_NX2fZKH z%yD!4oV61xDh9OUQ9OHTzymi3h41J>b{~d$%4QB>8Hh=h{9Z_9GlQm|HzzPQ`fNhB zCop=)jmLzm^xjI70!<}1?@0C^!c(tMor=oL-YVUCHq1GbL|MN8PXdUAV?X%}C#DZK z5`ad>~4o zcyy9~o2{^Yu?)K4%6ztorpQ*TAF#{N{Qa+BLl2MZzMG8dm({`yia!$6U$n^&q-u>X z6aIy5=8&ww)7dXfzDwcL{t8P=>ETua@iW z9HHh@Ixt*>`*D$=zd2mOI6@Zhf52~tS4oJ!V_JYE@6ZF3^c^>lR*l zV$J`m-D97>tqIokkNj=PsQ2!gL<2Is-;OMJmlm1G0>VdHC(;SSFD6LenY1# z0d#Ga_R9xK{YPmR4_y~H@@TK~XH)!3lIlHXDR_~Q9DH~q|F1$};gNYB>?iC&hgXPn z2BPOW80C4CGB-&@5vQOJQSlb^-`U4hxm!e&&GGQ7$2v`62jU${8$KQn@|W60!V=T^ z-lPIdLv|tUL?e_KOXYzvE#}jcUx|S63v})>03^!L_8K6I#-F9vDEUvvR+@)Dc)uy_ z$Cx%BSm8_b1m#?`q*gHVI2Q{v|0maaZ-q3B!EOskr*UDMrZM6l^5X&l)c)1!g=suB z4z3UYdNEj-2lvg?r3W`w!UjH>lI>`jh$TMV4~{RqO8m5H=OT zsZ)#fbt$6>H{Zyuwo-v|S)$tQgDs1*rn-kTvR}1oW0ikBP^7FvmhP))gICiWH{a=% z&fX}3pD22=l?}E%XneTO8UA=;7TaX{=!tMYH~|OV#|!WUI_&!UX%dbb361v<{2t>ndMH#UFPXn2o>FM zTkGVd3on!;z;R9j)(6(7Gu}R#7RH~C58BUE+-hM4OZ~`J0JW@TeSq*F;fPSnA#j4& zE|Y@u^H&Slf4PAcEe!+D9xaL#!RwUm<&j-liKq&$?`S}6T(}reIu%ZWuhRBZNSIOd zf*XgzI^~WP)~AzR^mnkj*QNd1a$aZNuV;Jsx!v;pXCqLZ&yQrI?x7@U^e;E{Wg8ywB$aQaiWagx0&+ z;G*rj{gZWru*jH5!!^0gz!Y zN7dH4Nz2<5ZO(C3{-WF24PfmF%r zUHRWfu0Ap zSxio8uk05Fm=jS?r)zKZ(oege zn?@gJ@A^#KAM`GxyLgTY&QWnGPgMpeK~p8eOQxB8mgnv0N=_WmJ7dkkJxO%i1emKD zVu$EL=xt-0?=T=q;8Lgezt5rw>G9A-$&b>%v6=1ySCt+jr~w-eb#|8{4>R z`4CD`fXP(S@+(5#GidQV$3l)4w0MeYaZ$k!02b=xXlD+@+Y~KuQSQDcKSFuZ+FN)X z7=XewmW_ixC#!s3Xwo#S;0lwksK@Z4OtP~*Jr?-_=xYXVEqXH&<7XCLSF$VpfLXLC zr^6Q#0Io+pe%jnqwCB!uyr=ED9w$2-YDC2)*Y|iiW{3qZ!^FT-7ZUE=;nl5wlO0C-eb1Xdq0C(gWE$7Dh`-epDLOYR(GY)2CESx$#cF-B=*xZh@b0M$^IZz4`06 zT6A;(8g5&&&QVuPwKjdv(G{qFY#RI++7cD=)tU{U>UEJ8kq(fV>hk1l z>#nfZ2`(U#nkD#onfut@j}g8f z0}d$Vb@oUX)IL!gn`6a5tG^MGQEFK#!Tn%WI=48K(oVZF-TYRB`#N%B&K?WtX8EsL z!ldN{q!@NS#%C75X+g&7JnYWzY6?_HNR#-#dcxQ}lvxe0IPP7qc%IZmF`HJ@Z&hlOkH6y)o^j z>v%v_gqyAPJ|Po3+Jo3H6`x#4m|9G1JnmBN;L2ZnudM|Zw)EeNILNQLq9k+?Wd3WY z29f}5>H?wfR99ejcCmc+_xru95F#1EYoJC{5}{+O>LDfR87TKon4JgeUMH7b;!&5; zP_FZ}C|Td2js$<#6Vwl?*5A!66A;qH9Sys5l6Su|=G_f%B0!>La53@#ERY3v4CPu$LF`LjmYM;)^QnW|<@a z^V|tpC8`M10z12rYyS0w$|hh?2^eN453~0seS4gK`!I!0*`k^;AP-POnv3LosS`y} z&SE`fl)$iPHI?21oAu2}`&$>10vRRU-=M9(pINc3PM7I-1G=$8sbfU`W9XSLLee^K z?!4CDc&kbCf*vL!T_q%D*+Pq2mKZhjOOa-=wDfCoySnICj5JsyE~C7=s=iY8elS_i zRwSu|?~f-j-?BF?jQ-oBrF*G~QBI5ABy911E-$3`w-k!~$WBl8c_&Bw1+3klz0O$< zTEewe^92vTent4hx1rA6sZSo<2Rgpe1PFcKIKj7%BW?_$2NF&?Rqij4tBzF*B6Uo_ zF&dTf-ID)bK?XfmRB?bZ=8%vhVz<}nk%7dyk?*vjENLL~tY__tC~n_ofklw|tfEav z3z7FZ;s|fVp*+ADgNQT-{XQyKhoOxA|n9e|= z6wp?cV3oczR9-W3Ed`kcImR!uI!^$Nn62}lTt#frSy$)(l`Oycc$$%oxlTBg$6{6= zUzL(FeqUd9(mLfmREZM(I7-k#*zrV2!r%UM12!ySI{B!w^r@s#HNcaEhq3YZ{AOVO zVcP(BcKasefty2;!Qy@8bWt)K( zZm%$cmN12g{KtA|o1}=>8Ok2(j9}qndygC8-KG>Yy!>k&{M)b$zG!=E|7s4a5i5&d z|CIIz+m!?h)j49s>UuNmxf7^g0AxnEIN-d@hotSaz|k)8na1o)MT$m=8rzKA=OmDu zmfxCbea7d}4+e(Mftp?1;ma)@HQefKMcqo*WUac^g%Eu8lx-&#>b=vc^I)2PuR(Jt ztMG+}LkG$cx#UU&0jaVX80EW|pA?z3^+kS_u&7Nb(fb0NWTq&Q7rBV$jbERihcS|B z2o7an=OlE@lZea5*S+wDJDMhtp~`F(O4zhbo`7?h_~pTdu;HhH6eW}Mm@xB-X4nH- z9AL@nfIgQq69GPjAa-P)FzB7i>e4b0k(8U|OW1yepyShLy|3w883RjInLVSNU`%Ds z$oNxskA7(psKISa^W--8}kuSa0q*{bIgp2FyyVu+c3#}P)f^Xw!C*6k$H z$8k@*nXgH1+>K&b$@nT>9INwG@0wZiGvdTwU%%J(4wJ)(4S1O$4C+9k59+#FMi-dc z%)~fIDg7h0us~sCE7CEI`jO3X)?Ja1ReFNIMPGHFdFOE60v zhi@%P%!K^Vrt}=@2Ajj_O@~~BEz@Mw6r}zvRzhO7mI_gkle}!m!+0)iM$@3=yPA2k zHWC}x85P2+E-;wH*!}ymG#irAfKxg%C2ckT<4%*txfNs02t%|doP(eR>lRsAF?i`i z{-j;Bc|q_1_C6hl=HZ1X4EYEg%m55@`F=C>y8iD=QpTN4*H54j#Fw(vXDv}T*?h`W z7d3mns=0{h&ZVfT@iPV5?@+#sy>SS?=Dbx>DOsQrf0u-p-;aFyeq@Mc&Cq?^vMDhC zMX?`+lA;;rPz`7-07`y%ktysr5GXDx!XEtt!x1h~B)`gl8m6ZfyO|pJ0A(+dMQ5kt zYZ;^>>kl#{9fD8@p^LeV_zN&iW|%`dZr!PQZmOS+-Rm;SS1??>Nv7D;{<6jooPD!t zFsofQ1g}`-pJATp4I0uJvac=-uwt57tGgDFM;|Su2D=}D6=o-EEA;JxCAep+mg}I$ z81^ca%x+vQ@0}n`emj!T)X5+va?9PS`I6x3Gc&WSN!<>KmJ1MZA_tKAP$mhpwWvJTGAY%U;CH(c!&;aa%^1 z3uNCmGBJumxeYx~MqH{&`HH*JK04#u$!W-nJHcNAN3rhxZ3~+mYV_G+jxs zvDi-^knW@orm=S-ziXFEkv>k1*VrZs^WpfE^*-Op5{B{@+%CslAw z9h?+s{=~Ow;GS8e4?=G@?TX?By}& zpz(JXuoNyT->iPQB^U;6yThin{(0BS(z{~p#?W>{xKmga2=?X z_(r{p7?WK<&>~l%|8u=aj_?T5 zRu9P>qJxgU=x?qNYoMz2?zbl6DmnWUboF@xxA7~&b966E%oNsp2NGaN)Om?dAJdf9 zBcwiU_Pz(cIMhsHwr5~s_SHfD5R}A8xetdkmcsVwu`&JvNqbpw=1m;C7VSZPazRa| zLqc4T2_Z;CFqOxmmpLy5`c+jZLe*hFuWsYuDojldnTkI4O3aULQ**lQVOwIxf9EuO$#Vz#ii>$ECvPqj#Hs5 z5~_}jv(6r-kDL3bt6$Jdgf)xRi(|rp;7f{iFhA=C;iR$1rFd}A`sn&+&VxSU>>(_1xX8Kt-)Z zA`p{hARhf!dIXjWoEMIu;uPE4%(K%LiVQo#TP*R`Z%n?}Q(SOz}4c1|w_Gu3N zSQWV%lv5hOmps!J849=I|M?>gOO>qb2t3i7TvklT@BFyo9oWI?6(y*3O8|S5OX;J5 zu>@q~G~j>lm4D#}F5LzA5uxgeTGX*b9CF*o`W{=OFwqs?u3g^|fZBM;4r(GCN_`8z za|n11rKo^Mpp`>j?$iY@1eB{>9f89G9l`6!%yO%U+(b}h4}En#c@{bUfYa+mRsi&p zTATbw#`Np1^wfRUw5j`|YZ5sohiIXSE_#m&lM;rHj?R5)>1+a|BuGPy@ApeB3o2U`*Ji+H@AM`60a8hs zyantW087lBc-zT|O7u>b!z;`)UxyS`zzbeEgw@++6U4j{I6*DdJ z?|&3BA3;=VP$}h)AeNdDbP2sgbyl^a`}fnDGtTFXBxqDE*F^;^y?3Y*$U_TS?L{_a zjLxw}Q<{<{ze}RLO1e1$uzE9eSt1cOtYpWc&=V>87;=GL5q+5NaE@QXn5=#{NzY)Q()%?H> z9sG_6vc|gKC)c`{%4?5NV=P)$%(dl-nF`DFBJSFZU$#l=5b8g69hkWYSu%E>F;reM zX}poHJcuMcH`s1RM`Uuh8@;qwZJt*EVR`0(A(;rx4OC?;*=(Exp&jpKRx z6{hlK$$S=rG#haF9>S1t3($Lk6vF{dJt_mtLdO13xkyoa!qoA>9`@qlaCCD~F&cu> zTFif>Z}oNB5+Gi?n70yVCifj)0juwKSVz$-Kc0s^+$?QCC53+Zt}3DE>qrWLBgnIo zF}Osa#fOED#vGyo_Q_f7dcO(-x0te{?JlgFxQL86#*vhtZf>!CeUTc!!>O|Uzpb%- zg_a}e8z|-p<0lMyc-79Z5$@w4FbWs`cJ`0GA?dKVTvn7oBS zlK>;Sa9Ar7`c@Beh(qb5ljSZs{%7z3++6XE#X+_hEW_EmbsNTp)y|KhK9^$^e~|x+ z-iqptjZp|*m0a$50ZSy%F)*=Gb50MeR3)~hpGN&PITQ^y(8`Lydq1_xGcI#@$@<40 zDjs`?m@3d1&e!u2S<)jS>$TALz(`KUuurslY~Wsi;`x#1bF>}Q&hlMYpHuQ>bPq^^ zLCE?<4Q9oJ4G6ocT6tcVy09X4bXjKxiU{pbu?g0}nyv07cGj$B^zon{?J;{>nE@x@ z55A2b3E?Jc3*QGE_(;c3T{A6&2~spZ@wp})ffE-jx&Nt9oL=z!@e@YoTqh~4UR1R|a|K$HzfLmsTx3J6NXzjISMaEfwp^!?V$-a`qcJlnwnFy{1>(Y}Y zHls3kJYo!#7=tb>_seK`5K(>oHmJ3BD8jn6*N?%cyC~5+c6;9^`n5&hZ{3}H$uL3N znq`dS6h9SM+{iI@zCcjR6e)yIIvMFU z^x!g0InsL>@GbKk=Hqtn$xt8MqxrI*-|KS9kADH3J<2Y4;sbt+io-~{og!W>{aIW2 zi=OY9#H#sx=Rx;AsW0KR^X(<|5npRCeR8BX8Hzj}35m$uE91nEV+b&a&2kZKKo3Od^rCPdiF@f!>=Oy3QkxIJe%% zMdnz`#1HXVPB)l!>uxMhhA)?9Fu=cm9phdH$URJ79bS@imw_T7EvU&gcp3pbasdi{ zB1%S{l)KBe@qxW|uA0(Uko|k$2KASe&tDRd#Anpc2we0LfVscCQufqb;a?o3%X(w0 zTM%n&pE>ywHumEa8XSA+r}9=W+ZXBJAI-&ji$9LX1!S*qq>SJHA5C8w)m9rV9o(S> zifeIqDehL>ic_?>JH=gtQ{3H(ySqzqcXxO0dGGh#wE`hOSSvYs&X(CTvp*-Cqp%Q# zgnB)s)h?rY8oZQ(5#SW6)3hhw?N;Un0|6n`iQ?HLNF^G3$>YJ;_H@cw3rO&K+7Wx- zq63k+m@+QjdAT6W(_}J;{N@qz8uAWyXX73R62(ZqbTHdOh$EdgBgU;*4bk^kqpf}FptL+g zpOxV*R6{DL;^;L;tVkbZr09Xi1L6-HRp-<7v{-*S?#xsURmla%#;7@^ZQ{Lx?!=I+ zljVi=8?TPKd^USHf@krjlYawc)KV=a;PuIphYNhhjWfXB8KT8reL zc#$hi6WwgJYx>-uQ?0-H?qGN}xnKQY$5kog(5u7a0rPlG<)atE81n{ds7XzdSBNMz z92;HSs@<@zsp3rOgCErwQ;D#m;X{Nq=0%HrodsDa*j{r5Vus~W zSX+IITrmJHiTFWuyv!aP`maDziwoL{BehJ;^G?PB&j7!Cj(a6`=V26*!~h9LR)ss3 zt+>4H{Ha+&N;*JRW%s8zF}S+3{%yw=KZo8nms)OF1`U}}Mw7UZnB zn+jt_c=?eI+_=A&#-r$OF1LTa=1KWS!VA5hvuW;tj7K#Kjh=lq0QXGRHvbqFgY_X@1EO_*`o>q$aIrKnO%u5I!|<5v00o7K%` z2y9lG!YX#!%kqK=;b)S(;^D$nYLp0!( z(e&)Huo0wAd~v)s`b$SfG2GWUU2_42SkYOHIDi_=kUTuCO1yUbl!PsO#FspdXr&`s z)fU~c5Lz+K{^eV&NF4b@dNX=6QwY(Kp4au3-Y;hjjYC#$3OQ}I3OQHj3q~*xWIhmY zsd5U#sfHdk`IWY_)?VSi%;_s5r_!r*h>EmR;jp*(=ef(ltOBO$*@4La@&?MhgfU@o z)?8{3fTVCga1R`g?pVArn=ac7l@vX%bRZcpVQCN?{`wanK6p1wFt_18skluX0ufWK zdo7PL)YQe26>vnuZI5~00k!^wmAB>?a^-`O@#|&G1c%9DXr_XHLjpgu4U<@?0b1A3 zfbDO9zmcMEBxLm3!7zm%C92&i*LBme!I?jQ5^+Zx{D(`s!CvmY;@SP zY9Xz0t=k&W%^L9!TPF0q**vN*-4Y~2k9d}TZt<)wMQ)CrqRXSnJ~Juen`xhKo=nDjnB2}ROCXO&INh1y|e zI9*@VLtvkYJ^Wid>G#e*)d5<@CVe8WlibQ8uwiEC&%!!^j<$z1CYhT?jbQ=WLyLR7 znB00oImNa!>Q&d|9O`w~Q6@X=xaM46_F+&^LXA)^jn?!V2OUBHnUL4Yn``d5rKNl3BIvsEJ*bO~ zE1-Q1F%Hh>v*}=fz5X?ez9eM>H#HL7d`PuNy+0#z-iltOf-3 zs_?@A5e~gBx>*l3_*q1d{&<58gdQQu^oGUF4fS!#h@&v*dFFEO-uin(5k@&XP(cUC zo|=|I%ZZX@D@zENMMcOlnDsYFugIEss6t7O{I+YA{T(O|a%i9^7reb(L z{Pz?Pk8z4mY*eahT*EEQgKM>E<4GP8+En$XI(Xhh*yU{fgRmFU*!8>v`~Ho=sC7f# z_h|rN_+qXu4Xb&{hOXh499K_Tn+xt*k<%YU?l<85HAXq&F=iz@O;Vv}u$D;O6OH}D zp*f(VsKHjT5FybKTH5R1+l<^)22<|7D``&rWG^MYiaHKGcq>JGwlI7JeX)nIeo`#L zLj!g(V?S$%n?6x5`-?|Xtc#`Ey0*Gt(9M$JuiR~Rgqcvrtmm6gFi3*SBosZ+C43K! zXJE%Z*AMt|=|}zGrAI}ECY&Nx=-m*IFW)cV>!1N$qtI{Nh#fdRWV*QW!oi4Bwc4!A zTWf%heknoJe91+YiFq6>1`rf&NI9KNGq@oS2}1)mjF-2O!UbOr#1!^_1#wh3^2suh z`!|`s-{OW{e5n>`HLTq~)+fCffX1+^FQqm8q9F8Sc|3{OucUgSUComLUZ?>oas%k- zYzLe}BzIwmndaVIp_B#LUEm;pg-#Vk{?SmgCLNU~m)<@q%HwCtx!lwmG|7?cyD9nC5w zO(Fd|!muvI?DCI1F73{4@l|TgdE@%AtA1w+us{WQr-}c4nqzdi`%8t-q#{Akd668c z06$`pQA^?1O*5FGY>M9^t>rI7h1%F3O_gAKdH(L?Wx>88Jf9Gs0`EwCNk@YAm6bDs z{(YT$s3Zz%V`rOsahfd*33p`37$u!~uTR_PA2`XQ%cwCMA6x89Hc!|{V}%Ql93Yg8 z!rXrERbi0w7Y(bX<|=VDpe3IL&|a4C>n2AC7bgFIvs-YnrbFtyn>`y!UYM#xduc?M zO?rN898!cXANZcbnX$0T!aK`!;niAG&KaGrqZi`GzjA{AVH5lGxy2C9R+$nRz^K{H#Nc!mM0oToI{qYZyG#Q)Xk-RS?RsW%vd4q*~oVUnn9I|L7%^QHXN8$?}LC3?-|ZW6bq)T7A6d^>zE=j4!=6lbtRnTUR5vWd9h?nB`|> zVx({wPZ-;|epXoMv}K=LM0*tzKfZ!yN`%~wKm2PK)3)Pw#)egMQuOP(?S)Pes*GOQ_5) z+duETDDn$fOABKr!S+|%LE30wBu?1^Qq>((jkw^9nXDKhCai3#9IZ5R z$*Gf9t51uQOF}u=3u6m{wb&iPSaq+u0*`LEg4Sx`Cp}s%hGI##l)(B;jC|_{Bdrk6 zoC_uk+xs*ciIF|vV`=>U;GOVc@^YXv*9T+Gbn@JEV$9ffOkDqH8v=?sfZ=3i*&%kd zyk>xrVtZGDCP~bbi0Bx~^{;F_+ zR8|YItXq?hlP5PT_3yTJajnn8bRIHmy_vXZY%PH+a)WRm`D5JcpJ%?&iGW7rIEk4g zB5z^39hvEyKqPD8q>Pv`+ha`iZttF^Z3sXfSOkrhu=MJ90rrV29E)tQH{b16wl=fR z5WoU3Yk45!7`H*dNR%Fi^-7_$@4PB$F+q}hFiglIxA^VnFzF?esc%~cIUiae>`*r2 z2{Q;c4IAX0T8_LoC|=WrJVka;1~vTsYkG#?PzJf%*vgRrtsF&5qkkDxP|i8ZySdEc!?+NtRVX zfM14`U*`M#Q@|iNx6{_r%xlk9wwW4-1!8wq`EREW8(oaj4&j+8pnZjkRu?LtEHsxR zH;G6_1AY+xQ4We-r5{@tI z{q0WKYlU%_VbA98+-pLyD^u{9ZKUrHH_Ze`#*IkJ0}CGEiD7n!6cR@s7P1KXLbeYta+Zel>Ez zwE3{_e)#@gnupLr2A5OwNy{DD^`V}?@T-cV2NE7jE@4)k>&D)LmIFCSJ;JAzTbF^#@1X~r?x#x>~_-dnyH!VIA$>*GFldae_LUzSG!=_yH?b@jmz(CQ~GMzfp# z-aXw~F!OGz-L{S-sUVhUvFDA;#xqw)M*#Fb5Hoc5)4={INMl{Q$^`Po3eo2EP6f`c zLzFzfR@$ZU7WtEosYRIDe#8-F80Rds!ClH#V6kpH!AkJt%kB2u zZ3LQ|MkA1Ol*#-Y<*JHE__HpG_h`bPRFc%5<<)oo(r=K9_I+B4Yo54odx3qqk**P%6dmbrU-2FNF+i{3{)Lkju)w>rE@07NUjI-1fdJQYKTR_=fy_zWmv^-j)4)u+(XB?W(4;eBPu z6lrniop~lzP^)3G->=*R-3+C3@wJphuaT0Fp@L1@cM>y%-Re@~X`7wL+}?H}a(I;5q#xksae#4?qgI#!+{MzUsr3J~rr4 z@9OtgOAO>;vTf+=nm(Nz{eu4W*r*VGT-Vh%p$KK6-lFMx_bqqU1pB<3c9A=2HSj{HgW{9c*B`j>; z?6FsaI9TjdWfXZW*nim7`Q>_G!hd$uMf${TJs{XZtHly7-HOuGhWJB0GK34&F3>yq zPDoInRdq-a%K;l08MZetM~<4TJ-`F-=*NuG>{|pm1`WDHc_y_x+ zr|iHhYEw!P05&k`ak-K8wHm5UADxtiEz>Uih{+Lwtv>la4X%xu<9E#t1DL>#Yzv;e zQ_v8Dsxa51%-)~x`C&pj5gOlo>;`=}^}xUXJ$_V=^4mc_&XuqO`*W}zERCoS;yrFh zNEW?Z2+hBTy)MZ!T9*AcTu~(w|I(j?e)c;f&h%C0@$^e<22W|e%Llfi&vxU~`W?)z z7pz91vO9V|=givHXnzw#e%7&>8nG9|1Sh+-IQ);td0wi3hpPz4N%;w~%BB`ma#anT zg_+nR)c3>KI8(@|Wz(8SE6-%Zw9v!}dlxlH(968o6K#_{ahI>61YlqGk zXsP7Rl1hBgx2ctMFjMl>5JjDzf(tL?NkdT0I7}gCwhR1-E5LP~+=q2>BL@TtzVq;Z zoVj$vf4zt_u9!C%EQP{B0UyZ#q#V9^eZSJf?6Ozg5ybvMuJZ(ydf}BIYUJwR_9|~G zfhq|rJ~(JG3lVMzJ=Af{Cz8<>>VpY0)Lh_J*^A#EM2aI0z8QtZfraS1ictL9HaV^* z(>pqhrFDmHROQX;;w!78L1u5j>@BpcJmh0R!z$!K11?;<@fHmtfCYGP#%$!=TCO?O z=d5{keHMa5$V$_qS?!B3(s$PShF=}wYVf=x?UySRftn(m#5<{O@2K)LkaxbSgIC-W zJ?_Pyf`WV>W;GP3ns#tH5nARM7NDkRc*Dr0NsjpLW|49xXku2LSJ}Wy)P=r8=6w86 zFC5JL7vb^8@G*9W*0r`FiEINHIP<#gE53s6%WfQ=qAFaV>kdfc{GXRNQ@SYK0KV7Z zgR#zqL_w&0OVtH^tbU&(Un5d!SvLHG2LhM!k0OW`+wq!)9g!YGzoxe|8-Br|ztQa;|B*LX z;SO#-Dcc(OOXBnUZM$r@MsCI^NnWUvZd%D1`alabOmNys9PLBi*7}TJb$i$=149XX z-7)euCmxptK^*JTsst42|Ln&$7FI4#Ys5HwN4S}#Xh~YfZiE2{Xmc7`>g9a_7`rha z8}b%PyQnf}viyZ~=gZuAS)qajxL1v2#s{!7t2;xJ-mp(hK8bw?-obtP@3hyoSuC>y z9zG=m*3UM0A(EP}o|1|(EVhs+2}QrRQNGf~RPMP$g7T9gaVeAr&`2r}jnf%VBzSig zr{aF^gfle~1Z$xbhY0Ub$(x?@} zFfZc@go7d%fX#$@ke=z}-l4qbm_Krm8?f`;FbJy`IGwcM!mt5BiQyU-s3Fs?#Z*<+ zVKdT64N?W#6p?tXboUw&{-1Gs>+mPvoz01lX9SGe?BSaTK*K>8QYZ&sJ{I&Eg_gPe zi6c_Dcb9> zTP@XaOITl9etd~K8y7(53Tv71mpN#$Y9wz^1w4Rof=JKU*zEKIl5Kj@;of+@z;9UN zl;&-)b|aCTjTpiIS8O?d*H)7G-RO~ep{`I{j&mOI3o9E7eO+1LE?w@S{ve=ST*2-t z+=oEo)ezH{0O7Ntk?GG3M7uhu-LXE*lcIOi4<Cpzb$-7SI&5X3ckwyV-$N>=|Mr60FtR+P3h4JmpZjQg=Gn|7$ zK9KxCGh zmQOyw>4zcQ!@1lQYRGd+|C}p12y1-OSLbiEz<)dLSrcFodhrgZ9g1`zX09DWH6(rW z$lg1$($4HVwE4$}u7x50j|-4D_pf|0oPB;7Rw%eSP-rYH>!sz3XvTg%GF%Zde0`6h z>Q4d*`bD!LbiNMU#laoH`78F{+lmn~Tg=S|{i~A(W2Jm9SqE8*{|(02)~8WO^gpjO zf5n=ch9fyJgdNPc;+CeAh-{y`;DvN0OfZBJW1`!CHrVNpxN51=m%$%*?m6lclmJ9~ zfQW~rHtUP)=-cJFWlr=k5b7a^oN7Luw5xKUsQu$p8&DRQyfq6`?jXkVyfl{u-Ld*X zutq*>_-ykZ&e<&pTkOVS%ios7EsejumKANz_#gbjV;j$0}BVX|gDnoR4jFt-ngi zB^F1*b4qbh^Hc|i?V!6PL*E@A_a0)BwXW$48(xTfHFsp7anV_TL#z;(=Q1Mvk$|o+ zYqR6Xd$Aj~Yz;NVP5NNmn;1R~$uC8875zqrMcvp6Wy}y9W>g0v`-a2hFiKzk%#F_4 zJjwLV!ns$C`s_SK40onLdXqYGP$sBwBEzl5g3Jw+{Luibg_%3GA(qo6=c+n;&mCxD zr^HfZXBKCyYT#d}Hg^S@9+=y1pxZ87^4f0#uY8K`yWP4q1~?P%6|->5ju+2t)L~YGEi~c~7f9g6VyZ^GrYc!B^5<&!UWGwT+#k|- zs!6h}wZ*nvsmECW2))mF#HO#>f}+O(?lM$8`ah_A6TA+k$ZtHR8@iPjoKgJN7DRw0 z@1&#^H7HuGOy-)Cw@7~9zo&IqbzX*Fr(8S)Sbd&pn5fm7ck4K2j^N>Er}ID~Xys-C z#mqyekX#$xaFO7aP7(FZs=pqdSxKT9qoVqScLy(9*O0F z;-MB-Lt~Qy12YsGn)quK#EQ;+?(2MMRTR(|1yBs-jN}PD>xaA|I_V()a`}E?Sba8n>T8qkvPB; z6}NLk(4aUnDALnyFeq(6t6`dmNGRGk<3cAsPa)G=8|C0$aFF-$f zT|+lxWhsrO6w=xu6jzlP{H1etVJi%}R`vDh5nh|?`6|;9e!nG1#iqS8E9k58;j8%R zXt(9Q7ojs19D&N~c;8Ld#lQW84qQ)Gs4yoqJUXn27|(b|x}sW}C7Y+`yTq;Tou0(9 zHw^O}AF31^ssRJvnn7MIwZXzOKSnNO?1uOy3}e^{36i%p#F)Zs4U|mlgcu4Nd}|(& zJY5r~8ieMLBjn=_c^KqLr*I!+9?eC+Y9ApsJ*w~TXc?hlE#&}HUE{x76b{`?On1Hz zSnF)I0Xmx$df_=#2oQkN*rAm}cnQ7Qp<0Li$3ozvpM}$k;dLs1F--AQC|I4So9ms$ z$(=~pQqI2y?OXua_8TNxI{W6OU;MyiktqsuIxU`z5hb}%RT72zXnVD#1}Now>qZ1O z=z7>UqLvxh*8+@0Vn{!I;D|fdrK&*6&ALgpR{SWnQn&O|9q49J7ySqK5=bost3;I7 zRL1`@)!Kp7MTS4%#gIXQDaJ!Fk&pg;O-@z@n$|x$Z7vDmT>u8^=d1&jPv8VJq!gvnuKu8&6 zV?YnZ{;|+o0qm9&B{8thO1C*g?QiSJ+I=Q=x7dh7u}Ruh0oFw@03<+11Hz#aK5#Sj zdeGqh7Mh;Ff&i?~0lvh`uF>Ta+td^(FVE_`XLpXC?keaA=D(&j&hKsSlNXy5q#RB% zKpaBGzc@6$hfbwVnJ+DzHfy9=z%M^jck?ny3>s&sIZ`D_wBKx2UwhGLN1pT=e|gIV zW?<*X9YzRSElCfnV(UhHd`!!HEe0erl>*^o1~!$JmbtPUJztNQ?ct;{rRjf2(ev^# z9eu{(Mctu_gf&@xUfnR+KX7vL{fD|)l|cC!3F2Wq79v_TTYkbZvc{;qDbQ!UFg zD{2|#TDQW6bm@l?qu^e#C>kw7_~6{L>QQ%u8tDg1sy*N$!ihOFr(O4PR{<^gZ$Ejn z!6+sj#tS091U04tl8d+MYwRZ?!)B+Nk6#!hAJdHn6-+hGJNgw_3bmCO=^2}N|pT8{w?%!>Al$_I8U zwkhnrQ`d}-H&DrfK4ts5{qN1Ph8P+$Ugv@d&vONy{4EFD-Iwdk2Veucx63z_&9yqG`i`Q2FzdCYBUzJItLo&edbSKs_fP-f+MYQ zf*eUUI5OCo^YSw}NrU9xQfFV4c811DB&394bKloCOdQ1Es>&m(5i;^t@X0u>8U6Y_ zE~gq{2|*eqYpR99Pn#f|XS$+Ld!s+_jtFn96WEO4l~;AWpBM)!Eh- z(1`FM`3##X>%X#d`*wN-|1muDqpM6pTWnT%>` zGHwi8DBm-(^hnh;Ix0{^2qnZrG2*rqfxY$iAj~L~`;ZkKC@8@8ZhvrV>!zU2#5CYA zJ`Rm{=VU|u-&E-zap0aL_;zj5qD;JgIzOk*oW`QKz2qS>Not6-Kqtw8Uq=&(X>Ypj zpaWL$_9lj|xeT1lpIJe2ZXs?SCmZg}Q+8~&j4>K;iG%sIf_Ea+LMcK!;PMdj0$x$J zX_fAuSNmy|v2?`*Ih)I>yW^L}Gyh#^c1~nBXsMApEsmO{uSiJUmi&m_J5i$tw?0?# z1Bw>9Vw#P>u8n~yb`)hzD6FE3z=Da)uqAoC^EOjZzHWm<|1<3GJqoFTv>;Lrwd-Gv zQx3!HF3iH#oDj_f>=9Fl`=SklyvyTG`WN=NF&J}Z2UQutBN)>;)kp}8;bX=KNsYM+ zvH$6}6p2=7Rq-U!N99o{2ZY`(>>e^=;zG~>=1D#vbeP&)2N;+TLmJ(H=I&Z$#|+Iz zac$M0jLoCH1(m7lxhzchG_L_h)@95r>d)M5Kf{MS#upNCwZZei4d5SPhW?fsefTei z`dB5WM{G$tYohtC{aPq_TNb#r&%=OOQX3d=AYX)?r)VPYEo1#FxkFfCVsYY8o&#T? za|=;1cON1TOtMf&&~s z10OkM#$IX8OB|b-Cs&I)p8R5EAN4{6*R1hoK0T?uP37QjY0APAHL811h#X_VNY*3& z)m{#Th$)*f0vu)+h7Z23`)NFIq#B2SGAofQc20ovOcqi zj6mPekK^>;-U=jcP7y#DdQ$u4hRHa+Q@}_0^Hk5QBk2 z!Sf;IrA8Zuhvf8GVE`inGTI&l2pEhoeP119OTDj-i*(l(?3>D8GOOfY^AKXbju>Gq z#zr>4(96J5+kA)R=0TJ}vLmVQwp^C7DjRiTUBW)VQTMVcF?49d2CRVW`DI<}b0+~C6Tl2l$;F2to*EV=Afh8X=niQUsn?Ik_`qHZ=*A>k2WNM z_DP)~m#fg=2sUIvG;PWCX!NLEV$(X^y?6L_E7S0B)~M=no!4f|+7CEq{l02gJ^sK* zBT~h^?M(!Y(g_z!kSE;X(C61h)CSqodT9NHEF`gO3iq~){!4TD&R@Wh1(PHzk|uMJ z?wy74|MedF1@CC4?u;q>q05F6A!-OVGRzx(A{aQm<+!Mm&X_I3JU@Qy=er;%sP z`mny&`(S^P`Q05#ynyWMRNJ(=pa
mx6_L=Y=7zhyP$qr-N8+lItri%~HcF%j|4Y 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

TJ)Zf1mCi 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/assets/logo.svg b/docs/src/assets/logo.svg new file mode 100644 index 00000000..92a297f0 --- /dev/null +++ b/docs/src/assets/logo.svg @@ -0,0 +1,474 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/assets/logo_README.svg b/docs/src/assets/logo_README.svg new file mode 100644 index 00000000..877c7be0 --- /dev/null +++ b/docs/src/assets/logo_README.svg @@ -0,0 +1,486 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BEAST + + diff --git a/docs/src/assets/logo_README_white.svg b/docs/src/assets/logo_README_white.svg new file mode 100644 index 00000000..62247f56 --- /dev/null +++ b/docs/src/assets/logo_README_white.svg @@ -0,0 +1,300 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BEAST + + diff --git a/docs/src/assets/postproc.md b/docs/src/assets/postproc.md deleted file mode 100644 index ce87622a..00000000 --- a/docs/src/assets/postproc.md +++ /dev/null @@ -1,25 +0,0 @@ -# Post-processing and Visualisation - -## Field computation - -The main API for the computation of fields in a vector of points is the potential function. This function is capable of computing - -```math -F(x) = \int_{\Gamma} K(x,y) u(y) dy -``` - -with ``u(y) = \Sigma_{i=1}^N u_i f_i(y)`` and ``K(x,y)`` the integation kernel defining the type of potential. For example, the far field for a vector valued surface density is - -```math -F(x) = \int_{\Gamma} e^{ik \frac{x \cdot y}{|x|}} u(y) dy -``` - -For a far field potential such as this, the value only depends on the direction, not the magnitude, of ``x``, as can be read off from the normalisation in the exponent. - -The following script computes the far field along a semi-circle in the xz-plane. - -```julia -Θ, Φ = range(0.0,stop=2π,length=100), 0.0 -dirs = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for θ in Θ for ϕ in Φ] -farfield = potential(MWFarField3D(wavenumber=κ), dirs, u, X) -``` diff --git a/docs/src/bases/brezzidouglasmarini.md b/docs/src/bases/brezzidouglasmarini.md new file mode 100644 index 00000000..6a9af90f --- /dev/null +++ b/docs/src/bases/brezzidouglasmarini.md @@ -0,0 +1,2 @@ + +# Title \ No newline at end of file diff --git a/docs/src/bases/buffachristiansen.md b/docs/src/bases/buffachristiansen.md new file mode 100644 index 00000000..91181233 --- /dev/null +++ b/docs/src/bases/buffachristiansen.md @@ -0,0 +1,20 @@ + +# [Buffa Christiansen Basis Functions](@id bcDef) + +Another citation [buffaDualFiniteElement2007](@cite) + +--- +## First Order + +```@docs +BEAST.buffachristiansen +``` + +### Triangles + +Some details. + + +### Quadrilaterals + +Not implemented yet. \ No newline at end of file diff --git a/docs/src/bases/gragliawiltonpeterson.md b/docs/src/bases/gragliawiltonpeterson.md new file mode 100644 index 00000000..8c8d1d17 --- /dev/null +++ b/docs/src/bases/gragliawiltonpeterson.md @@ -0,0 +1,4 @@ + +# [Graglia-Wilton-Peterson](@id gwpRef) + +TODO \ No newline at end of file diff --git a/docs/src/bases/overview.md b/docs/src/bases/overview.md new file mode 100644 index 00000000..1d4f3489 --- /dev/null +++ b/docs/src/bases/overview.md @@ -0,0 +1,9 @@ + +# [Bases Overview](@id basesRef) + +```@example introductory +using TypeTree +using BEAST + +print(join(tt(BEAST.Space), "")) +``` \ No newline at end of file diff --git a/docs/src/bases/raviartthomas.md b/docs/src/bases/raviartthomas.md new file mode 100644 index 00000000..f8ddc491 --- /dev/null +++ b/docs/src/bases/raviartthomas.md @@ -0,0 +1,28 @@ + +# [Raviart-Thomas Basis Functions](@id raviartthomasDef) + +Raviart-Thomas basis functions are also known as Rao-Wilton-Glisson functions [raoElectromagneticScatteringSurfaces1982](@cite) and commonly employed for ... + + +--- +## First Order + +```@docs +BEAST.raviartthomas +``` + +### Triangles + +Details on definition. + + +### Quadrilaterals + +Details on definition. + + + +--- +## Higher Order + +... same as GWP? \ No newline at end of file diff --git a/docs/src/contributing.md b/docs/src/contributing.md new file mode 100644 index 00000000..f207c5d8 --- /dev/null +++ b/docs/src/contributing.md @@ -0,0 +1,60 @@ + +# [Contributing](@id contribRef) + +In order to contribute to this package directly create a pull request against the `master` branch. Before doing so please: + +- Follow the style of the surrounding code. +- Supplement the documentation. +- Write tests and check that no errors occur. + + +--- +## Style + +For a consistent style the [JuliaFormatter.jl](https://github.com/domluna/JuliaFormatter.jl) package is used which enforces the style defined in the *.JuliaFormatter.toml* file. To follow this style simply run +```julia +using JuliaFormatter +format(pkgdir(BEAST)) +``` + +!!! note + That all files follow the JuliaFormatter style is tested during the unit tests. Hence, do not forget to execute the two lines above. Otherwise, the tests are likely to not pass. + + +--- +## Documentation + +Add documentation for any changes or new features following the style of the existing documentation. For more information you can have a look at the [Documenter.jl](https://documenter.juliadocs.org/stable/) documentation. + + +--- +## [Tests](@id tests) + +Write tests for your code changes and verify that no errors occur, e.g., by running +```julia +using Pkg +Pkg.test("BEAST") +``` + +For more detailed information on which parts are tested the coverage can be evaluated on your local machine, e.g., by +```julia +using Pkg +Pkg.test("BEAST"; coverage=true, julia_args=`--threads 4`) + +# determine coverage +using Coverage +src_folder = pkgdir(BEAST) * "/src" +coverage = process_folder(src_folder) +LCOV.writefile("path-to-folder-you-like" * "BEAST.lcov.info", coverage) + +clean_folder(src_folder) # delete .cov files + +# extract information about coverage +covered_lines, total_lines = get_summary(coverage) +@info "Current coverage:\n$covered_lines of $total_lines lines ($(round(Int, covered_lines / total_lines * 100)) %)" +``` + +In Visual Studio Code the [Coverage Gutters](https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-coverage-gutters) plugin can be used to visualize the tested lines of the code by inserting the path of the *BEAST.lcov.info* file in the settings. + +!!! note + From Julia 1.11 onwards the the coverage can be displayed in Visual Studio Code directly, since the [TestItemRunner.jl](https://www.julia-vscode.org/docs/stable/userguide/testitems/#Code-coverage) package is employed. \ No newline at end of file diff --git a/docs/src/excitations/dipole.md b/docs/src/excitations/dipole.md new file mode 100644 index 00000000..2c471d6e --- /dev/null +++ b/docs/src/excitations/dipole.md @@ -0,0 +1,15 @@ + +# [Dipole Excitation](@id dipoleRef) + +A dipole ... + +--- +## Time-Harmonic + +TODO + +### API + +```@docs +BEAST.dipolemw3d +``` \ No newline at end of file diff --git a/docs/src/excitations/linearpotential.md b/docs/src/excitations/linearpotential.md new file mode 100644 index 00000000..33217437 --- /dev/null +++ b/docs/src/excitations/linearpotential.md @@ -0,0 +1,11 @@ + +# [Linear Potential Excitation](@id linpotRef) + +Text + +## API + +```@docs; canonical=false +BEAST.HH3DLinearPotential +BEAST.linearpotentialvie +``` \ No newline at end of file diff --git a/docs/src/excitations/monopole.md b/docs/src/excitations/monopole.md new file mode 100644 index 00000000..330f2332 --- /dev/null +++ b/docs/src/excitations/monopole.md @@ -0,0 +1,11 @@ + +# [Monopole Excitation](@id monopoleRef) + +Text + + +## API + +```@docs; canonical=false +BEAST.HH3DMonopole +``` \ No newline at end of file diff --git a/docs/src/excitations/planewave.md b/docs/src/excitations/planewave.md new file mode 100644 index 00000000..c87e2436 --- /dev/null +++ b/docs/src/excitations/planewave.md @@ -0,0 +1,42 @@ + +# [Plane Wave Excitation](@id planewaveEx) + +A plane wave can be used as excitation in time-harmonic and time-domain scenarios. + +--- +## Time-Harmonic + +A time-harmonic plane wave with amplitude ``a``, wave vector ``\bm k = k \hat{\bm k}``, and polarization ``\hat{\bm p}`` (vectors with a hat denote unit vectors) is defined by the field +```math +\bm e_\mathrm{PW}(\bm x) = a \hat{\bm p} \, \mathrm{e}^{-\mathrm{j} \bm k \cdot \bm x} \,, +``` +where the polarization and wave vector are orthogonal, that is, +```math +\bm k \cdot \hat{\bm p} = 0 +``` +holds. + +### API + +!!! warning + There are currently two APIs for the BEM plane wave. Fix in future. + +```@docs +BEAST.planewavemw3d +Maxwell3D.planewave +BEAST.planewavevie +``` + +--- +## Time-Domain + +A plane wave in the time-domain is defined as ... + + +### API + +Some more details would be helpful here. + +```@docs +BEAST.planewave +``` \ No newline at end of file diff --git a/docs/src/geometry/flat.md b/docs/src/geometry/flat.md new file mode 100644 index 00000000..76bc3389 --- /dev/null +++ b/docs/src/geometry/flat.md @@ -0,0 +1,14 @@ +# Geometry + +```@setup geo +import PlotlyBase +import PlotlyDocumenter +``` + +```@example geo +using CompScienceMeshes +Γ = meshsphere(radius=1.0, h=0.35) +pt = CompScienceMeshes.patch(Γ) +pl = PlotlyBase.Plot(pt) +PlotlyDocumenter.to_documenter(pl) # hide +``` diff --git a/docs/src/index.md b/docs/src/index.md index 489fb146..71e46c99 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,73 +1,90 @@ -```@meta -CurrentModule = BEAST -``` - -# BEAST.jl documentation -BEAST provides a number of types modelling concepts and a number of algorithms for the efficient and simple implementation of boundary and finite element solvers. It provides full implementations of these concepts for the LU based solution of boundary integral equations for the Maxwell and Helmholtz systems. +# BEAST.jl -Because Julia only compiles code at execution time, users of this library can hook into the code provided in this package at any level. In the extreme case it suffices to provide overwrites of the `assemble` functions. In that case, only the LU solution will be performed by the code here. +This Julia package, the *boundary element analysis and simulation toolkit (BEAST)*, provides routines to convert integral and differential equations to linear systems of equations +via the boundary element method (BEM) and the finite element method (FEM). +To this end, the (Petrov-) **Galerkin method** is employed. -At the other end it suffices that users only supply integration kernels that act on the element-element interaction level. This package will manage all required steps for matrix assembly. +Currently, the focus is on equations encountered in **classical electromagnetism**, where frequency and time domain equations are covered. +Several [operators](@ref operator), [basis functions](@ref basesRef), and geometry representations are implemented. -For the Helmholtz 2D and Maxwell 3D systems, complete implementations are supplied. These models will be discussed in detail to give a more concrete idea of the APIs provides and how to extend them. +!!! note + SI units and for time-harmonic simulations a time convention of ``\mathrm{e}^{\,\mathrm{j}\omega t}`` are used everywhere. -Central to the solution of boundary integral equations is the assembly of the system matrix. The system matrix is fully determined by specifying a kernel G, a set of trial functions, and a set of test functions. +!!! tip + To use the code have a look at the [general usage](@ref usageRef). -## Basis + However, the code is designed such that users can easily hook into the code at any level and implement new features. + To do so, have a look at the [internals documentation](@ref InternalsRef) and the [contribution guidelines](@ref contribRef). + Design goals are extendability and a performant execution. -Sets of both trial and testing functions are implemented by models following the basis concept. The term basis is somewhat misleading as it is nowhere required nor enforced that these functions are linearly independent. Models implementing the Basis concept need to comply to the following semantics. +--- +## Installation -- [`numfunctions(basis)`](@ref): number of functions in the Basis. -- [`coordtype(basis)`](@ref): type of (the components of) the values taken on by the functions in the Basis. -- [`scalartype(d)`](@ref): the scalar field underlying the vector space the basis functions take value in. -- [`refspace(basis)`](@ref): returns the ReferenceSpace of local shape functions on which the Basis is built. -- [`assemblydata(basis)`](@ref): `assemblydata` returns an iterable collection `elements` of geometric elements and a look table `ad` for use in assembly of interaction matrices. In particular, for an index `element_idx` into `elements` and an index `local_shape_idx` in basis of local shape functions `refspace(basis)`, `ad[element_idx, local_shape_idx]` returns the iterable collection of `(global_idx, weight)` tuples such that the local shape function at `local_shape_idx` defined on the element at `element_idx` contributes to the basis function at `global_idx` with a weight of `weight`. -- [`geometry(basis)`](@ref): returns an iterable collection of Elements. The order in which these Elements are encountered corresponds to the indices used in the assembly data structure. +Installing BEAST is done by entering the package manager (enter `]` at the julia REPL) and issuing: +``` +pkg> add BEAST +``` -## Reference Space -The *reference space* concept defines an API for working with spaces of local shape functions. The main role of objects implementing this concept is to allow specialization of the functions that depend on the precise reference space used. +--- +## Overview -The functions that depend on the type and value of arguments modeling *reference space* are: +The following [operators](@ref operator), [basis functions](@ref basesRef), and geometry representations are implemented. +To see details and all variations, have a look at the corresponding sections of this documentation. -- [`numfunctions(refspace, domain)`](@ref): returns the number of shape functions on each element. +### Operators -## Kernel +- **Boundary Integral operators** + + Maxwell (3D) + - Single Layer (time-harmonic & time-domain) + - Double Layer (time-harmonic & time-domain) + + Helmholtz (2D & 3D) + - Single Layer + - Double Layer -A kernel is a fairly simple concept that mainly exists as part of the definition of a Discrete Operator. A kernel should obey the following semantics: +- **Volume Integral operators** + + Maxwell + - Single Layer (time-harmonic) + - Double Layer (time-harmonic) -In many function definitions the kernel object is referenced by `operator` or something similar. This is a misleading name as an operator definition should always be accompanied by the domain and range space. +- **Local Operators** + + Identity (+ variations thereof) -## Discrete Operator +```@raw html +
+``` -Informally speaking, a Discrete Operator is a concept that allows for the computation of an interaction matrix. It is a kernel together with a test and trial basis. A Discrete Operator can be passed to `assemble` and friends to compute its matrix representation. +### Basis functions -A discrete operator is a triple `(kernel, test_basis, trial_basis)`, where `kernel` is a Kernel, and `test_basis` and `trial_basis` are Bases. In addition, the following expressions should be implemented and behave according to the correct semantics: +- **Spatial** + + Low Order + - Raviart-Thomas / Rao-Wilton-Glisson + - Buffa-Christiansen + - Brezzi-Douglas-Marini + + High Order + - Graglia-Wilton-Peterson (GWP) + - B-Spline based -- [`quaddata(operator,test_refspace,trial_refspace,test_elements,trial_elements)`](@ref): create the data required for the computation of element-element interactions during assembly of discrete operator matrices. -- [`quadrule(operator,test_refspace,trial_refspace,p,test_element,q_trial_element,qd)`](@ref): returns an integration strategy object that will be passed to `momintegrals!` to select an integration strategy. This rule can depend on the test/trial reference spaces and interacting elements. The indices `p` and `q` refer to the position of the interacting elements in the enumeration defined by `geometry(basis)` and allow for fast retrieval of any element specific data stored in the quadrature data object `qd`. -- [`momintegrals!(operator,test_refspace,trial_refspace,test_element,trial_element,zlocal,qr)`](@ref): this function computes the local interaction matrix between the set of local test and trial shape functions and a specific pair of elements. The target matrix `zlocal` is provided as an argument to minimise memory allocations over subsequent calls. `qr` is an object returned by `quadrule` and contains all static and dynamic data defining the integration strategy used. -In the context of fast methods such as the Fast Multipole Method other algorithms on Discrete Operators will typically be defined to compute matrix vector products. These algorithms do not explicitly compute and store the interaction matrix (this would lead to unacceptable computational and memory complexity). +- **Temporal** + + Lagrange? + + ... -```@docs -elements +```@raw html +
``` -```@docs -numfunctions -coordtype -scalartype -assemblydata -geometry -refspace -``` +### Geometry Representations + +- **Low Order** ("flat") + + Triangular + + Quadrilaterals + + Tetrahedra + + +- **High Order** (curvilinear) + + NURBS-surfaces -```@docs -quaddata -quadrule -momintegrals! -``` diff --git a/docs/src/assemble.md b/docs/src/internals/assemble.md similarity index 100% rename from docs/src/assemble.md rename to docs/src/internals/assemble.md diff --git a/docs/src/internals/overview.md b/docs/src/internals/overview.md new file mode 100644 index 00000000..7d1924ad --- /dev/null +++ b/docs/src/internals/overview.md @@ -0,0 +1,69 @@ + +# [Internals](@id InternalsRef) + + +- In general, the framework is designed such that it allows to easily add support for more kernels, finite element spaces, and excitations. +- Key are assembly routines that take in symbolic representations of the defining bilinear form. Support for block systems and finite element spaces defined in terms of direct products or tensor products of atomic spaces. + + +```@meta +CurrentModule = BEAST +``` + + +## Basis + +Sets of both trial and testing functions are implemented by models following the basis concept. The term basis is somewhat misleading as it is nowhere required nor enforced that these functions are linearly independent. Models implementing the Basis concept need to comply to the following semantics. + + +- [`numfunctions(basis)`](@ref numfunctions): number of functions in the Basis. +- [`coordtype(basis)`](): type of (the components of) the values taken on by the functions in the Basis. +- [`scalartype(d)`](@ref): the scalar field underlying the vector space the basis functions take value in. +- [`refspace(basis)`](@ref): returns the ReferenceSpace of local shape functions on which the Basis is built. +- [`assemblydata(basis)`](@ref): `assemblydata` returns an iterable collection `elements` of geometric elements and a look table `ad` for use in assembly of interaction matrices. In particular, for an index `element_idx` into `elements` and an index `local_shape_idx` in basis of local shape functions `refspace(basis)`, `ad[element_idx, local_shape_idx]` returns the iterable collection of `(global_idx, weight)` tuples such that the local shape function at `local_shape_idx` defined on the element at `element_idx` contributes to the basis function at `global_idx` with a weight of `weight`. +- [`geometry(basis)`](@ref): returns an iterable collection of Elements. The order in which these Elements are encountered corresponds to the indices used in the assembly data structure. + + +## Reference Space + +The *reference space* concept defines an API for working with spaces of local shape functions. The main role of objects implementing this concept is to allow specialization of the functions that depend on the precise reference space used. + +The functions that depend on the type and value of arguments modeling *reference space* are: + +- [`numfunctions(refspace, domain)`](@ref): returns the number of shape functions on each element. + +## Kernel + +A kernel is a fairly simple concept that mainly exists as part of the definition of a Discrete Operator. A kernel should obey the following semantics: + +In many function definitions the kernel object is referenced by `operator` or something similar. This is a misleading name as an operator definition should always be accompanied by the domain and range space. + +## Discrete Operator + +Informally speaking, a Discrete Operator is a concept that allows for the computation of an interaction matrix. It is a kernel together with a test and trial basis. A Discrete Operator can be passed to `assemble` and friends to compute its matrix representation. + +A discrete operator is a triple `(kernel, test_basis, trial_basis)`, where `kernel` is a Kernel, and `test_basis` and `trial_basis` are Bases. In addition, the following expressions should be implemented and behave according to the correct semantics: + +- [`quaddata(operator,test_refspace,trial_refspace,test_elements,trial_elements)`](@ref): create the data required for the computation of element-element interactions during assembly of discrete operator matrices. +- [`quadrule(operator,test_refspace,trial_refspace,p,test_element,q_trial_element,qd)`](@ref): returns an integration strategy object that will be passed to `momintegrals!` to select an integration strategy. This rule can depend on the test/trial reference spaces and interacting elements. The indices `p` and `q` refer to the position of the interacting elements in the enumeration defined by `geometry(basis)` and allow for fast retrieval of any element specific data stored in the quadrature data object `qd`. +- [`momintegrals!(operator,test_refspace,trial_refspace,test_element,trial_element,zlocal,qr)`](@ref): this function computes the local interaction matrix between the set of local test and trial shape functions and a specific pair of elements. The target matrix `zlocal` is provided as an argument to minimise memory allocations over subsequent calls. `qr` is an object returned by `quadrule` and contains all static and dynamic data defining the integration strategy used. + +In the context of fast methods such as the Fast Multipole Method other algorithms on Discrete Operators will typically be defined to compute matrix vector products. These algorithms do not explicitly compute and store the interaction matrix (this would lead to unacceptable computational and memory complexity). + +```@docs; canonical=false +elements +``` + +```@docs; canonical=false +numfunctions +scalartype +assemblydata +geometry +refspace +``` + +```@docs; canonical=false +quaddata +quadrule +momintegrals! +``` diff --git a/docs/src/quadstrat.md b/docs/src/internals/quadstrat.md similarity index 97% rename from docs/src/quadstrat.md rename to docs/src/internals/quadstrat.md index cc1a606d..ac2b37e4 100644 --- a/docs/src/quadstrat.md +++ b/docs/src/internals/quadstrat.md @@ -1,11 +1,12 @@ # Quadrature strategies -## Introduction - There are many ways to approximately compute the singular integrals that appear in boundary element discretisations of surface and volume integral euqations. BEAST.jl is configured to select reasonable defaults, but advanced users may want to select their own quadrature rules. This section provides information on how to do this. +!!! warning + TODO: discuss directly available/implemented singularity treatment + ## quaddata and quadrule Numerical quadrature is governed by a pair of functions that need to be designed to work together: diff --git a/docs/src/manual/bilinear.md b/docs/src/manual/bilinear.md new file mode 100644 index 00000000..13cdef33 --- /dev/null +++ b/docs/src/manual/bilinear.md @@ -0,0 +1,134 @@ +# Systems of boundary integral equations and bilinear forms + +For the most simple variational formulations such as the EFIE and MFIE, the boundary element matrices and right hand sided can be manually assembled. + +For more complex formulations, such as those encountered in solving the transmission problem and in problems involving composite systems, this approach quickly becomes unwieldy. To facilitate the formulation and solution of these problems, BEAST.jl provides the ability to define quite general linear and bilinear forms. + +## The single body transmission problem + +THe transmission problem is defined by the material properties of the exterior and interior domain and the incident field: + +```@example transmission +using CompScienceMeshes +using BEAST + +κ1 = 1.0 +κ2 = 2.0 + +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ1) +H = -1/(im*κ1)*curl(E) +nothing # hide +``` + +The PMCHWT can be used to model this transmission problem. To write down the variational formulation, pairs of placeholders for the trial functions and test functions are declared by the `@hilbertspace` macro. + +```@example transmission +T1 = Maxwell3D.singlelayer(wavenumber=κ1) +K1 = Maxwell3D.doublelayer(wavenumber=κ1) +T2 = Maxwell3D.singlelayer(wavenumber=κ2) +K2 = Maxwell3D.doublelayer(wavenumber=κ2) + +e = (n × E) × n +h = (n × H) × n + +@hilbertspace j m +@hilbertspace k l + +a = + (T1+T2)[k,j] + (-K1-K2)[k,m] + + (K1+K2)[l,j] + (T1+T2)[l,m] +b = + -e[k] -h[l] +``` + +Note that we are able to define the formulation without specifying or constructing the mesh, the boundary element spaces, or the system matrices. The above constitutes a lazy representation of the formulation that takes neglible time to construct! + +Now this formulation is applied to a concrete geometry. + +```@example transmission +Γ = meshsphere(;radius=1.0, h=0.35) +RT = raviartthomas(Γ) + +X = BEAST.DirectProductSpace([RT,RT]) + +bx = assemble(b, X) +Axx = assemble(a, X, X) +``` + +The type of `Axx` reveals that the structure of the underlying Hilbert space (with two generators in this case) is preserved. Also noteworthy is that only non-zero blocks in the system matrix are stored. This is important both for to limit memory consumption and to avoid unnecessary computations in performing the matrix-vector product. + +BEAST.jl provides wrappers for iterative solvers that conform to the `LinearMaps.jl` interface. For example, `SXX = GMRESSolver(Axx)` acts like a `LinearMap` representing the inverse of `Axx`. Computing the action `uX = SXX * bx` runs the Krylov iterative process with right hand side `bx` and returns the solution to the linear system: + +```@example transmission +SXX = BEAST.GMRESSolver(Axx) +uX = SXX * bx +typeof(uX) +``` + +The solution of this iterative process *remembers* its block structure. This makes it easy to select the electric and magnetic components: + +```@example transmission +import PlotlyBase +import PlotlyDocumenter # hide +using LinearAlgebra + +fcrm, geom = facecurrents(uX[m], RT) +fcrj, geoj = facecurrents(uX[j], RT) + +ptm = CompScienceMeshes.patch(geom, norm.(fcrm); caxis=(0,1.2) , showscale=false) +ptj= CompScienceMeshes.patch(geoj, norm.(fcrj); caxis=(0,1.2)) + +pl = [ PlotlyBase.Plot(ptm) PlotlyBase.Plot(ptj) ] +PlotlyDocumenter.to_documenter(pl) # hide +``` + +## Calderon preconditioning for the PMCHWT + +Let's try to speed up convergence of the iterative solver by constructing a Calderon preconditioner. We opt for a block diagonal preconditioner containing only single layer contributions. The wavenumber used for construction of the preconditioner is purely imaginary to avoid the introduction of resonances. + +```@example transmission +Ty = Maxwell3D.singlelayer(gamma=κ1) + +c = Ty[k,j] + Ty[l,m] +``` + +We also require discrete versions of the duality pairing to map primal test coefficients to dual expansion coefficients + +```@example transmission +Nx = BEAST.NCross() +d = Nx[k,j] + Nx[l,m] +``` + +The corresponding block matrices can be built as before by appealing to the `assemble` function + +```@example transmission +BC = buffachristiansen(Γ) +Y = BEAST.DirectProductSpace([BC,BC]) + +Cyy = assemble(c, Y, Y) +Dxy = assemble(d, X, Y) +``` + +A *lazy* inverse for the discrete duality can be constructed by wrapping the block matrix in a Krylov solver object: + +```@example transmission +DYX = BEAST.GMRESSolver(Dxy, verbose=false) +DXY = BEAST.GMRESSolver(Dxy', verbose=false) +``` + +We have now all the ingredients to write down the system we want to solve: + +```@example transmission +PAXx = DXY * Cyy * DYX * Axx +PbX = DXY * Cyy * DYX * bx + +PSXx = BEAST.GMRESSolver(PAXx) + +u1, ch1 = solve(SXX, bx) +u2, ch2 = solve(PSXx, PbX) + +using LinearAlgebra +norm(u1-u2), ch1.iters, ch2.iters +``` + +Even for this small example, the solution was reconstructed in a much smaller number of iterations. Because objects of type `GMRESSolver` effectively behave as the inverse of the wrapped `LinearMap`, an internal iterative solver to compute the action of the inverse of the duality pairing is triggered during each outer iteration without introducing notational overhead! \ No newline at end of file diff --git a/docs/src/manual/customexc.md b/docs/src/manual/customexc.md new file mode 100644 index 00000000..2c6aa1b4 --- /dev/null +++ b/docs/src/manual/customexc.md @@ -0,0 +1,36 @@ +# Defining Custom Excitation + +BEAST.jl aims to use your own fields as excitations as easy as possible. Specifying your own field can be done by defining a function of the Cartesian coordinates and supplementing this definition with a method of `BEAST.scalartype` to announce the scalar type of the value returned by the field evaluation. Scalar type is, as the name suggest, always a scalar type such as `ComplexF64`, even if the field is vector valued. + +Upon supplying this data, the field function happily interacts with the rest of the BEAST.jl infrastructure. For example, taking the trace of the field is in no way different from doing so for the supplied exciations: + +```julia + using CompScienceMeshes + + κ = 1.0 + + # Custom excitations can be defined as functions of a point in Cartesian space, + # complemented with a function specifying the return type. + E(x) = point(ComplexF64, 1.0, 1.0im, 0.0) * exp(-im*κ*x[3]) + BEAST.scalartype(::typeof(E)) = ComplexF64 + + # Such a custom field plays nicely with the tangential trace + # operators defined as part of the BEAST framework + e = (n × E) × n + + fn = joinpath(pathof(BEAST), "../../examples/assets/sphere45.in") + Γ = readmesh(fn) + RT = raviartthomas(Γ) + + t = Maxwell3D.singlelayer(wavenumber=κ) + + @hilbertspace j + @hilbertspace k + + Txx = assemble(t[k,j], j∈RT, k∈RT) + ex = assemble(e[k], k∈RT) + + iTXX = BEAST.GMRESSolver(Txx; maxiter=1000, reltol=1e-5) + uX = iTXX * ex +``` + diff --git a/docs/src/manual/customop.md b/docs/src/manual/customop.md new file mode 100644 index 00000000..dbe6d2e6 --- /dev/null +++ b/docs/src/manual/customop.md @@ -0,0 +1,58 @@ +# Custom Operators + +BEAST.jl provides the single and double layer operators for the 3D Laplace operator, the 2D and 3D Helmholtz equations, and the 3D Maxwell equations. Nevertheless there are situations where the user may want to define their own integral operator. BEAST.jl makes this process as easy as possible. + +Defining a new integral operator can be as simple as defining a type representing the operator, a method for the evaluation of the integral operator's integrand, and a method for `BEAST.scalartype` so the framework can determine the most appropriate storage type. + +```@example customop +using LinearAlgebra +using CompScienceMeshes +using BEAST + +struct BellCurveOp{T} <: BEAST.IntegralOperator + width::T +end +``` + +By default, assembly of new operators is dealt using the `DoubleNumSauterQstrat` quadrature strategy. This strategy selects either a simplex tensorial quadrature rule when triangles are well-separated, or a bespoke Sauter-Schwab rule when triangles have vertices in common. These rules are to a large extend independent of the integrand. When defining operators exhibiting highly irregular integrands, or when using panels for which the mentioned quadrature rules are not applicable, additional implementation may be required. + +At the heart of the operator defintion lies the routine that allows evaluation of the integrand. This should be provided in the following format: + +```@example customop +function (igd::BEAST.Integrand{<:BellCurveOp})(p,q,f,g) + α = igd.operator.width + + x = CompScienceMeshes.cartesian(p) + y = CompScienceMeshes.cartesian(q) + R = LinearAlgebra.norm(x-y) + + BEAST._integrands(f,g) do fi,gj + dot(fi.value, gj.value) * exp(-(R/α)^2) + end +end +``` + +Note that `(p,q)` are neighborhoods (see CompScienceMeshes.jl). The convenience function `BEAST._integrands` populates a `StaticArrays.SMatrix` with all posible combinations of the operator integrans with trial and test functions without allocating memory. + +Finally, a method declaring the scalar type used by the operator needs to be defined so that BEAST.jl can provide the most efficient storage for any combination of real or complex values operators and finite element spaces. + +```@example customop +BEAST.scalartype(op::BellCurveOp{T}) where {T} = T +``` + +```@example customop +Γ = CompScienceMeshes.meshsphere(radius=1.0, h=0.3) +X = BEAST.raviartthomas(Γ) + +op = BellCurveOp(2.0) +Z = assemble(op, X, X) + +sv = svdvals(Z) +import PlotlyBase +t1 = PlotlyBase.scatter(y=sv) +plt = PlotlyBase.Plot([t1]) +import PlotlyDocumenter # hide +PlotlyDocumenter.to_documenter(plt) #hide +``` + +Being a Hilbert-Schmidt operator, the quickly decaying spectrum is to be expected. \ No newline at end of file diff --git a/docs/src/tdefie.md b/docs/src/manual/examplesTD/tdefie.md similarity index 95% rename from docs/src/tdefie.md rename to docs/src/manual/examplesTD/tdefie.md index d4e2af38..0b2a0b2b 100644 --- a/docs/src/tdefie.md +++ b/docs/src/manual/examplesTD/tdefie.md @@ -1,8 +1,11 @@ # Solving the Time Domain EFIE using Marching-on-in-Time +!!! warning + Make the following an @example which is actually executed. + If broadband information is required or if the system under study will be coupled to non-linear components, the scattering problem should be solved directly in the time domain, i.e. as a hyperbolic evolution problem. -## Building the geometry +### Building the geometry Building the geometry and defining the spatial finite elements happens in completely the same manner as for frequency domain simulations: @@ -15,6 +18,8 @@ X = raviartthomas(Γ) nothing # hide ``` +### Basis functions + Time domain currents are approximated in the tensor product of a spatial and temporal finite element space: $j(x) \approx \sum_{i=1}^{N_T} \sum_{m=1}^{N_S} u_{i,m} T_i(t) f_m(x)$ @@ -34,6 +39,8 @@ U = BEAST.timebasisdelta(Δt, Nt) nothing # hide ``` +### Excitation + We want to solve the EFIE, i.e. we want to find the current $j$ such that $Tj = -e^i,$ @@ -50,6 +57,8 @@ T = BEAST.MWSingleLayerTDIO(1.0,-1.0,-1.0,2,0) nothing; # hide ``` +### Setting up the LSE + Using the finite element spaces defined above this retarded potential equation can be discretized. ```julia @@ -71,6 +80,9 @@ W0 = inv(Z0) x = BEAST.marchonintime(W0,Z,-B,Nt) nothing # hide ``` + +### Post processing + Computing the values of the induced current is now possible in the same manner as for frequency domain simulations by first converting our MOT solution back to the frequency domain using the fourier transform, along with some adjustments. ```julia diff --git a/docs/src/manual/examplesTH/efie.md b/docs/src/manual/examplesTH/efie.md new file mode 100644 index 00000000..3e2430ee --- /dev/null +++ b/docs/src/manual/examplesTH/efie.md @@ -0,0 +1,11 @@ + +# Electric Field Integral Equation + +Text + +--- +### Geometry + +Text + +... diff --git a/docs/src/manual/examplesTH/mfie.md b/docs/src/manual/examplesTH/mfie.md new file mode 100644 index 00000000..15f86992 --- /dev/null +++ b/docs/src/manual/examplesTH/mfie.md @@ -0,0 +1,4 @@ + +# Magnetic Field Integral Equation + +TODO \ No newline at end of file diff --git a/docs/src/manual/quadrule.md b/docs/src/manual/quadrule.md new file mode 100644 index 00000000..9f9c9c99 --- /dev/null +++ b/docs/src/manual/quadrule.md @@ -0,0 +1,196 @@ +# Designing your own quadrature rule and strategy + +In the context of multi-trace solvers, testing and trial functions with logically separate but geometrically coinciding support can interact. In general these support can be equipped with completely indepenent meshes. + +For meshes of flat faceted triangular panels, BEAST.jl defines the `BEAST.NonConformingIntegralOpQStrat` strategy. The constructor of `NonConformingIntegralOpQStrat` takes another quadratue strategy `ctrat` that is fit to deal with pairs of mutually conforming meshes of flat faceted meshes. For a pair comprising a test triangle and a trial triangle, the appropriate quadrature rule is chosen as follows: + +- If the triangles are well-separated, `cstrat` is run on the pair and the resulting quadrature strategy is chosen. +- If the triangles have a single vertex in common, the Sauter-Schwab quadrature rule for common vertices is chosen. +- If the triangles overlap or have edges that overlap, they are both refined so that a geometrically conforming mesh is obtained. For each pair of triangles in this refinement, `cstrat` is run and the resulting quadrature strategy is chosen. + +This quadrature strategy is powerful but requires a lot of geometric processing, resulting in significant increases in matrix assembly times. + +When the user has additional knowledge about the precise geometric constellation of the interacting meshes, a more economic approach can be desirable. It is of course the user's responsibility not to use this economic approach in a context where it is not applicable. + +In this tutorial we assume that the test mesh is conforming to the barycentric refinement of the trial mesh. We propose a quadrature strategy `NonConfTestBaryRefOfTrialQStrat`, parametrised by an underlying quadrature strategy fit for mutually conforming meshes, that implements the following algorithm: + +- If the triangles are well-separated, `cstrat` is run on the pair and the resulting quadrature strategy is chosen. +- If the test and trial triangle share a vertex, the barycentric refinement of the trial triangle is constructed and the decision on the quadrature rule is deferred to `cstrat`. + +Because of the a priori knowledge about the relative constellation of the meshes, the construction in the second case will automatically result in a pair of mutually conforming meshes that can safely be send off to `cstrat`. + +The first step in defining a new quadrature strategy is the definition of the corresponding type: + +```julia +struct NonConfTestBaryRefOfTrialQStrat{P} <: BEAST.AbstractQuadStrat + conforming_qstrat::P +end +``` + +The semantics of the quadrature strategy are captured by the definition of a pair of methods for the functions **quaddata** and **quadrule**, respectively. The purpose of quaddata is the computation of cache data that can speed up the assembly. Typically, this involves computing and storing triangle normals, and the quadrature points and weights for the various quadrature rules that the quadrature strategy considers. + +Because in the majority of cases, the computation of the interaction is deferred to the conforming quadrature strategy, the computation of the cache is forwarded to its method of quaddata, resulting simply in: + +```julia +function BEAST.quaddata(a, X, Y, test_charts, trial_charts, + quadstrat::NonConfTestBaryRefOfTrialQStrat) + + return quaddata(a, X, Y, test_charts, trial_charts, + quadstrat.conforming_qstrat) +end +``` + +The method of `quadrule` is key to the definition of the quadrature strategy and contains the the actual algorithm in charge of choosing the quadrature rule for any given pair of triangles: + +```julia +function BEAST.quadrule(a, X, Y, i, test_chart, j, trial_chart, qd, + quadstrat::NonConfTestBaryRefOfTrialQStrat) + + nh = BEAST._numhits(test_chart, trial_chart) + nh > 0 && return TestInBaryRefOfTrialQRule(quadstrat.conforming_qstrat) + return BEAST.quadrule(a, X, Y, i, test_chart, j, trial_chart, qd, + quadstrat.conforming_qstrat) +end +``` + +The function body is essentially a one-to-one translation of the quadrature rule selection algorithm above to julia. The object `qd` passed to `quadrule` is the cache computed by quadrule. In our case, it is simply passed on to the underlying conforming quadrature rule in the case of well separated triangles. + +Next, we define a type representing the quadrature rule that is used when test triangle and trial triangle are not well-separated: + +```julia +struct TestInBaryRefOfTrialQRule{S} + conforming_qstrat::S +end +``` + +The type `TestInBaryRefOfTrialQRule` refers to the quadrature rule that is responsible for the actual computation of the interactions in case of adjacency or overlap. Quadrature rules are implemented by specifying a method for the function `BEAST.momintegrals!`. + +```julia +function BEAST.momintegrals!(out, op, + test_functions, test_cell, test_chart, + trial_functions, trial_cell, trial_chart, + qr::TestInBaryRefOfTrialQRule) + + test_local_space = refspace(test_functions) + trial_local_space = refspace(trial_functions) + + num_tshapes = numfunctions(test_local_space, domain(test_chart)) + num_bshapes = numfunctions(trial_local_space, domain(trial_chart)) + + T = coordtype(test_chart) + z, u, h, t = zero(T), one(T), T(1//2), T(1//3) + + c = CompScienceMeshes.point(T, t, t) + v = ( + CompScienceMeshes.point(T, u, z), + CompScienceMeshes.point(T, z, u), + CompScienceMeshes.point(T, z, z)) + e = ( + CompScienceMeshes.point(T, z, h), + CompScienceMeshes.point(T, h, z), + CompScienceMeshes.point(T, h, h)) + + X = ( + CompScienceMeshes.simplex(v[1], e[3], c), + CompScienceMeshes.simplex(v[2], c, e[3]), + CompScienceMeshes.simplex(v[2], e[1], c), + CompScienceMeshes.simplex(v[3], c, e[1]), + CompScienceMeshes.simplex(v[3], e[2], c), + CompScienceMeshes.simplex(v[1], c, e[2])) + + C = CompScienceMeshes.cartesian( + CompScienceMeshes.neighborhood(trial_chart, c)) + V = CompScienceMeshes.cartesian.(( + CompScienceMeshes.neighborhood(trial_chart, v[1]), + CompScienceMeshes.neighborhood(trial_chart, v[2]), + CompScienceMeshes.neighborhood(trial_chart, v[3]))) + E = CompScienceMeshes.cartesian.(( + CompScienceMeshes.neighborhood(trial_chart, e[1]), + CompScienceMeshes.neighborhood(trial_chart, e[2]), + CompScienceMeshes.neighborhood(trial_chart, e[3]))) + + trial_charts = ( + CompScienceMeshes.simplex(V[1], E[3], C), + CompScienceMeshes.simplex(V[2], C, E[3]), + CompScienceMeshes.simplex(V[2], E[1], C), + CompScienceMeshes.simplex(V[3], C, E[1]), + CompScienceMeshes.simplex(V[3], E[2], C), + CompScienceMeshes.simplex(V[1], C, E[2])) + + quadstrat = qr.conforming_qstrat + qd = BEAST.quaddata(op, test_local_space, trial_local_space, + (test_chart,), trial_charts, quadstrat) + + Q = zeros(T, num_tshapes, num_bshapes) + out1 = zero(out) + for (q,chart) in enumerate(trial_charts) + qr1 = BEAST.quadrule(op, test_local_space, trial_local_space, + 1, test_chart, q ,chart, qd, quadstrat) + + BEAST.restrict!(Q, trial_local_space, trial_chart, chart, X[q]) + + fill!(out1, 0) + BEAST.momintegrals!(out1, op, + test_functions, nothing, test_chart, + trial_functions, nothing, chart, qr1) + + for j in 1:num_bshapes + for i in 1:num_tshapes + for k in 1:size(Q, 2) + out[i,j] += out1[i,k] * Q[j,k] +end end end end end +``` + +The algorithm constructs the charts of the barycentric refinement of the trial chart, which either share a vertex or an edge with the test chart, or completely coincide with the test chart. Because of this, contributions from any of the refinement charts can be computed accuratey by a classic quadrature rule: + +```julia +momintegrals!(outq, op, + test_functions, nothing, test_chart, + trial_functions, nothing, chart, qr) +``` + +This call to `momintegrals!` calculates interactions between the shape functions on the original test chart and the shape functions on one of the six subcharts in the refinement of the trial chart. The corresponding contribution to the interaction with the shape functions on the coarse trial chart can be calculated if we know how the restriction of the coarse shape functions to any of the subcharts can be written as linear combinations of the shape on that subchart. This information is provided by + +```julia +BEAST.restrict!(Q, trial_local_space, trial_chart, chart, X[q]) +``` + +For efficiency, the overlap function from the domain of `chart` to the domain of `trial_chart` has to be supplied. + +!!! note + The quadrature strategy and related quadrature rules implemented here can be rearded as meta-strategies, and meta-rules, as they defer most of the heavy lifting to underlying strategies and rules for mutually conforming meshes. + + In a *primitive* rule, methods of `momintegrals!` typically contain implementations of numerical quadrature methods. + +To verify correctness of the above strategy, we can compare the results against existing routines that either provide less accurate results or similar results at reduced efficiency. + +```julia +@testitem "NonConfTestBaryRefOfTrialQStrat" begin + using CompScienceMeshes + + fnm = joinpath(dirname(pathof(BEAST)), "../test/assets/sphere45.in") + Γ1 = BEAST.readmesh(fnm) + Γ2 = deepcopy(Γ1) + + X = raviartthomas(Γ1) + Y1 = buffachristiansen(Γ1) + Y = buffachristiansen(Γ2) + + K = Maxwell3D.doublelayer(gamma=1.0) + qs1 = BEAST.DoubleNumWiltonSauterQStrat(2, 3, 6, 7, 5, 5, 4, 3) + qs2 = BEAST.NonConformingIntegralOpQStrat(qs1) + qs3 = BEAST.NonConfTestBaryRefOfTrialQStrat(qs1) + + @time Kyx1 = assemble(K, Y, X; quadstrat=qs1) + @time Kyx2 = assemble(K, Y, X; quadstrat=qs2) + @time Kyx3 = assemble(K, Y, X; quadstrat=qs3) + @time Kyx4 = assemble(K, Y1, X; quadstrat=qs1) + + using LinearAlgebra + @test norm(Kyx1 - Kyx2) < 0.05 + @test norm(Kyx1 - Kyx3) < 0.05 + @test norm(Kyx2 - Kyx3) < 0.002 + @test norm(Kyx2 - Kyx4) < 0.002 + @test norm(Kyx3 - Kyx4) < 1e-12 +end +``` diff --git a/docs/src/manual/quadstrat.md b/docs/src/manual/quadstrat.md new file mode 100644 index 00000000..1b7322c4 --- /dev/null +++ b/docs/src/manual/quadstrat.md @@ -0,0 +1,121 @@ +# Customising the choice of quadrature rules + +At the heart of any boundary element code are routines that can compute matrix entries that take on the form of double integrals over pairs of panels of an integrand that is the product of a test function, a trial function, and the fundamental solution of the equation under study. The fundamental solution is singular at the origin, implying that simple quadrature methods are not sufficient to compute the entries to satisfactory accuracy. A mechanism to activate the most appropriate rule is required. BEAST.jl attempts to offer a reasonable default, but the advanced user may wish to take control beyond this. + +This page describes how the user can intervene in this system. + +## List of implemented quadrature strategies + +In BEAST, a clear distinction is made between a **quadrature rule**, which is a numerical algorithm that computes approximate values of a given integral with a given domain (a pair of panels in the case of BEM) and a given integral (comprising testing functions, trial functions, and a fundamental solution in the case of BEM). Different integrands and domains call for different quadrature rules. For a fixed integrand structure, the algorithm that chooses for any given constellation of panels the most appropriate rule is called the **quadrature strategy**. + +The algorithm that determines which quadrature rule is used for any given geometric constellation of interacting panels is called the quadrature strategy. The list of strategies currently implemented in BEAST is: + +```@example introductory +using TypeTree +using BEAST + +print(join(tt(BEAST.AbstractQuadStrat), "")) +nothing # hide +``` + +The type of the quadrature strategy object determines the quadrature rule selection algorithm. The value of the fields contained by the quadrature strategy object typically selects the order or number of quadrature points used by the various possible quadrature rules. + +The methods responsible for caching quadrature related data and quadrature rule selection are named `quaddata` and `quadrule`, respectively. For a given combination of a discrete boundary integral operator and quadrature strategy, the methods that will be dispatched to can be queried as follows: + +```@example introductory +using CompScienceMeshes +using BEAST + +Γ = meshsphere(radius=1.0, h=0.45) # triangulate sphere of radius one +RT = raviartthomas(Γ) +𝑇 = Maxwell3D.singlelayer(wavenumber=2.0) +qs = BEAST.DoubleNumWiltonSauterQStrat(6, 7, 7, 8, 6, 6, 6, 6) +``` + +```@example introductory +BEAST.quadinfo(𝑇, RT, RT; quadstrat=qs) +nothing # hide +``` + +## Explicitly providing the quadrature strategy + +The assembly function takes a keyword argument that allows to specify a specific quadrature strategy to use. + +```@example introductory +using CompScienceMeshes +using BEAST + +Γ = meshsphere(radius=1.0, h=0.45) # triangulate sphere of radius one +RT = raviartthomas(Γ) +𝑇 = Maxwell3D.singlelayer(wavenumber=2.0) + +Z1 = assemble(𝑇, RT, RT; quadstrat=BEAST.DoubleNumWiltonSauterQStrat(2, 3, 6, 7, 5, 5, 5, 5)) +Z2 = assemble(𝑇, RT, RT; quadstrat=BEAST.DoubleNumWiltonSauterQStrat(6, 7, 7, 8, 6, 6, 6, 6)) + +using LinearAlgebra +2 * norm(Z1-Z2) / norm(Z1+Z2) +``` + +This method works well when only a single integral operator appears in the boundary integral equation. For systems containing multiple equations or when linear combinations of different operators appear, specifying a single quadrature strategy at the callsite of `assemble` will likely not be appropriate. + + +## Setting the default quadrature rule + +To query the set default quadrature strategy for a triple `(op, testfns, trialfns)`, + +```@example introductory +using CompScienceMeshes +using BEAST + +Γ = meshsphere(radius=1.0, h=0.45) # triangulate sphere of radius one +RT = raviartthomas(Γ) +𝑇 = Maxwell3D.singlelayer(wavenumber=2.0) + +BEAST.defaultquadstrat(𝑇, RT, RT) +``` + +A new default can be set using the `@defaultquadstrat` macro. This creates a new method for the function `defaultquadstrat` that will be dispatched to for the arguments of the provided types. + +```@example introductory +BEAST.@defaultquadstrat (𝑇, RT, RT) BEAST.DoubleNumWiltonSauterQStrat(6, 7, 7, 8, 6, 6, 6, 6) +Z2 = assemble(𝑇, RT, RT) + +BEAST.@defaultquadstrat (𝑇, RT, RT) BEAST.DoubleNumWiltonSauterQStrat(2, 3, 6, 7, 5, 5, 5, 5) +Z1 = assemble(𝑇, RT, RT) + +using LinearAlgebra +2 * norm(Z1-Z2) / norm(Z1+Z2) +``` + +The above number provides some insight into the accuracy of the selected quadrature strategy. The advantage of setting the default quadrature strategy like this is that it is global and will automatically affect all subsequent calls to assembly. This means the set strategy will be used even if assembly is called as part of the assembly of a more complicated larger system, potentially containing linear combinations of integral operators. + +The downside is that the default can only be set for concrete types of `(op, testfns, trialfns)` and that the call to the macro needs to be at Module scope. This makes it less appealing for complicated simulations and sweeps over the quadrature accuracy. + +# Specifying a quadrature strategy selection method + +This method is somehwat more involved, but is the most general and should allow for full control of the quadrature rules used in assembly. + +```@example introductory +function myquadstrat1(op, testfns, trialfns) + if op isa BEAST.MWSingleLayer3D + return BEAST.DoubleNumWiltonSauterQStrat(2, 3, 6, 7, 5, 5, 5, 5) + end + return BEAST.defaultquadstrat(op, testfns, trialfns) +end + +function myquadstrat2(op, testfns, trialfns) + if op isa BEAST.MWSingleLayer3D + return BEAST.DoubleNumWiltonSauterQStrat(6, 7, 7, 8, 6, 6, 6, 6) + end + return BEAST.defaultquadstrat(op, testfns, trialfns) +end + + +Z1 = assemble(𝑇, RT, RT; quadstrat=myquadstrat1) +Z3 = assemble(𝑇, RT, RT; quadstrat=myquadstrat2) +2 * norm(Z1-Z2) / norm(Z1+Z2) +``` + +Best practice is too return `BEAST.defaultquadstrat(op, testnfs, trialfns)` by default to ensure that all operators are supported, also those for which no explicit overwrite is specified. + + diff --git a/docs/src/manual/usage.md b/docs/src/manual/usage.md new file mode 100644 index 00000000..a32ed0dc --- /dev/null +++ b/docs/src/manual/usage.md @@ -0,0 +1,114 @@ + +# [General Usage](@id usageRef) + +!!! info + The fundamental approach, which applies in most cases is: + 1. Define trial and test functions. + 2. Define an operator and an excitation. + 3. Assemble the system matrix and the right-hand side + +The available basis functions and corresponding geometry representations, available [operators](@ref operator), and excitations are defined in the corresponding sections of this documentation. + + +--- +## Introductory Example: EFIE + +The fundamental procedure is exemplified for the electric field integral equation (EFIE); further common steps are discussed afterwards: + +```@setup introductory +import PlotlyBase +import PlotlyDocumenter +``` + +```@example introductory +using CompScienceMeshes +using BEAST + +# --- 1. basis functions +Γ = meshsphere(radius=1.0, h=0.4) # triangulate sphere of radius one +RT = raviartthomas(Γ) # define basis functions + +# --- 2. operators & excitation +𝑇 = Maxwell3D.singlelayer(wavenumber=2.0) # integral operator +𝐸 = Maxwell3D.planewave(direction=x̂, polarization=ẑ, wavenumber=2.0) # excitation +𝑒 = (n × 𝐸) × n # tangential part + +# --- 3. compute the RHS and system matrix +e = assemble(𝑒, RT) # assemble RHS +T = assemble(𝑇, RT, RT) # assemble system matrix +nothing #hide +``` + +--- +### Explanation + +The example follows the 3 steps. +Specifically, in the example trial and test functions are the same ([Raviart-Thomas](@ref raviartthomasDef)), the excitation is a [plane wave](@ref planewaveEx), and the operator is the [Maxwell single layer operator](@ref MWsinglelayerDef). + +!!! tip + The [`assemble`](@ref assemble) function is the key function of this package, it accepts either *excitation + test function* or *operator + test + trial function*. + + The operator can also be a linear combination of several operators. + +```@docs +assemble +``` + +--- +### Further Common Steps + +The linear system of equations can now, for example, be solved via the iterative GMRES solver of the [Krylov.jl](https://github.com/JuliaSmoothOptimizers/Krylov.jl) package. +However, other solver could be used. +Subsequently, different post-processing steps can be conducted, such as computing the scattered field from the determined expansion coefficients. + +!!! tip + Key functions for the post-processing are the [`potential`](@ref potential) and [`facecurrents`](@ref facecurrents) functions. + +This is shown in the following: + + +```@example introductory +using Krylov, LinearAlgebra + +# --- solve linear system iteratively +u, ch = Krylov.gmres(T, -e, rtol=1e-5) + +fcr, geo = facecurrents(u, RT) +pt = CompScienceMeshes.patch(Γ, norm.(fcr)) +pl = PlotlyBase.Plot(pt) +PlotlyDocumenter.to_documenter(pl) # hide +``` + +```@example introductory +# --- post processing: compute scattered electric field at two Cartesian points +points = [[3.0, 4.0, 2.0], [3.0, 4.0, 3.0]] +EF = potential(MWSingleLayerField3D(gamma=im*2.0), points, u, RT) +``` + + +!!! warning + more details + +```@raw html +

+ Setup +             + Setup +
+
+``` + +--- +## Plotting & Exporting + +Plotting details. + +Export VTK. + +... \ No newline at end of file diff --git a/docs/src/operators/helmholtz.md b/docs/src/operators/helmholtz.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/src/operators/identity.md b/docs/src/operators/identity.md new file mode 100644 index 00000000..944fce7b --- /dev/null +++ b/docs/src/operators/identity.md @@ -0,0 +1,54 @@ + +# [Identity Operator](@id identityDef) + +The identity operator is implemented in two flavors `BEAST.Identity()` and `BEAST.NCross()`. + +--- +## Definition + +The identity operator +```math +\bm{\mathcal{I}} \bm b = \bm b +``` +returns the function it is provided unchanged. + + +### As bilinear form + +When handed to the [`assemble`](@ref assemble) function, the operator is interpreted as the bilinear form +```math +a(\bm t, \bm b) = \int_\Gamma \bm t(\bm x) ⋅ \bm{\mathcal{I}} \bm b(\bm x) \,\mathrm{d}\bm x = \int_\Gamma \bm t(\bm x) ⋅ \bm b(\bm x) \,\mathrm{d}\bm x \,. +``` +Hence, the resulting matrix ``\bm A`` contains the entries +```math +[\bm A]_{mn} = \int_\Gamma \bm t_m(\bm x) ⋅ \bm b_n(\bm x) \,\mathrm{d}\bm x \,. +``` + +### API + +```@docs; canonical=false +BEAST.Identity +``` + + +--- +## Variant with ``\bm n \times`` + +As a variation, the identity operator is provided with a cross product with the normal vector ``\bm n`` of the surface ``\Gamma``. + +### The bilinear form + +The [`assemble`](@ref assemble) function, interprets the operator as the bilinear form +```math +a(\bm t, \bm b) = \int_\Gamma \bm t(\bm x) ⋅ \bm{\mathcal{I}} (\bm n(\bm x) \times \bm b(\bm x)) \,\mathrm{d}\bm x = \int_\Gamma \bm t(\bm x) ⋅ (\bm n(\bm x) \times \bm b(\bm x)) \,\mathrm{d}\bm x \,. +``` +Hence, the resulting matrix ``\bm A`` contains the entries +```math +[\bm A]_{mn} = \int_\Gamma \bm t_m(\bm x) ⋅ (\bm n(\bm x) \times \bm b_n(\bm x)) \,\mathrm{d}\bm x \,. +``` + +### API + +```@docs; canonical=false +BEAST.NCross +``` \ No newline at end of file diff --git a/docs/src/operators/maxwelldoublelayer.md b/docs/src/operators/maxwelldoublelayer.md new file mode 100644 index 00000000..83685609 --- /dev/null +++ b/docs/src/operators/maxwelldoublelayer.md @@ -0,0 +1,76 @@ + +# [Maxwell Double Layer Operator](@id MWdoublelayerDef) + +The Maxwell double layer operator is encountered in many time-harmonic BEM scattering formulations in electromagnetics. +So far, only the 3D variant is implemented. + +--- +## Definition + +The operator is defined as (see, e.g., ...) +```math +\bm{\mathcal{K}} \bm b = α \int_\Gamma ∇_{\!x} g_γ(\bm x,\bm y) \times \bm b(\bm y) \,\mathrm{d}\bm y +``` +for a vector field ``\bm{b}`` and a parameter ``α`` with the free-space Green's function +```math +g_{γ}(\bm x,\bm y) = \dfrac{\mathrm{e}^{-γ|x-y|}}{4π|x-y|} \,. +``` +The parameters are typically ``α=1`` and ``γ = \mathrm{j}k`` with ``k`` denoting the wavenumber and ``\mathrm{j}`` the imaginary unit. +As variation, the rotaded double layer operator +```math +\bar{\bm{\mathcal{K}}} = \bm{n} \times \bm{\mathcal{K}} +``` +can be used which simply involves the cross product with the normal vector ``\bm{n}`` of the surface ``\Gamma``. + + +--- +## As Bilinear Form + +When handed to the [`assemble`](@ref assemble) function, the operators are interpreted as the corresponding bilinear forms +```math +a(\bm t, \bm b) = α ∬_{\Gamma \times \Gamma} \bm t(\bm x) ⋅ \,( ∇_{\!x} g_γ(\bm x,\bm y) \times \bm b(\bm y) ) \,\mathrm{d}\bm y \mathrm{d}\bm x +``` +and +```math +\bar{a}(\bm t, \bm b) = α ∬_{\Gamma \times \Gamma} \bm t(\bm x) ⋅ \, (\bm{n} \times ( ∇_{\!x} g_γ(\bm x,\bm y) \times \bm b(\bm y) )) \,\mathrm{d}\bm y \mathrm{d}\bm x +``` +resulting in the matrix +```math +[\bm A]_{mn} = a(\bm t_m, \bm b_n) \,. +``` + + +### API + +```@docs +Maxwell3D.doublelayer +``` + + +--- +## As Linear Map + +When handed to the [`potential`](@ref potential) function, the operator can be evaluated at provided points in space. +Commonly, this is used in post-processing. + + +### Far-Field + +In the limit that the observation point ``\bm x \rightarrow \infty``, the operator simplifies to the far-field (FF) version +```math +(\bm{\mathcal{K}}_\mathrm{FF} \bm b)(\bm x) = α \bm{u}_r \times \int_\Gamma \bm{b}(\bm y) \mathrm{e}^{\mathrm{j}\bm{u}_r \cdot\, \bm{y}} \,\mathrm{d}\bm{y} +``` + +### API + +```@docs +BEAST.MWDoubleLayerField3D +BEAST.MWDoubleLayerFarField3D +MWDoubleLayerRotatedFarField3D +``` + +!!! tip + The provided points for the [`potential`](@ref potential) should be in Cartesian coordinates. The returned fields are also in Cartesian from. + +!!! warning + Singularities are not addressed: the case when the evaluation point is close to the surface is not treated properly, so far. \ No newline at end of file diff --git a/docs/src/operators/maxwelldoublelayer_td.md b/docs/src/operators/maxwelldoublelayer_td.md new file mode 100644 index 00000000..5e82365e --- /dev/null +++ b/docs/src/operators/maxwelldoublelayer_td.md @@ -0,0 +1,4 @@ + +# [Maxwell Double Layer Operator](@id MWdoublelayerTDDef) + +Text \ No newline at end of file diff --git a/docs/src/operators/maxwellsinglelayer.md b/docs/src/operators/maxwellsinglelayer.md new file mode 100644 index 00000000..e4324a68 --- /dev/null +++ b/docs/src/operators/maxwellsinglelayer.md @@ -0,0 +1,92 @@ + +# [Maxwell Single Layer Operator](@id MWsinglelayerDef) + +The Maxwell single layer operator, also known als electric field integral operator (EFIO) is encountered in many time-harmonic BEM scattering formulations in electromagnetics. +So far, only the 3D variant is implemented. + +--- +## Definition + +The operator is defined as (see, e.g., [raoElectromagneticScatteringSurfaces1982](@cite)) +```math +\bm{\mathcal{T}} \bm b = α \bm{\mathcal{T}}_{\!\!s} \bm b + β \bm{\mathcal{T}}_{\!\!h} \bm b +``` +for a vector field ``\bm{b}`` and parameters ``α`` and ``β``, as well as, the weakly singular operator (also known as vector potential operator) +```math +\bm{\mathcal{T}}_{\!\!s} \bm b = \int_\Gamma g_γ(\bm x,\bm y) \, \bm b(\bm y) \,\mathrm{d}\bm y +``` +and the hyper singular operator (also known as scalar potential operator) +```math +\bm{\mathcal{T}}_{\!\!h} \bm b = ∇\int_\Gamma g_γ(\bm x,\bm y) \, ∇_Γ⋅\bm b(\bm y) \,\mathrm{d}\bm y +``` +with the free-space Green's function +```math +g_{γ}(\bm x,\bm y) = \dfrac{\mathrm{e}^{-γ|x-y|}}{4π|x-y|} \,. +``` +The parameters are typically ``α=-\mathrm{j}k``, ``β=-1/(\mathrm{j}k)``, and ``γ = \mathrm{j}k`` with ``k`` denoting the wavenumber and ``\mathrm{j}`` the imaginary unit. + + +--- +## As Bilinear Form + +When handed to the [`assemble`](@ref assemble) function, the operators are interpreted as the corresponding bilinear forms +```math +a(\bm t, \bm b) = α ∬_{\Gamma \times \Gamma} \bm t(\bm x) ⋅ \bm b(\bm y) \, g_γ(\bm x,\bm y) \,\mathrm{d}\bm y \mathrm{d}\bm x + β ∬_{Γ×Γ} ∇_Γ⋅\bm t(\bm x) \, ∇_Γ⋅\bm b(\bm y) \, g_γ(\bm x,\bm y) \,\mathrm{d}\bm y \mathrm{d}\bm x +``` +for the complete Maxwell single layer operator, +```math +a_s(\bm t, \bm b) = α ∬_{\Gamma \times \Gamma} \bm t(\bm x) ⋅ \bm b(\bm y) \, g_γ(\bm x,\bm y) \,\mathrm{d}\bm y \mathrm{d}\bm x +``` +for the weakly singular part, and +```math +a_h(\bm t, \bm b) = β ∬_{Γ×Γ} ∇_Γ⋅\bm t(\bm x) \, ∇_Γ⋅\bm b(\bm y) \, g_γ(\bm x,\bm y) \,\mathrm{d}\bm y \mathrm{d}\bm x +``` +for the hyper singular part. +Note that the gradient in the hypersingular operator has been moved to the test function using, e.g., a Stokes identity [nedelecWaveEquations2001; p. 73](@cite). +The corresponding matrices contain the entries +```math +[\bm A]_{mn} = a_x(\bm t_m, \bm b_n) \,. +``` + +### API + +The weakly singular and the hyper singular operator can be used as such or combined in the single layer operator. + +!!! tip + The qualifier `Maxwell3D` has to be used. + + +```@docs +Maxwell3D.singlelayer +Maxwell3D.weaklysingular +Maxwell3D.hypersingular +``` + +--- +## As Linear Map + +When handed to the [`potential`](@ref potential) function, the operator can be evaluated at provided points in space. +Commonly, this is used in post-processing. + + +### Far-Field + +In the limit that the observation point ``\bm x \rightarrow \infty``, the operator simplifies to the far-field (FF) version +```math +(\bm{\mathcal{T}}_\mathrm{FF} \bm b)(\bm x) = α \bm{u}_r \times \int_\Gamma \bm{b}(\bm y) \mathrm{e}^{\mathrm{j}\bm{u}_r \cdot\, \bm{y}} \,\mathrm{d}\bm{y} \times \bm{u}_r +``` +where ``\bm{u}_r`` is the unit vector in the direction of the evaluation point. + + +### API + +```@docs +MWSingleLayerField3D +MWFarField3D +``` + +!!! tip + The provided points for the [`potential`](@ref potential) should be in Cartesian coordinates. The returned fields are also in Cartesian from. + +!!! warning + Singularities are not addressed: the case when the evaluation point is close to the surface is not treated properly, so far. \ No newline at end of file diff --git a/docs/src/operators/maxwellsinglelayerVIE.md b/docs/src/operators/maxwellsinglelayerVIE.md new file mode 100644 index 00000000..4d4f4b56 --- /dev/null +++ b/docs/src/operators/maxwellsinglelayerVIE.md @@ -0,0 +1,4 @@ + +# [Maxwell Single Layer Operator](@id MWsinglelayerVIEDef) + +TODO \ No newline at end of file diff --git a/docs/src/operators/maxwellsinglelayer_td.md b/docs/src/operators/maxwellsinglelayer_td.md new file mode 100644 index 00000000..48eb41a8 --- /dev/null +++ b/docs/src/operators/maxwellsinglelayer_td.md @@ -0,0 +1,14 @@ + +# [Maxwell Single Layer Operator](@id MWsinglelayerTDDef) + +Text + +--- +## Definition + +Text + + + +--- +## As ... \ No newline at end of file diff --git a/docs/src/operators/overview.md b/docs/src/operators/overview.md new file mode 100644 index 00000000..bdab8bc7 --- /dev/null +++ b/docs/src/operators/overview.md @@ -0,0 +1,11 @@ + +# [Operator Overview](@id operator) + +```@example introductory +using TypeTree +using BEAST + +print(join(tt(BEAST.Operator), "")) +print("\n\n\n")#hide +print(join(tt(BEAST.SpaceTimeOperator), "")) +``` \ No newline at end of file diff --git a/docs/src/references.md b/docs/src/references.md new file mode 100644 index 00000000..a56db3de --- /dev/null +++ b/docs/src/references.md @@ -0,0 +1,5 @@ + +# References + +```@bibliography +``` \ No newline at end of file diff --git a/docs/src/refs.bib b/docs/src/refs.bib new file mode 100644 index 00000000..dbd949b6 --- /dev/null +++ b/docs/src/refs.bib @@ -0,0 +1,31 @@ + +@article{raoElectromagneticScatteringSurfaces1982, + title = {Electromagnetic Scattering by Surfaces of Arbitrary Shape}, + author = {Rao, S. and Wilton, D. and Glisson, A.}, + year = {1982}, + month = {may}, + journal = {IEEE Transactions on Antennas and Propagation}, + volume = {30}, + number = {3}, + pages = {409--418} +} + +@article{buffaDualFiniteElement2007, + title = {A Dual Finite Element Complex on the Barycentric Refinement}, + author = {Buffa, Annalisa and Christiansen, Snorre}, + year = {2007}, + journal = {Mathematics of Computation}, + volume = {76}, + number = {260}, + pages = {1743--1769} +} + +@book{nedelecWaveEquations2001, + title = {Acoustic and Electromagnetic Equations}, + author = {N\'ed\'elec, Jean-Claude}, + year = {2001}, + series = {Applied Mathematical Sciences}, + publisher = {Springer}, + location = {New York}, + isbn = {978-1-4757-4393-7} +} diff --git a/docs/src/tutorial.md b/docs/src/tutorial.md deleted file mode 100644 index dd380779..00000000 --- a/docs/src/tutorial.md +++ /dev/null @@ -1,74 +0,0 @@ -# Tutorial - -```math -\newcommand{\vt}[1]{\boldsymbol{#1}} -\newcommand{\uv}[1]{\hat{\boldsymbol{#1}}} -\newcommand{\arr}[1]{\mathsf{#1}} -\newcommand{\mat}[1]{\boldsymbol{\mathsf{#1}}} -``` - -In this tutorial we will go through the steps required for the formulation and the solution of the scattering of a time harmonic electromagnetic wave by a rectangular plate by means of the solution of the electric field integral equation. - -## Building the geometry - -The sibling package `CompScienceMeshes` provides data structures and algorithms for working with simplical meshes in computational science. We will use it to create the geometry: - -```@example 1 -using CompScienceMeshes, BEAST -o, x, y, z = euclidianbasis(3) - -h = 0.2 -Γ = meshrectangle(1.0, 1.0, h) -@show numvertices(Γ) -@show numcells(Γ) -nothing # hide -``` - -Next, we create the finite element space of Raviart-Thomas aka Rao-Wilton-Glisson functions subordinate to the triangulation `Γ` constructed above: - -```@example 1 -X = raviartthomas(Γ) -nothing # hide -``` - -The scattering problem is defined by specifying the single layer operator and the functional acting as excitation. Here, the plate is illuminated by a plane wave. The actual excitation is the tangential trace of this electric field. This trace be constructed easily by using the symbolic normal vector field `n` defined as part of the `BEAST` package. - -```@example 1 -κ = 1.0 -E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) -e = (n × E) × n -nothing # hide -``` - -The single layer potential is also predefined by the `BEAST` package: - -```@example 1 -t = Maxwell3D.singlelayer(wavenumber=κ) -nothing # hide -``` - -It corresponds to the bilinear form - -```math -t(\vt{k},\vt{j}) = \frac{1}{ik} \int_{\Gamma} \int_{\Gamma'} \nabla \cdot \vt{k}(x) \nabla \cdot \vt{j}(y) \frac{e^{-ik|x-y|}}{4\pi|x-y|} dy dx - ik \int_{\Gamma} \int_{\Gamma'} \vt{k}(x) \cdot \vt{j}(y) \frac{e^{-ik|x-y|}}{4\pi|x-y|} dy dx -``` - -Using the `LinearForms` package, which implements a simple form compiler for Julia (`@varform`), the EFIE can be defined and discretised by - -```@example 1 -@hilbertspace j -@hilbertspace k -efie = @discretise t[k,j]==e[k] j∈X k∈X -nothing # hide -``` -Solving and computing the values of the induced current in the centers of the triangles of the mesh is now straightforward: - -```@example 1 -u = solve(efie) -fcr, geo = facecurrents(u,X) -nothing # hide -``` - -The resulting current distribution can be visualised by e.g. Matlab, Paraview, Plotly,... - -![](assets/facecurrents.png) diff --git a/examples/assets/sphere45.in b/examples/assets/sphere45.in new file mode 100644 index 00000000..f1d63191 --- /dev/null +++ b/examples/assets/sphere45.in @@ -0,0 +1,323 @@ +1 +109 212 +0.0 0.0 0.0 +1.0 0.0 0.0 +0.0 1.0 0.0 +-1.0 0.0 0.0 +0.0 -1.0 0.0 +0.0 0.0 1.0 +0.0 0.0 -1.0 +0.923879532082714 0.3826834333997559 0.0 +0.7071067795767625 0.7071067827963327 0.0 +0.3826834312295727 0.9238795329816333 0.0 +-0.3826834333997559 0.923879532082714 0.0 +-0.7071067827963327 0.7071067795767625 0.0 +-0.9238795329816333 0.3826834312295727 0.0 +-0.923879532082714 -0.3826834333997559 0.0 +-0.7071067795767625 -0.7071067827963327 0.0 +-0.3826834312295727 -0.9238795329816333 0.0 +0.3826834333997559 -0.923879532082714 0.0 +0.7071067827963327 -0.7071067795767625 0.0 +0.9238795329816333 -0.3826834312295727 0.0 +0.0 0.3826834333997559 0.923879532082714 +0.0 0.7071067827963327 0.7071067795767625 +0.0 0.9238795329816333 0.3826834312295727 +0.0 0.923879532082714 -0.3826834333997559 +0.0 0.7071067795767625 -0.7071067827963327 +0.0 0.3826834312295727 -0.9238795329816333 +0.0 -0.3826834333997559 -0.923879532082714 +0.0 -0.7071067827963327 -0.7071067795767625 +0.0 -0.9238795329816333 -0.3826834312295727 +0.0 -0.923879532082714 0.3826834333997559 +0.0 -0.7071067795767625 0.7071067827963327 +0.0 -0.3826834312295727 0.9238795329816333 +0.7712990324977224 -0.5261836316964337 -0.3580901956251096 +0.7832272477767324 0.5233357175207212 -0.3356706795464225 +0.3356706794722104 -0.183771065508847 -0.923879532419897 +0.3298749710844383 0.7884309806178305 -0.5191908052479812 +0.3364914460521806 -0.5356371469604048 -0.7745103960114611 +0.4943952293272733 -0.7818671890563442 -0.3798118690820019 +0.6320232753776562 -0.3163329681330331 -0.7074461341000068 +0.9179912585278028 0.1751715968730272 -0.3558187191752205 +0.7153911295286901 0.2972003361018707 -0.632366580404594 +0.9247531464560557 -0.2034408415038567 -0.3216262460183055 +0.3182620562185089 0.5161949160681655 -0.7951427998774474 +0.2034339032461793 0.191010725638466 -0.9602757675277923 +0.3318408257257821 -0.7290341394220538 -0.5986575731908655 +0.5143125430732531 0.08759839546161578 -0.8531173009323357 +0.5429324605256425 -0.5927866926067963 -0.5948346664207352 +0.7870222775749804 0.001326899353937282 -0.6169231507560687 +0.5709878661731511 0.6078735380892875 -0.5517813139041982 +0.5481592105556712 0.79368928263904 -0.2637779416609913 +0.2458184845179587 0.9376281485435064 -0.2458184853264101 +0.3356706784848729 -0.1837710644888045 0.9238795329815229 +0.3808964856541316 0.5323383179218478 0.7559985333904224 +0.9238795324198404 0.1837710655050317 0.3356706794744551 +0.7981217874283407 -0.4624241295859055 0.3862195448302793 +0.3239596594514724 -0.7578760046838178 0.5662809378502687 +0.3379819903774152 0.7843488125142359 0.5201587397016689 +0.7746503893874388 0.54404208556427 0.3223894901462653 +0.6762424833857329 0.3096909027609308 0.6684217593790739 +0.7952068118384303 -0.1011561765969377 0.5978407432937709 +0.9361182415423337 -0.1795770587008401 0.3023817419092033 +0.5777467983327468 -0.4173250611831379 0.7014616385268339 +0.2994166271545614 0.2150136179002493 0.9295799199105109 +0.5623828314586349 -0.7723464303375097 0.2953075387210339 +0.2757249103344169 -0.5179234431073846 0.809772240139701 +0.577313184287358 -0.02857822908601 0.8160225315947462 +0.5386170963264684 0.795865112582042 0.2765688813290775 +0.5745678182057902 0.6089374928036108 0.5468701419352591 +0.2640750467215406 0.9279472313776503 0.2630176904270309 +0.26349397886833 -0.9286880418750045 0.2609778610888406 +-0.7782808169164066 -0.5197822937894205 -0.3522858740894094 +-0.7832272498784564 0.5233357150134625 -0.335670678551431 +-0.3356706794739734 -0.1837710655065382 -0.9238795324197158 +-0.5233357175261585 0.7832272477702242 -0.3356706795531307 +-0.3223894903743268 -0.5440420859519188 -0.7746503890202773 +-0.54510520194119 0.5451051986321889 -0.636962040659266 +-0.7050491528145805 0.3255910057135039 -0.629996975479971 +-0.9313698262072214 -0.2067000597440215 -0.2997087455056128 +-0.769142567051783 -0.2521831216418175 -0.5872166420563893 +-0.4625858909497461 -0.7952774519371975 -0.3918520995662882 +-0.5066801882500737 0.3013212963140176 -0.8077627518167138 +-0.5595946567772138 -0.5549240882847868 -0.6155591574720422 +-0.2885513214281583 0.1343905017269157 -0.9479859323574646 +-0.6326688931838378 0.01593457962769268 -0.7742584586361497 +-0.294002442557577 0.5074470400059689 -0.8099753486121409 +-0.2206785158701794 -0.8109710613787934 -0.5418735371278878 +-0.9037335009357622 0.1116491840495477 -0.4132798313340016 +-0.2761954901451649 0.7695834710164464 -0.5757233123313235 +-0.2657595433294145 0.9261685617758262 -0.2675512293140113 +-0.5610283527690639 -0.3131152481702204 -0.7662936961456306 +-0.8986511462892972 -0.2234519520144048 0.3774855525896111 +-0.7832272498729063 0.5233357150155545 0.3356706785611194 +-0.4063938126531793 0.1675901986980527 0.898196857229839 +-0.3862195463776329 -0.4624241289548164 0.7981217870452071 +-0.5163412162948556 0.7883957432990804 0.3344008078684016 +-0.3223894901399288 0.5440420855535103 0.7746503893976325 +-0.5613920645927912 -0.7538101640409378 0.3414808726719812 +-0.6259579262136886 0.3574127511108542 0.6931325991133507 +-0.5654909040789513 -0.2106776780151426 0.7973925967740848 +-0.2733654792854793 -0.2010368837014314 0.9406675747184216 +-0.6948473160149551 -0.4285218225779893 0.5775432927506401 +-0.7787080013966404 -0.546208098467257 0.3086592971702507 +-0.9346316977779511 0.1510686554600859 0.3219345443505349 +-0.2953075384818359 -0.7723464300921336 0.5623828319212242 +-0.2762115444539761 0.7961810039040885 0.5383335320529707 +-0.788420074759952 0.01466700895224715 0.6149623277599258 +-0.5809154623227699 0.5832144663068862 0.5678011200452993 +-0.2626771544471102 0.9281258472912962 0.2637861333000328 +-0.2665923219327522 -0.9241423246462652 0.2736594556815108 +-0.8002939633135487 0.3205923101786786 0.5067051834530513 +72 89 83 +78 86 83 +83 89 78 +81 89 74 +78 89 81 +78 81 70 +77 78 70 +77 86 78 +4 86 77 +4 77 14 +14 77 70 +14 70 15 +70 81 79 +70 79 15 +15 79 16 +16 79 28 +79 85 28 +16 28 5 +26 72 7 +72 82 7 +72 83 82 +74 89 72 +26 74 72 +27 74 26 +27 85 74 +74 85 81 +81 85 79 +28 85 27 +26 36 27 +36 44 27 +44 46 37 +36 46 44 +27 44 28 +28 17 5 +28 44 37 +28 37 17 +37 46 32 +18 37 32 +17 37 18 +18 32 19 +32 46 38 +38 46 36 +34 45 38 +45 47 38 +38 47 41 +41 47 39 +38 41 32 +32 41 19 +19 41 2 +2 41 39 +34 36 26 +34 38 36 +7 34 26 +25 43 7 +7 43 34 +43 45 34 +42 45 43 +42 43 25 +2 39 8 +8 39 33 +39 40 33 +39 47 40 +40 47 45 +40 48 33 +42 48 40 +40 45 42 +8 33 9 +9 49 10 +33 49 9 +48 49 33 +35 49 48 +35 50 49 +49 50 10 +10 50 3 +3 50 23 +23 50 35 +23 35 24 +35 48 42 +35 42 24 +24 42 25 +25 84 24 +84 87 24 +24 87 23 +73 87 75 +75 87 84 +75 80 76 +75 76 71 +83 86 76 +80 83 76 +82 83 80 +7 82 25 +82 84 25 +80 84 82 +75 84 80 +13 86 4 +71 86 13 +76 86 71 +12 73 71 +73 75 71 +12 71 13 +11 73 12 +11 88 73 +73 88 87 +87 88 23 +3 88 11 +23 88 3 +3 107 22 +11 107 3 +22 107 104 +104 107 94 +94 107 11 +12 94 11 +13 91 12 +91 106 94 +91 94 12 +91 109 106 +102 109 91 +13 102 91 +4 102 13 +95 97 92 +20 95 92 +20 92 6 +97 105 92 +97 109 105 +105 109 102 +106 109 97 +95 106 97 +104 106 95 +94 106 104 +22 104 21 +21 104 95 +21 95 20 +20 52 21 +52 56 21 +52 67 56 +21 56 22 +56 68 22 +22 68 3 +3 68 10 +10 68 66 +66 68 56 +56 67 66 +66 67 57 +9 66 57 +10 66 9 +9 57 8 +57 67 58 +58 67 52 +58 65 59 +62 65 58 +58 59 53 +57 58 53 +8 57 53 +8 53 2 +20 62 52 +52 62 58 +51 62 6 +6 62 20 +31 51 6 +51 65 62 +51 64 61 +31 64 51 +53 60 2 +2 60 19 +19 60 54 +54 60 59 +59 60 53 +61 65 51 +59 65 61 +59 61 54 +55 63 61 +19 54 18 +18 63 17 +54 63 18 +61 63 54 +55 69 63 +63 69 17 +17 69 5 +5 69 29 +29 69 55 +29 55 30 +61 64 55 +55 64 30 +30 64 31 +30 103 29 +100 103 93 +93 103 30 +31 93 30 +31 99 93 +93 99 98 +98 99 92 +92 99 6 +6 99 31 +5 108 16 +29 108 5 +103 108 29 +96 108 103 +16 108 96 +96 101 15 +16 96 15 +100 101 96 +15 101 14 +14 101 90 +14 90 4 +90 102 4 +90 105 102 +90 101 100 +98 105 100 +100 105 90 +92 105 98 +98 100 93 +96 103 100 diff --git a/examples/calderon.jl b/examples/calderon.jl index 4e2d6084..5e2631c2 100644 --- a/examples/calderon.jl +++ b/examples/calderon.jl @@ -1,24 +1,45 @@ -# If you want a scatter plot of the spectra, define `plotresults = true` -# prior to running this script. - using CompScienceMeshes, BEAST using LinearAlgebra Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -println("Mesh with $(numvertices(Γ)) vertices and $(numcells(Γ)) cells.") X = raviartthomas(Γ) -Y = BEAST.buffachristiansen2(Γ) +Y = BEAST.buffachristiansen(Γ) -κ = ω = 1.0; γ = κ*im -T = MWSingleLayer3D(γ) +κ = 1.0; +T = Maxwell3D.singlelayer(wavenumber=κ) N = NCross() -Txx = assemble(T,X,X); println("primal discretisation assembled.") +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) +e = (n × E) × n + +b = assemble(e, X) + +A = assemble(T,X,X); println("primal discretisation assembled.") + Tyy = assemble(T,Y,Y); println("dual discretisation assembled.") Nxy = Matrix(assemble(N,X,Y)); println("duality form assembled.") - iNxy = inv(Nxy); println("duality form inverted.") -A = iNxy' * Tyy * iNxy * Txx +P = iNxy' * Tyy * iNxy + +iT1 = BEAST.GMRESSolver(A; restart=1_500, abstol=1e-8, reltol=1e-8, maxiter=1_500) +iT2 = BEAST.GMRESSolver(A; restart=1_500, abstol=1e-8, reltol=1e-8, maxiter=1_500, left_preconditioner=P) + +u1, ch1 = BEAST.solve(iT1, bx) +u2, ch2 = BEAST.solve(iT2, bx) + +using Plots +Plots.plot(title="log10 residual error vs iteration count") +Plots.plot!(log10.(ch1[:resnorm]), label="A=b", l=2) +Plots.plot!(log10.(ch2[:resnorm]), label="P*A=P*b", l=2) + +ss1 = svdvals(Txx) +ss2 = svdvals(P*Txx) + +Plots.plot(title="singular value spectrum") +Plots.plot!(ss1, label="A", l=2) +Plots.plot!(ss2, label="P*A", l=2) + +@show norm(u1-u2) / norm(u1) @show cond(Txx) @show cond(Tyy) diff --git a/examples/customexcitation.jl b/examples/customexcitation.jl new file mode 100644 index 00000000..b951b44e --- /dev/null +++ b/examples/customexcitation.jl @@ -0,0 +1,41 @@ +macro testitem(name, tags, code) esc(code) end + +using BEAST, Test + +@testitem "example: custom excitation" tags=[:example] begin + using CompScienceMeshes + + κ = 1.0 + + # Custom excitations can be defined as functions of a point in Cartesian space, + # complemented with a function specifying the return type. + E(x) = point(ComplexF64, 1.0, 1.0im, 0.0) * exp(-im*κ*x[3]) + BEAST.scalartype(::typeof(E)) = ComplexF64 + + # Such a custom field plays nicely with the tangential trace + # operators defined as part of the BEAST framework + e = (n × E) × n + + fn = joinpath(pathof(BEAST), "../../examples/assets/sphere45.in") + Γ = readmesh(fn) + # Γ = meshsphere(radius=1.0, h=0.1) + RT = raviartthomas(Γ) + + t = Maxwell3D.singlelayer(wavenumber=κ) + + @hilbertspace j + @hilbertspace k + + Txx = assemble(t[k,j], j∈RT, k∈RT) + ex = assemble(e[k], k∈RT) + + iTXX = BEAST.GMRESSolver(Txx; maxiter=1000, reltol=1e-5) + uX, ch = solve(iTXX, ex) + @test ch.isconverged +end + +using LinearAlgebra +import Plotly + +fcr, geo = facecurrents(uX, RT) +Plotly.plot(patch(geo, norm.(fcr))) \ No newline at end of file diff --git a/examples/disabled/tdhh3d_neumann.jl b/examples/disabled/tdhh3d_neumann.jl index 0c7f6508..adf753ab 100644 --- a/examples/disabled/tdhh3d_neumann.jl +++ b/examples/disabled/tdhh3d_neumann.jl @@ -2,8 +2,8 @@ using CompScienceMeshes using BEAST using LinearAlgebra -# G = meshsphere(1.0, 0.25) -G = meshsphere(1.0, 0.30) +#G = meshsphere(1.0, 0.30) +G = CompScienceMeshes.meshmobius(h=0.2) c = 1.0 S = BEAST.HH3DSingleLayerTDBIO(c) @@ -20,9 +20,9 @@ h = BEAST.gradient(e) # X = lagrangecxd0(G) X = lagrangecxd0(G) -Y = duallagrangec0d1(G) -# Y = lagrangecxd0(G) - +#Y = duallagrangec0d1(G) +Y = lagrangecxd0(G) +numfunctions(X) # X = duallagrangecxd0(G, boundary(G)) # Y = lagrangec0d1(G) @@ -31,21 +31,29 @@ Y = duallagrangec0d1(G) Δt, Nt = 0.16, 300 # T = timebasisc0d1(Δt, Nt) P = timebasiscxd0(Δt, Nt) -H = timebasisc0d1(Δt, Nt) +#H = timebasisc0d1(Δt, Nt) δ = timebasisdelta(Δt, Nt) # assemble the right hand side bd = assemble(n⋅h, X ⊗ P) -Z1d = assemble(Id ⊗ Id, X ⊗ P, X ⊗ P, Val{:bandedstorage}) -Z0d = assemble(D, X ⊗ P, X ⊗ P, Val{:bandedstorage}) +Z1d = assemble(Id ⊗ Id, X ⊗ P, X ⊗ P) +Z0d = assemble(D, X ⊗ P, X ⊗ P) Zd = Z0d + (-0.5)*Z1d u = marchonintime(inv(Zd[:,:,1]), Zd, bd, Nt) bs = assemble(e, X ⊗ δ) -Zs = assemble(S, X ⊗ δ, X ⊗ P, Val{:bandedstorage}) +Zs = assemble(S, X ⊗ δ, X ⊗ P) v = marchonintime(inv(Zs[:,:,1]), Zs, -bs, Nt) + +tdacusticsl = @discretise D[j′,j] == 1.0(n⋅h)[j′] j∈ (X ⊗ P) j′∈ (X ⊗ δ) +xacusticsl = solve(tdacusticsl) + + +import Plots +Plots.plot(xacusticsl[1000,2900:3000]) + U, Δω, ω0 = fouriertransform(u, Δt, 0.0, 2) V, Δω, ω0 = fouriertransform(v, Δt, 0.0, 2) diff --git a/examples/discontinuous_pairing.jl b/examples/discontinuous_pairing.jl new file mode 100644 index 00000000..fb7be053 --- /dev/null +++ b/examples/discontinuous_pairing.jl @@ -0,0 +1,81 @@ +# compute the continuity and infsup constants for various continuous and discontinuous pairings + +using CompScienceMeshes +using BEAST + +using Makeitso +using BakerStreet + +h = 0.1 +Γ = meshcuboid(1.0, 1.0, 1.0, h) + +id = BEAST.Identity() +nx = BEAST.NCross() +s = Maxwell3D.singlelayer(gamma=1.0) +t = Maxwell3D.singlelayer(wavenumber=1.0) + +X = raviartthomas(Γ) +Y = buffachristiansen(Γ) + +A = assemble(id, Y, X) +B = assemble(nx, Y, X) +C = assemble(t, X, X) + +SXX = Symmetric(-assemble(s, X, X)) +SYY = Symmetric(-assemble(s, Y, Y)) + +using LinearAlgebra +HX = sqrt(SXX) +HY = sqrt(SYY) + +QX = inv(HX) +QY = inv(HY) + +svdA = svd(QY*A*QX) +svdB = svd(QY*B*QX) +svdC = svd(QX*C*QX) + +using Plots +plot(svdA.S) +plot!(svdB.S) +plot!(svdC.S) + +E = Maxwell3D.planewave( + direction=point(0,0,1), + polarization=point(1,0,0), + wavenumber=1.0) + +e = (n × E) × n + +struct SingField{T} <: BEAST.Functional + a::T + b::T +end + +function (f::SingField)(p) + x = cartesian(p) + x[3] ≈ 1.0 || return point(0,0,0) + return sqrt(f.b - x[2]) * sqrt(x[2] - f.a) / sqrt(f.b-x[1]) / sqrt(x[1]-f.a) * point(0,1,0) +end + +function BEAST.integrand(f::SingField, gx, ϕx) + return dot(gx[1], ϕx) +end + +e = SingField(0.0, 1.0) + +b = assemble(e, X) +G = assemble(id, X, X) +u = G \ b + +import Plotly +fcr, geo = facecurrents(u, X) +Plotly.plot( + [ + # patch(geo, norm.(fcr), opacity=0.5), + CompScienceMeshes.cones(geo, real.(fcr), sizeref=10.0), + ]) + +q = adjoint(svdA.U) * HY * b +plot(log10.(abs.(q))) +plot!(log10.(abs.(svdA.S))) \ No newline at end of file diff --git a/examples/efie.jl b/examples/efie.jl index 89df72d6..aeb5223c 100644 --- a/examples/efie.jl +++ b/examples/efie.jl @@ -1,8 +1,8 @@ using CompScienceMeshes using BEAST -Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -# Γ = meshsphere(radius=1.0, h=0.1) +# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +Γ = meshsphere(radius=1.0, h=0.1) @show length(Γ) X = raviartthomas(Γ) diff --git a/examples/efie_convergence_cuboid.jl b/examples/efie_convergence_cuboid.jl index 6c4c27ce..530489e5 100644 --- a/examples/efie_convergence_cuboid.jl +++ b/examples/efie_convergence_cuboid.jl @@ -33,50 +33,67 @@ using DataFrames α = 0.8 h = collect(0.4 * α.^(0:15)) runsims(payload, "solutions"; h) + return loadsims("solutions") end -@make solutions -df = loadsims("solutions") -error() - -using LinearAlgebra -using Plots -plot(df.h, real.(df.u_Snorm), marker=:.) - -# α = df.h[1] / df.h[2] -W1 = reverse(df.u_Snorm) -# W1 = reverse((df.h).^2.231) -W2 = Iterators.drop(W1,1) -W3 = Iterators.drop(W1,2) -p = [log(abs((w2-w1)/(w3-w2))) / log(1/α) for (w1,w2,w3) in zip(W1,W2,W3)] -plot(collect(Iterators.drop(reverse(df.h),2)), p, marker=:.) - -refsol = df.u_Snorm[1] -plot(log.(df.h), log.(abs.(df.u_Snorm .- refsol)), marker=:.) -plot!(log.(df.h), 1.5 * log.(df.h)) - -fcr, geo = facecurrents(df.u[1], df.X[1]) -import Plotly -Plotly.plot(patch(geo, log10.(norm.(fcr)))) - -uref = df.u[1] -Xref = df.X[1] -Γref = geometry(Xref) -errs = zeros(length(df.h)) -s = -Maxwell3D.singlelayer(gamma=1.0) -@hilbertspace k -@hilbertspace j -S11 = assemble(s, Xref, Xref) -for i in reverse(2:length(df.h)) - @show i, df.h[i] - u = df.u[i] - X = df.X[i] - Γ = geometry(X) +@target compute_errors (solutions) -> begin + df = loadsims("solutions") + numrows = size(df,1) + uref = df.u[1] + Xref = df.X[1] + Γref = geometry(Xref) + + errs = zeros(numrows) + s = -Maxwell3D.singlelayer(gamma=1.0) qstrat12 = BEAST.NonConformingIntegralOpQStrat(BEAST.DoubleNumSauterQstrat(3, 4, 6, 6, 6, 6)) - S12 = assemble(s, Xref, X; quadstrat=[qstrat12]) - S22 = assemble(s, X, X) - errs[i] = sqrt(real(dot(uref, S11*uref) - 2*real(dot(uref, S12*u)) + real(dot(u, S22*u)))) - @show errs[i] + + S11 = assemble(s, Xref, Xref) + term11 = real(dot(uref, S11*uref)) + + for i in reverse(2:numrows) + r = df[i,:] + (;h, u, X) = r + @show r, length(u) + Γ = geometry(X) + + S12 = assemble(s, Xref, X; quadstrat=[qstrat12]) + S22 = assemble(s, X, X) + errs[i] = sqrt(term11 - 2*real(dot(uref, S12*u)) + real(dot(u, S22*u))) + @show errs[i] + end + + return errs end + +@target weak_errors (solutions) -> begin + df = loadsims("solutions") + + α = df.h[1] / df.h[2] + W1 = reverse(df.u_Snorm) + W2 = Iterators.drop(W1,1) + W3 = Iterators.drop(W1,2) + p = [log(abs((w2-w1)/(w3-w2))) / log(1/α) for (w1,w2,w3) in zip(W1,W2,W3)] +end + + +@make compute_errors +# df = loadsims("solutions") +# error() + +# using LinearAlgebra +# using Plots +# plot(df.h, real.(df.u_Snorm), marker=:.) +# plot(df.h[end:-1:2], p, marker=:.) + +# refsol = df.u_Snorm[1] +# plot(log.(df.h), log.(abs.(df.u_Snorm .- refsol)), marker=:.) +# plot!(log.(df.h), 1.5 * log.(df.h)) + +# fcr, geo = facecurrents(df.u[1], df.X[1]) +# import Plotly +# Plotly.plot(patch(geo, log10.(norm.(fcr)))) + + + diff --git a/examples/efie_convergence_screen.jl b/examples/efie_convergence_screen.jl new file mode 100644 index 00000000..bd939708 --- /dev/null +++ b/examples/efie_convergence_screen.jl @@ -0,0 +1,104 @@ +using CompScienceMeshes +using BEAST + +using Makeitso +using BakerStreet +using DataFrames + +fn = splitext(splitdir(@__FILE__)[end])[1] + +@target solutions () -> begin + function payload(;h) + d = 1.0 + # Γ = meshsphere(radius=d, h=h) + Γ = meshrectangle(d, d, h, 3) + # Γ = meshcuboid(d, d, d/2, h) + X = raviartthomas(Γ) + + κ, η = 1.0, 1.0 + t = Maxwell3D.singlelayer(wavenumber=κ) + s = -Maxwell3D.singlelayer(gamma=κ) + E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) + e = (n × E) × n + + @hilbertspace j + @hilbertspace k + + A = assemble(t[k,j], X, X) + S = assemble(s[k,j], X, X) + b = assemble(e[k], X) + + Ai = BEAST.GMRESSolver(A; restart=1500, abstol=1e-8, maxiter=10_000) + u = Ai * b + u_Snorm = real(sqrt(dot(u, S*u))) + return (;u, X, u_Snorm) + end + + α = 0.8 + h = collect(0.4 * α.^(0:15)) + runsims(payload, "$(fn)/solutions"; h) + return true + # return loadsims("$(fn)/solutions") +end + +@target errors (solutions) -> begin + df = loadsims("$(fn)/solutions") + numrows = size(df,1) + + uref = df.u[1] + Xref = df.X[1] + Γref = geometry(Xref) + + errs = zeros(numrows) + s = -Maxwell3D.singlelayer(gamma=1.0) + qstrat12 = BEAST.NonConformingIntegralOpQStrat(BEAST.DoubleNumSauterQstrat(3, 4, 6, 6, 6, 6)) + + S11 = assemble(s, Xref, Xref) + term11 = real(dot(uref, S11*uref)) + + function payload(;h) + r = getrow(df; h=h) + (;h, u, X) = r + Γ = geometry(X) + + S12 = assemble(s, Xref, X; quadstrat=[qstrat12]) + S22 = assemble(s, X, X) + err = sqrt(term11 - 2*real(dot(uref, S12*u)) + real(dot(u, S22*u))) + return (;err) + end + + runsims(payload, "$(fn)/errors", h=df.h[end:-1:2]) + return true +end + + +@target weak_errors (solutions) -> begin + df = loadsims("sphere/solutions") + + α = df.h[1] / df.h[2] + W1 = reverse(df.u_Snorm) + W2 = Iterators.drop(W1,1) + W3 = Iterators.drop(W1,2) + p = [log(abs((w2-w1)/(w3-w2))) / log(1/α) for (w1,w2,w3) in zip(W1,W2,W3)] +end + + +@make errors + +df = loadsims("$fn/solutions") +hmin, i = findmin(df.h) +(;u, X) = df[i,:] + +using LinearAlgebra +import Plotly +using Plots +fcr, geo = facecurrents(u, X) +Plotly.plot(patch(geo, log10.(norm.(fcr)))) + +df2 = loadsims("$fn/errors") +plot(log10.(df2.h), log10.(df2.err), marker=:.) +plot!(log10.(df2.h), -0.25 .+ 0.5*log10.(df2.h), label="p = 0.5") +plot!(log10.(df2.h), 0.1 .+ 0.7*log10.(df2.h), label="p = 0.625") +plot!(log10.(df2.h), 0.2 .+ 0.75*log10.(df2.h), label="p = 0.75") +plot!(log10.(df2.h), 0.625 .+ 1.0*log10.(df2.h), label="p = 1") +plot!(log10.(df2.h), 1.5 .+ 1.5*log10.(df2.h), label="p = 1.5") \ No newline at end of file diff --git a/examples/efie_convergence_sphere.jl b/examples/efie_convergence_sphere.jl new file mode 100644 index 00000000..6c4a4f05 --- /dev/null +++ b/examples/efie_convergence_sphere.jl @@ -0,0 +1,133 @@ +using CompScienceMeshes +using BEAST + +using Makeitso +using BakerStreet +using DataFrames + +fn = splitext(splitdir(@__FILE__)[end])[1] + +@target solutions () -> begin + function payload(;h) + d = 1.0 + Γ = meshsphere(radius=d, h=h) + # Γ = meshcuboid(d, d, d/2, h) + X = raviartthomas(Γ) + + κ, η = 1.0, 1.0 + t = Maxwell3D.singlelayer(wavenumber=κ) + s = -Maxwell3D.singlelayer(gamma=κ) + E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) + e = (n × E) × n + + @hilbertspace j + @hilbertspace k + + A = assemble(t[k,j], X, X) + S = assemble(s[k,j], X, X) + b = assemble(e[k], X) + + Ai = BEAST.GMRESSolver(A; restart=1500, abstol=1e-8, maxiter=10_000) + u = Ai * b + u_Snorm = real(sqrt(dot(u, S*u))) + return (;u, X, u_Snorm) + end + + α = 0.8 + h = collect(0.4 * α.^(0:15)) + runsims(payload, "$fn/solutions"; h) + return loadsims("$fn/solutions") +end + + +@target errors (solutions) -> begin + df = loadsims("$(fn)/solutions") + numrows = size(df,1) + + uref = df.u[1] + Xref = df.X[1] + Γref = geometry(Xref) + + errs = zeros(numrows) + s = -Maxwell3D.singlelayer(gamma=1.0) + qstrat12 = BEAST.NonConformingIntegralOpQStrat(BEAST.DoubleNumSauterQstrat(3, 4, 6, 6, 6, 6)) + + S11 = assemble(s, Xref, Xref) + term11 = real(dot(uref, S11*uref)) + + function payload(;h) + r = getrow(df; h=h) + (;h, u, X) = r + Γ = geometry(X) + + S12 = assemble(s, Xref, X; quadstrat=[qstrat12]) + S22 = assemble(s, X, X) + err = sqrt(term11 - 2*real(dot(uref, S12*u)) + real(dot(u, S22*u))) + return (;err) + end + + runsims(payload, "$(fn)/errors", h=df.h[end:-1:2]) + return loadsims("$(fn)/errors") +end + +# @target compute_errors (solutions) -> begin +# df = loadsims("sphere/solutions") +# numrows = size(df,1) + +# uref = df.u[1] +# Xref = df.X[1] +# Γref = geometry(Xref) + +# errs = zeros(numrows) +# s = -Maxwell3D.singlelayer(gamma=1.0) +# qstrat12 = BEAST.NonConformingIntegralOpQStrat(BEAST.DoubleNumSauterQstrat(3, 4, 6, 6, 6, 6)) + +# S11 = assemble(s, Xref, Xref) +# term11 = real(dot(uref, S11*uref)) + +# for i in reverse(2:numrows) +# r = df[i,:] +# (;h, u, X) = r +# @show r, length(u) +# Γ = geometry(X) + +# S12 = assemble(s, Xref, X; quadstrat=[qstrat12]) +# S22 = assemble(s, X, X) +# errs[i] = sqrt(term11 - 2*real(dot(uref, S12*u)) + real(dot(u, S22*u))) +# @show errs[i] +# end + +# return errs +# end + + +@target weak_errors (solutions) -> begin + df = loadsims("sphere/solutions") + + α = df.h[1] / df.h[2] + W1 = reverse(df.u_Snorm) + W2 = Iterators.drop(W1,1) + W3 = Iterators.drop(W1,2) + p = [log(abs((w2-w1)/(w3-w2))) / log(1/α) for (w1,w2,w3) in zip(W1,W2,W3)] +end + + +@make errors +# df = loadsims("solutions") +# error() + +# using LinearAlgebra +# using Plots +# plot(df.h, real.(df.u_Snorm), marker=:.) +# plot(df.h[end:-1:2], p, marker=:.) + +# refsol = df.u_Snorm[1] +# plot(log.(df.h), log.(abs.(df.u_Snorm .- refsol)), marker=:.) +# plot!(log.(df.h), 1.5 * log.(df.h)) + +# fcr, geo = facecurrents(df.u[1], df.X[1]) +# import Plotly +# Plotly.plot(patch(geo, log10.(norm.(fcr)))) + + + diff --git a/examples/efie_graded.jl b/examples/efie_graded.jl new file mode 100644 index 00000000..da2988a5 --- /dev/null +++ b/examples/efie_graded.jl @@ -0,0 +1,44 @@ +using CompScienceMeshes +using BEAST + +# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +# Γ = meshsphere(radius=1.0, h=0.1) +Γ = CompScienceMeshes.meshrectangle_unit_tri_2graded(0.25, 3) +@show length(Γ) +X = raviartthomas(Γ) + +κ, η = 4.0, 1.0 +t = Maxwell3D.singlelayer(wavenumber=κ) +E = Maxwell3D.planewave(direction=ŷ, polarization=x̂, wavenumber=κ) +e = (n × E) × n + +@hilbertspace j +@hilbertspace k + +qs = BEAST.DoubleNumWiltonSauterQStrat{Int64, Int64}(11, 11, 11, 12, 8, 8, 8, 8) +qsl = BEAST.SingleNumQStrat(13) + +U = BEAST.DirectProductSpace([X]) +A = assemble(t[k,j], U, U; quadstrat=qs) +b = assemble(e[k], U; quadstrat=qsl) + +u = AbstractMatrix(A) \ b +fcr, geo = facecurrents(u, X) + +import Plotly +using LinearAlgebra +Plotly.plot(patch(geo, real.(getindex.(fcr,1)), caxis=(-10,10))) +Plotly.plot(patch(geo, real.(getindex.(fcr,2)), caxis=(-10,10))) +Plotly.plot(patch(geo, norm.(fcr), caxis=(0,10))) + +# efie = @discretise t[k,j]==e[k] j∈X k∈X +# u, ch = BEAST.gmres_ch(efie; restart=1500) + +# include("utils/postproc.jl") +# include("utils/plotresults.jl") +Id = Identity() +Nuu = assemble(Id[k,j], U, U) +v = AbstractMatrix(Nuu) \ b +fcr, geo = facecurrents(v, X) + +Plotly.plot(patch(geo, real(getindex.(fcr,1)))) \ No newline at end of file diff --git a/examples/helmholtz2d.jl b/examples/helmholtz2d.jl index 8227e108..82987645 100644 --- a/examples/helmholtz2d.jl +++ b/examples/helmholtz2d.jl @@ -4,8 +4,10 @@ h = 2π / 51; Γ = meshcircle(1.0, h) X, Y = lagrangecxd0(Γ), lagrangec0d1(Γ) κ = 1.0 -S, Dᵀ = SingleLayer(κ), DoubleLayerTransposed(κ) -D, N = DoubleLayer(κ), HyperSingular(κ) +S = Helmholtz2D.singlelayer(; wavenumber=κ) +Dᵀ = Helmholtz2D.doublelayer_transposed(; wavenumber = κ) +D = Helmholtz2D.doublelayer(; wavenumber=κ) +N = Helmholtz2D.hypersingular(; wavenumber=κ) I = Identity() # TM scattering diff --git a/src/BEAST.jl b/src/BEAST.jl index fbabc3c9..35846316 100644 --- a/src/BEAST.jl +++ b/src/BEAST.jl @@ -56,10 +56,6 @@ export TimeBasisDeltaShifted export ntrace export strace export ttrace -export SingleLayer -export DoubleLayer -export DoubleLayerTransposed -export HyperSingular export HH3DSingleLayerTDBIO export HH3DDoubleLayerTDBIO export ∂n, grad @@ -195,6 +191,7 @@ include("bases/tensorbasis.jl") include("operator.jl") +include("quadrature/strategies/quadstrat.jl") include("quadrature/quadstrats.jl") include("quadrature/doublenumqstrat.jl") include("quadrature/doublenumsauterqstrat.jl") @@ -206,10 +203,13 @@ include("quadrature/commonfaceoverlappingedgeqstrat.jl") include("quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl") include("quadrature/strategies/testrefinestrialqstrat.jl") include("quadrature/strategies/trialrefinestestqstrat.jl") +include("quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl") + include("excitation.jl") include("gridfunction.jl") include("localop.jl") +include("operators/quasilocalops.jl") include("multiplicativeop.jl") include("identityop.jl") include("integralop.jl") @@ -219,11 +219,13 @@ include("interpolation.jl") include("quadrature/rules/momintegrals.jl") include("quadrature/doublenumints.jl") include("quadrature/singularityextractionints.jl") +include("quadrature/SauterSchwabQuadrature1D.jl") include("quadrature/sauterschwabints.jl") include("quadrature/nonconformingoverlapqrule.jl") include("quadrature/nonconformingtouchqrule.jl") include("quadrature/rules/testrefinestrialqrule.jl") include("quadrature/rules/trialrefinestestqrule.jl") +include("quadrature/rules/testinbaryrefoftrialqrule.jl") include("postproc.jl") include("postproc/segcurrents.jl") @@ -237,12 +239,18 @@ include("timedomain/rkcq.jl") include("timedomain/zdomain.jl") include("timedomain/td_symmetric_quadstrat.jl") +include("quadrature/strategies/timedomain/excitation/numspacenumtimeqstrat.jl") + +include("quadrature/rules/timedomain/excitation/multiquadqrule.jl") +include("quadrature/rules/timedomain/excitation/singlequad2qrule.jl") # Support for Maxwell equations include("maxwell/mwexc.jl") include("maxwell/mwops.jl") -include("maxwell/wiltonints.jl") +include("maxwell/qlmwops.jl") + include("maxwell/nxdbllayer.jl") +include("maxwell/wiltonints.jl") include("maxwell/sauterschwabints_bdm_rt.jl") #include("maxwell/sauterschwabints_rt.jl") #include("maxwell/sauterschwabints_bdm.jl") @@ -254,8 +262,6 @@ include("maxwell/maxwell.jl") include("maxwell/sourcefield.jl") # Support for the Helmholtz equation -include("helmholtz2d/helmholtzop.jl") - include("helmholtz3d/hh3dexc.jl") include("helmholtz3d/hh3dops.jl") include("helmholtz3d/nitsche.jl") @@ -265,6 +271,10 @@ include("helmholtz3d/hh3d_sauterschwabqr.jl") include("helmholtz3d/helmholtz3d.jl") include("helmholtz3d/wiltonints.jl") +include("helmholtz2d/hh2dexc.jl") +include("helmholtz2d/hh2dops.jl") +include("helmholtz2d/helmholtz2d.jl") + #suport for Volume Integral equation include("volumeintegral/vie.jl") include("volumeintegral/vieexc.jl") diff --git a/src/bases/bdmdiv.jl b/src/bases/bdmdiv.jl index 97f08083..52ba6b01 100644 --- a/src/bases/bdmdiv.jl +++ b/src/bases/bdmdiv.jl @@ -19,7 +19,8 @@ end function brezzidouglasmarini(mesh, cellpairs::Array{Int,2}) - @warn "brezzidouglasmarini(mesh, cellpairs) assumes mesh is oriented" + @assert CompScienceMeshes.isoriented(mesh) "brezzidouglasmarini assumes mesh is oriented" + # @warn "brezzidouglasmarini(mesh, cellpairs) assumes mesh is oriented" @assert size(cellpairs,1) == 2 diff --git a/src/bases/global/gwpglobal.jl b/src/bases/global/gwpglobal.jl index f302632f..b0bda1c8 100644 --- a/src/bases/global/gwpglobal.jl +++ b/src/bases/global/gwpglobal.jl @@ -39,7 +39,7 @@ end localspace = BEAST.GWPCurlRefSpace{T,degree}() dofs = BEAST.GWPGlobalEdgeDoFs(degree) A = BEAST.globaldofs(edge_ch, face_ch, localspace, dofs) - display(round.(A, digits=3)) + # display(round.(A, digits=3)) end function globaldofs(edge_ch, face_ch, localspace, dof::GWPGlobalFaceDoFs) @@ -92,7 +92,7 @@ end localspace = BEAST.GWPCurlRefSpace{T,degree}() dofs = BEAST.GWPGlobalFaceDoFs(degree) A = BEAST.globaldofs(edge_ch, face_ch, localspace, dofs) - display(round.(A, digits=3)) + # display(round.(A, digits=3)) end function _addshapes!(fns, cell, gids, lids, β) diff --git a/src/bases/local/bdmlocal.jl b/src/bases/local/bdmlocal.jl index c24af58d..d1a87df1 100644 --- a/src/bases/local/bdmlocal.jl +++ b/src/bases/local/bdmlocal.jl @@ -25,6 +25,8 @@ end divergence(ref::BDMRefSpace, sh, el) = [Shape(sh.cellid, 1, sh.coeff/(2*volume(el)))] +numfunctions(x::BDMRefSpace, dom::CompScienceMeshes.ReferenceSimplex{2}) = 6 + const _vert_perms_bdm = [ (1,2,3), (2,3,1), diff --git a/src/bases/local/laglocal.jl b/src/bases/local/laglocal.jl index b8216152..0907f6b1 100644 --- a/src/bases/local/laglocal.jl +++ b/src/bases/local/laglocal.jl @@ -12,8 +12,8 @@ numfunctions(s::LagrangeRefSpace{T,Dg}, ch::CompScienceMeshes.ReferenceSimplex{D #valuetype(ref::LagrangeRefSpace{T}, charttype) where {T} = # SVector{numfunctions(ref), Tuple{T,T}} -valuetype(ref::LagrangeRefSpace{T}, charttype) where {T} = - SVector{1, T} +valuetype(ref::LagrangeRefSpace{T}, charttype) where {T} = T + # SVector{1, T} # Evaluate constant lagrange elements on anything @@ -475,4 +475,26 @@ function interpolate(fields, interpolant::LagrangeRefSpace{T,Degree,3}, chart) w Q[:,i] .= vals[i] end return Q -end \ No newline at end of file +end + + +function interpolate!(out, fields, interpolant::LagrangeRefSpace{T,Degree,3}, chart) where {T,Degree} + Is = zip((0:Degree), range(0,1,length=Degree+1)) + idx = 0 + for (i,ui) in Is + for (j,vj) in Is + for (k,wk) in Is + i + j + k == Degree || continue; idx += 1 + @assert ui + vj + wk ≈ 1 + p = neighborhood(chart, (ui,vj)) + vals = fields(p) + for (g, val) in zip(axes(out, 1), vals) + out[g,idx] = val +end end end end end + +function interpolate!(out, fields, interpolant::LagrangeRefSpace{T,0,3}, chart) where {T} + p = center(chart) + vals = fields(p) + for (g, val) in zip(axes(out, 1), vals) + out[g,1] = val +end end \ No newline at end of file diff --git a/src/bases/local/ncrossbdmlocal.jl b/src/bases/local/ncrossbdmlocal.jl index 5f7fc211..cb332499 100644 --- a/src/bases/local/ncrossbdmlocal.jl +++ b/src/bases/local/ncrossbdmlocal.jl @@ -18,3 +18,5 @@ function (f::NCrossBDMRefSpace{T})(p) where T (value= n × (u*tu)/j, curl=d), (value= n × (v*tv)/j, curl=d),] end + +numfunctions(x::NCrossBDMRefSpace, dom::CompScienceMeshes.ReferenceSimplex{2}) = 6 diff --git a/src/bases/local/rtlocal.jl b/src/bases/local/rtlocal.jl index c9350014..5aca7cff 100644 --- a/src/bases/local/rtlocal.jl +++ b/src/bases/local/rtlocal.jl @@ -101,6 +101,17 @@ function interpolate(interpolant::RefSpace, chart1, interpolee::RefSpace, chart2 interpolate(fields, interpolant, chart1) end +function interpolate!(out, interpolant::RefSpace, chart1, interpolee::RefSpace, chart2) + function fields(p) + x = cartesian(p) + v = carttobary(chart2, x) + r = neighborhood(chart2, v) + fieldvals = [f.value for f in interpolee(r)] + end + + interpolate!(out, fields, interpolant, chart1) +end + function interpolate(interpolant::RefSpace, chart1, interpolee::RefSpace, chart2, ch1toch2) function fields(p1) @@ -113,35 +124,81 @@ function interpolate(interpolant::RefSpace, chart1, interpolee::RefSpace, chart2 interpolate(fields, interpolant, chart1) end +function interpolate!(out, interpolant::RefSpace, chart1, interpolee::RefSpace, chart2, ch1toch2) + function fields(p1) + u1 = parametric(p1) + u2 = cartesian(ch1toch2, u1) + p2 = neighborhood(chart2, u2) + fieldvals = [f.value for f in interpolee(p2)] + end -function interpolate(fields, interpolant::RTRefSpace, chart) - Q = map(faces(chart)) do face - p = center(face) - x = cartesian(p) - u = carttobary(chart, x) - q = neighborhood(chart, u) - n = normal(q) + interpolate!(out, fields, interpolant, chart1) +end + + +function interpolate(fields, interpolant::RTRefSpace{T}, chart) where {T} + Q = map(CompScienceMeshes.subcharts(chart, Val{1})) do (face, inj) + + u_face = T(1//2) + p_face = neighborhood(face, (u_face,)) + t_face = -tangents(p_face, 1) + u_chart = cartesian(inj, u_face) + p_chart = neighborhood(chart, u_chart) + n_chart = normal(p_chart) + m_face = cross(t_face, n_chart) + + # p = center(face) + # x = cartesian(p) + # u = carttobary(chart, x) + # q = neighborhood(chart, u) + # n = normal(q) # minus because in CSM the tangent points towards vertex[1] - t = -tangents(p,1) - m = cross(t,n) + # t = -tangents(p,1) + # m = cross(t,n) - fieldvals = fields(q) - q = [dot(fv,m) for fv in fieldvals] + fieldvals = fields(p_chart) + q = [dot(fv,m_face) for fv in fieldvals] end return hcat(Q...) end +function interpolate!(out, fields, interpolant::RTRefSpace{T}, chart) where {T} + + for (f,(face, inj)) in zip(axes(out,2), + CompScienceMeshes.subcharts(chart, Val{1})) + + u_face = T(1//2) + p_face = neighborhood(face, (u_face,)) + t_face = -tangents(p_face, 1) + u_chart = cartesian(inj, u_face) + p_chart = neighborhood(chart, u_chart) + n_chart = normal(p_chart) + m_face = cross(t_face, n_chart) + vals = fields(p_chart) + for (g,val) in zip(axes(out, 1), vals) + out[g,f] = dot(m_face, val) +end end end + + function restrict(ϕ::RefSpace, dom1, dom2) interpolate(ϕ, dom2, ϕ, dom1) end +function restrict!(out, ϕ::RefSpace, dom1, dom2) + interpolate!(out, ϕ, dom2, ϕ, dom1) +end + function restrict(ϕ::RefSpace, dom1, dom2, dom2todom1) interpolate(ϕ, dom2, ϕ, dom1, dom2todom1) end +function restrict!(out, ϕ::RefSpace, dom1, dom2, dom2todom1) + interpolate!(out, ϕ, dom2, ϕ, dom1, dom2todom1) +end + const _dof_rtperm_matrix = [ @SMatrix[1 0 0; # 1. {1,2,3} 0 1 0; @@ -167,6 +224,3 @@ const _dof_rtperm_matrix = [ 0 1 0; 1 0 0] ] - - -# Support for zeroth order elements on quadrilaterals diff --git a/src/bases/local/rtqlocal.jl b/src/bases/local/rtqlocal.jl index f1472d52..cf6e13ea 100644 --- a/src/bases/local/rtqlocal.jl +++ b/src/bases/local/rtqlocal.jl @@ -63,8 +63,8 @@ end val1 = f(p)[1] val2 = sum(Q[1,i] * ϕ.value for (i,ϕ) in zip(axes(Q,2), rtq(p))) - @show val1 - @show val2 + # @show val1 + # @show val2 @test val1 ≈ val2 end @@ -113,7 +113,7 @@ end point(0,1,0)] chart1 = CompScienceMeshes.Quadrilateral(vertices...) for I in BEAST._vrtperm_matrix_rtq - @show I + # @show I chart2 = CompScienceMeshes.Quadrilateral( vertices[I[1]], vertices[I[2]], diff --git a/src/bases/ncrossbdmspace.jl b/src/bases/ncrossbdmspace.jl index c5ed0364..77f5aba0 100644 --- a/src/bases/ncrossbdmspace.jl +++ b/src/bases/ncrossbdmspace.jl @@ -17,7 +17,8 @@ end function ncrossbdm(mesh, cellpairs::Array{Int,2}) - @warn "brezzidouglasmarini(mesh, cellpairs) assumes mesh is oriented" + @assert CompScienceMeshes.isoriented(mesh) "brezzidouglasmarini assumes mesh is oriented" + # @warn "brezzidouglasmarini(mesh, cellpairs) assumes mesh is oriented" @assert size(cellpairs,1) == 2 diff --git a/src/bases/rtqspace.jl b/src/bases/rtqspace.jl index 7f701ee6..ce0ad06c 100644 --- a/src/bases/rtqspace.jl +++ b/src/bases/rtqspace.jl @@ -68,7 +68,7 @@ end @testitem "RTQSpace construction" begin using CompScienceMeshes - m = CompScienceMeshes.meshrectangle(2.0, 2.0, 1.0; structured=:quadrilateral) + m = CompScienceMeshes.meshrectangle(2.0, 2.0, 1.0; element=:quadrilateral) edges = skeleton(m, 1) edges_bnd = boundary(m) # @show length(edges_bnd) @@ -85,7 +85,7 @@ end @testitem "RTQSpace assembly data" begin using CompScienceMeshes - m = CompScienceMeshes.meshrectangle(2.0, 2.0, 1.0; structured=:quadrilateral) + m = CompScienceMeshes.meshrectangle(2.0, 2.0, 1.0; element=:quadrilateral) edges = skeleton(m, 1) edges_bnd = boundary(m) pred = !in(edges_bnd) diff --git a/src/excitation.jl b/src/excitation.jl index 17bc17f0..5051517e 100644 --- a/src/excitation.jl +++ b/src/excitation.jl @@ -1,6 +1,7 @@ -abstract type Functional end +abstract type Functional{T} end +scalartype(::Functional{T}) where {T} = T defaultquadstrat(fn::Functional, basis) = SingleNumQStrat(8) quaddata(fn::Functional, refs, cells, qs::SingleNumQStrat) = @@ -37,10 +38,11 @@ end function assemble!(field::Functional, tfs::Space, store; quadstrat=defaultquadstrat(field, tfs)) + qs = quadstrat(field, tfs) tels, tad = assemblydata(tfs) trefs = refspace(tfs) - qd = quaddata(field, trefs, tels, quadstrat) + qd = quaddata(field, trefs, tels, qs) tgeo = geometry(tfs) tdom = domain(chart(tgeo, first(tgeo))) @@ -49,7 +51,7 @@ function assemble!(field::Functional, tfs::Space, store; for (t, tcell) in enumerate(tels) # compute the testing with the reference elements - qr = quadrule(field, trefs, t, tcell, qd, quadstrat) + qr = quadrule(field, trefs, t, tcell, qd, qs) blocal = celltestvalues(trefs, tcell, field, qr) for i in 1 : num_trefs @@ -136,3 +138,64 @@ function celltestvalues(tshs::subReferenceSpace{T,D}, tcell, field, qr) where {T return interactions end + + + +### Framework allowing to create Functional from Field by taking the trace +mutable struct CrossTraceMW{T,F} <: Functional{T} + field::F +end + +mutable struct TangTraceMW{T,F} <: Functional{T} + field::F +end + +CrossTraceMW(f::F) where {F} = CrossTraceMW{scalartype(f), F}(f) +CrossTraceMW{T}(f::F) where {T,F} = CrossTraceMW{T,F}(f) + +TangTraceMW(f::F) where {F} = TangTraceMW{scalartype(f), F}(f) +TangTraceMW{T}(f::F) where {T,F} = TangTraceMW{T,F}(f) + +# scalartype(x::CrossTraceMW) = scalartype(x.field) +# scalartype(::CrossTraceMW{T}) where {T} = T +# scalartype(::TangTraceMW{T}) where {T} = T + + +# scalartype(t::TangTraceMW) = scalartype(t.field) + +cross(::NormalVector, p) = CrossTraceMW(p) +cross(t::CrossTraceMW, ::NormalVector) = TangTraceMW(t.field) + +function (ϕ::CrossTraceMW)(p) + F = ϕ.field + x = cartesian(p) + n = normal(p) + return n × F(x) +end + +function (ϕ::TangTraceMW)(p) + F = ϕ.field + x = cartesian(p) + n = normal(p) + return (n × F(x)) × n +end + + + +struct NDotTrace{T,F} <: Functional{T} + field::F +end + + +NDotTrace(f::F) where {F} = NDotTrace{scalartype(f), F}(f) +NDotTrace{T}(f::F) where {T,F} = NDotTrace{T,F}(f) +LinearAlgebra.dot(::NormalVector, f) = NDotTrace(f) + +# scalartype(s::NDotTrace{T}) where {T} = T + +(ϕ::NDotTrace)(p) = dot(normal(p), ϕ.field(cartesian(p))) + +integrand(::Any, testvals, fieldval) = dot(testvals[1], fieldval) +# integrand(::TangTraceMW, gx, ϕx) = gx[1] ⋅ ϕx +# integrand(::CrossTraceMW, test_vals, field_val) = test_vals[1] ⋅ field_val +# integrand(::NDotTrace, g, ϕ) = dot(g.value, ϕ) \ No newline at end of file diff --git a/src/helmholtz2d/helmholtz2d.jl b/src/helmholtz2d/helmholtz2d.jl new file mode 100644 index 00000000..012e8a54 --- /dev/null +++ b/src/helmholtz2d/helmholtz2d.jl @@ -0,0 +1,87 @@ +module Helmholtz2D + + using ..BEAST + const Mod = BEAST + using LinearAlgebra + + function singlelayer(; + #alpha=nothing, + #gamma=nothing, + wavenumber=nothing + ) + + #alpha, gamma = Mod.operator_parameter_handler(alpha, gamma, wavenumber) + + return Mod.SingleLayer(wavenumber) + end + + function doublelayer(; + #alpha=nothing, + #gamma=nothing, + wavenumber=nothing + ) + + #alpha, gamma = Mod.operator_parameter_handler(alpha, gamma, wavenumber) + + return Mod.DoubleLayer(wavenumber) + end + + function doublelayer_transposed(; + #alpha=nothing, + #gamma=nothing, + wavenumber=nothing + ) + + #alpha, gamma = Mod.operator_parameter_handler(alpha, gamma, wavenumber) + + return Mod.DoubleLayerTransposed(wavenumber) + end + + function hypersingular(; + #alpha=nothing, + #beta=nothing, + #gamma=nothing, + wavenumber=nothing + ) + #= + gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) + + if alpha === nothing + if Mod.isstatic(gamma) #static case + alpha = 0.0 # In the long run, this should probably be rather 'nothing' + else + alpha = gamma^2 + end + + end + + if beta === nothing + if Mod.isstatic(gamma) #static case + beta = one(alpha) + else + beta = one(gamma) + end + end + =# + return Mod.HyperSingular(wavenumber) + end + + function planewave(; + direction=error("direction is a required argument"), + gamma=nothing, + wavenumber=nothing, + amplitude=one(eltype(direction))) + + gamma, wavenumber = Mod.gamma_wavenumber_handler(gamma, wavenumber) + + # Note: Unlike for the operators, there seems little benefit in + # explicitly declaring a Laplace-Type excitation. + + Mod.isstatic(gamma) && (gamma = zero(amplitude)) + + return Mod.HH2DPlaneWave(direction, gamma, amplitude) + end + +end + +export Helmholtz2D \ No newline at end of file diff --git a/src/helmholtz2d/helmholtzop.jl b/src/helmholtz2d/helmholtzop.jl deleted file mode 100644 index ee3806f1..00000000 --- a/src/helmholtz2d/helmholtzop.jl +++ /dev/null @@ -1,187 +0,0 @@ -import SpecialFunctions: hankelh2 - -abstract type HelmholtzOperator2D <: IntegralOperator end -scalartype(::HelmholtzOperator2D) = ComplexF64 - -struct SingleLayer{T} <: HelmholtzOperator2D - wavenumber::T -end - -struct HyperSingular{T} <: HelmholtzOperator2D - wavenumber::T -end - -struct DoubleLayer{T} <: HelmholtzOperator2D - wavenumber::T -end - -struct DoubleLayerTransposed{T} <: HelmholtzOperator2D - wavenumber::T -end - -function cellcellinteractions!(biop::HelmholtzOperator2D, tshs, bshs, tcell, bcell, z) - - regularcellcellinteractions!(biop, tshs, bshs, tcell, bcell, z) - -end - -function testfunc1() - print("test function!") -end - -# defaultquadstrat(op::HelmholtzOperator2D, tfs, bfs) = DoubleNumWiltonSauterQStrat(4,3,4,3,4,4,4,4) -defaultquadstrat(op::HelmholtzOperator2D, tfs, bfs) = DoubleNumQStrat(4,3) - - -function quaddata(op::HelmholtzOperator2D, g::LagrangeRefSpace, f::LagrangeRefSpace, tels, bels, - qs::DoubleNumWiltonSauterQStrat) - - tqd = quadpoints(g, tels, (qs.outer_rule_far,)) - bqd = quadpoints(f, bels, (qs.inner_rule_far,)) - - return (tpoints=tqd, bpoints=bqd) -end - -function quadrule(op::HelmholtzOperator2D, g::LagrangeRefSpace, f::LagrangeRefSpace, i, τ, j, σ, qd, - qs::DoubleNumWiltonSauterQStrat) - - DoubleQuadRule( - qd.tpoints[1,i], - qd.bpoints[1,j] - ) - -end - - -mutable struct KernelValsHelmholtz2D - wavenumber - vect - dist - green - gradgreen - txty -end - - -function kernelvals(biop::HelmholtzOperator2D, tgeo, bgeo) - - k = biop.wavenumber - r = tgeo.cart - bgeo.cart - R = norm(r) - - kr = k * R - hankels = hankelh2.([0 1], kr) - green = -im / 4 * hankels[1] - gradgreen = k * im / 4 * hankels[2] * r / R - - txty = dot(normal(tgeo), normal(bgeo)) - - KernelValsHelmholtz2D(k, r, R, green, gradgreen, txty) -end - - -shapevals(op::HelmholtzOperator2D, ϕ, ts) = shapevals(ValDiff(), ϕ, ts) - - -function integrand(biop::SingleLayer, kerneldata, tvals, - tgeo, bvals, bgeo) - - gx = tvals[1] - fy = bvals[1] - - gx * kerneldata.green * fy -end - - -function integrand(biop::HyperSingular, kernel, tvals, tgeo, - bvals, bgeo) - - gx = tvals[1] - fy = bvals[1] - - dgx = tvals[2] - dfy = bvals[2] - - k = kernel.wavenumber - G = kernel.green - txty = kernel.txty - - (dgx * dfy - k*k * txty * gx * fy) * G -end - -function integrand(biop::DoubleLayer, kernel, fp, mp, fq, mq) - nq = normal(mq) - fp[1] * dot(nq, -kernel.gradgreen) * fq[1] -end - -function integrand(biop::DoubleLayerTransposed, kernel, fp, mp, fq, mq) - np = normal(mp) - fp[1] * dot(np, kernel.gradgreen) * fq[1] -end - -mutable struct PlaneWaveDirichlet{T,P} <: Functional - wavenumber::T - direction::P -end - -scalartype(x::PlaneWaveDirichlet{T}) where {T} = complex(T) - -mutable struct PlaneWaveNeumann{T,P} <: Functional - wavenumber::T - direction::P -end - -scalartype(x::PlaneWaveNeumann{T}) where {T} = complex(T) - -mutable struct ScalarTrace{T,F} <: Functional - field::F -end - -ScalarTrace(f::F) where {F} = ScalarTrace{scalartype(f), F}(f) -ScalarTrace{T}(f::F) where {T,F} = ScalarTrace{T,F}(f) - -strace(f, mesh::Mesh) = ScalarTrace(f) - -(s::ScalarTrace)(x) = s.field(cartesian(x)) -integrand(s::ScalarTrace, tx, fx) = dot(tx.value, fx) -scalartype(s::ScalarTrace{T}) where {T} = T - -shapevals(f::Functional, ϕ, ts) = shapevals(ValOnly(), ϕ, ts) - -function (field::PlaneWaveDirichlet)(mp) - - wavenumber = field.wavenumber - direction = field.direction - - cart = cartesian(mp) - exp(-im * wavenumber * dot(direction, cart)) -end - - -function(field::PlaneWaveNeumann)(mp) - - wavenumber = field.wavenumber - direction = field.direction - - cart = cartesian(mp) - norm = normal(mp) - - wave = exp(-im * wavenumber * dot(direction, cart)) - grad = -im * wavenumber * direction * wave - - d = norm[1] * grad[1] - for i in 2:length(norm) d += norm[i] * grad[i] end - return d -end - -function integrand(pw::PlaneWaveDirichlet, sv, fx) - tx = sv[1] - return dot(tx, fx) -end - -function integrand(pw::PlaneWaveNeumann, sv, fx) - tx = sv[1] - d = tx[1] * fx[1] - for i in 2:length(tx) d += tx[i]*fx[i] end - return d -end diff --git a/src/helmholtz2d/hh2dexc.jl b/src/helmholtz2d/hh2dexc.jl new file mode 100644 index 00000000..85e75a2c --- /dev/null +++ b/src/helmholtz2d/hh2dexc.jl @@ -0,0 +1,138 @@ + +struct HH2DPlaneWave{P,K,T} <: Functional{T} + direction::P + gamma::K + amplitude::T +end + +function (f::HH2DPlaneWave)(r) + d = f.direction + γ = f.gamma + a = f.amplitude + return a * exp(-γ*dot(d,r)) +end + +function (f::HH2DPlaneWave)(r::CompScienceMeshes.MeshPointNM) + return f(cartesian(r)) +end + +scalartype(f::HH2DPlaneWave{P,K,T}) where {P,K,T} = promote_type(eltype(P), K, T) + +struct gradHH2DPlaneWave{P,K,T} <: Functional{T} + direction::P + gamma::K + amplitude::T +end + +function (f::gradHH2DPlaneWave)(r) + d = f.direction + γ = f.gamma + a = f.amplitude + + return -γ * d * exp(-γ * dot(d, r)) +end + +function (f::gradHH2DPlaneWave)(mp::CompScienceMeshes.MeshPointNM) + r = cartesian(mp) + return dot(normal(mp), f(r)) +end + +scalartype(f::gradHH2DPlaneWave{P,K,T}) where {P,K,T} = promote_type(eltype(P), K, T) + +struct ScalarTrace{T,F} <: Functional{T} + field::F +end + +ScalarTrace(f::F) where {F} = ScalarTrace{scalartype(f), F}(f) +ScalarTrace{T}(f::F) where {T,F} = ScalarTrace{T,F}(f) + +strace(f::F) where {F} = ScalarTrace{scalartype(f), F}(f) +strace(f, mesh::Mesh) = ScalarTrace(f) + +@testitem "scalar trace" begin + using CompScienceMeshes + fn = joinpath(dirname(pathof(BEAST)), "../test/assets/sphere45.in") + Γ = readmesh(fn) + X = lagrangecxd0(Γ) + + U = Helmholtz3D.planewave(wavenumber=1.0, direction=ẑ) + u = strace(U) + + ux = assemble(u, X) + @test u isa BEAST.ScalarTrace + @test BEAST.scalartype(u) == ComplexF64 +end + +(s::ScalarTrace)(x) = s.field(cartesian(x)) +integrand(s::ScalarTrace, tx, fx) = dot(tx.value, fx) +scalartype(s::ScalarTrace{T}) where {T} = T + +shapevals(f::Functional, ϕ, ts) = shapevals(ValOnly(), ϕ, ts) + +function (f::NormalDerivative{T,F})(manipoint) where {T,F<:HH2DPlaneWave} + d = f.field.direction + γ = f.field.gamma + a = f.field.amplitude + n = normal(manipoint) + r = cartesian(manipoint) + return -γ*a * dot(d,n) * exp(-γ*dot(d,r)) +end + +*(a::Number, m::HH2DPlaneWave) = HH2DPlaneWave(m.direction, m.gamma, a * m.amplitude) +*(a::Number, m::gradHH2DPlaneWave) = gradHH2DPlaneWave(m.direction, m.gamma, a * m.amplitude) + +dot(::NormalVector, m::gradHH2DPlaneWave) = NormalDerivative(HH2DPlaneWave(m.direction, m.gamma, m.amplitude)) + + +struct PlaneWaveDirichlet{T,P} <: Functional{T} + wavenumber::T + direction::P +end + +scalartype(x::PlaneWaveDirichlet{T}) where {T} = complex(T) + +struct PlaneWaveNeumann{T,P} <: Functional{T} + wavenumber::T + direction::P +end + +scalartype(x::PlaneWaveNeumann{T}) where {T} = complex(T) + + +function (field::PlaneWaveDirichlet)(mp) + + wavenumber = field.wavenumber + direction = field.direction + + cart = cartesian(mp) + exp(-im * wavenumber * dot(direction, cart)) +end + + +function(field::PlaneWaveNeumann)(mp) + + wavenumber = field.wavenumber + direction = field.direction + + cart = cartesian(mp) + norm = normal(mp) + + wave = exp(-im * wavenumber * dot(direction, cart)) + grad = -im * wavenumber * direction * wave + + d = norm[1] * grad[1] + for i in 2:length(norm) d += norm[i] * grad[i] end + return d +end + +function integrand(pw::PlaneWaveDirichlet, sv, fx) + tx = sv[1] + return dot(tx, fx) +end + +function integrand(pw::PlaneWaveNeumann, sv, fx) + tx = sv[1] + d = tx[1] * fx[1] + for i in 2:length(tx) d += tx[i]*fx[i] end + return d +end diff --git a/src/helmholtz2d/hh2dops.jl b/src/helmholtz2d/hh2dops.jl new file mode 100644 index 00000000..d8d63d04 --- /dev/null +++ b/src/helmholtz2d/hh2dops.jl @@ -0,0 +1,136 @@ +import SpecialFunctions: hankelh2 + +abstract type HelmholtzOperator2D <: IntegralOperator end +scalartype(::HelmholtzOperator2D) = ComplexF64 + +struct SingleLayer{T} <: HelmholtzOperator2D + wavenumber::T +end + +struct HyperSingular{T} <: HelmholtzOperator2D + wavenumber::T +end + +struct DoubleLayer{T} <: HelmholtzOperator2D + wavenumber::T +end + +struct DoubleLayerTransposed{T} <: HelmholtzOperator2D + wavenumber::T +end + + + +mutable struct KernelValsHelmholtz2D + wavenumber + vect + dist + green + gradgreen + txty +end + + +function kernelvals(biop::HelmholtzOperator2D, tgeo, bgeo) + + k = biop.wavenumber + r = tgeo.cart - bgeo.cart + R = norm(r) + + kr = k * R + hankels = hankelh2.([0 1], kr) + green = -im / 4 * hankels[1] + gradgreen = k * im / 4 * hankels[2] * r / R + + txty = dot(normal(tgeo), normal(bgeo)) + + KernelValsHelmholtz2D(k, r, R, green, gradgreen, txty) +end + + +shapevals(op::HelmholtzOperator2D, ϕ, ts) = shapevals(ValDiff(), ϕ, ts) + + +function integrand(biop::SingleLayer, kerneldata, tvals, + tgeo, bvals, bgeo) + + gx = tvals[1] + fy = bvals[1] + + gx * kerneldata.green * fy +end + +function integrand(biop::HyperSingular, kernel, tvals, tgeo, + bvals, bgeo) + + gx = tvals[1] + fy = bvals[1] + + dgx = tvals[2] + dfy = bvals[2] + + k = kernel.wavenumber + G = kernel.green + txty = kernel.txty + + (dgx * dfy - k*k * txty * gx * fy) * G +end + +function integrand(biop::DoubleLayer, kernel, fp, mp, fq, mq) + nq = normal(mq) + fp[1] * dot(nq, -kernel.gradgreen) * fq[1] +end + +function integrand(biop::DoubleLayerTransposed, kernel, fp, mp, fq, mq) + np = normal(mp) + fp[1] * dot(np, kernel.gradgreen) * fq[1] +end + +function cellcellinteractions!(biop::HelmholtzOperator2D, tshs, bshs, tcell, bcell, z) + + regularcellcellinteractions!(biop, tshs, bshs, tcell, bcell, z) + +end + +defaultquadstrat(op::HelmholtzOperator2D, tfs, bfs) = DoubleNumSauterQstrat(3,3,0,4,10,10) + +function quaddata(op::HelmholtzOperator2D, + test_local_space::RefSpace, trial_local_space::RefSpace, + test_charts, trial_charts, qs::DoubleNumSauterQstrat) + + T = coordtype(test_charts[1]) + + tqd = quadpoints(test_local_space, test_charts, (qs.outer_rule,)) + bqd = quadpoints(trial_local_space, trial_charts, (qs.inner_rule,)) + + leg = ( + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_vert,0,1)), + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_edge,0,1)), + convert.(NTuple{2,T},_legendre(qs.sauter_schwab_common_face,0,1)), + ) + + mrw = ( + convert.(NTuple{2,T},BEAST.SauterSchwabQuadrature1D._MRWrules(qs.sauter_schwab_common_vert,0,1)), + convert.(NTuple{2,T},BEAST.SauterSchwabQuadrature1D._MRWrules(qs.sauter_schwab_common_edge,0,1)), + convert.(NTuple{2,T},BEAST.SauterSchwabQuadrature1D._MRWrules(qs.sauter_schwab_common_face,0,1)), + ) + + return (tpoints=tqd, bpoints=bqd, gausslegendre=leg, marokhlinwandura=mrw) +end + +function quadrule(op::HelmholtzOperator2D, g::LagrangeRefSpace, f::LagrangeRefSpace, + i, τ::CompScienceMeshes.Simplex{<:Any, 1}, + j, σ::CompScienceMeshes.Simplex{<:Any, 1}, + qd, qs::DoubleNumSauterQstrat) + + hits = _numhits(τ, σ) + @assert hits <= 2 + + hits == 2 && return BEAST.SauterSchwabQuadrature1D.CommonEdge(qd.marokhlinwandura[2], qd.gausslegendre[2]) + hits == 1 && return BEAST.SauterSchwabQuadrature1D.CommonVertex(qd.marokhlinwandura[1], qd.gausslegendre[1]) + + return DoubleQuadRule( + qd.tpoints[1,i], + qd.bpoints[1,j], + ) +end \ No newline at end of file diff --git a/src/helmholtz3d/hh3dexc.jl b/src/helmholtz3d/hh3dexc.jl index 59d96e30..9333e6ab 100644 --- a/src/helmholtz3d/hh3dexc.jl +++ b/src/helmholtz3d/hh3dexc.jl @@ -122,7 +122,7 @@ end dot(::NormalVector, m::gradHH3DMonopole) = NormalDerivative(HH3DMonopole(m.position, m.gamma, m.amplitude)) -mutable struct DirichletTrace{T,F} <: Functional +mutable struct DirichletTrace{T,F} <: Functional{T} field::F end @@ -138,7 +138,7 @@ end integrand(::DirichletTrace, test_vals, field_vals) = dot(test_vals[1], field_vals) -struct NormalDerivative{T,F} <: Functional +struct NormalDerivative{T,F} <: Functional{T} field::F end diff --git a/src/helmholtz3d/timedomain/tdhh3dexc.jl b/src/helmholtz3d/timedomain/tdhh3dexc.jl index c3413610..bd9aa7ce 100644 --- a/src/helmholtz3d/timedomain/tdhh3dexc.jl +++ b/src/helmholtz3d/timedomain/tdhh3dexc.jl @@ -10,7 +10,8 @@ function planewave(direction, speedoflight::Number, signature, amplitude=one(spe PlaneWaveHH3DTD(direction, speedoflight, signature, amplitude) end -scalartype(p::PlaneWaveHH3DTD) = eltype(p.amplitude) +#scalartype(p::PlaneWaveHH3DTD) = eltype(p.amplitude) +scalartype(p::PlaneWaveHH3DTD) = scalartype(p.amplitude) *(a, f::PlaneWaveHH3DTD) = PlaneWaveHH3DTD(f.direction, f.speed_of_light, f.signature, a*f.amplitude) diff --git a/src/identityop.jl b/src/identityop.jl index 883fa945..6d70e61e 100644 --- a/src/identityop.jl +++ b/src/identityop.jl @@ -1,3 +1,9 @@ + +""" + Identity <: LocalOperator + +The identity operator. +""" struct Identity <: LocalOperator end @@ -5,6 +11,11 @@ kernelvals(biop::Identity, x) = nothing integrand(op::Identity, kernel, x, g, f) = dot(f[1], g[1]) scalartype(op::Identity) = Union{} +""" + NCross <: LocalOperator + +The identity operator where the trial function is rotated. +""" struct NCross <: LocalOperator end diff --git a/src/integralop.jl b/src/integralop.jl index 32621123..83228a23 100644 --- a/src/integralop.jl +++ b/src/integralop.jl @@ -69,6 +69,9 @@ Computes the matrix of operator biop wrt the finite element spaces tfs and bfs function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; quadstrat=defaultquadstrat(biop, tfs, bfs)) + numfunctions(tfs) == 0 && return + numfunctions(bfs) == 0 && return + tr = assemblydata(tfs); tr == nothing && return br = assemblydata(bfs); br == nothing && return @@ -98,33 +101,32 @@ function assemblechunk!(biop::IntegralOperator, tfs::Space, bfs::Space, store; tfs, test_elements, tad, tcells, bfs, bsis_elements, bad, bcells, qd, zlocal, store; quadstrat=qs) +end - # if CompScienceMeshes.refines(tgeo, bgeo) - # assemblechunk_body_test_refines_trial!(biop, - # tfs, test_elements, tad, tcells, - # bfs, bsis_elements, bad, bcells, - # qd, zlocal, store; quadstrat) - # qs = TestRefinesTrialQStrat(quadstrat) - # # assemblechunk_body!(biop, - # # tfs, test_elements, tad, tcells, - # # bfs, bsis_elements, bad, bcells, - # # qd, zlocal, store; quadstrat=qs) - # elseif CompScienceMeshes.refines(bgeo, tgeo) - # qs = TrialRefinesTestQStrat(quadstrat) - # assemblechunk_body!(biop, - # tfs, test_elements, tad, tcells, - # bfs, bsis_elements, bad, bcells, - # qd, zlocal, store; quadstrat=qs) - # # assemblechunk_body_trial_refines_test!(biop, - # # tfs, test_elements, tad, tcells, - # # bfs, bsis_elements, bad, bcells, - # # qd, zlocal, store; quadstrat) - # else - # assemblechunk_body!(biop, - # tfs, test_elements, tad, tcells, - # bfs, bsis_elements, bad, bcells, - # qd, zlocal, store; quadstrat) - # end +@testitem "assemble!: zero sized block" begin + using CompScienceMeshes + + fn = joinpath(dirname(pathof(BEAST)), "../examples/assets/sphere45.in") + m1 = readmesh(fn) + m2 = m1[Int[]] + + X = BEAST.DirectProductSpace([raviartthomas(m) for m in [m1, m2]]) + T = Maxwell3D.singlelayer(gamma=1.0) + + @hilbertspace j[1:2] + @hilbertspace k[1:2] + a = T[k[1],j[1]] + T[k[1],j[2]] + T[k[2],j[2]] + T[k[2],j[1]] + + A = assemble(a, X, X) + import BEAST.BlockArrays + + n1 = numfunctions(X[1]) + n2 = numfunctions(X[2]) + + @test n2 == 0 + + @test BlockArrays.blocksize(A) == (2,2) + @test BlockArrays.blocksizes(A) == ([n1,n2], [n1,n2]) end @@ -136,8 +138,10 @@ function assemblechunk_body!(biop, test_shapes = refspace(test_space) trial_shapes = refspace(trial_space) + + verbose = (length(test_elements) > 256) myid = Threads.threadid() - myid == 1 && print("dots out of 10: ") + verbose && myid == 1 && print("dots out of 10: ") todo, done, pctg = length(test_elements), 0, 0 for (p,(tcell,tptr)) in enumerate(zip(test_elements, test_cell_ptrs)) for (q,(bcell,bptr)) in enumerate(zip(trial_elements, trial_cell_ptrs)) @@ -160,94 +164,14 @@ function assemblechunk_body!(biop, done += 1 new_pctg = round(Int, done / todo * 100) if new_pctg > pctg + 9 - myid == 1 && print(".") + verbose && myid == 1 && print(".") pctg = new_pctg end end - myid == 1 && println("") + verbose && myid == 1 && println("") end -# function assemblechunk_body_test_refines_trial!(biop, -# test_functions, test_charts, test_assembly_data, test_cells, -# trial_functions, trial_charts, trial_assembly_data, trial_cells, -# qd, zlocal, store; quadstrat) - -# test_shapes = refspace(test_functions) -# trial_shapes = refspace(trial_functions) -# myid = Threads.threadid() -# myid == 1 && print("dots out of 10: ") -# todo, done, pctg = length(test_charts), 0, 0 -# for (p,(tcell,tchart)) in enumerate(zip(test_cells, test_charts)) -# for (q,(bcell,bchart)) in enumerate(zip(trial_cells, trial_charts)) - -# fill!(zlocal, 0) -# qrule = quadrule(biop, test_shapes, trial_shapes, p, tchart, q, bchart, qd, quadstrat) -# # @show ("1", qrule) -# momintegrals_test_refines_trial!(zlocal, biop, -# test_functions, tcell, tchart, -# trial_functions, bcell, bchart, -# qrule, quadstrat) - -# I = length(test_assembly_data[p]) -# J = length(trial_assembly_data[q]) -# for j in 1 : J, i in 1 : I -# zij = zlocal[i,j] -# for (n,b) in trial_assembly_data[q][j] -# zb = zij*b -# for (m,a) in test_assembly_data[p][i] -# store(a*zb, m, n) -# end end end end - -# done += 1 -# new_pctg = round(Int, done / todo * 100) -# if new_pctg > pctg + 9 -# myid == 1 && print(".") -# pctg = new_pctg -# end end -# myid == 1 && println("") -# end - - -# function assemblechunk_body_trial_refines_test!(biop, -# test_functions, test_charts, test_assembly_data, test_cells, -# trial_functions, trial_charts, trial_assembly_data, trial_cells, -# qd, zlocal, store; quadstrat) - -# test_shapes = refspace(test_functions) -# trial_shapes = refspace(trial_functions) - -# myid = Threads.threadid() -# myid == 1 && print("dots out of 10: ") -# todo, done, pctg = length(test_charts), 0, 0 -# for (p,(tcell,tchart)) in enumerate(zip(test_cells, test_charts)) -# for (q,(bcell,bchart)) in enumerate(zip(trial_cells, trial_charts)) - -# fill!(zlocal, 0) -# qrule = quadrule(biop, test_shapes, trial_shapes, p, tchart, q, bchart, qd, quadstrat) -# momintegrals_trial_refines_test!(zlocal, biop, -# test_functions, tcell, tchart, -# trial_functions, bcell, bchart, -# qrule, quadstrat) - -# I = length(test_assembly_data[p]) -# J = length(trial_assembly_data[q]) -# for j in 1 : J, i in 1 : I -# zij = zlocal[i,j] -# for (n,b) in trial_assembly_data[q][j] -# zb = zij*b -# for (m,a) in test_assembly_data[p][i] -# store(a*zb, m, n) -# end end end end - -# done += 1 -# new_pctg = round(Int, done / todo * 100) -# if new_pctg > pctg + 9 -# myid == 1 && print(".") -# pctg = new_pctg -# end end -# myid == 1 && println("") -# end diff --git a/src/localop.jl b/src/localop.jl index a169ae7d..d80616a0 100644 --- a/src/localop.jl +++ b/src/localop.jl @@ -59,8 +59,13 @@ end function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, threading::Type{Threading{:multi}}; - quadstrat=defaultquadstrat(biop, tfs, bfs)) + quadstrat=defaultquadstrat) + quadstrat = quadstrat(biop, tfs, bfs) + + numfunctions(tfs) == 0 && return + numfunctions(bfs) == 0 && return + if geometry(tfs) == geometry(bfs) return assemble_local_matched!(biop, tfs, bfs, store; quadstrat) end @@ -74,7 +79,9 @@ end function assemble!(biop::LocalOperator, tfs::Space, bfs::Space, store, threading::Type{Threading{:single}}; - quadstrat=defaultquadstrat(biop, tfs, bfs)) + quadstrat=defaultquadstrat) + + quadstrat = quadstrat(biop, tfs, bfs) if geometry(tfs) == geometry(bfs) return assemble_local_matched!(biop, tfs, bfs, store; quadstrat) @@ -138,7 +145,7 @@ end function assemble_local_refines!(biop::LocalOperator, tfs::Space, bfs::Space, store; quadstrat=defaultquadstrat(biop, tfs, bfs)) - println("Using 'refines' algorithm for local assembly:") + # println("Using 'refines' algorithm for local assembly:") tgeo = geometry(tfs) bgeo = geometry(bfs) @@ -230,7 +237,7 @@ function assemble_local_matched!(biop::LocalOperator, tfs::subdBasis, bfs::subdB end end end end -function elementstree(elements) +function elementstree(elements, expansion_ratio=1) nverts = dimension(eltype(elements)) + 1 ncells = length(elements) @@ -257,7 +264,7 @@ function elementstree(elements) end end - return Octree(points, radii) + return Octree(points, radii, T(expansion_ratio)) end @@ -395,3 +402,30 @@ function cellinteractions(biop, trefs::U, brefs::V, cell, qr) where {U<:RefSpace return zlocal end + + +@testitem "assemble!: zero sized block" begin + using CompScienceMeshes + + fn = joinpath(dirname(pathof(BEAST)), "../examples/assets/sphere45.in") + m1 = readmesh(fn) + m2 = m1[Int[]] + + X = BEAST.DirectProductSpace([raviartthomas(m) for m in [m1, m2]]) + Id = BEAST.Identity() + + @hilbertspace j[1:2] + @hilbertspace k[1:2] + a = Id[k[1],j[1]] + Id[k[2],j[2]] + + A = assemble(a, X, X) + import BEAST.BlockArrays + + n1 = numfunctions(X[1]) + n2 = numfunctions(X[2]) + + @test n2 == 0 + + @test BlockArrays.blocksize(A) == (2,2) + @test BlockArrays.blocksizes(A) == ([n1,n2], [n1,n2]) +end \ No newline at end of file diff --git a/src/maxwell/farfield.jl b/src/maxwell/farfield.jl index 615b3991..348e61b2 100644 --- a/src/maxwell/farfield.jl +++ b/src/maxwell/farfield.jl @@ -1,14 +1,5 @@ abstract type MWFarField <: FarField end -""" -Operator to compute the far field of a current distribution. In particular, given the current distribution ``j`` this operator allows for the computation of - -```math -A j = n × ∫_Γ e^{γ ̂x ⋅ y} dy -``` - -where ``̂x`` is the unit vector in the direction of observation. Note that the assembly routing expects the observation directions to be normalised by the caller. -""" struct MWFarField3D{K, U} <: MWFarField gamma::K amplitude::U @@ -23,6 +14,11 @@ struct MWDoubleLayerRotatedFarField3D{K,U} <: MWFarField amplitude::U end +""" + MWFarField3D(;gamma, amplitude) + +Maxwell single layer far-field operator for 3D. +""" function MWFarField3D(; gamma=nothing, wavenumber=nothing, @@ -38,6 +34,11 @@ end MWFarField3D(op::MWSingleLayer3D{T,U}) where {T,U} = MWFarField3D(op.gamma, sqrt(op.α*op.β)) +""" + MWDoubleLayerFarField3D(;gamma, amplitude) + +Maxwell double layer far-field operator for 3D. +""" function MWDoubleLayerFarField3D(; gamma=nothing, wavenumber=nothing, @@ -74,6 +75,11 @@ function integrand(op::MWFarField3DDropConstant,krn,y,f,p) op.coeff*(y × (krn * f[1])) × y end +""" + MWDoubleLayerRotatedFarField3D + +Rotated Maxwell double layer far-field operator for 3D. +""" function MWDoubleLayerRotatedFarField3D(; gamma=nothing, wavenumber=nothing, diff --git a/src/maxwell/maxwell.jl b/src/maxwell/maxwell.jl index 79fa275c..082bb63b 100644 --- a/src/maxwell/maxwell.jl +++ b/src/maxwell/maxwell.jl @@ -7,13 +7,11 @@ module Maxwell3D singlelayer(;gamma, alpha, beta) singlelayer(;wavenumber, alpha, beta) - Bilinear form given by: + Maxwell 3D single layer operator. - ```math - α ∬_{Γ×Γ} j(x)⋅k(y) G_{γ}(x,y) + β ∬_{Γ×Γ} div j(x) div k(y) G_{γ}(x,y) - ``` + Either gamma, or the wavenumber, or α and β must be provided. - with ``G_{γ} = e^{-γ|x-y|} / 4π|x-y|``. + If α and β are not provided explitly, they are set to ``α = -γ`` and ``β = -1/γ`` with ``γ=\\mathrm{j} k``. """ function singlelayer(; gamma=nothing, @@ -34,20 +32,33 @@ module Maxwell3D Mod.MWSingleLayer3D(gamma, alpha, beta) end + """ + weaklysingular(;wavenumber) + + Weakly singular part of the Maxwell 3D single layer operator. + + ``α = -\\mathrm{j} k`` is set with the wavenumber ``k``. + """ weaklysingular(;wavenumber) = singlelayer(;wavenumber, alpha=-im*wavenumber, beta=zero(im*wavenumber)) + + """ + hypersingular(;wavenumber) + + Hyper singular part of the Maxwell 3D single layer operator. + + ``β = -1/\\mathrm{j} k`` is set with the wavenumber ``k``. + """ hypersingular(;wavenumber) = singlelayer(; wavenumber, alpha=zero(im*wavenumber), beta=-1/(im*wavenumber)) """ doublelayer(;gamma) - doublelaher(;wavenumber) + doublelayer(;wavenumber) - Bilinear form given by: + Maxwell double layer operator. - ```math - α ∬_{Γ^2} k(x) ⋅ (∇G_γ(x-y) × j(y)) - ``` + Either gamma or the wavenumber must be provided. Optionally, also alpha can be provided. - with ``G_γ = e^{-γ|x-y|} / 4π|x-y|`` + If alpha is not provided explitly, it is set to ``α = 1``. """ function doublelayer(; alpha=nothing, @@ -67,6 +78,15 @@ module Maxwell3D Mod.MWDoubleLayer3D(alpha, gamma) end + """ + planewave(; + direction = error("missing arguement `direction`"), + polarization = error("missing arguement `polarization`"), + wavenumber = error("missing arguement `wavenumber`"), + amplitude = one(real(typeof(wavenumber)))) + + Time-harmonic plane wave. + """ planewave(; direction = error("missing arguement `direction`"), polarization = error("missing arguement `polarization`"), diff --git a/src/maxwell/mwexc.jl b/src/maxwell/mwexc.jl index 7e98b862..0c38d89c 100644 --- a/src/maxwell/mwexc.jl +++ b/src/maxwell/mwexc.jl @@ -185,54 +185,6 @@ end *(a::Number, d::DipoleMW) = DipoleMW(d.location, a .* d.orientation, d.gamma) *(a::Number, d::curlDipoleMW) = curlDipoleMW(d.location, a .* d.orientation, d.gamma) -mutable struct CrossTraceMW{F} <: Functional - field::F -end - -scalartype(x::CrossTraceMW) = scalartype(x.field) - -mutable struct TangTraceMW{F} <: Functional - field::F -end - -scalartype(t::TangTraceMW) = scalartype(t.field) - -cross(::NormalVector, p::Function) = CrossTraceMW(p) -cross(::NormalVector, p::PlaneWaveMW) = CrossTraceMW(p) -cross(::NormalVector, p::PlaneWaveExtractedKernelMW) = CrossTraceMW(p) -cross(::NormalVector, p::Dipole) = CrossTraceMW(p) -cross(t::CrossTraceMW, ::NormalVector) = TangTraceMW(t.field) - -function (ϕ::CrossTraceMW)(p) - F = ϕ.field - x = cartesian(p) - n = normal(p) - return n × F(x) -end - -function (ϕ::TangTraceMW)(p) - F = ϕ.field - x = cartesian(p) - n = normal(p) - return (n × F(x)) × n -end - -integrand(::TangTraceMW, gx, ϕx) = gx[1] ⋅ ϕx -integrand(::CrossTraceMW, test_vals, field_val) = test_vals[1] ⋅ field_val - -struct NDotTrace{T,F} <: Functional - field::F -end - -NDotTrace(f::F) where {F} = NDotTrace{scalartype(f), F}(f) -NDotTrace{T}(f::F) where {T,F} = NDotTrace{T,F}(f) -scalartype(s::NDotTrace{T}) where {T} = T - -(ϕ::NDotTrace)(p) = dot(normal(p), ϕ.field(cartesian(p))) -integrand(::NDotTrace, g, ϕ) = dot(g.value, ϕ) -LinearAlgebra.dot(::NormalVector, f) = NDotTrace(f) - - mutable struct CurlGreen{T,U,V} wavenumber::T @@ -256,8 +208,8 @@ mutable struct CurlCurlGreen{T,U,V} position::V end -cross(::NormalVector, p::CurlGreen) = CrossTraceMW(p) -cross(::NormalVector, p::CurlCurlGreen) = CrossTraceMW(p) +# cross(::NormalVector, p::CurlGreen) = CrossTraceMW(p) +# cross(::NormalVector, p::CurlCurlGreen) = CrossTraceMW(p) function (f::CurlCurlGreen)(x) γ = im * f.wavenumber diff --git a/src/maxwell/mwops.jl b/src/maxwell/mwops.jl index 4ef2b022..1f6696d0 100644 --- a/src/maxwell/mwops.jl +++ b/src/maxwell/mwops.jl @@ -86,75 +86,8 @@ struct MWDoubleLayer3DLoop{T,K} <: MaxwellOperator3D{T,K} gamma::K end - MWDoubleLayer3DLoop(gamma) = MWDoubleLayer3DLoop(1.0, gamma) # For legacy purposes - - - -# function quadrule(op::MaxwellOperator3D, g::BDMRefSpace, f::BDMRefSpace, i, τ, j, σ, qd, -# qs::DoubleNumWiltonSauterQStrat) - -# hits = 0 -# dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) -# dmin2 = floatmax(eltype(eltype(τ.vertices))) -# for t in τ.vertices -# for s in σ.vertices -# d2 = LinearAlgebra.norm_sqr(t-s) -# dmin2 = min(dmin2, d2) -# hits += (d2 < dtol) -# end -# end - -# hits == 3 && return SauterSchwabQuadrature.CommonFace(qd.gausslegendre[3]) -# hits == 2 && return SauterSchwabQuadrature.CommonEdge(qd.gausslegendre[2]) -# hits == 1 && return SauterSchwabQuadrature.CommonVertex(qd.gausslegendre[1]) - -# h2 = volume(σ) -# xtol2 = 0.2 * 0.2 -# k2 = abs2(gamma(op)) -# return DoubleQuadRule( -# qd.tpoints[1,i], -# qd.bpoints[1,j],) -# end - - -# function qrdf(op::MaxwellOperator3D, g::RTRefSpace, f::RTRefSpace, i, τ, j, σ, qd) -# # defines coincidence of points -# dtol = 1.0e3 * eps(eltype(eltype(τ.vertices))) - -# # decides on whether to use singularity extraction -# xtol = 0.2 - -# k = norm(gamma(op)) - -# hits = 0 -# xmin = xtol -# for t in τ.vertices -# for s in σ.vertices -# d = norm(t-s) -# xmin = min(xmin, k*d) -# if d < dtol -# hits +=1 -# break -# end -# end -# end - -# xmin < xtol && return WiltonSERule( -# qd.tpoints[1,i], -# DoubleQuadRule( -# qd.tpoints[2,i], -# qd.bpoints[2,j], -# ), -# ) -# return DoubleQuadRule( -# qd.tpoints[1,i], -# qd.bpoints[1,j], -# ) - -# end - ################################################################################ # # Kernel definitions diff --git a/src/maxwell/qlmwops.jl b/src/maxwell/qlmwops.jl new file mode 100644 index 00000000..87e3d018 --- /dev/null +++ b/src/maxwell/qlmwops.jl @@ -0,0 +1,103 @@ +struct QuasiLocalSingleLayerMW{R<:Real,F} <: QuasiLocalOperator + gamma::R + alpha::R + beta::R + range::R + nominator::F +end + +scalartype(op::QuasiLocalSingleLayerMW{T}) where {T} = T +defaultquadstrat(op::QuasiLocalSingleLayerMW, + tfs::RTRefSpace, bfs::RTRefSpace) = DoubleNumSauterQstrat(4,5,5,5,4,3) + +oprange(op::QuasiLocalSingleLayerMW) = op.range +Base.:*(a::T, op::QuasiLocalSingleLayerMW) where {T<:Number} = + QuasiLocalSingleLayerMW(op.gamma, a*op.alpha, a*op.beta, op.range, op.nominator) + +struct ExponentialNominator{R} + gamma::R +end +function (f::ExponentialNominator)(x,y) + exp(-f.gamma * norm(x-y)) +end + +function QuasiLocalSingleLayerMW(gamma, range) + f = ExponentialNominator(gamma) + QuasiLocalSingleLayerMW(gamma, -gamma, -1/(gamma), range, f) +end + +function QuasiLocalSingleLayerMW(gamma, range, f) + QuasiLocalSingleLayerMW(gamma, -gamma, -1/(gamma), range, f) +end + +function (igd::Integrand{<:QuasiLocalSingleLayerMW})(x,y,f,g) + α = igd.operator.alpha + β = igd.operator.beta + γ = igd.operator.gamma + F = igd.operator.nominator + + cx = cartesian(x) + cy = cartesian(y) + r = cx - cy + R = norm(r) + # iR = 1 / R + # green = exp(-γ*R) / (4π*R) + green = F(cx,cy) / (4π*R) + + αG = α * green + βG = β * green + + _integrands(f,g) do fi,gj + αG * dot(fi.value, gj.value) + βG * dot(fi.divergence, gj.divergence) + end +end + +@testitem "quasi-local singlelayer MW" begin + using CompScienceMeshes, LinearAlgebra + using SparseArrays + + fn = joinpath(dirname(pathof(BEAST)),"../test","assets","sphere45.in") + Γ1 = readmesh(fn) + Γ2 = CompScienceMeshes.translate(Γ1, point(0,0,3.0)) + # Γ = meshsphere(radius=1.0, h=0.075) + + import Pkg + @show pathof(BEAST.CollisionDetection) + for d in Pkg.project().dependencies + @show d + end + + δ = 0.025 + γ = 1/δ + t1 = BEAST.QuasiLocalSingleLayerMW(γ, 12*δ) + t2 = BEAST.MWSingleLayer3D(γ) + X1 = raviartthomas(Γ1) + X2 = raviartthomas(Γ2) + + qs = BEAST.defaultquadstrat(t1, X1, X1) + @show qs + + @time Z11 = assemble(t1, X1, X1) + @time W11 = assemble(t2, X1, X1) + @time Z12 = assemble(t1, X1, X2) + @time W12 = assemble(t2, X1, X2) + # @show norm(Z11-W11) + # @show norm(W11) + # @show norm(Z12-W12) + # @show norm(W12) + # @show norm(Z12) + @show length(nonzeros(Z11)) / length(Z11) + + @test norm(Z11-W11) < 1e-5 + @test norm(Z12-W12) < 1e-19 + @test norm(Z12) == 0 + @test length(nonzeros(Z11)) / length(Z11) < 0.86 + + @hilbertspace j + @hilbertspace k + Q1 = assemble(t1[k,j], j∈X1, k∈X1) + Q2 = assemble(t1[k,j] + t1[k,j], j∈X1, k∈X1) + + @test Q1.A.lmap isa SparseMatrixCSC + @test Q2.maps[1].A.lmap isa SparseMatrixCSC +end \ No newline at end of file diff --git a/src/maxwell/sourcefield.jl b/src/maxwell/sourcefield.jl index 8a6222e9..4520e89f 100644 --- a/src/maxwell/sourcefield.jl +++ b/src/maxwell/sourcefield.jl @@ -1,6 +1,6 @@ -struct SourceField{F} <: Functional - field::F -end +# struct SourceField{T,F} <: Functional{T} +# field::F +# end -(s::SourceField)(p) = s.f(cartesian(p)) -integrand(f::SourceField, tval, fval) = dot(fval, tval.value) +# (s::SourceField)(p) = s.f(cartesian(p)) +# integrand(f::SourceField, tval, fval) = dot(fval, tval.value) diff --git a/src/maxwell/timedomain/mwtdexc.jl b/src/maxwell/timedomain/mwtdexc.jl index 42cff3c0..1ab074e6 100644 --- a/src/maxwell/timedomain/mwtdexc.jl +++ b/src/maxwell/timedomain/mwtdexc.jl @@ -5,7 +5,11 @@ mutable struct PlaneWaveMWTD{T,F,P} <: TDFunctional{T} amplitude::F end +""" + planewave(polarisation,direction,amplitude,speedoflight) +Time-domain plane wave. +""" function planewave(polarisation,direction,amplitude,speedoflight) PlaneWaveMWTD(direction,polarisation,speedoflight,amplitude) end diff --git a/src/maxwell/timedomain/mwtdops.jl b/src/maxwell/timedomain/mwtdops.jl index 86f69d2b..dbc73aa1 100644 --- a/src/maxwell/timedomain/mwtdops.jl +++ b/src/maxwell/timedomain/mwtdops.jl @@ -14,7 +14,7 @@ mutable struct MWSingleLayerTDIO{T} <: RetardedPotential{T} end function Base.:*(a::Number, op::MWSingleLayerTDIO) - @info "scalar product a * op (SL)" + # @info "scalar product a * op (SL)" MWSingleLayerTDIO( op.speed_of_light, a * op.ws_weight, @@ -30,7 +30,7 @@ mutable struct MWDoubleLayerTDIO{T} <: RetardedPotential{T} end function Base.:*(a::Number, op::MWDoubleLayerTDIO) - @info "scalar product a * op (DL)" + # @info "scalar product a * op (DL)" MWDoubleLayerTDIO( op.speed_of_light, a * op.weight, @@ -44,7 +44,7 @@ mutable struct MWDoubleLayerTransposedTDIO{T} <: RetardedPotential{T} end function Base.:*(a::Number, op::MWDoubleLayerTransposedTDIO) - @info "scalar product a * op (DL)" + # @info "scalar product a * op (DL)" MWDoubleLayerTransposedTDIO( op.speed_of_light, a * op.weight, @@ -103,6 +103,8 @@ end function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, store, threading::Type{Threading{:multi}}; quadstrat=defaultquadstrat(dl,W,V)) + quadstrat = quadstrat(dl, W, V) + X, T = spatialbasis(W), temporalbasis(W) Y, U = spatialbasis(V), temporalbasis(V) if CompScienceMeshes.refines(geometry(Y), geometry(X)) @@ -118,7 +120,7 @@ function assemble!(dl::MWDoubleLayerTDIO, W::SpaceTimeBasis, V::SpaceTimeBasis, Y, S = spatialbasis(W), temporalbasis(W) splits = [round(Int,s) for s in range(0, stop=numfunctions(Y), length=P+1)] - @info "Starting assembly with $P threads:" + # @info "Starting assembly with $P threads:" Threads.@threads for i in 1:P lo, hi = splits[i]+1, splits[i+1] lo <= hi || continue diff --git a/src/operator.jl b/src/operator.jl index 07006ea7..0d488489 100644 --- a/src/operator.jl +++ b/src/operator.jl @@ -19,6 +19,11 @@ end scalartype(op::TransposedOperator) = scalartype(op.op) defaultquadstrat(op::TransposedOperator, tfs::Space, bfs::Space) = defaultquadstrat(op.op, tfs, bfs) +""" + LinearCombinationOfOperators{T} <: AbstractOperator + +A linear combination of operators. +""" mutable struct LinearCombinationOfOperators{T} <: AbstractOperator coeffs::Vector{T} ops::Vector @@ -76,20 +81,29 @@ transpose(op::Operator) = TransposedOperator(op) defaultquadstrat(lc::LinearCombinationOfOperators, tfs, bfs) = [defaultquadstrat(op,tfs,bfs) for op in lc.ops] +""" + assemble(operator, test_functions, trial_functions; + storage_policy = Val{:bandedstorage}, + threading = Threading{:multi}, + quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) + +Assemble the system matrix corresponding to the operator `operator` tested with the test functions `test_functions` and the trial functions `trial_functions`. +""" function assemble(operator::AbstractOperator, test_functions, trial_functions; storage_policy = Val{:bandedstorage}, - # long_delays_policy = LongDelays{:compress}, threading = Threading{:multi}, - quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) + quadstrat=defaultquadstrat) Z, store = allocatestorage(operator, test_functions, trial_functions, storage_policy) - assemble!(operator, test_functions, trial_functions, store, threading; quadstrat) + # qs = quadstrat(operator, test_functions, trial_functions) + assemble!(operator, test_functions, trial_functions, + store, threading; quadstrat) return Z() end -function assemble(A::Matrix, testfns, trialfns) +function assemble(A::AbstractMatrix, testfns, trialfns; kwargs...) @assert numfunctions(testfns) == size(A,1) @assert numfunctions(trialfns) == size(A,2) return A @@ -137,15 +151,15 @@ function allocatestorage(operator::AbstractOperator, test_functions, trial_funct end -function allocatestorage(operator::LinearCombinationOfOperators, - test_functions::SpaceTimeBasis, trial_functions::SpaceTimeBasis, - storage_policy::Type{Val{:bandedstorage}}, - long_delays_policy::Type{LongDelays{:ignore}}) +# function allocatestorage(operator::LinearCombinationOfOperators, +# test_functions::SpaceTimeBasis, trial_functions::SpaceTimeBasis, +# storage_policy::Type{Val{:bandedstorage}}, +# long_delays_policy::Type{LongDelays{:ignore}}) - # This works when are terms in the LC can share storage - return allocatestorage(operator.ops[end], test_functions, trial_functions, - storage_policy, long_delays_policy) -end +# # This works when are terms in the LC can share storage +# return allocatestorage(operator.ops[end], test_functions, trial_functions, +# storage_policy, long_delays_policy) +# end function allocatestorage(operator::LinearCombinationOfOperators, @@ -168,7 +182,9 @@ end function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, threading::Type{Threading{:multi}}; - quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) + quadstrat=defaultquadstrat) + + quadstrat = quadstrat(operator, test_functions, trial_functions) P = Threads.nthreads() numchunks = P @@ -185,8 +201,9 @@ end end function assemble!(operator::Operator, test_functions::Space, trial_functions::Space, store, threading::Type{Threading{:single}}; - quadstrat=defaultquadstrat(operator, test_functions, trial_functions)) + quadstrat=defaultquadstrat) + quadstrat = quadstrat(operator, test_functions, trial_functions) assemblechunk!(operator, test_functions, trial_functions, store; quadstrat) end @@ -205,8 +222,9 @@ function assemble!(op::LinearCombinationOfOperators, tfs::AbstractSpace, bfs::Ab store, threading = Threading{:multi}; quadstrat=defaultquadstrat(op, tfs, bfs)) - for (a,A,qs) in zip(op.coeffs, op.ops, quadstrat) + for (a,A) in zip(op.coeffs, op.ops) store1(v,m,n) = store(a*v,m,n) + qs = quadstrat(A, tfs, bfs) assemble!(A, tfs, bfs, store1, threading; quadstrat=qs) end end diff --git a/src/operators/quasilocalops.jl b/src/operators/quasilocalops.jl new file mode 100644 index 00000000..ca7565af --- /dev/null +++ b/src/operators/quasilocalops.jl @@ -0,0 +1,117 @@ +abstract type QuasiLocalOperator <: IntegralOperator end + +function allocatestorage(op::QuasiLocalOperator, + test_functions, trial_functions, + storage::Type{Val{:bandedstorage}}) + + T = scalartype(op, test_functions, trial_functions) + + nt = Threads.nthreads() + M = Vector{Vector{Int}}(undef, nt) + N = Vector{Vector{Int}}(undef, nt) + V = Vector{Vector{T}}(undef, nt) + for i in 1:nt + M[i] = Vector{Int}() + N[i] = Vector{Int}() + V[i] = Vector{T}() + end + # M = Int[] + # N = Int[] + # V = T[] + + # @show M + + function storeq2(v,m,n) + tid = Threads.threadid() + push!(M[tid],m) + # @show length(M) + push!(N[tid],n) + push!(V[tid],v) + end + + function freeze() + nrows = numfunctions(test_functions) + ncols = numfunctions(trial_functions) + Mall = reduce(vcat, M) + Nall = reduce(vcat, N) + Vall = reduce(vcat, V) + S = sparse(Mall,Nall,Vall, nrows, ncols) + @info "Compression rate: $(length(nonzeros(S)) / (nrows*ncols))" + return S + end + + return freeze, storeq2 +end + + +function assemblechunk!(op::QuasiLocalOperator, tfs::Space, bfs::Space, store321; + quadstrat=defaultquadstrat(op, tfs, bfs)) + + tr = assemblydata(tfs); tr == nothing && return + br = assemblydata(bfs); br == nothing && return + + T = scalartype(op, tfs, bfs) + tol = sqrt(eps(real(T))) + + trefs = refspace(tfs) + brefs = refspace(bfs) + + tgeo = geometry(tfs) + bgeo = geometry(bfs) + + num_trefs = numfunctions(trefs, domain(chart(tgeo, first(tgeo)))) + num_brefs = numfunctions(brefs, domain(chart(bgeo, first(bgeo)))) + + tels, tad, tact = tr + bels, bad, bact = br + + @assert length(tels) == size(tad.data, 3) + @assert length(bels) == size(bad.data, 3) + + qd = quaddata(op, trefs, brefs, tels, bels, quadstrat) + zlocal = zeros(T, num_trefs, num_brefs) + tree = elementstree(bels, 1.1) + + δ = oprange(op) + tid = Threads.threadid() + + tid == 1 && print("dots out of 10: ") + todo, done, pctg = length(tels), 0, 0 + for (p,(tcell,tptr)) in enumerate(zip(tels, tact)) + + tc, ts = boundingbox(tcell.vertices) + # pred = (c,s) -> boxesoverlap(c,s,tc,ts + δ/2) + + for box in boxes(tree, tc, ts + δ/2) + for q in box + bcell = bels[q] + bc, bs = boundingbox(bcell.vertices) + norm(tc-bc) - 2*(ts+bs)*sqrt(3) > δ && continue + + bptr = bact[q] + @assert q <= length(bels) + @assert q <= size(bad.data, 3) + + fill!(zlocal, 0) + qrule = quadrule(op, trefs, brefs, p, tcell, q, bcell, qd, quadstrat) + momintegrals!(zlocal, op, + tfs, tptr, tcell, + bfs, bptr, bcell, qrule) + + for j in 1 : length(bad[q]) + for i in 1 : length(tad[p]) + zij = zlocal[i,j] + for (n,b) in bad[q][j] + zb = zij*b + for (m,a) in tad[p][i] + store321(a*zb, m, n) + end end end end end end + + done += 1 + new_pctg = round(Int, done / todo * 100) + if new_pctg > pctg + 9 + tid == 1 && print(".") + pctg = new_pctg + end end + tid == 1 && println("") +end \ No newline at end of file diff --git a/src/postproc.jl b/src/postproc.jl index b720a9b5..8fb426c7 100644 --- a/src/postproc.jl +++ b/src/postproc.jl @@ -129,6 +129,12 @@ function facecurrents(u, X::DirectProductSpace) fcr, m end + +""" + potential(op, points, coeffs, basis) + +Evaluate operator for a given bases and expansion coefficients at the given points. +""" function potential(op, points, coeffs, basis; type=SVector{3,ComplexF64}, quadstrat=defaultquadstrat(op, basis)) diff --git a/src/postproc/segcurrents.jl b/src/postproc/segcurrents.jl index 3ba750f1..da905f06 100644 --- a/src/postproc/segcurrents.jl +++ b/src/postproc/segcurrents.jl @@ -29,21 +29,25 @@ function grideval(points, coeffs, basis; type=nothing) V = valuetype(refs, eltype(charts)) T = promote_type(eltype(coeffs), eltype(V)) - P = similar_type(V, T) + if !(V <: SVector) + P = T + else + P = similar_type(V, T) + end - type != nothing && (P = type) + type !== nothing && (P = type) values = zeros(P, size(points)) chart_tree = BEAST.octree(charts) for (j,point) in enumerate(points) i = CompScienceMeshes.findchart(charts, chart_tree, point) - if i != nothing + if i !== nothing # @show i chart = charts[i] u = carttobary(chart, point) vals = refs(neighborhood(chart,u)) - for r in 1 : numfunctions(refs) + for r in 1 : numfunctions(refs, domain(chart)) for (m,w) in ad[i, r] values[j] += w * coeffs[m] * P(vals[r][1]) end diff --git a/src/quadrature/SauterSchwabQuadrature1D.jl b/src/quadrature/SauterSchwabQuadrature1D.jl new file mode 100644 index 00000000..6193eae0 --- /dev/null +++ b/src/quadrature/SauterSchwabQuadrature1D.jl @@ -0,0 +1,14 @@ +module SauterSchwabQuadrature1D + +# -------- exportet parts +# types +export SauterSchwabStrategy1D +export CommonEdge, CommonVertex + +# functions +export sauterschwab_parameterized1D, _MRWrules, reorder + +# -------- included files +include("doublesauterschwabint.jl") + +end \ No newline at end of file diff --git a/src/quadrature/commonfaceoverlappingedgeqstrat.jl b/src/quadrature/commonfaceoverlappingedgeqstrat.jl index f7c98f6e..183223ba 100644 --- a/src/quadrature/commonfaceoverlappingedgeqstrat.jl +++ b/src/quadrature/commonfaceoverlappingedgeqstrat.jl @@ -1,4 +1,4 @@ -struct CommonFaceOverlappingEdgeQStrat{S} +struct CommonFaceOverlappingEdgeQStrat{S} <: AbstractQuadStrat conforming_qstrat::S end diff --git a/src/quadrature/doublenumqstrat.jl b/src/quadrature/doublenumqstrat.jl index 90a6d766..5282d619 100644 --- a/src/quadrature/doublenumqstrat.jl +++ b/src/quadrature/doublenumqstrat.jl @@ -1,3 +1,8 @@ +struct DoubleNumQStrat{R} <: AbstractQuadStrat + outer_rule::R + inner_rule::R +end + function quaddata(operator::IntegralOperator, local_test_basis, local_trial_basis, test_elements, trial_elements, qs::DoubleNumQStrat) diff --git a/src/quadrature/doublenumsauterqstrat.jl b/src/quadrature/doublenumsauterqstrat.jl index f869393f..02f2bafa 100644 --- a/src/quadrature/doublenumsauterqstrat.jl +++ b/src/quadrature/doublenumsauterqstrat.jl @@ -1,4 +1,4 @@ -struct DoubleNumSauterQstrat{R,S} +struct DoubleNumSauterQstrat{R,S} <: AbstractQuadStrat outer_rule::R inner_rule::R sauter_schwab_common_tetr::S diff --git a/src/quadrature/doublenumwiltonbogaertqstrat.jl b/src/quadrature/doublenumwiltonbogaertqstrat.jl index 99c97bee..f1aff891 100644 --- a/src/quadrature/doublenumwiltonbogaertqstrat.jl +++ b/src/quadrature/doublenumwiltonbogaertqstrat.jl @@ -1,4 +1,4 @@ -struct DoubleNumWiltonBogaertQStrat{R} +struct DoubleNumWiltonBogaertQStrat{R} <: AbstractQuadStrat outer_rule_far::R inner_rule_far::R outer_rule_near::R diff --git a/src/quadrature/doublenumwiltonsauterqstrat.jl b/src/quadrature/doublenumwiltonsauterqstrat.jl index 70918398..6853ce55 100644 --- a/src/quadrature/doublenumwiltonsauterqstrat.jl +++ b/src/quadrature/doublenumwiltonsauterqstrat.jl @@ -1,3 +1,14 @@ +struct DoubleNumWiltonSauterQStrat{R,S} <: AbstractQuadStrat + outer_rule_far::R + inner_rule_far::R + outer_rule_near::R + inner_rule_near::R + sauter_schwab_common_tetr::S + sauter_schwab_common_face::S + sauter_schwab_common_edge::S + sauter_schwab_common_vert::S +end + function quaddata(op::IntegralOperator, test_local_space, trial_local_space, test_charts, trial_charts, qs::DoubleNumWiltonSauterQStrat) diff --git a/src/quadrature/doublesauterschwabint.jl b/src/quadrature/doublesauterschwabint.jl new file mode 100644 index 00000000..ae3a5cc6 --- /dev/null +++ b/src/quadrature/doublesauterschwabint.jl @@ -0,0 +1,183 @@ +# -------- used packages +using FastGaussQuadrature + +using LinearAlgebra +using StaticArrays + +# -------- included files +include("gqlog.jl") + + +abstract type SauterSchwabStrategy1D end + +struct CommonEdge{A} <: SauterSchwabStrategy1D + qpso::A # MRW quadrature for the outer integral + qpsi::A # GL quadrature for the inner integral +end +struct CommonVertex{A} <: SauterSchwabStrategy1D + qpso::A # MRW quadrature for the outer integral + qpsi::A # GL quadrature for the inner integral +end + + + +""" + (::CommonEdge)(f, η, ξ) + +Regularizing coordinate transform for parametrization on the unit line: [0,1] ↦ Γ. +based on Boundary Element Methods by Sauter and Schwab, example, 5.2.3, p.308 + +Common face case. +""" +function (::CommonEdge)(f, η, ξ) + + return (1-ξ) * + ( + f( (1 - η) * (1 - ξ), (1 - η) * (1 - ξ) + ξ ) + + f( 1 - (1 - η) * (1 - ξ), 1 - (1 - η) * (1 - ξ) - ξ) + ) +end + + + +""" + (::CommonVertex)(f, η, ξ) + +Regularizing coordinate transform for parametrization on the unit line: [0,1] ↦ Γ. +based on Boundary Element Methods by Sauter and Schwab, example, 5.2.3, p.308 + +Common vertex case. +""" + + +# We apply the Duffy trick at vertex (0,0) of the two triangles +# created by splitting along the diagonal (0,0)-(1,1) +function (::CommonVertex)(f, η, ξ) + + return ξ * ( + f( ξ, η * ξ) + + f( η * ξ, ξ) + ) +end + + +function _legendre(n, a, b) + x, w = FastGaussQuadrature.gausslegendre(n) + w .*= (b - a) / 2 + x = (x .+ 1) / 2 * (b - a) .+ a + collect(zip(x, w)) +end + +# MRWRules abbreviation of Ma-Rocklin-Wandzura rule +# Ma, J., Rocklin, D., & Wandzura, S. (1996). +#"Generalized Gaussian Quadrature Rules for Systems of Arbitrary Functions." +function _MRWrules(n,a,b) + + x, w = mrwquadrature(n) + return collect(zip(x,w)) +end + +function sauterschwab_parameterized1D(integrand, strategy::SauterSchwabStrategy1D) + return sum(w1 * w2 * strategy(integrand, η, ξ) for (η, w1) in strategy.qpsi, (ξ, w2) in strategy.qpso) +end + +# In the reference domain [0, 1] x [0,1], we assume +# that the singularity is at [0, 0] and then apply the +# Duffy trick at the triangles created by splitting along +# the diagonal (0,0)-(1,1). +# ==> Therefore, the vertices must be mapped that the segments +# with vertices [vt1, vt2] and [vs1, vs2] meet at vertices +# vt2 and vs2 (since the barycentric coordinate ξ = 0 is at vt2/vs2) +function reorder(t, s, strat::CommonVertex) + + T = eltype(t[1]) + tol = 1e3 * eps(T) + # tol = 1e5 * eps(T) + # tol = sqrt(eps(T)) + + # Find the permutation P of t and s that make + # Pt = [P, A1, A2] + # Ps = [P, B1, B2] + I = zeros(Int, 1) + J = zeros(Int, 1) + e = 1 + for i in 1:2 + v = t[i] + for j in 1:2 + w = s[j] + if norm(w - v) < tol + I[e] = i + J[e] = j + e += 1 + break + end + end + e == 2 && break + end + + prepend!(I, setdiff([1, 2], I)) + prepend!(J, setdiff([1, 2], J)) + + K = zeros(Int, 2) + for i in 1:2 + for j in 1:2 + if I[j] == i + K[i] = j + break + end + end + end + + L = zeros(Int, 2) + for i in 1:2 + for j in 1:2 + if J[j] == i + L[i] = j + break + end + end + end + + return I, J, K, L +end + +function reorder(t, s, strat::CommonEdge) + + T = eltype(t[1]) + tol = 1e3 * eps(T) + # tol = 1e5 * eps(T) + # tol = sqrt(eps(T)) + + I = [1, 2] + J = zeros(Int, 2) + v = t[1] + w = s[1] + if norm(w - v) < tol + J[:] = I[:] + else # If first vertices do not coincide -> swap + J[1] = 2 + J[2] = 1 + end + + K = zeros(Int, 2) + for i in 1:2 + for j in 1:2 + if I[j] == i + K[i] = j + break + end + end + end + + L = zeros(Int, 2) + for i in 1:2 + for j in 1:2 + if J[j] == i + L[i] = j + break + end + end + end + + return I, J, K, L +end \ No newline at end of file diff --git a/src/quadrature/gqlog.jl b/src/quadrature/gqlog.jl new file mode 100644 index 00000000..5ef8d2bc --- /dev/null +++ b/src/quadrature/gqlog.jl @@ -0,0 +1,366 @@ +## precision 1e-64 +const gq3logx = [ + big"0.0288116625309518311743284463058892943833785410148091127120685877756728991180956", + big"0.3040637296121376526108623586392372317949520269925648162447492695778139996408323", + big"0.8116692253440781168637051777605030761450290604667706879097761140167527022589782" +] +const gq3logw = [ + big"0.1033307079649286467692515926638152925117601886607103120667018372055221973825252", + big"0.4546365259700987088406911214255950929711433227560482628824643195989125765801328", + big"0.4420327660649726443900572859105896145170964885832414250508338431955652260378603" +] + +const gq4logx = [ + big"0.0118025909978449182649173011095277243792040203690474965703379785154526233875205", + big"0.1428256799774836951368513691763590994938532316254834466840646624072159114529217", + big"0.4892015226545744787190313056994040673995128251953935115454141480106812059217529", + big"0.8786799740691837028076889062651203170983114607803952562798953418334277261264303" +] +const gq4logw = [ + big"0.04339102877841439110189836963212064031500034973926060944228561441031014255089125", + big"0.2404520976594606759784500615666669970425863019151864377534566626947204831710365", + big"0.421403452259775931978815024210724368880313959764404972252008918197462697072954", + big"0.2947534213023490009408365445904879937620993885811479805522488046975066771977948" +] + +const gq5logx = [ + big"0.005652228205080097135927256196733224364461736662521571634310808301956553659434744", + big"0.07343037174265227340615889388832093647469715917834067035684484541883673822337442", + big"0.2849574044625581537145276019260509101628963210662763809477575315819894593888129", + big"0.6194822640847783814068089430513733271561626825848934609549153022972420903233235", + big"0.9157580830046983337846091809279726334110698024540233984987896162377435354971433" +] +const gq5logw = [ + big"0.02104694579185462911900268264212980706242530912584843774923279348541444162086709", + big"0.1307055407444466975910762549921867773164715864654795406970237184043603827930743", + big"0.289702301671314156841590351056571717447074684999630662451019615095549595934383", + big"0.3502203701203987102855468041352946381567577080211603909627150348339178095411739", + big"0.2083248416719858061627839071738170600172707113878809681400088381807577699303166" +] + +const gq6logx = [ + big"0.003025802137546258709729970375256268401779482992828101213315602517070676774568322", + big"0.04097825415595061505346596310891297982215214416543044471948088845952583162746435", + big"0.1708632955268772947251498297861305222272021210798376492086997941491912322656478", + big"0.4132557088447932476664814551636301801016057123765087288754062575075223818171789", + big"0.7090951467906285439500459170837777217505798833565268634341442960650946180757895", + big"0.9382395903771670913550205947159654750322534581742281653799581714380847089429558" +] +const gq6logw = [ + big"0.01135133881727260944049112382835582058174453200264770791905594141446248858716565", + big"0.07524106995491652291735628910920604024498865691978884727033600167691095492167005", + big"0.1887900416154163546095079437717888698034483559720734297158003205881410216356809", + big"0.2858207218272273119866834800845419826732913507876038823492520584906108457473145", + big"0.2844864278914088000451516698444240021352894282576606882526226532705898320941591", + big"0.1543103998937584010008094933616832845612376760602254444929330245592848866881613" +] + +const gq7logx = [ + big"0.001759652118465774280562642849488452238847882376829417161020566844898763296815622", + big"0.02446965071251336742764533734970449814707465617881458711466958936398767171520647", + big"0.1067480568587889541802597810831012639984493524877324427179411407375702636036147", + big"0.2758076412959173830778595120569401032015064743411215315346947545778810161812877", + big"0.5178551421518337161586689619818074537726293041441122507100974625383718453865105", + big"0.7718154853623849002746468694943818398820314642582719680233938471526845409035046", + big"0.9528413405810905589943065885030879911652507982696548514590485932036664026265743" +] +const gq7logw = [ + big"0.006632666319025705117839049890505451545085156065257921564980554577340348816003555", + big"0.04579970797847533412557673481204533079388958290463806525668605988795024448820841", + big"0.1238402080713181945504895649219393280879395309587789720141373794746235307041714", + big"0.2121019260238119301079148754555698567157309653785676929348714013330178452209891", + big"0.2613906456720077256465806068585123980383539106322107830995996640959468257240063", + big"0.2316361802909093843188155261040439969529839487681492485634359818123016339590676", + big"0.1185986656444517261327836419573836378660169052923973165662889588188190507751178" +] + +const gq8logx = [ + big"0.001090693941921822894165111895272961656889761270364157490698764319877404204397833", + big"0.01544065354637409035654803129696733338088233302185817085956683953435628581173319", + big"0.06943486210070215589606820183704622774352513760912341739401993180500014904198664", + big"0.1874432442554370314741850139770783912914836031762547096874553060230836873430399", + big"0.3733044213430930068774224196130946427983769812240799839889190905932509629106671", + big"0.6004940136993972160075727034500910768028300567728462727056609455469481882732827", + big"0.8168773397346666264599684042762922851882693053719296062937312826985616566562561", + big"0.9628397592694479679767293938117621595514008471737935702073567287533849959502929" +] +const gq8logw = [ + big"0.004124301185198343019856732777906332094734815195995821999647282928812400963714979", + big"0.02927037967468729611607963459474011124474094870767491048230999412065760132430261", + big"0.08311406745317000356959934495432356781311685459112761469028864096298503979443943", + big"0.1537216703422877404504605696868084750732921159913022489119351065174839736994873", + big"0.2134976095222608553772321005742849287594848638665672219708479691269085320689126", + big"0.2318702724435750456389973716207802644106398233930637097216879048919245919447895", + big"0.1905362390403677390075584696403609750435838347240190689367511770476690710464044", + big"0.09386546033845297682021577615079534556040674353024940328653192440354873593922193" +] + +const gq9logx = [ + big"0.0007110732887084292371271948114411427130596929286827033747571630375829895092934655", + big"0.01019453302625490675705263439721390757082070374909770360626863228804971969139422", + big"0.0468338672211245120800478827726438577871775553713352006780256391811070482319059", + big"0.1303678313651315279092725116746256616748483487812351497491905645734524393839669", + big"0.2704472571889117534115792396210086109828026312327005462427877165292572019618002", + big"0.4583194570951279555704788423104835576655393832207886739103153471011494009470139", + big"0.6654446330703511931141011568840343222343257021433467228651963799007886473741206", + big"0.8501167298492689862135218078117680443898748048201621390257943239647520356529031", + big"0.9699770448705806690488601014922724532077813279649453870772921791520938067334167" +] +const gq9logw = [ + big"0.002694891149020972537650818274669972064785863224366740658735088194859850376821306", + big"0.01949806475263514725048677490738025162488067888132757835094210266913341165923162", + big"0.05727368794913121851622494268319581220230302710787939286951364778040103463322235", + big"0.1115510143487581908445290090392732768292549693822402575676675123047847211910685", + big"0.1671748768632405941331694750536953632851337573319718468823265216730223402765603", + big"0.2036971186947111327284495789202735996350952027805427819858864230265916190717751", + big"0.2033824531641998599297042266271605769330084459619524205450522923748533235284438", + big"0.158655279830106274450813985467734586755046487348205019646993658645688017119264", + big"0.07607261324819660960897118902661656067049156798151396149288275333032421965258767" +] +const gq10logx = [ + big"0.0004829617106896294943183149272749175233248985914823525942928633132005676508123314", + big"0.006988629214315765291321415052330852703903885292050311462565292717699814705239717", + big"0.03261139659467762873636756333311119502903377871063522373995097575113617928003805", + big"0.09282575738916595754304180899809018017197868009727626181054204984045825798259768", + big"0.1983272568954037952451448876788239049325686339457225672360989187271669627070847", + big"0.3488801429793531934308727052079965952821380621199977107169557491389991483084391", + big"0.5304405557879560773593628930481504753741482469114746733537471295142940779164014", + big"0.7167646485116550851271524596080857897065882439999679965244791572396532440846242", + big"0.8752345575062335681899918401828609991184087254011484223626515130124980427858389", + big"0.9752456986843928703116413484567293450966284004578761398160192959831174182935528" +] +const gq10logw = [ + big"0.001833400073789844974343039244187088756939297229916101905004850946833688773766269", + big"0.01345312234599178938388397441120775272856711649003550514202802923403524514255926", + big"0.04049719431695833328218443450460775397747621201836680039657708236714280143083774", + big"0.08182236965890360616068411173063619462800676003125110086271887123013027996979842", + big"0.1291923427701375391426228447040414123310685703067390418217018174652510377316056", + big"0.1695453195472587471761867424266567039339458883985308141716254981178276286005089", + big"0.1891002165329956092241637321518972638740135835067914498094639132865254218185757", + big"0.1779657539614705508518683516871686460340560486233669980423658137698553596240428", + big"0.1337247706154615197642626403147324065464076855087306464470729665894779967882703", + big"0.06286551017703246003980012882486477718951883788627154140144115699614809281187774" +] + +const gq15logx = [ + big"0.0001057845484586292753493864924668380916251024074531216298515114307885975888182837077025", + big"0.007595218903207091883247444376284730435089898982134959893543137589162227513216067560734", + big"0.001566243836167816956655385777415432235256871358416825366132255779407797797063564074336", + big"0.0228310673939862320908871357311429238465611023426736927457085695758783454249166053234", + big"0.05238863015682000512815362893330259140205201964625249898277863454897904936380636336242", + big"0.1007586852012129667391004051136416390637516216000338429059475799900456473867694200564", + big"0.1707407688499432897166276448099318000084410205203915118544771821000765281005023511881", + big"0.2625912061189931044996158999566953474829984421658717933965928855192943179193219106092", + big"0.3735365051845580413656463497279392875704241296651853711319349601068756502026863034781", + big"0.4977463584145334387874316512677272094693748259201281232961985452805697138926236954657", + big"0.6267890313923734128862401704231215953332762536281211672089523277856861188842261626273", + big"0.7505161034614075810996828048439310865633264955639212529377408870151103751864345161899", + big"0.8582553352078605177984423047477266972702530005265449789606259388659161604670935837999", + big"0.9401412912123457712018243306013409987706249783593107354789051443834065655521367132188", + big"0.98840159598634178326441972546720013231209699249441888675085570941504454528126530438" +] +const gq15logw = [ + big"0.0004032177246484613108113868855719067131609139354902442780939481796966138315560952884750", + big"0.009784212118766145151161398889593401651383575047217047223872276721858634524108733830682", + big"0.003062978434787002192079338214803518648777190553257027589956117925535847239417067776433", + big"0.02155875222558125941055846177671850985290937096257277641281484956583132046253300979456", + big"0.03832306737088916459447923899346820916960051106457997764057575699361858087669673468580", + big"0.05889819902630037803354721437087112558399556998861012518316858072147312154129159956634", + big"0.08111702993925953014418613733560273596656944057632845507111787372353568675899055124403", + big"0.1021221019720686409827399402219384580411590819905097066492730049234426900752992643841", + big"0.1187890590304012903122863318580292804774040165420944681986724151227161900089339293267", + big"0.1282103164466939209716269453809265385177800805573983908408790332666576381949672776871", + big"0.1281633274170931946155990062722928672897994926781647759252725389773093642921684420955", + big"0.1174894658884916604705425997823953678879687624841131431788200137454728360609792520454", + big"0.09632301856959041974222565383861801802356754266825087678627376540093734939016915901427", + big"0.06613453983189341688459160887643830449298566566458033351875146195781511683399415051504", + big"0.0296207140035355151835647373027317576829387852868326515024583627740990099088947327451" +] + +const gq20logx =[ + big"3.52330453033399471667816984505212163385372867701241177054557432008560084854639227897305e-05", + big"0.00052609398251740622931185201230712336983446921505852417585026623624467681675527629213", + big"0.00258751954058139354982343346427624186380724620758427946400293435944533268347785475355", + big"0.00793447194838036690898675737551375268645848500751865408409873748481145848080538488209", + big"0.01868288813744561783653938764750025119630809002162018788255377919833387462704672359943", + big"0.03709767336975036321140295948415363410628316780781877636363314115648013425635225927340", + big"0.06531248867402127000203627397232005971094419594710515781581286966321679280710510277392", + big"0.10504850471155061286810146844991137213753086149525569391876624641826242856000857964041", + big"0.15735969181900194255764110855237616685370902165441514721181535970287673096005782068619", + big"0.22243006276745466795344010160142143129919223811088898974943778369604881916260678361773", + big"0.29944376565409990140139180056840606405847048366147916785308499946161590450020290372508", + big"0.38654244694388192611269692971909327833983859776432003583652778494397228807150436302099", + big"0.48087645382678963009875046547745208340402413105861821236744947129359489183930838809025", + big"0.57874793220550687780118957035777358141664078156681562597114176771293610958660152141124", + big"0.67583547584003749409689635673268871053832441601541226337158644402514605525463511344023", + big"0.76748246087256435235881593088168817030085019655264020473919169064531201055566527849563", + big"0.84902525397032003391717514950021749147719817851968628768372316422346888625639398588588", + big"0.91613370324166446526561782425398464194563663350874014749925948312513509742697660053806", + big"0.96513542790025568797284412200174800997093368204723690145127197708961367148386513479096", + big"0.99330353645695419477749224434889710143097251086524695494463434414747362227484890290069" +] +const gq20logw =[ + big"0.0001344996764677571785755869109998153753077125501697493791309017346370516233712556344", + big"0.0010347769229506137001964267002086113327273066783579727965914510659856637214605457741", + big"0.0033772636772332014304071807493684205732945358218422159622340103282499537881211088281", + big"0.0076735561935946429061968738147092460135554371267072145698890068024955734275871246431", + big"0.0142054962855419692230156082678233228034489481480776486190695719837525785785047977667", + big"0.0229844384632086133642926604803330825551668634385287894069098453272948919261594660325", + big"0.0337363605577136353630349538167293653140706844323453207110012764976530438528535058147", + big"0.0459147630734521805763787261700834101052513301612066180095957478645178171817632110455", + big"0.0587404799428039977139522416532948172566068384302970423568064199808158670985424149117", + big"0.0712650131611019951597126355449221280821946137970396226433000650095010279558501379770", + big"0.0824518089775831713766154049650768079393967900663760427250713849268578291550998231446", + big"0.0912682015163873671456134433980263438610572775942202418862625342739119798024940831677", + big"0.0967797159091613537245599424606049227671293198402166937066405170578512391472371710119", + big"0.0982381433400897170264204215678838446666990091981590951692574443184601626076371233194", + big"0.0951553030540296616805586810238431737748294263531686413205589409221933865160968370927", + big"0.0873556504104573967653727605061775985247662198036079972745116768635241054129859568447", + big"0.0750027772122717350558059607955531490075827440767261036945605828913202042466050580026", + big"0.0585972958082336954531999669843095642303012092555129152937077146462168365372613268424", + big"0.0389472505496114365437373129779855044193052969187765645674151605884742219863536023733", + big"0.0171372052681058586123532112120668713973084363086635099074857469162865654340154497718" +] + +const gq25logx =[ + big"1.488052056467244909625638598196564951876569065220877785868029725929257175702073e-05", + big"0.0002230911595769679150052824827061963553887308057912589297421430508322235593830149", + big"0.001104643649055818355966527940649085000685595160923981048336572605217909595781463", + big"0.003419469468885920108435532061507438449748930344249701792199240567760744981134533", + big"0.008150529295033886863001737760922833583972167939553544027949623676022634980540248", + big"0.01642893749479774357536914607010297463429235120115467227534160504792104112918616", + big"0.02944598355986495611729043551183351592537620021435726249691258773257858495243842", + big"0.04835756970793361992206318289677079171377067665053611124161178808395997974547869", + big"0.07418709391973235055161345105314559978006226850379538594909956584122958662110417", + big"0.1077329558835263957555273771944780860183523493640476006201185800123101693565726", + big"0.1494866382580873514029205773718203880809421017595584426091740310673042986930094", + big"0.1995667309592877334892849598527591611240819852994166926207587305100971208280098", + big"0.2576733558313253022937358067504523798314306274156044714730893469651697969374821", + big"0.3230662664067204776334892632593698903208493483364823778280717807027167910904648", + big"0.3945685120691864972423465462055174654475617953335967500929296325180965705827934", + big"0.4705960495534080911589738896473523490798329385681206138773790559940837999359339", + big"0.5492121464331800113338053478110828306174234808821422519671406282623117939769409", + big"0.62820394224342956742721788713433694930593440087226148756627291757420870460847", + big"0.705177201069315684009216075313764796668874317071394637193611443315058812652506", + big"0.7776641844158138725454459327579467862496790765378705168736570171130565114296634", + big"0.8432387621385732265330937708707768036353565992240126265713341165914802212256237", + big"0.8996324161061799578911370633997479446760397229530673482338851736387075804002855", + big"0.9448447334057146581716256758822538016902245031938619437645914717774015185865162", + big"0.9772425752266930964071101829574726836904699310716772379925450956228825958567725", + big"0.9956472154564406578393873465081762532052103442281190457464841657872264187993479" +] +const gq25logw =[ + big"5.68460660251698016879030856022417630001430609016622367404055301532420517518146e-05", + big"0.0004399975857693375973647244464678378656571747820875928868053402781833492973839444", + big"0.001450718904759957538678349120904065177343596226567549479016314001060341544670339", + big"0.003344018738173782930390142077859263786746242661488414521712207604655464684262988", + big"0.006308099547359185301394009297771731168039890753375951467515248755245728133502821", + big"0.01044887231035341693833085280730910931287694687370918949624926858979554546942906", + big"0.01577950366313618961381321505098910773186227729332837668009224396751136533141598", + big"0.02221579084737618976616974555580489592306146268420586418029213097641676732770199", + big"0.02957770241410115563354566497367172700221328972120378713034720074800286876842385", + big"0.03759704560718378026508260437372317265973035980591719381931293743645095154963036", + big"0.04593085159498192065795356091614634903045149175052029457054511209195320135850725", + big"0.05417972366570000073562979252398890641461333672904032950920405925127192012041869", + big"0.0619100915223072560034657428054791826935387118547807898511465447238853319719781", + big"0.06867907489284756060553676062557690702482539043342506486021238240403629718273571", + big"0.0740604961651022732248948486171917260340764931698174907073336916833564924542006", + big"0.07767050451276309473099166537844979688969030762299763172471541920363489078274423", + big"0.07919128776745480042852947234337603958242405415397338299700888106674317554728525", + big"0.0783914525088150148888334078694416997243579916181124400848147761113332917972084", + big"0.07514184161385324278653409971527606906237843712486869148047723092914634697649294", + big"0.06942582121576333636781033240187019134337158605139855903585929269150790978196787", + big"0.06134339190482063881362517452637817009569584955196664117532098895052422109096163", + big"0.05110885129800288147090741101894360205572976775703651201802794222866948111927817", + big"0.03904218956400919229641780441420384985429514672241964476559246224509873932540361", + big"0.02555547136263279771626117616472806907229536248219411749824155386571674026297952", + big"0.01115035472670782388615153988884628873172468911466282782341636466564633607871119" +] +const gq30logx = [ + big"7.3237974427259711014121960070261558027540416963138812613431263278327151190959804366e-06", + big"0.0001100447004577745007716984769879706584453254088480184618404891401583368805852606", + big"0.0005469183261839665800863499163609522643981329961208508247165296601075876520669445", + big"0.0017018575191016396996609998210211063372476794957637728051826599621512487361999522", + big"0.0040838636097143723718644912310460035773312700037827241100616448033337453278004756", + big"0.0083000411768823359286287204715113149951924068604220901716433344126671711618772075", + big"0.0150229781560799918407734738092689183169334797613696585255554437053296826559583688", + big"0.0249539236157545452623839559701354241297597712533297138906598587000838054214649009", + big"0.0387833861710629296537643873484755707882131324051356611114889508499039466807898273", + big"0.0571508984811763829294398275011739764422515015438295843301931490904109469284692630", + big"0.0806057414726551613156465122418320971118831782536207674741123918423202714028978615", + big"0.1095703942345179343611158071642564640247389773976704867002267074489662838861495058", + big"0.1443083730015008037356197789175449537749814887445783639406919211150498752312441063", + big"0.1848979494275319495770725900200967399299860317548604528456354192477147513110879917", + big"0.2312130015482551721163548462811451176341279422817911572631877314792390476096369565", + big"0.3394354811908526513550841586470308321278005121866896994848949413776526679876480729", + big"0.2829119601972077387080427035003151356127360118392446697437361910056006346697600011", + big"0.4000131131094501820250769897649776296380125851532828677483580844712844326518780426", + big"0.4636788569393062849191467490065869972502641315496346884536143223006897517940708194", + big"0.5292951427410362462923319470735170564284332778195315985758918131361310822398350604", + big"0.5955843953256450027825382220533229626567086395897337474045253978208215527348048846", + big"0.6611670403969151929139247690126765162243640607544294092814316869342595350201284609", + big"0.7246045281760327657790707990935709795002999206671464914538818667060410225576321978", + big"0.7844457347349415400509678017888811719495951273900367011694108038702325222630460459", + big"0.8392749514786630154000514251848497883439805962605613432967984751367075590980896528", + big"0.8877595976173431134701092007511246347018682591436474515354898615262600610351937045", + big"0.9286957959632275789862107959484967949713679877492330402104285186684398776724010128", + big"0.9610500591874097103222899330483108727864338896073734995030477023581225096095722360", + big"0.9839957035212889808179890593218683763658708185435852305227846725586056753688294546", + big"0.9969459586797630509861337066494387438585030158185224196851893029505421506025964264" +] +const gq30logw = [ + big"2.79892154309545405739723510489380069210793611655050550747711543479704102561861919685e-05", + big"0.00021736552650254116285124642012621259181832733064311295409672178173804747259446376", + big"0.00072070358653438901040194053761631875159806889598654616182904411596462505061716433", + big"0.00167446096505498902233281391415105911504274990708724661497346440039733067657261145", + big"0.00319128240641146441610066277996670825343132418947802492373263356129383541125471007", + big"0.00535378831352933473172631190251374346030494065364313342637259357587998203452787619", + big"0.00820962136808195471589891530215803621769361461546980563318888002909952552567724036", + big"0.01176802921308481152797096394244181125451658226184469697172509738148802153652282010", + big"0.01599814350489140149763603068428629666775039563032076452547616160487744529727224182", + big"0.02082904109362429386427713970365819913806790570243416235608770390517636839882038207", + big"0.02615159766130931255371398669267371160770236587219917317217286765695263056664607358", + big"0.03182206826945638096504346131337724704287534948009793135273588003504229344353078382", + big"0.03766725599980671697189382643523378842658467493644965516029104462279299436170505501", + big"0.04349106226322339672911771677370940528027127799845281803511333713210191659662041518", + big"0.04908215322840304030099174860840581446598241617157948889097724877574668651907173348", + big"0.05869594429097691387579357598711458627497606185797316466625163785697740760494015714", + big"0.05422242866126261587110150982953097239633598880605386130322259535988549142217642663", + big"0.06229791811493648011168304384832523168380265713682130944354943903684490153886454541", + big"0.06484344570723305525372721814737560442230745015510585776187183540111537894524832593", + big"0.06617555985125437859534613656176695554584622877929469097064774571451332612064623233", + big"0.06617229527720047913131104667640576429008335550213045345021005286440129176246678245", + big"0.06475245892292092403406452472768098104967651874004644271092850354821405524611080741", + big"0.06187985834995463849925309457706242097748883881928765598589247454949234397734149989", + big"0.05756580366344204424412375601782622950190169249457352156861756311335070073919719047", + big"0.05186976919246567846117295123649254581339360407011283296332312580588896440891173441", + big"0.04489817849556722268070380739886932332497986016922684846199394709407370500832867311", + big"0.03680136250036949402188921872896857566077811836438319898471514629234808804665804404", + big"0.02776887037741359260389115885664022791979235167927439041544240586106164189739881477", + big"0.01802387378416074345440757639679616037798750110594698516423528647063618940758975519", + big"0.00782767019549675715100064364777713048008869931291672091525079129829684057243125330" +] + +const gqlogx = [gq3logx, gq4logx, gq5logx, gq6logx, gq7logx, gq8logx, gq9logx, gq10logx, gq15logx, gq20logx, gq25logx, gq30logx] +const gqlogw = [gq3logw, gq4logw, gq5logw, gq6logw, gq7logw, gq8logw, gq9logw, gq10logw, gq15logw, gq20logw, gq25logw, gq30logw] + +mutable struct GQExceptionDegree <: Exception +end + +Base.showerror(io::IO, e::GQExceptionDegree) = print( + io, + "Only quadratures of degree 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25 and 30 available." +) + +function mrwquadrature(n::Int) + rules = [15, 20, 25, 30] + if n >= 3 && n <= 10 + return Float64.(gqlogx[n-2]), Float64.(gqlogw[n-2]) + else + if n in rules + gqlogx[8+Int((n-10)/5)], gqlogw[8+Int((n-10)/5)] + else + throw(GQExceptionDegree()) + end + end +end \ No newline at end of file diff --git a/src/quadrature/nonconformingintegralopqstrat.jl b/src/quadrature/nonconformingintegralopqstrat.jl index 7d0bd004..abd2c394 100644 --- a/src/quadrature/nonconformingintegralopqstrat.jl +++ b/src/quadrature/nonconformingintegralopqstrat.jl @@ -1,4 +1,4 @@ -struct NonConformingIntegralOpQStrat{S} +struct NonConformingIntegralOpQStrat{S} <: AbstractQuadStrat conforming_qstrat::S end diff --git a/src/quadrature/nonconformingoverlapqrule.jl b/src/quadrature/nonconformingoverlapqrule.jl index 12c563b0..c93dd267 100644 --- a/src/quadrature/nonconformingoverlapqrule.jl +++ b/src/quadrature/nonconformingoverlapqrule.jl @@ -8,8 +8,11 @@ function momintegrals!(op, test_chart::CompScienceMeshes.Simplex, basis_chart::CompScienceMeshes.Simplex, out, qrule::NonConformingOverlapQRule) + num_tshapes = numfunctions(test_local_space, domain(test_chart)) + num_bshapes = numfunctions(basis_local_space, domain(basis_chart)) + test_charts, tclps = CompScienceMeshes.intersection_keep_clippings(test_chart, basis_chart) - _, bclps = CompScienceMeshes.intersection_keep_clippings(basis_chart, test_chart) + _, bclps = CompScienceMeshes.intersection_keep_clippings(basis_chart, test_chart) bsis_charts = copy(test_charts) for tclp in tclps append!(test_charts, tclp) end @@ -26,32 +29,36 @@ function momintegrals!(op, isempty(test_charts) && return isempty(bsis_charts) && return - # @assert volume(test_chart) ≈ sum(volume.(test_charts)) - # if volume(basis_chart) ≈ sum(volume.(bsis_charts)) else - # @show volume(basis_chart) - # @show sum(volume.(bsis_charts)) - # error() - # end - # test_local_space = refspace(test_functions) - # basis_local_space = refspace(basis_functions) + test_overlaps = map(test_charts) do tchart + simplex( + carttobary(test_chart, tchart.vertices[1]), + carttobary(test_chart, tchart.vertices[2]), + carttobary(test_chart, tchart.vertices[3])) + end + + trial_overlaps = map(bsis_charts) do bchart + simplex( + carttobary(basis_chart, bchart.vertices[1]), + carttobary(basis_chart, bchart.vertices[2]), + carttobary(basis_chart, bchart.vertices[3])) + end qstrat = CommonFaceOverlappingEdgeQStrat(qrule.conforming_qstrat) qdata = quaddata(op, test_local_space, basis_local_space, test_charts, bsis_charts, qstrat) + zlocal = zero(out) + P = zeros(T, num_tshapes, num_tshapes) + Q = zeros(T, num_bshapes, num_bshapes) for (p,tchart) in enumerate(test_charts) + restrict!(P, test_local_space, test_chart, tchart, test_overlaps[p]) for (q,bchart) in enumerate(bsis_charts) + restrict!(Q, basis_local_space, basis_chart, bchart, trial_overlaps[q]) + qrule = quadrule(op, test_local_space, basis_local_space, p, tchart, q, bchart, qdata, qstrat) - # @show qrule - - P = restrict(test_local_space, test_chart, tchart) - Q = restrict(basis_local_space, basis_chart, bchart) - zlocal = zero(out) - # momintegrals!(zlocal, op, - # test_local_space, nothing, tchart, - # basis_local_space, nothing, bchart, qrule) + fill!(zlocal, 0) momintegrals!(op, test_local_space, basis_local_space, tchart, bchart, zlocal, qrule) @@ -60,5 +67,4 @@ function momintegrals!(op, for k in axes(P,2) for l in axes(Q,2) out[i,j] += P[i,k] * zlocal[k,l] * Q[j,l] - # out .+= P * zlocal * Q' end end end end end end end \ No newline at end of file diff --git a/src/quadrature/nonconformingtouchqrule.jl b/src/quadrature/nonconformingtouchqrule.jl index d2da86ab..e2b63866 100644 --- a/src/quadrature/nonconformingtouchqrule.jl +++ b/src/quadrature/nonconformingtouchqrule.jl @@ -9,8 +9,8 @@ function momintegrals!(op, τ::CompScienceMeshes.Simplex, σ::CompScienceMeshes.Simplex, out, qrule::NonConformingTouchQRule) - # test_locspace = refspace(test_functions) - # bsis_locspace = refspace(bsis_functions) + num_tshapes = numfunctions(test_locspace, domain(τ)) + num_bshapes = numfunctions(bsis_locspace, domain(σ)) T = coordtype(τ) P = eltype(τ.vertices) @@ -25,50 +25,44 @@ function momintegrals!(op, isempty(τs) && return isempty(σs) && return - # test conformity - for a in τs - for b in σs - if !_test_conformity(a, b) - @infiltrate - end - end - end - - # volume(σ) ≈ sum(volume.(σs)) || @infiltrate - - # if volume(τ) ≈ sum(volume.(τs)) else - # @show volume(τ) - # @show sum(volume.(τs)) - # error() - # end - # @assert volume(σ) ≈ sum(volume.(σs)) - @assert all(volume.(τs) .> 1e3 * eps(T) * (volume(τ))) @assert all(volume.(σs) .> 1e3 * eps(T) * (volume(σ))) + test_overlaps = map(τs) do tchart + simplex( + carttobary(τ, tchart.vertices[1]), + carttobary(τ, tchart.vertices[2]), + carttobary(τ, tchart.vertices[3])) + end + + trial_overlaps = map(σs) do bchart + simplex( + carttobary(σ, bchart.vertices[1]), + carttobary(σ, bchart.vertices[2]), + carttobary(σ, bchart.vertices[3])) + end + qstrat = qrule.conforming_qstrat qdata = quaddata(op, test_locspace, bsis_locspace, τs, σs, qstrat) - any(volume.(τs) .< 1e-13) && @infiltrate - any(volume.(σs) .< 1e-13) && @infiltrate + @assert !any(volume.(τs) .< 1e-13) + @assert !any(volume.(σs) .< 1e-13) + zlocal = zero(out) + P = zeros(T, num_tshapes, num_tshapes) + Q = zeros(T, num_bshapes, num_bshapes) for (p,tchart) in enumerate(τs) + restrict!(P, test_locspace, τ, tchart, test_overlaps[p]) for (q,bchart) in enumerate(σs) + restrict!(Q, bsis_locspace, σ, bchart, trial_overlaps[q]) + qrule = quadrule(op, test_locspace, bsis_locspace, p, tchart, q, bchart, qdata, qstrat) - # @show qrule - P = restrict(test_locspace, τ, tchart) - Q = restrict(bsis_locspace, σ, bchart) - zlocal = zero(out) - - # momintegrals!(zlocal, op, - # test_locspace, nothing, tchart, - # bsis_locspace, nothing, bchart, qrule) + fill!(zlocal, 0) momintegrals!(op, test_locspace, bsis_locspace, tchart, bchart, zlocal, qrule) - # out .+= P * zlocal * Q' for i in axes(P,1) for j in axes(Q,1) for k in axes(P,2) @@ -144,13 +138,8 @@ function _conforming_refinement_touching_triangles(τ,σ,i,j) μ = faces(σ)[j] ρ = CompScienceMeshes.intersection(λ,μ)[1] - # τ_verts = [τ[mod1(i+2,3)], τ[mod1(i,3)], τ[mod1(i+1,3)]] - # σ_verts = [σ[mod1(j+2,3)], σ[mod1(j,3)], σ[mod1(j+1,3)]] - # τ_verts = [v for v in τ.vertices] - # σ_verts = [v for v in σ.vertices] τ_verts = Array(τ.vertices) σ_verts = Array(σ.vertices) - # @show typeof(τ_verts) T = coordtype(τ) P = eltype(τ.vertices) @@ -187,28 +176,20 @@ function _conforming_refinement_touching_triangles(τ,σ,i,j) insert!(σ_verts, p, V[2]) insert!(σ_verts, p, V[1]) - # println(τ_verts) - # println(σ_verts) - τ_charts = [ simplex(τ_verts[mod1(new_i,5)], τ_verts[mod1(new_i+s,5)], τ_verts[mod1(new_i+s+1,5)]) for s in 1:3 ] σ_charts = [ simplex(σ_verts[mod1(new_j,5)], σ_verts[mod1(new_j+s,5)], σ_verts[mod1(new_j+s+1,5)]) for s in 1:3 ] - # σ_charts = [ simplex(σ_verts[1], σ_verts[i], σ_verts[i+1]) for i in 2:length(σ_verts)-1 ] - - h = volume(τ) - τ_charts = [ch for ch in τ_charts if volume(ch) .> 1e6 * eps(T) * h] - σ_charts = [ch for ch in σ_charts if volume(ch) .> 1e6 * eps(T) * h] + τ_charts = [ch for ch in τ_charts if volume(ch) .> 1e6 * eps(T) * volume(τ)] + σ_charts = [ch for ch in σ_charts if volume(ch) .> 1e6 * eps(T) * volume(σ)] τ_charts = [ch for ch in τ_charts if volume(ch) .> 1e6 * eps(T)] σ_charts = [ch for ch in σ_charts if volume(ch) .> 1e6 * eps(T)] - signs = Int.(sign.(dot.(normal.(τ_charts),Ref(normal(τ))))) - τ_charts = flip_normal.(τ_charts,signs) - signs = Int.(sign.(dot.(normal.(σ_charts),Ref(normal(σ))))) - σ_charts = flip_normal.(σ_charts,signs) - - - # τ_charts = τ_charts[volume.(τ_charts) .> 1e3 * eps(T) * h] - # σ_charts = τ_charts[volume.(σ_charts) .> 1e3 * eps(T) * h] + # signs = Int.(sign.(dot.(normal.(τ_charts),Ref(normal(τ))))) + # @assert all(signs .== 1) + # τ_charts = flip_normal.(τ_charts,signs) + # signs = Int.(sign.(dot.(normal.(σ_charts),Ref(normal(σ))))) + # @assert all(signs .== 1) + # σ_charts = flip_normal.(σ_charts,signs) return τ_charts, σ_charts end diff --git a/src/quadrature/quadstrats.jl b/src/quadrature/quadstrats.jl index dba1fc33..ec9f2e58 100644 --- a/src/quadrature/quadstrats.jl +++ b/src/quadrature/quadstrats.jl @@ -1,23 +1,11 @@ using InteractiveUtils -struct DoubleNumWiltonSauterQStrat{R,S} - outer_rule_far::R - inner_rule_far::R - outer_rule_near::R - inner_rule_near::R - sauter_schwab_common_tetr::S - sauter_schwab_common_face::S - sauter_schwab_common_edge::S - sauter_schwab_common_vert::S -end -struct DoubleNumQStrat{R} - outer_rule::R - inner_rule::R -end -struct SauterSchwab3DQStrat{R,S} + + +struct SauterSchwab3DQStrat{R,S} <: AbstractQuadStrat outer_rule::R inner_rule::R sauter_schwab_1D::S @@ -26,7 +14,7 @@ struct SauterSchwab3DQStrat{R,S} sauter_schwab_4D::S end -struct OuterNumInnerAnalyticQStrat{R} +struct OuterNumInnerAnalyticQStrat{R} <: AbstractQuadStrat outer_rule::R end @@ -57,8 +45,8 @@ macro defaultquadstrat(dop, body) error("@defaultquadstrat expects a first argument of the for (op,tfs,bfs) or (linform,tfs)") end -struct SingleNumQStrat - quad_rule::Int +struct SingleNumQStrat{R} <: AbstractQuadStrat + quad_rule::R end function quadinfo(op, tfs, bfs; quadstrat=defaultquadstrat(op, tfs, bfs)) diff --git a/src/quadrature/rules/momintegrals.jl b/src/quadrature/rules/momintegrals.jl index 6b749033..a599c539 100644 --- a/src/quadrature/rules/momintegrals.jl +++ b/src/quadrature/rules/momintegrals.jl @@ -1,6 +1,6 @@ function momintegrals!(out, op, - test_functions::Space, test_cellptr, test_chart, - trial_functions::Space, trial_cellptr, trial_chart, + test_functions, test_cellptr, test_chart, + trial_functions, trial_cellptr, trial_chart, quadrule) local_test_space = refspace(test_functions) diff --git a/src/quadrature/rules/testinbaryrefoftrialqrule.jl b/src/quadrature/rules/testinbaryrefoftrialqrule.jl new file mode 100644 index 00000000..6f5239b7 --- /dev/null +++ b/src/quadrature/rules/testinbaryrefoftrialqrule.jl @@ -0,0 +1,76 @@ +struct TestInBaryRefOfTrialQRule{S} + conforming_qstrat::S +end + +function BEAST.momintegrals!(out, op, + test_functions, test_cell, test_chart, + trial_functions, trial_cell, trial_chart, + qr::TestInBaryRefOfTrialQRule) + + test_local_space = refspace(test_functions) + trial_local_space = refspace(trial_functions) + + num_tshapes = numfunctions(test_local_space, domain(test_chart)) + num_bshapes = numfunctions(trial_local_space, domain(trial_chart)) + + T = coordtype(test_chart) + z, u, h, t = zero(T), one(T), T(1//2), T(1//3) + + c = CompScienceMeshes.point(T, t, t) + v = ( + CompScienceMeshes.point(T, u, z), + CompScienceMeshes.point(T, z, u), + CompScienceMeshes.point(T, z, z)) + e = ( + CompScienceMeshes.point(T, z, h), + CompScienceMeshes.point(T, h, z), + CompScienceMeshes.point(T, h, h)) + + X = ( + CompScienceMeshes.simplex(v[1], e[3], c), + CompScienceMeshes.simplex(v[2], c, e[3]), + CompScienceMeshes.simplex(v[2], e[1], c), + CompScienceMeshes.simplex(v[3], c, e[1]), + CompScienceMeshes.simplex(v[3], e[2], c), + CompScienceMeshes.simplex(v[1], c, e[2])) + + C = CompScienceMeshes.cartesian(trial_chart, c) + V = ( + CompScienceMeshes.cartesian(trial_chart, v[1]), + CompScienceMeshes.cartesian(trial_chart, v[2]), + CompScienceMeshes.cartesian(trial_chart, v[3])) + E = ( + CompScienceMeshes.cartesian(trial_chart, e[1]), + CompScienceMeshes.cartesian(trial_chart, e[2]), + CompScienceMeshes.cartesian(trial_chart, e[3])) + + trial_charts = ( + CompScienceMeshes.simplex(V[1], E[3], C), + CompScienceMeshes.simplex(V[2], C, E[3]), + CompScienceMeshes.simplex(V[2], E[1], C), + CompScienceMeshes.simplex(V[3], C, E[1]), + CompScienceMeshes.simplex(V[3], E[2], C), + CompScienceMeshes.simplex(V[1], C, E[2])) + + quadstrat = qr.conforming_qstrat + qd = BEAST.quaddata(op, test_local_space, trial_local_space, + (test_chart,), trial_charts, quadstrat) + + Q = zeros(T, num_tshapes, num_tshapes) + out1 = zero(out) + for (q,chart) in enumerate(trial_charts) + qr1 = BEAST.quadrule(op, test_local_space, trial_local_space, + 1, test_chart, q ,chart, qd, quadstrat) + + BEAST.restrict!(Q, trial_local_space, trial_chart, chart, X[q]) + + fill!(out1, 0) + BEAST.momintegrals!(out1, op, + test_functions, nothing, test_chart, + trial_functions, nothing, chart, qr1) + + for j in 1:num_bshapes + for i in 1:num_tshapes + for k in 1:size(Q, 2) + out[i,j] += out1[i,k] * Q[j,k] +end end end end end diff --git a/src/quadrature/rules/testrefinestrialqrule.jl b/src/quadrature/rules/testrefinestrialqrule.jl index e2d86b99..baf388f7 100644 --- a/src/quadrature/rules/testrefinestrialqrule.jl +++ b/src/quadrature/rules/testrefinestrialqrule.jl @@ -22,17 +22,26 @@ function momintegrals!(out, op, parent_mesh = CompScienceMeshes.parent(test_mesh) trial_charts = [chart(test_mesh, p) for p in CompScienceMeshes.children(parent_mesh, trial_cell)] + trial_overlaps = map(trial_charts) do chart + simplex( + carttobary(trial_chart, chart.vertices[1]), + carttobary(trial_chart, chart.vertices[2]), + carttobary(trial_chart, chart.vertices[3])) + end + quadstrat = qr.conforming_qstrat qd = quaddata(op, test_local_space, trial_local_space, [test_chart], trial_charts, quadstrat) + zlocal = zero(out) + Q = zeros(coordtype(trial_chart), num_bshapes, num_bshapes) for (q,chart) in enumerate(trial_charts) + restrict!(Q, trial_local_space, trial_chart, chart, trial_overlaps[q]) + qr = quadrule(op, test_local_space, trial_local_space, 1, test_chart, q ,chart, qd, quadstrat) - - Q = restrict(trial_local_space, trial_chart, chart) - zlocal = zero(out) + fill!(zlocal, 0) momintegrals!(zlocal, op, test_functions, nothing, test_chart, trial_functions, nothing, chart, qr) diff --git a/src/quadrature/rules/timedomain/excitation/multiquadqrule.jl b/src/quadrature/rules/timedomain/excitation/multiquadqrule.jl new file mode 100644 index 00000000..53766782 --- /dev/null +++ b/src/quadrature/rules/timedomain/excitation/multiquadqrule.jl @@ -0,0 +1,22 @@ +mutable struct MultiQuadStrategy{P,R} #<: NumQuadStrategy + quad_points::P + inner_rule::R +end + + +timequadrule(qr::MultiQuadStrategy, p) = qr.inner_rule + +function momintegrals!(z, exc::TDFunctional, testrefs, timerefs, τ, ρ, qr::MultiQuadStrategy) + + for p in qr.quad_points + x = p.point + w = p.weight + f = p.value + dx = w + + tqr = timequadrule(qr,p) + timeintegrals!(z, exc, testrefs, timerefs, x, ρ, dx, tqr, f) + + end + +end \ No newline at end of file diff --git a/src/quadrature/rules/timedomain/excitation/singlequad2qrule.jl b/src/quadrature/rules/timedomain/excitation/singlequad2qrule.jl new file mode 100644 index 00000000..218f76d0 --- /dev/null +++ b/src/quadrature/rules/timedomain/excitation/singlequad2qrule.jl @@ -0,0 +1,42 @@ +# TODO: consolidate with the existing definition of SingleQuadStrategy +mutable struct SingleQuadStrategy2{P} #<: NumQuadStrategy + quad_points::P +end + + + +function timeintegrals!(z, exc::TDFunctional, + testrefs, timerefs, + testpoint, timeelement, dx, qr::SingleQuadStrategy2, f) + + num_tshapes = numfunctions(testrefs, domain(chart(testpoint))) + for p in qr.quad_points + t = p.point + w = p.weight + U = p.value + dt = w #* jacobian(t) # * volume(timeelement) + + for i in 1 : num_tshapes + for k in 1 : numfunctions(timerefs) + z[i,k] += dot(f[i][1]*U[k], exc(testpoint,t)) * dt * dx + end + end + end +end + + +function timeintegrals!(z, exc::TDFunctional, + spacerefs, timerefs::DiracBoundary, + testpoint, timeelement, + dx, qr::Nothing, testvals) + + num_tshapes = numfunctions(spacerefs, domain(chart(testpoint))) + # since timeelement uses barycentric coordinates, + # the first/left vertex has coords u = 1.0! + testtime = neighborhood(timeelement, point(0.0)) + @assert cartesian(testtime)[1] ≈ timeelement.vertices[2][1] + + for i in 1 : num_tshapes + z[i,1] += dot(testvals[i][1], exc(testpoint, testtime)) * dx + end +end \ No newline at end of file diff --git a/src/quadrature/sauterschwabints.jl b/src/quadrature/sauterschwabints.jl index 1d56cdb3..1688ab08 100644 --- a/src/quadrature/sauterschwabints.jl +++ b/src/quadrature/sauterschwabints.jl @@ -62,6 +62,7 @@ function _krondot_gen(a::Type{U}, b::Type{V}) where {U<:SVector{N}, V<:SVector{M end return ex end + @generated function _krondot(a::SVector{N}, b::SVector{M}) where {M,N} ex = _krondot_gen(a,b) return ex @@ -77,15 +78,13 @@ function _integrands_gen(::Type{U}, ::Type{V}) where {U<:SVector{N}, V<:SVector{ end return ex end + @generated function _integrands(f, a::SVector{N}, b::SVector{M}) where {M,N} ex = _integrands_gen(a,b) # println(ex) return ex end - - - function _integrands_leg_gen(f::Type{U}, g::Type{V}) where {U<:SVector{N}, V<:SVector{M}} where {M,N} ex = :(SMatrix{N,M}(())) for m in 1:M @@ -105,17 +104,10 @@ function (igd::Integrand)(x,y,f,g) op = igd.operator kervals = kernelvals(op, x, y) - _integrands_leg(op, kervals, f, x, g, y) + return _integrands_leg(op, kervals, f, x, g, y) end -# function CompScienceMeshes.permute_vertices( -# ch::CompScienceMeshes.RefQuadrilateral, I) - -# V = vertices(ch) -# return Quadrilateral(V[I[1]], V[I[2]], V[I[3]], V[I[4]]) -# end - struct PulledBackIntegrand{I,C1,C2} igd::I chart1::C1 @@ -141,25 +133,50 @@ function pulledback_integrand(igd, PulledBackIntegrand(igd, ichart1, ichart2) end +function sauterschwab_parameterized(igdp, rule::SauterSchwabStrategy) + return SauterSchwabQuadrature.sauterschwab_parameterized(igdp, rule) +end + +function sauterschwab_parameterized(igdp, rule::SauterSchwabQuadrature1D.SauterSchwabStrategy1D) + return SauterSchwabQuadrature1D.sauterschwab_parameterized1D(igdp, rule) +end + +function sauterschwab_reorder(test_vertices, trial_vertices, rule::SauterSchwabStrategy) + I, J, _, _ = SauterSchwabQuadrature.reorder(test_vertices, trial_vertices, rule) + + return I, J +end + +function sauterschwab_reorder(test_vertices, trial_vertices, rule::SauterSchwabQuadrature1D.SauterSchwabStrategy1D) + I, J, _, _ = SauterSchwabQuadrature1D.reorder(test_vertices, trial_vertices, rule) + + return I, J +end + function momintegrals!(op::Operator, test_local_space, trial_local_space, test_chart, trial_chart, - out, rule::SauterSchwabStrategy) + out, rule::Union{SauterSchwabStrategy,SauterSchwabQuadrature1D.SauterSchwabStrategy1D}) - I, J, _, _ = SauterSchwabQuadrature.reorder( + I, J = sauterschwab_reorder( vertices(test_chart), - vertices(trial_chart), rule) + vertices(trial_chart), + rule + ) num_tshapes = numfunctions(test_local_space, domain(test_chart)) num_bshapes = numfunctions(trial_local_space, domain(trial_chart)) igd = Integrand(op, test_local_space, trial_local_space, test_chart, trial_chart) igdp = pulledback_integrand(igd, I, test_chart, J, trial_chart) - G = SauterSchwabQuadrature.sauterschwab_parameterized(igdp, rule) - out[1:num_tshapes, 1:num_bshapes] .+= G - - nothing -end + G = sauterschwab_parameterized(igdp, rule) + for j in 1:num_bshapes + for i in 1:num_tshapes + out[i,j] += G[i,j] + end + end + nothing +end \ No newline at end of file diff --git a/src/quadrature/selfsauterdnumotherwiseqstrat.jl b/src/quadrature/selfsauterdnumotherwiseqstrat.jl index 857d1b46..1cbca3a4 100644 --- a/src/quadrature/selfsauterdnumotherwiseqstrat.jl +++ b/src/quadrature/selfsauterdnumotherwiseqstrat.jl @@ -1,4 +1,4 @@ -struct SelfSauterOtherwiseDNumQStrat{R,S} +struct SelfSauterOtherwiseDNumQStrat{R,S} <: AbstractQuadStrat outer_rule::R inner_rule::R sauter_schwab_common::S diff --git a/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl b/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl index 5726e610..5e86b67c 100644 --- a/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl +++ b/src/quadrature/strategies/cfcvsautercewiltonpdnumqstrat.jl @@ -1,4 +1,4 @@ -struct CommonFaceVertexSauterCommonEdgeWiltonPostitiveDistanceNumQStrat{R,S} +struct CommonFaceVertexSauterCommonEdgeWiltonPostitiveDistanceNumQStrat{R,S} <: AbstractQuadStrat outer_rule_far::R inner_rule_far::R outer_rule_near::R diff --git a/src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl b/src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl new file mode 100644 index 00000000..51c1394d --- /dev/null +++ b/src/quadrature/strategies/nonconftestbaryrefoftrialqstrat.jl @@ -0,0 +1,48 @@ +struct NonConfTestBaryRefOfTrialQStrat{P} <: BEAST.AbstractQuadStrat + conforming_qstrat::P +end + +function BEAST.quaddata(a, X, Y, tels, bels, qs::NonConfTestBaryRefOfTrialQStrat) + return BEAST.quaddata(a, X, Y, tels, bels, qs.conforming_qstrat) +end + +function BEAST.quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, + quadstrat::NonConfTestBaryRefOfTrialQStrat) + + # return TestInBaryRefOfTrialQRule(quadstrat.conforming_qstrat) + nh = BEAST._numhits(τ, σ) + nh > 0 && return TestInBaryRefOfTrialQRule(quadstrat.conforming_qstrat) + return BEAST.quadrule(a, 𝒳, 𝒴, i, τ, j, σ, qd, + quadstrat.conforming_qstrat) +end + +@testitem "NonConfTestBaryRefOfTrialQStrat" begin + using BEAST, Test + using CompScienceMeshes, LinearAlgebra + + # @show pathof(CompScienceMeshes) + + fnm = joinpath(dirname(pathof(BEAST)), "../test/assets/sphere45.in") + Γ1 = BEAST.readmesh(fnm) + Γ2 = deepcopy(Γ1) + + X = raviartthomas(Γ1) + Y1 = buffachristiansen(Γ1) + Y = buffachristiansen(Γ2) + + K = Maxwell3D.doublelayer(gamma=1.0) + qs1 = BEAST.DoubleNumWiltonSauterQStrat(2, 3, 6, 7, 5, 5, 4, 3) + qs2 = BEAST.NonConformingIntegralOpQStrat(qs1) + qs3 = BEAST.NonConfTestBaryRefOfTrialQStrat(qs1) + + @time Kyx1 = assemble(K, Y, X; quadstrat=qs1) + @time Kyx2 = assemble(K, Y, X; quadstrat=qs2) + @time Kyx3 = assemble(K, Y, X; quadstrat=qs3) + @time Kyx4 = assemble(K, Y1, X; quadstrat=qs1) + + @test norm(Kyx1 - Kyx2) < 0.05 + @test norm(Kyx1 - Kyx3) < 0.05 + @test norm(Kyx2 - Kyx3) < 0.002 + @test norm(Kyx2 - Kyx4) < 0.002 + @test norm(Kyx3 - Kyx4) < 1e-12 +end \ No newline at end of file diff --git a/src/quadrature/strategies/quadstrat.jl b/src/quadrature/strategies/quadstrat.jl new file mode 100644 index 00000000..38c88ca7 --- /dev/null +++ b/src/quadrature/strategies/quadstrat.jl @@ -0,0 +1,9 @@ +abstract type AbstractQuadStrat end + +function (qs::AbstractQuadStrat)(a, X, Y) + qs +end + +function (qs::AbstractQuadStrat)(l, X) + qs +end \ No newline at end of file diff --git a/src/quadrature/strategies/testrefinestrialqstrat.jl b/src/quadrature/strategies/testrefinestrialqstrat.jl index f36b48c6..e295c6e5 100644 --- a/src/quadrature/strategies/testrefinestrialqstrat.jl +++ b/src/quadrature/strategies/testrefinestrialqstrat.jl @@ -1,4 +1,4 @@ -struct TestRefinesTrialQStrat{S} +struct TestRefinesTrialQStrat{S} <: AbstractQuadStrat conforming_qstrat::S end diff --git a/src/quadrature/strategies/timedomain/excitation/numspacenumtimeqstrat.jl b/src/quadrature/strategies/timedomain/excitation/numspacenumtimeqstrat.jl new file mode 100644 index 00000000..a3655512 --- /dev/null +++ b/src/quadrature/strategies/timedomain/excitation/numspacenumtimeqstrat.jl @@ -0,0 +1,91 @@ +struct NumSpaceNumTimeQStrat{S,T} <: AbstractQuadStrat + space_rule::S + time_rule::T +end + + +function quaddata(exc::TDFunctional, + testrefs, timerefs, + testels, timeels, quadstrat::NumSpaceNumTimeQStrat) + + r = quadstrat.space_rule + s = quadstrat.time_rule + + testqd = quadpoints(testrefs, testels, (r,)) + timeqd = quadpoints(timerefs, timeels, (s,)) + + testqd, timeqd + +end + + +function quadrule(exc::TDFunctional, + testrefs, timerefs, + p, τ, r, ρ, + qd, quadstrat::NumSpaceNumTimeQStrat) + + MultiQuadStrategy( + qd[1][1,p], + SingleQuadStrategy2( + qd[2][1,r] + ) + ) + +end + + +function quaddata(excitation::TDFunctional, + test_refspace, time_refspace::DiracBoundary, + test_elements, time_elements, quadstrat::NumSpaceNumTimeQStrat) + + r = quadstrat.space_rule + test_quad_data = quadpoints(test_refspace, test_elements, (r,)) + + test_quad_data, nothing +end + + +function quadrule(exc::TDFunctional, + testrefs, timerefs::DiracBoundary, + p, τ, r, ρ, + qd, quadstrat::NumSpaceNumTimeQStrat) + + MultiQuadStrategy( + qd[1][1,p], + nothing + ) + +end + + +@testitem "tdexc: multiquadqrule" begin + using CompScienceMeshes + using LinearAlgebra + + fn = joinpath(dirname(pathof(BEAST)), "../test/assets/sphere45.in") + + mesh = readmesh(fn) + RT = raviartthomas(mesh) + + Δt = 0.1 + Nt = 200 + T = timebasisshiftedlagrange(Δt, Nt, 3) + U = timebasisdelta(Δt, Nt) + + X = RT ⊗ U + + duration = 2 * 20 * Δt + delay = 1.5 * duration + amplitude = 1.0 + gaussian = creategaussian(duration, delay, amplitude) + direction, polarisation = ẑ, x̂ + E = planewave(polarisation, direction, derive(gaussian), 1.0) + + qs1 = BEAST.NumSpaceNumTimeQStrat(2, 10) + qs2 = BEAST.NumSpaceNumTimeQStrat(6, 20) + + b1 = assemble(E, X; quadstrat=qs1) + b2 = assemble(E, X; quadstrat=qs2) + + @test norm(b1-b2, Inf) < 1e-4 +end \ No newline at end of file diff --git a/src/quadrature/strategies/trialrefinestestqstrat.jl b/src/quadrature/strategies/trialrefinestestqstrat.jl index b7f69546..7cd70e03 100644 --- a/src/quadrature/strategies/trialrefinestestqstrat.jl +++ b/src/quadrature/strategies/trialrefinestestqstrat.jl @@ -1,4 +1,4 @@ -struct TrialRefinesTestQStrat{S} +struct TrialRefinesTestQStrat{S} <: AbstractQuadStrat conforming_qstrat::S end diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 92542222..2419d8d7 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -146,7 +146,8 @@ end scalartype(lf::LinForm) = scalartype(lf.terms...) scalartype(lt::LinTerm) = scalartype(lt.coeff, lt.functional) -function assemble(lform::LinForm, X::DirectProductSpace) +function assemble(lform::LinForm, X::DirectProductSpace; + quadstrat=BEAST.defaultquadstrat) @assert !isempty(lform.terms) @@ -178,7 +179,7 @@ function assemble(lform::LinForm, X::DirectProductSpace) x = X.factors[m] for op in reverse(t.test_ops) x = op[end](op[1:end-1]..., x) end - b = assemble(t.functional, x) + b = assemble(t.functional, x; quadstrat) B[Block(m),Block(1)] = t.coeff * b end @@ -221,8 +222,9 @@ lift(a,I,J,U,V) = LiftedMaps.LiftedMap(a,I,J,U,V) lift(a::ConvolutionOperators.AbstractConvOp ,I,J,U,V) = ConvolutionOperators.LiftedConvOp(a, U, V, I, J) + function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; - materialize=BEAST.assemble) + materialize=BEAST.assemble, quadstrat=BEAST.defaultquadstrat) T = Int32 @assert !isempty(bf.terms) @@ -258,7 +260,8 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; end a = term.coeff * term.kernel - z = materialize(a, x, y) + # qs = quadstrat(a, x, y) + z = materialize(a, x, y; quadstrat) Smap = lift(z, Block(term.test_id), Block(term.trial_id), U, V) T = promote_type(T, eltype(Smap)) @@ -283,4 +286,9 @@ end function assemble(bf::BilForm, pairs::Pair...) dbf = discretise(bf, pairs...) assemble(dbf) +end + +function assemble(bf::LinForm, pairs::Pair...) + dbf = discretise(bf, pairs...) + assemble(dbf) end \ No newline at end of file diff --git a/src/timedomain/tdexcitation.jl b/src/timedomain/tdexcitation.jl index e6805273..a0e23a02 100644 --- a/src/timedomain/tdexcitation.jl +++ b/src/timedomain/tdexcitation.jl @@ -1,62 +1,24 @@ abstract type TDFunctional{T} end Base.eltype(x::TDFunctional{T}) where {T} = T +defaultquadstrat(exc::TDFunctional, testfns) = NumSpaceNumTimeQStrat(2, 10) -function quaddata(exc::TDFunctional, testrefs, timerefs, testels, timeels) - - testqd = quadpoints(testrefs, testels, (2,)) - timeqd = quadpoints(timerefs, timeels, (10,)) - - testqd, timeqd - -end - -function quaddata(excitation::TDFunctional, - test_refspace, time_refspace::DiracBoundary, - test_elements, time_elements) - - test_quad_data = quadpoints(test_refspace, test_elements, (2,)) - - test_quad_data, nothing -end - -function quadrule(exc::TDFunctional, testrefs, timerefs, p, τ, r, ρ, qd) - - MultiQuadStrategy( - qd[1][1,p], - SingleQuadStrategy2( - qd[2][1,r] - ) - ) - -end - -function quadrule(exc::TDFunctional, testrefs, timerefs::DiracBoundary, p, τ, r, ρ, qd) - - MultiQuadStrategy( - qd[1][1,p], - nothing - ) - -end - -function assemble(exc::TDFunctional, testST; quaddata=quaddata, quadrule=quadrule) +function assemble(exc::TDFunctional, testST; quadstrat=defaultquadstrat) stagedtimestep = isa(temporalbasis(testST), BEAST.StagedTimeStep) if stagedtimestep - return staged_assemble(exc, testST; quaddata=quaddata, quadrule=quadrule) + return staged_assemble(exc, testST; quadstrat) end testfns = spatialbasis(testST) timefns = temporalbasis(testST) Z = zeros(eltype(exc), numfunctions(testfns), numfunctions(timefns)) store(v,m,k) = (Z[m,k] += v) - assemble!(exc, testST, store, quaddata=quaddata, quadrule=quadrule) + assemble!(exc, testST, store; quadstrat) return Z end -function staged_assemble(exc::TDFunctional, testST::SpaceTimeBasis; - quaddata=quaddata, quadrule=quadrule) +function staged_assemble(exc::TDFunctional, testST::SpaceTimeBasis; quadstrat=defaultquadstrat) @warn "staged assemble of the right-hand side" testfns = spatialbasis(testST) @@ -68,14 +30,13 @@ function staged_assemble(exc::TDFunctional, testST::SpaceTimeBasis; for i = 1:stageCount store(v,m,k) = (Z[(m-1)*stageCount+i,k] += v) tbsd = TimeBasisDeltaShifted(timebasisdelta(Δt, Nt), timefns.c[i]) - assemble!(exc, testfns ⊗ tbsd, store, - quaddata=quaddata, quadrule=quadrule) + assemble!(exc, testfns ⊗ tbsd, store; quadstrat) end return Z end -function assemble!(exc::TDFunctional, testST, store; - quaddata=quaddata, quadrule=quadrule) +function assemble!(exc::TDFunctional, testST, store; quadstrat=defaultquadstrat) + testfns = spatialbasis(testST) timefns = temporalbasis(testST) @@ -85,8 +46,10 @@ function assemble!(exc::TDFunctional, testST, store; testels, testad = assemblydata(testfns) timeels, timead = assemblydata(timefns) - - qd = quaddata(exc, testrefs, timerefs, testels, timeels) + @show quadstrat + @assert quadstrat != nothing + qs = quadstrat(exc, testST) + qd = quaddata(exc, testrefs, timerefs, testels, timeels, qs) num_testshapes = numfunctions(testrefs, domain(first(testels))) z = zeros(eltype(exc), num_testshapes, numfunctions(timerefs)) @@ -96,7 +59,7 @@ function assemble!(exc::TDFunctional, testST, store; ρ = timeels[r] fill!(z, 0) - qr = quadrule(exc, testrefs, timerefs, p, τ, r, ρ, qd) + qr = quadrule(exc, testrefs, timerefs, p, τ, r, ρ, qd, qs) momintegrals!(z, exc, testrefs, timerefs, τ, ρ, qr) for i in 1 : num_testshapes @@ -114,80 +77,3 @@ function assemble!(exc::TDFunctional, testST, store; end end end - - -abstract type NumQuadStrategy end - -mutable struct MultiQuadStrategy{P,R} <: NumQuadStrategy - quad_points::P - inner_rule::R -end - -# TODO: consolidate with the existing definition of SingleQuadStrategy -mutable struct SingleQuadStrategy2{P} <: NumQuadStrategy - quad_points::P -end - -timequadrule(qr::MultiQuadStrategy, p) = qr.inner_rule - -function momintegrals!(z, exc::TDFunctional, testrefs, timerefs, τ, ρ, qr) - - for p in qr.quad_points - x = p.point - w = p.weight - f = p.value - dx = w - - # try - # @assert ρ.vertices[1][1] <= cartesian(x)[1] <= ρ.vertices[2][1] - # catch - # @show ρ.vertices[1][1] - # @show cartesian(x)[1] - # @show ρ.vertices[2][1] - # error("") - # end - - tqr = timequadrule(qr,p) - timeintegrals!(z, exc, testrefs, timerefs, x, ρ, dx, tqr, f) - - end - -end - - -function timeintegrals!(z, exc::TDFunctional, testrefs, timerefs, testpoint, timeelement, dx, qr, f) - - num_tshapes = numfunctions(testrefs, domain(chart(testpoint))) - - for p in qr.quad_points - t = p.point - w = p.weight - U = p.value - dt = w #* jacobian(t) # * volume(timeelement) - - for i in 1 : num_tshapes - for k in 1 : numfunctions(timerefs) - z[i,k] += dot(f[i][1]*U[k], exc(testpoint,t)) * dt * dx - end - end - end -end - - - -function timeintegrals!(z, exc::TDFunctional, - spacerefs, timerefs::DiracBoundary, - testpoint, timeelement, - dx, qr, testvals) - - num_tshapes = numfunctions(spacerefs, domain(chart(testpoint))) - - # since timeelement uses barycentric coordinates, - # the first/left vertex has coords u = 1.0! - testtime = neighborhood(timeelement, point(0.0)) - @assert cartesian(testtime)[1] ≈ timeelement.vertices[2][1] - - for i in 1 : num_tshapes - z[i,1] += dot(testvals[i][1], exc(testpoint, testtime)) * dx - end -end diff --git a/src/timedomain/tdintegralop.jl b/src/timedomain/tdintegralop.jl index d4ba8b93..76e85574 100644 --- a/src/timedomain/tdintegralop.jl +++ b/src/timedomain/tdintegralop.jl @@ -14,10 +14,10 @@ function assemble(operator::AbstractSpaceTimeOperator, test_functions, trial_fun if stagedtimestep return assemble(RungeKuttaConvolutionQuadrature(operator), test_functions, trial_functions) end - Z, store = allocatestorage(operator, test_functions, trial_functions, + freeze, store = allocatestorage(operator, test_functions, trial_functions, storage_policy, long_delays_policy) assemble!(operator, test_functions, trial_functions, store, threading; quadstrat) - return Z() + return freeze() end @@ -256,6 +256,7 @@ function assemble_chunk!(op::RetardedPotential, testST, trialST, store; V = refspace(trialspace) W = refspace(timebasisfunction) + # qs = quadstrat(op, testST, trialST) qd = quaddata(op, U, V, W, testels, trialels, nothing, quadstrat) ugeo = geometry(testspace) diff --git a/src/utils/variational.jl b/src/utils/variational.jl index aa0a5158..7bb9930a 100644 --- a/src/utils/variational.jl +++ b/src/utils/variational.jl @@ -286,6 +286,11 @@ function getindex(A, v::HilbertVector, u::HilbertVector) BilForm(v.space, u.space, terms) end +function getindex(A::AbstractMatrix, v::HilbertVector, u::HilbertVector) + terms = [ BilTerm(v.idx, u.idx, v.opstack, u.opstack, 1, A) ] + BilForm(v.space, u.space, terms) +end + function getindex(A::BEAST.BlockDiagonalOperator, V::Vector{HilbertVector}, U::Vector{HilbertVector}) op = A.op diff --git a/src/utils/zeromap.jl b/src/utils/zeromap.jl index b19d2cdc..207d6fa4 100644 --- a/src/utils/zeromap.jl +++ b/src/utils/zeromap.jl @@ -15,10 +15,6 @@ function LinearMaps._unsafe_mul!(y::AbstractVector, L::ZeroMap, x::AbstractVecto y .*= β end -# function LinearAlgebra.mul!(Y::PseudoBlockMatrix, c::Number, X::ZeroMap, a::Number, b::Number) -# @assert b == 1 -# return Y -# end function LinearMaps._unsafe_mul!(Y::AbstractMatrix, X::ZeroMap, c::Number, a::Number, b::Number) rmul!(Y, b) diff --git a/src/volumeintegral/sauterschwab_ints.jl b/src/volumeintegral/sauterschwab_ints.jl index 56cae3e1..6324c60b 100644 --- a/src/volumeintegral/sauterschwab_ints.jl +++ b/src/volumeintegral/sauterschwab_ints.jl @@ -66,7 +66,7 @@ function reorder_dof(space::RTRefSpace,I) return SVector(K),SVector{3,Int64}(1,1,1) end -function reorder_dof(space::LagrangeRefSpace{Float64,0,3,1},I) +function reorder_dof(space::LagrangeRefSpace{T,0,3,1},I) where T return SVector{1,Int64}(1),SVector{1,Int64}(1) end @@ -102,16 +102,19 @@ function reorder_dof(space::LagrangeRefSpace{T,1,4,4},I) where T end function momintegrals!(out, op::VIEOperator, - test_local_space::RefSpace, test_ptr, test_tetrahedron_element, - trial_local_space::RefSpace, trial_ptr, trial_tetrahedron_element, + test_functions::Space, test_ptr, test_tetrahedron_element, + trial_functions::Space, trial_ptr, trial_tetrahedron_element, strat::SauterSchwab3DStrategy) + local_test_space = refspace(test_functions) + local_trial_space = refspace(trial_functions) + #Find permutation of vertices to match location of singularity to SauterSchwab J, I= SauterSchwab3D.reorder(strat.sing) #Get permutation and rel. orientatio of DoFs - K,O1 = reorder_dof(test_local_space, I) - L,O2 = reorder_dof(trial_local_space, J) + K,O1 = reorder_dof(local_test_space, I) + L,O2 = reorder_dof(local_trial_space, J) #Apply permuation to elements if length(I) == 4 @@ -146,7 +149,7 @@ function momintegrals!(out, op::VIEOperator, #Define integral (returns a function that only needs barycentric coordinates) igd = VIEIntegrand(test_tetrahedron_element, trial_tetrahedron_element, - op, test_local_space, trial_local_space) + op, local_test_space, local_trial_space) #Evaluate integral Q = SauterSchwab3D.sauterschwab_parameterized(igd, strat) @@ -163,8 +166,8 @@ function momintegrals!(out, op::VIEOperator, end function momintegrals!(z, biop::VIEOperator, - tshs, tptr, tcell, - bshs, bptr, bcell, + test_functions::Space, tptr, tcell, + trial_functions::Space, bptr, bcell, strat::DoubleQuadRule) # memory allocation here is a result from the type instability on strat diff --git a/src/volumeintegral/vieexc.jl b/src/volumeintegral/vieexc.jl index 7a5ab206..f83fb6d0 100644 --- a/src/volumeintegral/vieexc.jl +++ b/src/volumeintegral/vieexc.jl @@ -1,4 +1,4 @@ -mutable struct PlaneWaveVIE{T,P} <: Functional +mutable struct PlaneWaveVIE{T,P} <: Functional{T} direction::P polarisation::P wavenumber::T @@ -13,6 +13,16 @@ mutable struct PlaneWaveVIE{T,P} <: Functional PlaneWaveVIE{T,P}(d,p,k,a) end +""" + planewavevie(; + direction = error("missing arguement `direction`"), + polarization = error("missing arguement `polarization`"), + wavenumber = error("missing arguement `wavenumber`"), + amplitude = 1, + ) + +For volume integral equations +""" planewavevie(; direction = error("missing arguement `direction`"), polarization = error("missing arguement `polarization`"), @@ -54,7 +64,7 @@ integrand(::PlaneWaveVIE, test_vals, field_val) = test_vals[1] ⋅ field_val # Excitation for Lippmann Schwinger Volume Integral Equation -mutable struct LinearPotentialVIE{T,P} <: Functional +mutable struct LinearPotentialVIE{T,P} <: Functional{T} direction::P amplitude::T end @@ -67,6 +77,14 @@ function LinearPotentialVIE_(d,a = 1) return LinearPotentialVIE{T,P}(d,a) end +""" + linearpotentialvie(; + direction = error("missing argument `direction`"), + amplitude = 1, + ) + +Linear potential for volume integral equations. +""" linearpotentialvie(; direction = error("missing argument `direction`"), amplitude = 1, diff --git a/test/Manifest.toml b/test/Manifest.toml deleted file mode 100644 index 8479a59f..00000000 --- a/test/Manifest.toml +++ /dev/null @@ -1,671 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.10.2" -manifest_format = "2.0" -project_hash = "1b78102b70e82631772273acf6687b12f1d8fa74" - -[[deps.ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" -version = "1.1.1" - -[[deps.ArrayLayouts]] -deps = ["FillArrays", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "4efc22e4c299e49995a38d503d9dbb0544a37838" -uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "1.0.4" - -[[deps.Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" - -[[deps.Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" - -[[deps.BlockArrays]] -deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] -git-tree-sha1 = "14d688a2254ca2242d834176a385cc9b3bceeb02" -uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" -version = "0.16.28" - -[[deps.Bzip2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" -uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+0" - -[[deps.Cairo_jll]] -deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2" -uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.16.1+1" - -[[deps.ClusterTrees]] -deps = ["DelimitedFiles", "LinearAlgebra", "Pkg"] -git-tree-sha1 = "4114d60c95974edf9272d88571d14abeed598942" -uuid = "5100927d-02aa-593a-b4f9-7235df19f0db" -version = "0.2.1" - -[[deps.CollisionDetection]] -deps = ["Compat", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "8d86c864d69f72e23adcd7c2014d205bf32c90a1" -uuid = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" -version = "0.1.5" - -[[deps.Combinatorics]] -git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" -uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" -version = "1.0.2" - -[[deps.CompScienceMeshes]] -deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays"] -git-tree-sha1 = "133f6ac95849d0f96ecc82378e625ffa3790b940" -uuid = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" -version = "0.5.2" - -[[deps.Compat]] -deps = ["UUIDs"] -git-tree-sha1 = "7a60c856b9fa189eb34f5f8a6f6b5529b7942957" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.6.1" -weakdeps = ["Dates", "LinearAlgebra"] - - [deps.Compat.extensions] - CompatLinearAlgebraExt = "LinearAlgebra" - -[[deps.CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.1.0+0" - -[[deps.DataStructures]] -deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.13" - -[[deps.Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" - -[[deps.DelimitedFiles]] -deps = ["Mmap"] -git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" -version = "1.9.1" - -[[deps.Distributed]] -deps = ["Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" - -[[deps.DocStringExtensions]] -deps = ["LibGit2"] -git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" -uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.9.3" - -[[deps.Downloads]] -deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -version = "1.6.0" - -[[deps.Expat_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "bad72f730e9e91c08d9427d5e8db95478a3c323d" -uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.4.8+0" - -[[deps.FLTK_jll]] -deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "72a4842f93e734f378cf381dae2ca4542f019d23" -uuid = "4fce6fc7-ba6a-5f4c-898f-77e99806d6f8" -version = "1.3.8+0" - -[[deps.FastGaussQuadrature]] -deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "0f478d8bad6f52573fb7658a263af61f3d96e43a" -uuid = "442a2c76-b920-505d-bb47-c5924d526838" -version = "0.5.1" - -[[deps.FileWatching]] -uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" - -[[deps.FillArrays]] -deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"] -git-tree-sha1 = "589d3d3bff204bdd80ecc53293896b4f39175723" -uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.1.1" - -[[deps.Fontconfig_jll]] -deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" -uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.93+0" - -[[deps.FreeType2_jll]] -deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "87eb71354d8ec1a96d4a7636bd57a7347dde3ef9" -uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.10.4+0" - -[[deps.GLU_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] -git-tree-sha1 = "65af046f4221e27fb79b28b6ca89dd1d12bc5ec7" -uuid = "bd17208b-e95e-5925-bf81-e2f59b3e5c61" -version = "9.0.1+0" - -[[deps.GMP_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" -version = "6.2.1+6" - -[[deps.Gettext_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" -uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" -version = "0.21.0+0" - -[[deps.Glib_jll]] -deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "d3b3624125c1474292d0d8ed0f65554ac37ddb23" -uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.74.0+2" - -[[deps.GmshTools]] -deps = ["Libdl", "gmsh_jll"] -git-tree-sha1 = "299aa66053646db77f8aa7fafcebe0f9e5c0d1dc" -uuid = "82e2f556-b1bd-5f1a-9576-f93c0da5f0ee" -version = "0.5.2" - -[[deps.HDF5_jll]] -deps = ["Artifacts", "JLLWrappers", "LibCURL_jll", "Libdl", "OpenSSL_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "4cc2bb72df6ff40b055295fdef6d92955f9dede8" -uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" -version = "1.12.2+2" - -[[deps.InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" - -[[deps.IrrationalConstants]] -git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" -uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.2.2" - -[[deps.JLLWrappers]] -deps = ["Preferences"] -git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1" -uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.4.1" - -[[deps.JpegTurbo_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "6f2675ef130a300a112286de91973805fcc5ffbc" -uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "2.1.91+0" - -[[deps.LLVMOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f689897ccbe049adb19a065c495e75f372ecd42b" -uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" -version = "15.0.4+0" - -[[deps.LZO_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" -uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.1+0" - -[[deps.LegendrePolynomials]] -deps = ["OffsetArrays", "SpecialFunctions"] -git-tree-sha1 = "78e5288b179f2ae90ccbaa1799e9b0cb82ef5e04" -uuid = "3db4a2ba-fc88-11e8-3e01-49c72059a882" -version = "0.4.4" - -[[deps.LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" -version = "0.6.4" - -[[deps.LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "8.4.0+0" - -[[deps.LibGit2]] -deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" - -[[deps.LibGit2_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] -uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" -version = "1.6.4+0" - -[[deps.LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "MbedTLS_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" -version = "1.11.0+1" - -[[deps.Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" - -[[deps.Libffi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" -uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.2.2+1" - -[[deps.Libgcrypt_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] -git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" -uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.7+0" - -[[deps.Libglvnd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] -git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" -uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" -version = "1.6.0+0" - -[[deps.Libgpg_error_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" -uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.42.0+0" - -[[deps.Libiconv_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c7cb1f5d892775ba13767a87c7ada0b980ea0a71" -uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.16.1+2" - -[[deps.Libmount_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73" -uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.35.0+0" - -[[deps.Libuuid_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066" -uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.36.0+0" - -[[deps.LinearAlgebra]] -deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" - -[[deps.LinearElasticity_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "71e8ee0f9fe0e86a8f8c7f28361e5118eab2f93f" -uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" -version = "5.0.0+0" - -[[deps.LinearMaps]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "ee79c3208e55786de58f8dcccca098ced79f743f" -uuid = "7a12625a-238d-50fd-b39a-03d52299707e" -version = "3.11.3" - - [deps.LinearMaps.extensions] - LinearMapsChainRulesCoreExt = "ChainRulesCore" - LinearMapsSparseArraysExt = "SparseArrays" - LinearMapsStatisticsExt = "Statistics" - - [deps.LinearMaps.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[deps.LogExpFunctions]] -deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "c3ce8e7420b3a6e071e0fe4745f5d4300e37b13f" -uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.24" - - [deps.LogExpFunctions.extensions] - LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" - LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" - LogExpFunctionsInverseFunctionsExt = "InverseFunctions" - - [deps.LogExpFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[deps.Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" - -[[deps.METIS_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "1fd0a97409e418b78c53fac671cf4622efdf0f21" -uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" -version = "5.1.2+0" - -[[deps.MMG_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] -git-tree-sha1 = "70a59df96945782bb0d43b56d0fbfdf1ce2e4729" -uuid = "86086c02-e288-5929-a127-40944b0018b7" -version = "5.6.0+0" - -[[deps.Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" - -[[deps.MbedTLS_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.2+1" - -[[deps.Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" - -[[deps.MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2023.1.10" - -[[deps.NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" -version = "1.2.0" - -[[deps.OCCT_jll]] -deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "acc8099ae8ed10226dc8424fb256ec9fe367a1f0" -uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" -version = "7.6.2+2" - -[[deps.OffsetArrays]] -git-tree-sha1 = "e64b4f5ea6b7389f6f046d13d4896a8f9c1ba71e" -uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.14.0" - - [deps.OffsetArrays.extensions] - OffsetArraysAdaptExt = "Adapt" - - [deps.OffsetArrays.weakdeps] - Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" - -[[deps.OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.23+4" - -[[deps.OpenLibm_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" - -[[deps.OpenSSL_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1aa4b74f80b01c6bc2b89992b861b5f210e665b5" -uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "1.1.21+0" - -[[deps.OpenSpecFun_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" -uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.5+0" - -[[deps.OrderedCollections]] -git-tree-sha1 = "d321bf2de576bf25ec4d3e4360faca399afca282" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.6.0" - -[[deps.PCRE2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" -version = "10.42.0+1" - -[[deps.Pixman_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29" -uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.40.1+0" - -[[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.10.0" - -[[deps.Preferences]] -deps = ["TOML"] -git-tree-sha1 = "7eb1686b4f04b82f96ed7a4ea5890a4f0c7a09f1" -uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.4.0" - -[[deps.Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" - -[[deps.REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" - -[[deps.Random]] -deps = ["SHA"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" - -[[deps.Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.0" - -[[deps.SCOTCH_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "7110b749766853054ce8a2afaa73325d72d32129" -uuid = "a8d0f55d-b80e-548d-aff6-1a04c175f0f9" -version = "6.1.3+0" - -[[deps.SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" -version = "0.7.0" - -[[deps.SauterSchwabQuadrature]] -deps = ["FastGaussQuadrature", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "80a8bf94c550ff26ef7778921b3e3833ac422be6" -uuid = "535c7bfe-2023-5c1d-b712-654ef9d93a38" -version = "2.2.1" - -[[deps.Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" - -[[deps.Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" - -[[deps.SparseArrays]] -deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -version = "1.10.0" - -[[deps.SpecialFunctions]] -deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "ef28127915f4229c971eb43f3fc075dd3fe91880" -uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.2.0" - - [deps.SpecialFunctions.extensions] - SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" - - [deps.SpecialFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - -[[deps.SphericalScattering]] -deps = ["LegendrePolynomials", "LinearAlgebra", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "6acefb2a8f8142e601f68e4bda5b37f6731e1455" -uuid = "1a9ea918-b599-4f1f-bd9a-d681e8bb5b3e" -version = "0.7.2" - - [deps.SphericalScattering.extensions] - SphericalScatteringExt = "PlotlyJS" - - [deps.SphericalScattering.weakdeps] - PlotlyJS = "f0f68f2c-4968-5e81-91da-67840de0976a" - -[[deps.StaticArrays]] -deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] -git-tree-sha1 = "8982b3607a212b070a5e46eea83eb62b4744ae12" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.5.25" - -[[deps.StaticArraysCore]] -git-tree-sha1 = "6b7ba252635a5eff6a0b0664a41ee140a1c9e72a" -uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.0" - -[[deps.Statistics]] -deps = ["LinearAlgebra", "SparseArrays"] -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -version = "1.10.0" - -[[deps.SuiteSparse_jll]] -deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] -uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.2.1+1" - -[[deps.TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.3" - -[[deps.Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" -version = "1.10.0" - -[[deps.Test]] -deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[deps.TestItemRunner]] -deps = ["Pkg", "TOML", "Test", "TestItems", "UUIDs"] -git-tree-sha1 = "cb2b53fd36a8fe20c0b9f55da6244eb4818779f5" -uuid = "f8b46487-2199-4994-9208-9a1283c18c0a" -version = "0.2.3" - -[[deps.TestItems]] -git-tree-sha1 = "8621ba2637b49748e2dc43ba3d84340be2938022" -uuid = "1c621080-faea-4a02-84b6-bbd5e436b8fe" -version = "0.1.1" - -[[deps.UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" - -[[deps.Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" - -[[deps.WiltonInts84]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "446d97faa3e974e8a4d406ecc873284f5aa9558a" -uuid = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" -version = "0.2.4" - -[[deps.XML2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "93c41695bc1c08c46c5899f4fe06d6ead504bb73" -uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.10.3+0" - -[[deps.XSLT_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] -git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" -uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.34+0" - -[[deps.Xorg_libX11_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] -git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527" -uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.6.9+4" - -[[deps.Xorg_libXau_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e" -uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.9+4" - -[[deps.Xorg_libXdmcp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4" -uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.3+4" - -[[deps.Xorg_libXext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" -uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.4+4" - -[[deps.Xorg_libXfixes_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" -uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" -version = "5.0.3+4" - -[[deps.Xorg_libXft_jll]] -deps = ["Fontconfig_jll", "Libdl", "Pkg", "Xorg_libXrender_jll"] -git-tree-sha1 = "754b542cdc1057e0a2f1888ec5414ee17a4ca2a1" -uuid = "2c808117-e144-5220-80d1-69d4eaa9352c" -version = "2.3.3+1" - -[[deps.Xorg_libXinerama_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] -git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" -uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" -version = "1.1.4+4" - -[[deps.Xorg_libXrender_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96" -uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.10+4" - -[[deps.Xorg_libpthread_stubs_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb" -uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" -version = "0.1.0+3" - -[[deps.Xorg_libxcb_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6" -uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.13.0+3" - -[[deps.Xorg_xtrans_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845" -uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.4.0+3" - -[[deps.Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.2.13+1" - -[[deps.gmsh_jll]] -deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "d15409a4b9f1d14f1e1f9e910cd00f7d6695c261" -uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" -version = "4.11.1+0" - -[[deps.libblastrampoline_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.8.0+1" - -[[deps.libpng_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c" -uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.38+0" - -[[deps.nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.52.0+1" - -[[deps.p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.4.0+2" diff --git a/test/Project.toml b/test/Project.toml index c9ab94c0..b84f134f 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,5 +1,6 @@ [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" +CollisionDetection = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" diff --git a/test/runtests.jl b/test/runtests.jl index bbcee67f..52622073 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,6 +4,8 @@ using StaticArrays module PkgTests +using TestItemRunner + using Distributed using LinearAlgebra using SparseArrays @@ -45,12 +47,14 @@ include("test_local_storage.jl") include("test_embedding.jl") include("test_assemblerow.jl") -include("test_mixed_blkassm.jl") +# include("test_mixed_blkassm.jl") include("test_local_assembly.jl") include("test_assemble_refinements.jl") include("test_dipole.jl") +include("test_sauterschwabints1D.jl") + include("test_wiltonints.jl") include("test_sauterschwabints.jl") include("test_hh3dints.jl") @@ -60,7 +64,7 @@ include("test_nitschehh3d.jl") include("test_curlcurlgreen.jl") include("test_hh3dtd_exc.jl") -include("test_hh3dexc.jl") +# include("test_hh3dexc.jl") include("test_hh3d_nearfield.jl") include("test_tdassembly.jl") include("test_tdhhdbl.jl") @@ -77,10 +81,14 @@ include("test_td_tensoroperator.jl") include("test_variational.jl") include("test_handlers.jl") +include("test_ncrossbdm.jl") +include("test_curl_lagc0d1_lagc0d2.jl") include("test_gridfunction.jl") -using TestItemRunner -@run_package_tests +include("test_hh_lsvie.jl") + + +@run_package_tests filter=ti->!(:example in ti.tags) verbose=true try diff --git a/test/test_assemble_refinements.jl b/test/test_assemble_refinements.jl index 04d7ef60..294b5a7f 100644 --- a/test/test_assemble_refinements.jl +++ b/test/test_assemble_refinements.jl @@ -28,9 +28,15 @@ Kop = Maxwell3D.doublelayer(wavenumber=0.0) qs = BEAST.defaultquadstrat(Kop, X, Y) qs = BEAST.DoubleNumWiltonSauterQStrat(1, 1, 12, 13, 12, 12, 12, 12) +@show qs +@show qs(Kop, X, Y) +@show qs(Kop, Y, X) K1 = assemble(Kop, X, Y; quadstrat=qs) K2 = assemble(Kop, Y, X; quadstrat=qs) +@show K1[1,1] +@show K2[1,1] + K1[1,1] - K2[1,1] @test K1[1,1] ≈ K2[1,1] rtol=1e-7 diff --git a/test/test_basis.jl b/test/test_basis.jl index 64a7d0ef..cebb14e1 100644 --- a/test/test_basis.jl +++ b/test/test_basis.jl @@ -13,9 +13,9 @@ for T in [Float32, Float64] X = lagrangec0d1(Γ) @test numvertices(Γ)-2 == numfunctions(X) - hypersingular = HyperSingular(κ) + hypersingular = Helmholtz2D.hypersingular(; wavenumber=κ) identityop = Identity() - doublelayer = DoubleLayer(κ) + doublelayer = Helmholtz2D.doublelayer(; wavenumber=κ) @show BEAST.defaultquadstrat(hypersingular, X, X) # @show @which BEAST.defaultquadstrat(hypersingular, X, X) @@ -26,7 +26,9 @@ for T in [Float32, Float64] @test size(I) == (numfunctions(X), numfunctions(X)) @test rank(I) == numfunctions(X) - @time e = assemble(PlaneWaveNeumann(κ, point(0.0, 1.0)), X) + E = Helmholtz2D.planewave(wavenumber=κ, direction=point(1.0,0.0)) + @time e = assemble(BEAST.NormalDerivative(E), X) + #e = assemble(PlaneWaveNeumann(κ, point(0.0, 1.0)), X) @test length(e) == numfunctions(X) x1 = N \ e; diff --git a/test/test_dipole.jl b/test/test_dipole.jl index f11e277f..34b27699 100644 --- a/test/test_dipole.jl +++ b/test/test_dipole.jl @@ -6,7 +6,9 @@ using StaticArrays using LinearAlgebra using BlockArrays +# U = Float32 for U in [Float32,Float64] + @show U c = U(3e8) μ0 = U(4*π*1e-7) @@ -81,6 +83,9 @@ for U in [Float32,Float64] nf_H_BCMFIE = potential(BEAST.MWDoubleLayerField3D(𝓚), pts, j_BCMFIE, X) ff_E_BCMFIE = potential(MWFarField3D(𝓣), pts, j_BCMFIE, X) + # @show length(pts) + # @show norm.(nf_E_BCMFIE - E.(pts)) ./ norm.(E.(pts)) + @test norm(nf_E_BCMFIE - E.(pts))/norm(E.(pts)) ≈ 0 atol=0.01 @test norm(nf_H_BCMFIE - H.(pts))/norm(H.(pts)) ≈ 0 atol=0.01 @test norm(ff_E_BCMFIE - E.(pts, isfarfield=true))/norm(E.(pts, isfarfield=true)) ≈ 0 atol=0.01 diff --git a/test/test_gridfunction.jl b/test/test_gridfunction.jl index f951c7ea..d23e2ae8 100644 --- a/test/test_gridfunction.jl +++ b/test/test_gridfunction.jl @@ -10,7 +10,9 @@ U = Float64 a = U(1) # Cube between (3,3,3) and (4,4,4) -Γ = translate(CompScienceMeshes.meshcuboid(a,a,a,U(1.0)), SVector(3.0,3.0,3.0)) +Γ = translate( + CompScienceMeshes.meshcuboid(a,a,a,U(1.0); generator=:gmsh), + SVector(3.0,3.0,3.0)) C0 = lagrangec0d1(Γ) @@ -102,7 +104,7 @@ glbf_toppplate = BEAST.GlobalFunction(f, Γ, idx_topplate) ## -Γ2 = CompScienceMeshes.meshcuboid(a,a,a,U(1.0)) +Γ2 = CompScienceMeshes.meshcuboid(a,a,a,U(1.0); generator=:gmsh) coeffsΓ2 = [v[3] for v in vertices(Γ2)] diff --git a/test/test_gwp.jl b/test/test_gwp.jl index ca2210e8..f6bb6cad 100644 --- a/test/test_gwp.jl +++ b/test/test_gwp.jl @@ -29,7 +29,7 @@ end nf = numfunctions(ϕ, dom) @test coeffs ≈ Matrix{T}(I,nf,nf) atol=sqrt(eps(T)) - display(round.(coeffs, digits=3)) + # display(round.(coeffs, digits=3)) end @@ -53,7 +53,7 @@ end nf1 = numfunctions(ψ, domain(supp)) nf2 = numfunctions(ϕ, domain(supp)) @test size(coeffs) == (nf1, nf2) - display(round.(coeffs, digits=3)) + # display(round.(coeffs, digits=3)) pts = [ point(T, 0.3, 0.1), diff --git a/test/test_hh3dexc.jl b/test/test_hh3dexc.jl index cb11981c..cd67ef35 100644 --- a/test/test_hh3dexc.jl +++ b/test/test_hh3dexc.jl @@ -1,14 +1,16 @@ +@testitem "excitation: HH3D" begin using CompScienceMeshes -using BEAST -using Test +# using BEAST +# using Test for T in [Float32, Float64] - sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in"), T=T) - numcells(sphere) + # sphere = readmesh(joinpath(dirname(@__FILE__),"assets","sphere5.in"), T=T) + sphere = readmesh(joinpath(dirname(pathof(BEAST)),"../test","assets","sphere5.in"), T=T) + # numcells(sphere) - local κ = T(2π) + κ = T(2π) direction = point(T,0,0,1) - local f = BEAST.HH3DPlaneWave(direction, im*κ, T(1.0)) + f = BEAST.HH3DPlaneWave(direction, im*κ, T(1.0)) v1 = f(point(T,0,0,0)) v2 = f(point(T,0,0,0.5)) @@ -24,27 +26,29 @@ for T in [Float32, Float64] γnlp = dot(BEAST.NormalVector(), gradlp) - import BEAST.∂n + # import BEAST.∂n p = ∂n(f) - local s = chart(sphere,first(sphere)) - local c = neighborhood(s, T.([1,1]/3)) + s = chart(sphere,first(sphere)) + c = neighborhood(s, T.([1,1]/3)) r = cartesian(c) - local n = normal(s) + nr = normal(s) w1 = p(c) - w2 = -im*κ*dot(direction, n)*f(r) + w2 = -im*κ*dot(direction, nr)*f(r) w1 ≈ w2 - local N = BEAST.HH3DHyperSingularFDBIO(im*κ) - local X = BEAST.lagrangec0d1(sphere) + N = BEAST.HH3DHyperSingularFDBIO(im*κ) + X = BEAST.lagrangec0d1(sphere) - numfunctions(X) + # numfunctions(X) - BEAST.quadinfo(N, X, X) + # BEAST.quadinfo(N, X, X) Nxx = assemble(N, X, X) @test size(Nxx) == (numfunctions(X), numfunctions(X)) +end + end \ No newline at end of file diff --git a/test/test_hh3dtd_exc.jl b/test/test_hh3dtd_exc.jl index a400dad6..75432cbe 100644 --- a/test/test_hh3dtd_exc.jl +++ b/test/test_hh3dtd_exc.jl @@ -1,35 +1,37 @@ -using BEAST -using CompScienceMeshes -using Test -for T in [Float32, Float64] - dir = point(T,0,0,1) - local width = T(1.0) - delay = T(1.5) - scaling = T(1.0) - sig = creategaussian(width, delay, scaling) +@testitem "excitation: TD Helmholtz 3D" begin + # using BEAST + using CompScienceMeshes + # using Test + for T in [Float32, Float64] + dir = point(T,0,0,1) + width = T(1.0) + delay = T(1.5) + scaling = T(1.0) + sig = creategaussian(width, delay, scaling) - pw = BEAST.planewave(dir, T(1.0), sig) + pw = BEAST.planewave(dir, T(1.0), sig) - CompScienceMeshes.cartesian(p::typeof(dir)) = p - CompScienceMeshes.cartesian(p::Number) = p - local x = point(T,1,0,0) - local t = T(1.0) - @test pw(x,t) ≈ sig(t-dot(dir,x)) + CompScienceMeshes.cartesian(p::typeof(dir)) = p + CompScienceMeshes.cartesian(p::Number) = p + x = point(T,1,0,0) + t = T(1.0) + @test pw(x,t) ≈ sig(t-dot(dir,x)) - pw2 = BEAST.gradient(pw) - @test pw2.direction ≈ dir - @test pw2.polarisation ≈ -dir - @test pw2.speedoflight ≈ pw.speed_of_light + pw2 = BEAST.gradient(pw) + @test pw2.direction ≈ dir + @test pw2.polarisation ≈ -dir + @test pw2.speedoflight ≈ pw.speed_of_light - dsig = derive(sig) - @test pw2(x,t) ≈ -dir*dsig(t-dot(dir,x)) + dsig = derive(sig) + @test pw2(x,t) ≈ -dir*dsig(t-dot(dir,x)) - trc = dot(BEAST.n,pw2) - ch = simplex( - point(T,0,0,0), - point(T,1,0,0), - point(T,0,1,0)) - ctr = center(ch) - val = trc(ctr,t) - @test val ≈ -dsig(t-dot(dir,x)) + trc = dot(BEAST.n,pw2) + ch = simplex( + point(T,0,0,0), + point(T,1,0,0), + point(T,0,1,0)) + ctr = center(ch) + val = trc(ctr,t) + @test val ≈ -dsig(t-dot(dir,x)) + end end \ No newline at end of file diff --git a/test/test_hh_lsvie.jl b/test/test_hh_lsvie.jl index 899c55b1..cc16cb35 100644 --- a/test/test_hh_lsvie.jl +++ b/test/test_hh_lsvie.jl @@ -11,12 +11,12 @@ using Test @testset "Lippmann Schwinger Volume Integral Equation" begin # Environment - ε1 = 1.0*ε0 - μ1 = μ0 + ε1 = 1.0*SphericalScattering.ε0 + μ1 = SphericalScattering.μ0 # Dielectic Sphere - ε2 = 5.0*ε0 - μ2 = μ0 + ε2 = 5.0*SphericalScattering.ε0 + μ2 = SphericalScattering.μ0 r = 1.0 sp = DielectricSphere(; radius = r, filling = Medium(ε2, μ2)) @@ -56,7 +56,7 @@ using Test # Assembly - b = assemble(Φ_inc, X) + b = real.(assemble(Φ_inc, X)) Z_I = assemble(I, X, X) @@ -69,8 +69,8 @@ using Test Z_version2 = Z_I + Z_Y # MoM solution - u_version1 = Z_version1 \ Vector(b) - u_version2 = Z_version2 \ Vector(b) + u_version1 = Z_version1 \ b + u_version2 = Z_version2 \ b # Observation points range_ = range(-1.0*r,stop=1.0*r,length=14) @@ -84,8 +84,8 @@ using Test Φ = field(sp, ex, ScalarPotential(points_sp)) # MoM solution inside the dielectric sphere - Φ_MoM_version1 = BEAST.grideval(points_sp, u_version1, X, type=Float64) - Φ_MoM_version2 = BEAST.grideval(points_sp, u_version2, X, type=Float64) + Φ_MoM_version1 = BEAST.grideval(points_sp, u_version1, X) + Φ_MoM_version2 = BEAST.grideval(points_sp, u_version2, X) diff --git a/test/test_higher_order_lagrange_functions.jl b/test/test_higher_order_lagrange_functions.jl index 5f6bab8d..aec7f292 100644 --- a/test/test_higher_order_lagrange_functions.jl +++ b/test/test_higher_order_lagrange_functions.jl @@ -187,7 +187,7 @@ end val2 = sum(Q[j,i] * b.value for (i,b) in enumerate(basis)) @test val1≈val2 atol=1e-8 end - println() + # println() end end diff --git a/test/test_interpolate_and_restrict.jl b/test/test_interpolate_and_restrict.jl index 369a61af..9671c0e6 100644 --- a/test/test_interpolate_and_restrict.jl +++ b/test/test_interpolate_and_restrict.jl @@ -28,7 +28,7 @@ vals = [f.value for f in X(ctr)] itpol = sum(w*val for (w,val) in zip(Q3,vals)) @test itpol ≈ constant_vector_field -using TestItems +# using TestItems @testitem "restrict RT0" begin using CompScienceMeshes diff --git a/test/test_local_storage.jl b/test/test_local_storage.jl index 7e186ae1..a5cd8bb5 100644 --- a/test/test_local_storage.jl +++ b/test/test_local_storage.jl @@ -1,22 +1,22 @@ -using Test -using BEAST -using CompScienceMeshes -using SparseArrays -for T in [Float32, Float64] - local fn = joinpath(@__DIR__, "assets/sphere5.in") - local m = readmesh(fn, T=T) +@testitem "storage: local operators" begin + using CompScienceMeshes + using SparseArrays + for T in [Float32, Float64] + fn = joinpath(@__DIR__, "assets/sphere5.in") + m = readmesh(fn, T=T) - Id = BEAST.Identity() - local X = BEAST.raviartthomas(m) + Id = BEAST.Identity() + X = BEAST.raviartthomas(m) - Z1 = assemble(Id, X, X, storage_policy=Val{:densestorage}) - Z2 = assemble(Id, X, X, storage_policy=Val{:bandedstorage}) - Z3 = assemble(Id, X, X, storage_policy=Val{:sparsedicts}) + Z1 = assemble(Id, X, X, storage_policy=Val{:densestorage}) + Z2 = assemble(Id, X, X, storage_policy=Val{:bandedstorage}) + Z3 = assemble(Id, X, X, storage_policy=Val{:sparsedicts}) - @test Z1 isa DenseMatrix - @test Z2 isa SparseMatrixCSC - @test Z3 isa SparseMatrixCSC + @test Z1 isa DenseMatrix + @test Z2 isa SparseMatrixCSC + @test Z3 isa SparseMatrixCSC - @test Z1 ≈ Z2 atol=1e-8 - @test Z1 ≈ Z3 atol=1e-8 + @test Z1 ≈ Z2 atol=1e-8 + @test Z1 ≈ Z3 atol=1e-8 + end end \ No newline at end of file diff --git a/test/test_mixed_blkassm.jl b/test/test_mixed_blkassm.jl index d49d485c..f1ddcdf4 100644 --- a/test/test_mixed_blkassm.jl +++ b/test/test_mixed_blkassm.jl @@ -1,68 +1,71 @@ -# test resolutuion of #66 +@testitem "Issue #66: block assembly dbl-layer" begin + # test resolutuion of #66 -using BEAST -using Test -using CompScienceMeshes -using LinearAlgebra + # using BEAST + # using Test + using CompScienceMeshes + using LinearAlgebra -function hassemble(operator::BEAST.AbstractOperator, - test_functions, - trial_functions) + function hassemble2(operator::BEAST.AbstractOperator, + test_functions, + trial_functions) - blkasm = BEAST.blockassembler(operator, test_functions, trial_functions) + blkasm = BEAST.blockassembler(operator, test_functions, trial_functions) - function assembler(Z, tdata, sdata) - store(v,m,n) = (Z[m,n] += v) - blkasm(tdata,sdata,store) - end + function assembler2(Z, tdata, sdata) + store(v,m,n) = (Z[m,n] += v) + blkasm(tdata,sdata,store) + end - mat = zeros(scalartype(operator), - numfunctions(test_functions), - numfunctions(trial_functions)) + mat = zeros(scalartype(operator), + numfunctions(test_functions), + numfunctions(trial_functions)) - assembler(mat, 1:numfunctions(test_functions), 1:numfunctions(trial_functions)) - return mat -end + assembler2(mat, 1:numfunctions(test_functions), 1:numfunctions(trial_functions)) + return mat + end -for T in [Float32, Float64] - c = T(3e8) - μ = T(4π * 1e-7) - ε = T(1/(μ*c^2)) - local f = T(1e8) - λ = T(c/f) - k = T(2π/λ) - local ω = T(k*c) - η = T(sqrt(μ/ε)) + for T in [Float32, Float64] + c = T(3e8) + μ = T(4π * 1e-7) + ε = T(1/(μ*c^2)) + f = T(1e8) + λ = T(c/f) + k = T(2π/λ) + ω = T(k*c) + η = T(sqrt(μ/ε)) - a = T(1) - local Γ = CompScienceMeshes.meshcuboid(a,a,a,T(0.2)) + a = T(1) + Γ = CompScienceMeshes.meshcuboid(a,a,a,T(0.2)) - 𝓣 = Maxwell3D.singlelayer(wavenumber=k) - 𝓚 = Maxwell3D.doublelayer(wavenumber=k) + 𝓣 = Maxwell3D.singlelayer(wavenumber=k) + 𝓚 = Maxwell3D.doublelayer(wavenumber=k) - local X = raviartthomas(Γ) - local Y = buffachristiansen(Γ) + X = raviartthomas(Γ) + Y = buffachristiansen(Γ) - println("Number of RWG functions: ", numfunctions(X)) + # println("Number of RWG functions: ", numfunctions(X)) - T_blockassembler = hassemble(𝓣, X, X) - T_standardassembler = assemble(𝓣, X, X) + T_blockassembler = hassemble2(𝓣, X, X) + T_standardassembler = assemble(𝓣, X, X) - @test norm(T_blockassembler - T_standardassembler)/norm(T_standardassembler) ≈ 0.0 atol=100*eps(T) + @test norm(T_blockassembler - T_standardassembler)/norm(T_standardassembler) ≈ 0.0 atol=100*eps(T) - T_bc_blockassembler = hassemble(𝓣, Y, Y) - T_bc_standardassembler = assemble(𝓣, Y, Y) + T_bc_blockassembler = hassemble2(𝓣, Y, Y) + T_bc_standardassembler = assemble(𝓣, Y, Y) - @test norm(T_bc_blockassembler - T_bc_standardassembler)/norm(T_bc_standardassembler) ≈ 0.0 atol=100*eps(T) + @test norm(T_bc_blockassembler - T_bc_standardassembler)/norm(T_bc_standardassembler) ≈ 0.0 atol=100*eps(T) - K_mix_blockassembler = hassemble(𝓚,Y,X) - K_mix_standardassembler = assemble(𝓚,Y,X) + K_mix_blockassembler = hassemble2(𝓚,Y,X) + K_mix_standardassembler = assemble(𝓚,Y,X) - T_mix_blockassembler = hassemble(𝓣, Y, X) - T_mix_standardassembler = assemble(𝓣, Y, X) + T_mix_blockassembler = hassemble2(𝓣, Y, X) + T_mix_standardassembler = assemble(𝓣, Y, X) - if T==Float64 - @test norm(K_mix_blockassembler - K_mix_standardassembler)/norm(K_mix_standardassembler) ≈ 0.0 atol=100*eps(T) - @test norm(T_mix_blockassembler - T_mix_standardassembler)/norm(T_mix_standardassembler) ≈ 0.0 atol=100*eps(T) + if T==Float64 + @test norm(K_mix_blockassembler - K_mix_standardassembler)/norm(K_mix_standardassembler) ≈ 0.0 atol=100*eps(T) + @test norm(T_mix_blockassembler - T_mix_standardassembler)/norm(T_mix_standardassembler) ≈ 0.0 atol=100*eps(T) + @info "Tests executed!" + end end end \ No newline at end of file diff --git a/test/test_mult.jl b/test/test_mult.jl index 4c5399df..420b30ae 100644 --- a/test/test_mult.jl +++ b/test/test_mult.jl @@ -1,31 +1,30 @@ -using CompScienceMeshes -using BEAST +@testitem "loops have div zero" begin + using CompScienceMeshes + using LinearAlgebra -using Test -using LinearAlgebra + for T in [Float32, Float64] + faces = meshrectangle(T(1.0), T(1.0), T(0.5), 3) -for T in [Float32, Float64] - faces = meshrectangle(T(1.0), T(1.0), T(0.5), 3) + bnd = boundary(faces) + edges = submesh(!in(bnd), skeleton(faces,1)) - local bnd = boundary(faces) - local edges = submesh(!in(bnd), skeleton(faces,1)) + bnd_nodes = skeleton(bnd, 0) + @test length(bnd_nodes) == 8 + nodes = submesh(!in(bnd_nodes), skeleton(faces,0)) + @test length(nodes) == 1 - local bnd_nodes = skeleton(bnd, 0) - @test length(bnd_nodes) == 8 - local nodes = submesh(!in(bnd_nodes), skeleton(faces,0)) - @test length(nodes) == 1 + Conn = connectivity(nodes, edges, sign) - Conn = connectivity(nodes, edges, sign) + X = raviartthomas(faces, cellpairs(faces,edges)) + @test numfunctions(X) == 8 - local X = raviartthomas(faces, cellpairs(faces,edges)) - @test numfunctions(X) == 8 - - divX = divergence(X) - Id = BEAST.Identity() - DD = Matrix(assemble(Id, divX, divX)) - @test rank(DD) == 7 - L = divX * Conn - for sh in L.fns[1] - @test isapprox(sh.coeff, 0, atol=1e-8) + divX = divergence(X) + Id = BEAST.Identity() + DD = Matrix(assemble(Id, divX, divX)) + @test rank(DD) == 7 + L = divX * Conn + for sh in L.fns[1] + @test isapprox(sh.coeff, 0, atol=1e-8) + end end end \ No newline at end of file diff --git a/test/test_ncrossbdm.jl b/test/test_ncrossbdm.jl index bb6c9f53..1df7ec8a 100644 --- a/test/test_ncrossbdm.jl +++ b/test/test_ncrossbdm.jl @@ -1,47 +1,40 @@ -using Test -using BEAST -using CompScienceMeshes -#testing local value in the center of a triangle -for T in [Float64] - for j in [1,2,3,4,5,6] - local o, x, y, z = euclidianbasis(3,T) - triang = simplex(x,y,o) - ctr = center(triang) - n = normal(triang) - oref = BEAST.NCrossBDMRefSpace{T}() - oref2= BEAST.BDMRefSpace{T}() - oshp=BEAST.Shape(1,j,1.0) - oshp2=BEAST.Shape(1,j,1.0) - - o1 = oref(ctr)[oshp.refid].value * oshp.coeff - o2=n × oref2(ctr)[oshp2.refid].value * oshp2.coeff - @test o1 ≈ o2 +@testitem "local basis: nxBDM and BDM consistency" begin + using CompScienceMeshes + #testing local value in the center of a triangle + for T in [Float64] + for j in [1,2,3,4,5,6] + o, x, y, z = euclidianbasis(3,T) + triang = simplex(x,y,o) + ctr = center(triang) + n = normal(triang) + oref = BEAST.NCrossBDMRefSpace{T}() + oref2= BEAST.BDMRefSpace{T}() + oshp=BEAST.Shape(1,j,1.0) + oshp2=BEAST.Shape(1,j,1.0) + + o1 = oref(ctr)[oshp.refid].value * oshp.coeff + o2=n × oref2(ctr)[oshp2.refid].value * oshp2.coeff + @test o1 ≈ o2 + end end -end - -#testing their connectivity on a mesh - -m=meshsphere(radius=1,h=1.0) -nodes = skeleton(m,0) -edges = skeleton(m,1) - -Z = BEAST.brezzidouglasmarini(m) -NZ = BEAST.ncrossbdm(m) -for i=(1,11,36,52,111) - for j=(1,2) - @test Z.fns[i][j].coeff ≈ NZ.fns[i][j].coeff - @test Z.fns[i][j].refid ≈ NZ.fns[i][j].refid - @test Z.fns[i][j].cellid ≈ NZ.fns[i][j].cellid + m=meshsphere(radius=1.0, h=1.0) + Z = BEAST.brezzidouglasmarini(m) + NZ = BEAST.ncrossbdm(m) + for i=(1,11,36,52,111) + for j=(1,2) + @test Z.fns[i][j].coeff ≈ NZ.fns[i][j].coeff + @test Z.fns[i][j].refid ≈ NZ.fns[i][j].refid + @test Z.fns[i][j].cellid ≈ NZ.fns[i][j].cellid + end end -end -#testing the values on a mesh through Gram matrices + #testing the values on a mesh through Gram matrices + Id = BEAST.Identity() + qs = BEAST.SingleNumQStrat(8) -Id = BEAST.Identity() -qs = BEAST.SingleNumQStrat(8) - -Gzz= assemble(Id,Z,Z,quadstrat=qs) -Gnznz= assemble(Id,NZ,NZ,quadstrat=qs) -@test Gzz ≈ Gnznz + Gzz= assemble(Id,Z,Z,quadstrat=qs) + Gnznz= assemble(Id,NZ,NZ,quadstrat=qs) + @test Gzz ≈ Gnznz +end diff --git a/test/test_ndlcd_restrict.jl b/test/test_ndlcd_restrict.jl index fc88e1e0..6b7d306e 100644 --- a/test/test_ndlcd_restrict.jl +++ b/test/test_ndlcd_restrict.jl @@ -1,38 +1,38 @@ -using CompScienceMeshes -using BEAST -using Test -using LinearAlgebra +@testitem "restrict for 3D Nedelec-curl" begin + using CompScienceMeshes + using LinearAlgebra -for T in [Float32, Float64] - local o, x, y, z = CompScienceMeshes.euclidianbasis(3,T) - tet = simplex(x,y,z,o) + for T in [Float32, Float64] + o, x, y, z = CompScienceMeshes.euclidianbasis(3,T) + tet = simplex(x,y,z,o) - rs = BEAST.NDLCDRefSpace{T}() - Q = BEAST.restrict(rs, tet, tet) - @test Q ≈ Matrix(T(1.0)LinearAlgebra.I, 4, 4) + rs = BEAST.NDLCDRefSpace{T}() + Q = BEAST.restrict(rs, tet, tet) + @test Q ≈ Matrix(T(1.0)LinearAlgebra.I, 4, 4) - rs = BEAST.NDLCCRefSpace{T}() - Q = BEAST.restrict(rs, tet, tet) - @test Q ≈ Matrix(T(1.0)LinearAlgebra.I, 6, 6) + rs = BEAST.NDLCCRefSpace{T}() + Q = BEAST.restrict(rs, tet, tet) + @test Q ≈ Matrix(T(1.0)LinearAlgebra.I, 6, 6) - c = cartesian(center(tet)) - smalltet = simplex(x,y,z,c) - p_smalltet = center(smalltet) - p_tet = neighborhood(tet, carttobary(tet, cartesian(p_smalltet))) - @show cartesian(p_smalltet) - @show cartesian(p_tet) - @assert cartesian(p_tet) ≈ cartesian(p_smalltet) - @assert volume(tet) / volume(smalltet) ≈ 4 + c = cartesian(center(tet)) + smalltet = simplex(x,y,z,c) + p_smalltet = center(smalltet) + p_tet = neighborhood(tet, carttobary(tet, cartesian(p_smalltet))) + # @show cartesian(p_smalltet) + # @show cartesian(p_tet) + @assert cartesian(p_tet) ≈ cartesian(p_smalltet) + @assert volume(tet) / volume(smalltet) ≈ 4 - Q = BEAST.restrict(rs, tet, smalltet) - Fp = rs(p_tet) - fp = rs(p_smalltet) + Q = BEAST.restrict(rs, tet, smalltet) + Fp = rs(p_tet) + fp = rs(p_smalltet) - for j in axes(Q,1) - x = Fp[j].value - y = sum(Q[j,i]*fp[i].value for i in axes(Q,2)) - @show x - @show y - @test x ≈ y + for j in axes(Q,1) + x = Fp[j].value + y = sum(Q[j,i]*fp[i].value for i in axes(Q,2)) + # @show x + # @show y + @test x ≈ y + end end end \ No newline at end of file diff --git a/test/test_nonconf_quadrules.jl b/test/test_nonconf_quadrules.jl new file mode 100644 index 00000000..e81bb5a3 --- /dev/null +++ b/test/test_nonconf_quadrules.jl @@ -0,0 +1,42 @@ +@testitem "norm of constant field" begin + using CompScienceMeshes + using LinearAlgebra + + h1 = 0.2 + h2 = 0.176 + m1 = meshcuboid(1.0, 1.0, 1.0, h1; generator=:gmsh) + m2 = meshcuboid(1.0, 1.0, 1.0, h2; generator=:gmsh) + + E = Maxwell3D.planewave(;direction=point(0,0,1), polarization=point(1,0,0), wavenumber=0.0) + e = n × E + + cstrat = BEAST.DoubleNumWiltonSauterQStrat{Int64, Int64}(2, 3, 6, 7, 5, 5, 4, 3) + # cstrat = BEAST.DoubleNumWiltonSauterQStrat{Int64, Int64}(12, 13, 12, 13, 7, 7, 7, 7) + nstrat = BEAST.NonConformingIntegralOpQStrat(cstrat) + + Id = BEAST.Identity() + S = Maxwell3D.singlelayer(alpha=1.0, beta=1.0, gamma=1.0) + + X1 = raviartthomas(m1) + X2 = raviartthomas(m2) + + G11 = assemble(Id, X1, X1) + G22 = assemble(Id, X2, X2) + + u1 = G11 \ assemble(e, X1) + u2 = G22 \ assemble(e, X2) + + S11 = assemble(S, X1, X1; quadstrat=cstrat) + S12 = assemble(S, X1, X2; quadstrat=nstrat) + S22 = assemble(S, X2, X2; quadstrat=cstrat) + + term1 = real(u1' * S11 * u1) + term2 = real(u1' * S12 * u2) + term3 = real(u2' * S22 * u2) + + norm1 = sqrt(term1) + norm3 = sqrt(term2) + testval = norm(term1 - 2*term2 + term3) + @show testval / norm1 + @test testval / norm1 < 3e-2 +end \ No newline at end of file diff --git a/test/test_overlapping_edge_remeshing.jl b/test/test_overlapping_edge_remeshing.jl new file mode 100644 index 00000000..8384ac7f --- /dev/null +++ b/test/test_overlapping_edge_remeshing.jl @@ -0,0 +1,32 @@ +@testitem "conformity" begin + +using CompScienceMeshes + τ = simplex( + point(0.0624999999994547, 0.6856034446626162, 0.0), + point(0.06944444444542179, 0.6735753140519203, 0.0), + point(0.05555730739992443, 0.6735783483387338, 0.0)) + + σ = simplex( + point(0.07017543859225508, 0.6988976942759024, 0.0), + point(0.06249997795123046, 0.6856034064739717, 0.0), + point(0.06140343344926745, 0.6837043913625904, 0.0)) + + for (k,λ) in pairs(faces(τ)) + for (l,μ) in pairs(faces(σ)) + if CompScienceMeshes.overlap(λ, μ) + global i = k + global j = l + end + end end + + @test i == 2 + @test j == 3 + + τs, σs = BEAST._conforming_refinement_touching_triangles(τ,σ,i,j) + + # Ideally both should be true but the round-off causes CompScienceMeshes.overlap to + # incorrectly report. + @test BEAST._test_conformity(τs[1], σs[1])==true + @test BEAST._test_conformity(τs[2], σs[1])==false + +end \ No newline at end of file diff --git a/test/test_quadstrat_as_fn.jl b/test/test_quadstrat_as_fn.jl new file mode 100644 index 00000000..fc180c6d --- /dev/null +++ b/test/test_quadstrat_as_fn.jl @@ -0,0 +1,47 @@ +@testitem "quadstrat as function" begin + using CompScienceMeshes + using LinearAlgebra + + pd = dirname(pathof(BEAST)) + fn = joinpath(pd, "../test/assets/sphere45.in") + Γ = CompScienceMeshes.readmesh(fn) + # @show length(Γ) + + X = raviartthomas(Γ) + @show numfunctions(X) + t = Maxwell3D.singlelayer(wavenumber=1.0) + + qsp(p) = (op, X, Y) -> BEAST.DoubleNumWiltonSauterQStrat(2+p, 3+p, 6+p, 7+p, 5+p, 5+p, 4+p, 3+p) + Z = map(0:3) do p + assemble(t, X, X; quadstrat=qsp(p)) + end + + W = map(0:3) do p + qs = qsp(p)(t, X, X) + assemble(t, X, X; quadstrat=qs) + end + + @test all(Z .≈ W) +end + +@testitem "quadstrat for linear combinations" begin + using CompScienceMeshes + using LinearAlgebra + + pd = dirname(pathof(BEAST)) + fn = joinpath(pd, "../test/assets/sphere45.in") + Γ = CompScienceMeshes.readmesh(fn) + # @show length(Γ) + + X = raviartthomas(Γ) + @show numfunctions(X) + t = Maxwell3D.singlelayer(wavenumber=1.0) + k = Maxwell3D.doublelayer(wavenumber=1.0) + + qsp(p) = (op, X, Y) -> BEAST.DoubleNumWiltonSauterQStrat(2+p, 3+p, 6+p, 7+p, 5+p, 5+p, 4+p, 3+p) + + a = t + k + qsp0 = qsp(0) + Z = assemble(a, X, X; quadstrat=qsp0) + # @which assemble(a, X, X; quadstrat=qsp0) +end \ No newline at end of file diff --git a/test/test_restrict.jl b/test/test_restrict.jl index e06a6a58..69d03137 100644 --- a/test/test_restrict.jl +++ b/test/test_restrict.jl @@ -1,84 +1,73 @@ -using Test - -using CompScienceMeshes -using BEAST - -const e0 = point(0.0,0.0,0.0) -const e1 = point(1.0,0.0,0.0) -const e2 = point(0.0,1.0,0.0) -const e3 = point(0.0,0.0,1.0) - -for T in [Float32, Float64] - - - p = simplex( - [ - point(T,0.0,0.0,0.0), - point(T,1.0,0.0,0.0) - ], - Val{1} - ) - - q = simplex( - [ - point(T,0.0,0.0,0.0), - point(T,0.5,0.0,0.0) - ], - Val{1} - ) - - f = BEAST.LagrangeRefSpace{T,1,2,2}() - local x = neighborhood(p, T.([0.0])) - v = f(x) - - @test v[1].value == 0 - @test v[2].value == 1 - - @test v[1].derivative == -1 - @test v[2].derivative == +1 - - Q = restrict(f, p, q) - @test Q == [ - T(1.0) T(0.5) - T(0.0) T(0.5)] - - - # Test restriction of RT elements - # ni, no = 6, 7; - # ui = transpose([CompScienceMeshes.triangleGaussA[ni] CompScienceMeshes.triangleGaussB[ni] ]); - # uo = transpose([CompScienceMeshes.triangleGaussA[no] CompScienceMeshes.triangleGaussB[no] ]); - # wi = triangleGaussW[ni]; - # wo = triangleGaussW[no]; - # universe = Universe(1.0, ui, wi, uo, wo); - - p = simplex([T.(2*e0),T.(2*e1),T.(2*e2)], Val{2}) - x = neighborhood(p,T.([0.5, 0.5])) - ϕ = BEAST.RTRefSpace{T}() - v = ϕ(x) - - Q = restrict(ϕ, p, p) - if T==Float64 - @test Q == Matrix(LinearAlgebra.I, 3, 3) - - q = simplex([T.(e0+e1), T.(2*e1), T.(e1+e2)], Val{2}) - Q = restrict(ϕ, p, q) - @test Q == [ - 2 -1 0 - 0 1 0 - 0 -1 2] // 4 - end - - # Test restriction of Nedelec elements - ψ = BEAST.NDRefSpace{T}() - Q = restrict(ψ, p, p) - if T==Float64 - @test Q == Matrix(LinearAlgebra.I, 3, 3) - - q = simplex([T.(e0+e1), T.(2*e1), T.(e1+e2)], Val{2}) - Q = restrict(ψ, p, q) - @test Q == [ - 2 -1 0 - 0 1 0 - 0 -1 2] // 4 +@testitem "restrict" begin + using LinearAlgebra + using CompScienceMeshes + + const e0 = point(0.0,0.0,0.0) + const e1 = point(1.0,0.0,0.0) + const e2 = point(0.0,1.0,0.0) + const e3 = point(0.0,0.0,1.0) + + for T in [Float32, Float64] + p = simplex( + [ + point(T,0.0,0.0,0.0), + point(T,1.0,0.0,0.0) + ], + Val{1} + ) + + q = simplex( + [ + point(T,0.0,0.0,0.0), + point(T,0.5,0.0,0.0) + ], + Val{1} + ) + + f = BEAST.LagrangeRefSpace{T,1,2,2}() + x = neighborhood(p, T.([0.0])) + v = f(x) + + @test v[1].value == 0 + @test v[2].value == 1 + + @test v[1].derivative == -1 + @test v[2].derivative == +1 + + Q = restrict(f, p, q) + @test Q == [ + T(1.0) T(0.5) + T(0.0) T(0.5)] + + p = simplex([T.(2*e0),T.(2*e1),T.(2*e2)], Val{2}) + x = neighborhood(p,T.([0.5, 0.5])) + ϕ = BEAST.RTRefSpace{T}() + v = ϕ(x) + + Q = restrict(ϕ, p, p) + if T==Float64 + @test Q == Matrix(LinearAlgebra.I, 3, 3) + + q = simplex([T.(e0+e1), T.(2*e1), T.(e1+e2)], Val{2}) + Q = restrict(ϕ, p, q) + @test Q == [ + 2 -1 0 + 0 1 0 + 0 -1 2] // 4 + end + + # Test restriction of Nedelec elements + ψ = BEAST.NDRefSpace{T}() + Q = restrict(ψ, p, p) + if T==Float64 + @test Q == Matrix(LinearAlgebra.I, 3, 3) + + q = simplex([T.(e0+e1), T.(2*e1), T.(e1+e2)], Val{2}) + Q = restrict(ψ, p, q) + @test Q == [ + 2 -1 0 + 0 1 0 + 0 -1 2] // 4 + end end end \ No newline at end of file diff --git a/test/test_sauterschwabints1D.jl b/test/test_sauterschwabints1D.jl new file mode 100644 index 00000000..0cdb2427 --- /dev/null +++ b/test/test_sauterschwabints1D.jl @@ -0,0 +1,113 @@ +using Test +using LinearAlgebra + +using BEAST, CompScienceMeshes, StaticArrays + + +# Float32 not working since hankelh2 returns always F64 +for T in [Float64] + T = Float64 + Γto = meshsegment(T(1.0), T(0.5)) + Γt = Γto + Γs = Γt + + Xt = lagrangecxd0(Γt) + Xs = lagrangecxd0(Γs) + + λ = 10 + k = 2π/λ + + ops = [ + Helmholtz2D.singlelayer(; wavenumber=k) + ] + + refstrat = BEAST.DoubleNumSauterQstrat(3,3,0,4,30,30) + + for op in ops + Sref = assemble(op, Xt, Xs; quadstrat=refstrat) + ref = Sref[1, 1] + + n = 25 + + #Sgl = assemble(op, Xt, Xs; quadstrat=BEAST.DoubleNumQStrat(n, n+1)) + Sss = assemble(op, Xt, Xs; quadstrat=BEAST.DoubleNumSauterQstrat(3,3,0,4,n,n)) + + #gl = Sgl[1, 1] + ss = Sss[1, 1] + + #glrel = norm(gl - ref) / norm(ref) + ssrel = norm(ss - ref) / norm(ref) + + @test ssrel < 1e-14 + end + + Xt = lagrangec0d1(Γt) + Xs = lagrangec0d1(Γs) + + ops = [ + Helmholtz2D.singlelayer(; wavenumber=k) + Helmholtz2D.hypersingular(; wavenumber=k) + ] + 𝒮 = + + refstrat = BEAST.DoubleNumSauterQstrat(3,3,0,4,30,30) + + for op in ops + Sref = assemble(op, Xt, Xs; quadstrat=refstrat) + ref = Sref[1, 1] + + n = 25 + + #Sgl = assemble(op, Xt, Xs; quadstrat=BEAST.DoubleNumQStrat(n, n+1)) + Sss = assemble(op, Xt, Xs; quadstrat=BEAST.DoubleNumSauterQstrat(3,3,0,4,n,n)) + + #gl = Sgl[1, 1] + ss = Sss[1, 1] + + #glrel = norm(gl - ref) / norm(ref) + ssrel = norm(ss - ref) / norm(ref) + + @test ssrel < 1e-14 + end + + ## Test accuracy of vertex integrations + + T = Float64 + Γto = meshsegment(T(1.0), T(1.0)) + Γt = Γto + Γs = translate(Γto, SVector(1.0, 0.0)) + + Γs = Mesh([SVector(0.0, 0.0), SVector(0.0, 1.0)], [SVector(1, 2)]) + Xt = lagrangecxd0(Γt) + Xs = lagrangecxd0(Γs) + + λ = 10 + k = 2π/λ + + ops = [ + Helmholtz2D.singlelayer(; wavenumber=k) + Helmholtz2D.doublelayer(; wavenumber=k) + Helmholtz2D.doublelayer_transposed(; wavenumber=k) + ] + 𝒮 = + + refstrat = BEAST.DoubleNumSauterQstrat(3,3,0,4,30,30) + + for op in ops + Sref = assemble(op, Xt, Xs; quadstrat=refstrat) + ref = Sref[1, 1] + + n = 25 + + #Sgl = assemble(op, Xt, Xs; quadstrat=BEAST.DoubleNumQStrat(n, n+1)) + Sss = assemble(op, Xt, Xs; quadstrat=BEAST.DoubleNumSauterQstrat(3,3,0,4,n,n)) + + #gl = Sgl[1, 1] + ss = Sss[1, 1] + + #glrel = norm(gl - ref) / norm(ref) + ssrel = norm(ss - ref) / norm(ref) + + @test ssrel < 1e-14 + end +end \ No newline at end of file diff --git a/test/test_tdmwdbl.jl b/test/test_tdmwdbl.jl index 81fb700f..60d6c453 100644 --- a/test/test_tdmwdbl.jl +++ b/test/test_tdmwdbl.jl @@ -66,7 +66,7 @@ qrl = BEAST.quadrule(K, refspace(X), refspace(Y), refspace(T2), 1, test_els[1], 1, trial_els[1], 1, time_els[1], qdt, qs) z = zeros(Float64, 3, 3, 10) BEAST.innerintegrals!(z, K, x, refspace(X), refspace(Y), refspace(T2), test_els[1], trial_els[1], (0.0, 20.0), qrl, w) -@test_broken !any(isnan.(z)) +@test !any(isnan.(z)) verts = trial_els[1].vertices import WiltonInts84 @@ -85,4 +85,4 @@ h = 0.0 using StaticArrays m = SVector(-0.7071067811865474, 0.7071067811865478, -0.0) iG, vG = WiltonInts84.segintsg(a, b, p, h, m, Val{2}) -@test_broken !any(isnan.(iG)) +@test !any(isnan.(iG)) diff --git a/test/test_variational.jl b/test/test_variational.jl index eee2b42e..7b053d67 100644 --- a/test/test_variational.jl +++ b/test/test_variational.jl @@ -45,9 +45,9 @@ dbf = BEAST.discretise(bf, j=>X, k=>X) dlf = BEAST.discretise(lf, j=>X) space_mappings = (j=>X, k=>X,) -for sm in space_mappings - @show sm -end +# for sm in space_mappings +# @show sm +# end M = assemble(dbf) A = Matrix(M) diff --git a/test/untested/test_mot.jl b/test/untested/test_mot.jl index 847a15e8..929fc87a 100644 --- a/test/untested/test_mot.jl +++ b/test/untested/test_mot.jl @@ -18,7 +18,7 @@ W0 = inv(Z0) x = BEAST.mot(W0,Z,B,Nt) n = 20 -@show x[n] +# @show x[n] @show exp(-n*Δt) @show norm(x[n]-exp(-n*Δt)) From da9ae909cddc699172128f371e640cbce96ce564 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Fri, 9 May 2025 15:58:16 +0200 Subject: [PATCH 496/528] tutorial pages generated by Literate --- .gitignore | 1 + docs/Project.toml | 1 + docs/make.jl | 3 + docs/src/tutorials/efie.md | 117 +++ examples/Manifest.toml | 1646 ------------------------------------ examples/Project.toml | 3 + examples/efie.jl | 78 +- src/solvers/itsolver.jl | 6 +- 8 files changed, 192 insertions(+), 1663 deletions(-) create mode 100644 docs/src/tutorials/efie.md delete mode 100644 examples/Manifest.toml diff --git a/.gitignore b/.gitignore index 8a679b85..d426ff0f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ profile/ /Manifest.toml /test/Manifest.toml /docs/Manifest.toml +/examples/Manifest.toml /data/ \ No newline at end of file diff --git a/docs/Project.toml b/docs/Project.toml index c325337c..e0042947 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -4,6 +4,7 @@ CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244" Krylov = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" +Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5" PlotlyDocumenter = "9b90f1cd-2639-4507-8b17-2fbe371ceceb" TypeTree = "04da0e3b-1cad-4b2c-a963-fc1602baf1af" diff --git a/docs/make.jl b/docs/make.jl index f542cc1e..4051c366 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -35,6 +35,9 @@ makedocs(; ], "System of Equations and Bilinear Forms" => "manual/bilinear.md", ], + "Tutorials" => Any[ + "tutorials/efie.md", + ], "Operators & Excitations" => Any[ "Overview"=>"operators/overview.md", "Local Operators"=>Any["Identiy"=>"operators/identity.md",], diff --git a/docs/src/tutorials/efie.md b/docs/src/tutorials/efie.md new file mode 100644 index 00000000..3e6ffac2 --- /dev/null +++ b/docs/src/tutorials/efie.md @@ -0,0 +1,117 @@ +```@meta +EditURL = "../../../examples/efie.jl" +``` + +# EFIE + +This example demonstrates the modelling of scattering by a perfectly conducting +sphere using the Electric Field Integral Equation (EFIE). + +````julia +using LinearAlgebra +using CompScienceMeshes +using BEAST +import PlotlyDocumenter # hide +```` + +We call upon GMsh to create a flat-faceted triangular mesh for the sphere. +Subordinate to this mesh the space of lowest order Raviart-Thomas elements is +constructed. For reproducibility of this example, a pre-constructed mesh is +loaded from disk. + +````julia +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +X = raviartthomas(Γ); +```` + +The integral equation can be wrtiten down in terms of the single layer operaotr +and a plane wave excitation. + +````julia +κ, η = 1.0, 1.0; +t = Maxwell3D.singlelayer(wavenumber=κ); +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ); +e = (n × E) × n; +```` + +To write down the final variational formulation, placeholders for the test and +trial functions are required. + +````julia +@hilbertspace j; +@hilbertspace k; +efie = @discretise t[k,j]==e[k] j∈X k∈X; +```` + +The system is assembled and solved by GMRES. When this processs terminated, the +solution and the convergence history are returned. + +````julia +u, ch = BEAST.gmres_ch(efie; restart=1500); +@show ch +```` + +```` +Converged after 188 iterations. +```` + +With the solution in hand, various secondary quatities can be computed. Here we +query the far field pattern, the near field, and the norm of the solution at the +barycenters of the triangles in the mesh. + +````julia +Φ, Θ = [0.0], range(0,stop=π,length=100); +pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ]; +ffd = potential(MWFarField3D(wavenumber=κ), pts, u, X); + +fcr, geo = facecurrents(u, X); + +ys = range(-2,stop=2,length=50); +zs = range(-4,stop=4,length=100); +gridpoints = [point(0,y,z) for y in ys, z in zs]; +Esc = potential(MWSingleLayerField3D(wavenumber = κ), gridpoints, u, X); +Ein = E.(gridpoints); +```` + +```` +"" +```` + +The results can be exported or visualised using e.g. Plotly + +````julia +import PlotlyBase as Plt +tr1 = Plt.scatter(x=Θ, y=norm.(ffd)) +pl1 = Plt.Plot(tr1) + +tr2 = Plt.heatmap(x=ys, y=zs, z=norm.(Esc-Ein), + colorscale="Viridis", zmin=0, zmax=2, showscale=false) +pl2 = Plt.Plot(tr2) + +tr3 = CompScienceMeshes.patch(geo, norm.(fcr); + caxis=(0,2), colorscale="Viridis") +pl3 = Plt.Plot(tr3) +pl = [[pl1; pl2] pl3] +```` + +```@raw html +
+ + +``` + +--- + +*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).* + diff --git a/examples/Manifest.toml b/examples/Manifest.toml deleted file mode 100644 index fb7d1bbf..00000000 --- a/examples/Manifest.toml +++ /dev/null @@ -1,1646 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -[[AbstractFFTs]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" -uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" -version = "1.5.0" - - [AbstractFFTs.extensions] - AbstractFFTsChainRulesCoreExt = "ChainRulesCore" - AbstractFFTsTestExt = "Test" - - [AbstractFFTs.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[AbstractTrees]] -git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" -uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" -version = "0.4.5" - -[[ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" -version = "1.1.2" - -[[ArrayLayouts]] -deps = ["FillArrays", "LinearAlgebra"] -git-tree-sha1 = "0dd7edaff278e346eb0ca07a7e75c9438408a3ce" -uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "1.10.3" -weakdeps = ["SparseArrays"] - - [ArrayLayouts.extensions] - ArrayLayoutsSparseArraysExt = "SparseArrays" - -[[Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" -version = "1.11.0" - -[[AssetRegistry]] -deps = ["Distributed", "JSON", "Pidfile", "SHA", "Test"] -git-tree-sha1 = "b25e88db7944f98789130d7b503276bc34bc098e" -uuid = "bf4720bc-e11a-5d0c-854e-bdca1663c893" -version = "0.1.0" - -[[BEAST]] -deps = ["AbstractTrees", "BlockArrays", "CollisionDetection", "Combinatorics", "CompScienceMeshes", "Compat", "ConvolutionOperators", "Distributed", "ExtendableSparse", "FFTW", "FastGaussQuadrature", "FillArrays", "Infiltrator", "InteractiveUtils", "IterativeSolvers", "LiftedMaps", "LinearAlgebra", "LinearMaps", "NestedUnitRanges", "Requires", "SauterSchwab3D", "SauterSchwabQuadrature", "SharedArrays", "SparseArrays", "SpecialFunctions", "StaticArrays", "TestItems", "WiltonInts84"] -git-tree-sha1 = "757efc563022d39e95f751b7e5a29d020a57e049" -uuid = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" -version = "2.5.0" - -[[Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" -version = "1.11.0" - -[[BinDeps]] -deps = ["Libdl", "Pkg", "SHA", "URIParser", "Unicode"] -git-tree-sha1 = "1289b57e8cf019aede076edab0587eb9644175bd" -uuid = "9e28174c-4ba2-5203-b857-d8d62c4213ee" -version = "1.0.2" - -[[Blink]] -deps = ["Base64", "BinDeps", "Distributed", "JSExpr", "JSON", "Lazy", "Logging", "MacroTools", "Mustache", "Mux", "Reexport", "Sockets", "WebIO", "WebSockets"] -git-tree-sha1 = "08d0b679fd7caa49e2bca9214b131289e19808c0" -uuid = "ad839575-38b3-5650-b840-f874b8c74a25" -version = "0.12.5" - -[[BlockArrays]] -deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] -git-tree-sha1 = "9a9610fbe5779636f75229e423e367124034af41" -uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" -version = "0.16.43" - -[[Bzip2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9e2a6b69137e6969bab0152632dcb3bc108c8bdd" -uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+1" - -[[Cairo_jll]] -deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "009060c9a6168704143100f36ab08f06c2af4642" -uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.18.2+1" - -[[ClusterTrees]] -deps = ["DelimitedFiles", "LinearAlgebra", "Pkg"] -git-tree-sha1 = "4114d60c95974edf9272d88571d14abeed598942" -uuid = "5100927d-02aa-593a-b4f9-7235df19f0db" -version = "0.2.1" - -[[CollisionDetection]] -deps = ["Compat", "LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "88a33f2fba4ef1065edd06164c84d8b03ee4d049" -uuid = "2b5bf9a6-f3f8-5352-af9c-82bb4af718d8" -version = "0.1.6" - -[[ColorSchemes]] -deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] -git-tree-sha1 = "b5278586822443594ff615963b0c09755771b3e0" -uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.26.0" - -[[ColorTypes]] -deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "b10d0b65641d57b8b4d5e234446582de5047050d" -uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.11.5" - -[[ColorVectorSpace]] -deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] -git-tree-sha1 = "a1f44953f2382ebb937d60dafbe2deea4bd23249" -uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" -version = "0.10.0" -weakdeps = ["SpecialFunctions"] - - [ColorVectorSpace.extensions] - SpecialFunctionsExt = "SpecialFunctions" - -[[Colors]] -deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] -git-tree-sha1 = "362a287c3aa50601b0bc359053d5c2468f0e7ce0" -uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.12.11" - -[[Combinatorics]] -git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" -uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" -version = "1.0.2" - -[[CompScienceMeshes]] -deps = ["ClusterTrees", "CollisionDetection", "Combinatorics", "Compat", "DataStructures", "DelimitedFiles", "FastGaussQuadrature", "GmshTools", "LinearAlgebra", "Permutations", "Requires", "SparseArrays", "StaticArrays", "TestItems"] -git-tree-sha1 = "6bb7cd3831707872fe404e01d156e1b149fcfa3a" -uuid = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" -version = "0.8.3" - -[[Compat]] -deps = ["TOML", "UUIDs"] -git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.16.0" -weakdeps = ["Dates", "LinearAlgebra"] - - [Compat.extensions] - CompatLinearAlgebraExt = "LinearAlgebra" - -[[CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.1.1+0" - -[[Contour]] -git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8" -uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" -version = "0.6.3" - -[[ConvolutionOperators]] -deps = ["LinearAlgebra", "LinearMaps"] -git-tree-sha1 = "ae44e38013c05c7ec59f428b4ea7ad7d34926b63" -uuid = "15927181-a1bb-497c-b745-8dbf505c019d" -version = "0.4.1" - -[[DataAPI]] -git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" -uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.16.0" - -[[DataStructures]] -deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "1d0a14036acb104d9e89698bd408f63ab58cdc82" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.20" - -[[DataValueInterfaces]] -git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" -uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" -version = "1.0.0" - -[[Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" -version = "1.11.0" - -[[Dbus_jll]] -deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl"] -git-tree-sha1 = "fc173b380865f70627d7dd1190dc2fce6cc105af" -uuid = "ee1fde0b-3d02-5ea6-8484-8dfef6360eab" -version = "1.14.10+0" - -[[DelimitedFiles]] -deps = ["Mmap"] -git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" -version = "1.9.1" - -[[Distributed]] -deps = ["Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" -version = "1.11.0" - -[[DocStringExtensions]] -deps = ["LibGit2"] -git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" -uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.9.3" - -[[Downloads]] -deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -version = "1.6.0" - -[[EpollShim_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8e9441ee83492030ace98f9789a654a6d0b1f643" -uuid = "2702e6a9-849d-5ed8-8c21-79e8b8f9ee43" -version = "0.0.20230411+0" - -[[Expat_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7" -uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.6.2+0" - -[[ExtendableSparse]] -deps = ["DocStringExtensions", "ILUZero", "LinearAlgebra", "Printf", "SparseArrays", "Sparspak", "StaticArrays", "SuiteSparse", "Test"] -git-tree-sha1 = "3dc0ead7baa71735e03be4379d812dd8167e904a" -uuid = "95c220a8-a1cf-11e9-0c77-dbfce5f500b3" -version = "1.5.2" - - [ExtendableSparse.extensions] - ExtendableSparseAMGCLWrapExt = "AMGCLWrap" - ExtendableSparseAlgebraicMultigridExt = "AlgebraicMultigrid" - ExtendableSparseIncompleteLUExt = "IncompleteLU" - ExtendableSparsePardisoExt = "Pardiso" - - [ExtendableSparse.weakdeps] - AMGCLWrap = "4f76b812-4ba5-496d-b042-d70715554288" - AlgebraicMultigrid = "2169fc97-5a83-5252-b627-83903c6c433c" - IncompleteLU = "40713840-3770-5561-ab4c-a76e7d0d7895" - Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" - -[[FFMPEG]] -deps = ["FFMPEG_jll"] -git-tree-sha1 = "53ebe7511fa11d33bec688a9178fac4e49eeee00" -uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" -version = "0.4.2" - -[[FFMPEG_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] -git-tree-sha1 = "466d45dc38e15794ec7d5d63ec03d776a9aff36e" -uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" -version = "4.4.4+1" - -[[FFTW]] -deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] -git-tree-sha1 = "4820348781ae578893311153d69049a93d05f39d" -uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" -version = "1.8.0" - -[[FFTW_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4d81ed14783ec49ce9f2e168208a12ce1815aa25" -uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" -version = "3.3.10+1" - -[[FLTK_jll]] -deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "72a4842f93e734f378cf381dae2ca4542f019d23" -uuid = "4fce6fc7-ba6a-5f4c-898f-77e99806d6f8" -version = "1.3.8+0" - -[[FastGaussQuadrature]] -deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] -git-tree-sha1 = "fd923962364b645f3719855c88f7074413a6ad92" -uuid = "442a2c76-b920-505d-bb47-c5924d526838" -version = "1.0.2" - -[[FileWatching]] -uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" -version = "1.11.0" - -[[FillArrays]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a" -uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.13.0" - - [FillArrays.extensions] - FillArraysPDMatsExt = "PDMats" - FillArraysSparseArraysExt = "SparseArrays" - FillArraysStatisticsExt = "Statistics" - - [FillArrays.weakdeps] - PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" - SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[FixedPointNumbers]] -deps = ["Statistics"] -git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" -uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.8.5" - -[[Fontconfig_jll]] -deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] -git-tree-sha1 = "db16beca600632c95fc8aca29890d83788dd8b23" -uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.96+0" - -[[Format]] -git-tree-sha1 = "9c68794ef81b08086aeb32eeaf33531668d5f5fc" -uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8" -version = "1.3.7" - -[[FreeType2_jll]] -deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc" -uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.13.2+0" - -[[FriBidi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1ed150b39aebcc805c26b93a8d0122c940f64ce2" -uuid = "559328eb-81f9-559d-9380-de523a88c83c" -version = "1.0.14+0" - -[[FunctionalCollections]] -deps = ["Test"] -git-tree-sha1 = "04cb9cfaa6ba5311973994fe3496ddec19b6292a" -uuid = "de31a74c-ac4f-5751-b3fd-e18cd04993ca" -version = "0.5.0" - -[[GLFW_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll", "libdecor_jll", "xkbcommon_jll"] -git-tree-sha1 = "532f9126ad901533af1d4f5c198867227a7bb077" -uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" -version = "3.4.0+1" - -[[GLU_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] -git-tree-sha1 = "65af046f4221e27fb79b28b6ca89dd1d12bc5ec7" -uuid = "bd17208b-e95e-5925-bf81-e2f59b3e5c61" -version = "9.0.1+0" - -[[GMP_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" -version = "6.3.0+0" - -[[GR]] -deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Preferences", "Printf", "Qt6Wayland_jll", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "p7zip_jll"] -git-tree-sha1 = "ee28ddcd5517d54e417182fec3886e7412d3926f" -uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" -version = "0.73.8" - -[[GR_jll]] -deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "FreeType2_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Qt6Base_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "f31929b9e67066bee48eec8b03c0df47d31a74b3" -uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" -version = "0.73.8+0" - -[[Gettext_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" -uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" -version = "0.21.0+0" - -[[Glib_jll]] -deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "674ff0db93fffcd11a3573986e550d66cd4fd71f" -uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.80.5+0" - -[[GmshTools]] -deps = ["Libdl", "gmsh_jll"] -git-tree-sha1 = "299aa66053646db77f8aa7fafcebe0f9e5c0d1dc" -uuid = "82e2f556-b1bd-5f1a-9576-f93c0da5f0ee" -version = "0.5.2" - -[[Graphite2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" -uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" -version = "1.3.14+0" - -[[Grisu]] -git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" -uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" -version = "1.0.2" - -[[GrundmannMoeller]] -deps = ["LinearAlgebra", "StaticArrays", "Test"] -git-tree-sha1 = "e264cf5f081091e4af712a911d3b620567c565e3" -uuid = "36aa67b7-9d79-4e90-bbc0-05abd90a007e" -version = "0.1.2" - -[[HDF5_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "LibCURL_jll", "Libdl", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "OpenSSL_jll", "TOML", "Zlib_jll", "libaec_jll"] -git-tree-sha1 = "82a471768b513dc39e471540fdadc84ff80ff997" -uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" -version = "1.14.3+3" - -[[HTTP]] -deps = ["Base64", "Dates", "IniFile", "Logging", "MbedTLS", "NetworkOptions", "Sockets", "URIs"] -git-tree-sha1 = "0fa77022fe4b511826b39c894c90daf5fce3334a" -uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "0.9.17" - -[[HarfBuzz_jll]] -deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"] -git-tree-sha1 = "401e4f3f30f43af2c8478fc008da50096ea5240f" -uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" -version = "8.3.1+0" - -[[Hiccup]] -deps = ["MacroTools", "Test"] -git-tree-sha1 = "6187bb2d5fcbb2007c39e7ac53308b0d371124bd" -uuid = "9fb69e20-1954-56bb-a84f-559cc56a8ff7" -version = "0.2.2" - -[[Hwloc_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "dd3b49277ec2bb2c6b94eb1604d4d0616016f7a6" -uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" -version = "2.11.2+0" - -[[ILUZero]] -deps = ["LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "b007cfc7f9bee9a958992d2301e9c5b63f332a90" -uuid = "88f59080-6952-5380-9ea5-54057fb9a43f" -version = "0.2.0" - -[[Infiltrator]] -deps = ["InteractiveUtils", "Markdown", "REPL", "UUIDs"] -git-tree-sha1 = "38298a8eabe09e49e6f60927c9e1ca3481688ba0" -uuid = "5903a43b-9cc3-4c30-8d17-598619ec4e9b" -version = "1.8.3" - -[[IniFile]] -git-tree-sha1 = "f550e6e32074c939295eb5ea6de31849ac2c9625" -uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f" -version = "0.5.1" - -[[IntelOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] -git-tree-sha1 = "10bd689145d2c3b2a9844005d01087cc1194e79e" -uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2024.2.1+0" - -[[InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -version = "1.11.0" - -[[IrrationalConstants]] -git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" -uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.2.2" - -[[IterativeSolvers]] -deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] -git-tree-sha1 = "59545b0a2b27208b0650df0a46b8e3019f85055b" -uuid = "42fd0dbc-a981-5370-80f2-aaf504508153" -version = "0.9.4" - -[[IteratorInterfaceExtensions]] -git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" -uuid = "82899510-4779-5014-852e-03e436cf321d" -version = "1.0.0" - -[[JLFzf]] -deps = ["Pipe", "REPL", "Random", "fzf_jll"] -git-tree-sha1 = "39d64b09147620f5ffbf6b2d3255be3c901bec63" -uuid = "1019f520-868f-41f5-a6de-eb00f4b6a39c" -version = "0.1.8" - -[[JLLWrappers]] -deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "be3dc50a92e5a386872a493a10050136d4703f9b" -uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.6.1" - -[[JSExpr]] -deps = ["JSON", "MacroTools", "Observables", "WebIO"] -git-tree-sha1 = "b413a73785b98474d8af24fd4c8a975e31df3658" -uuid = "97c1335a-c9c5-57fe-bc5d-ec35cebe8660" -version = "0.5.4" - -[[JSON]] -deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.4" - -[[JpegTurbo_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c" -uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.0.4+0" - -[[Kaleido_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "43032da5832754f58d14a91ffbe86d5f176acda9" -uuid = "f7e6163d-2fa5-5f23-b69c-1db539e41963" -version = "0.2.1+0" - -[[LAME_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "170b660facf5df5de098d866564877e119141cbd" -uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" -version = "3.100.2+0" - -[[LERC_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "36bdbc52f13a7d1dcb0f3cd694e01677a515655b" -uuid = "88015f11-f218-50d7-93a8-a6af411a945d" -version = "4.0.0+0" - -[[LLVMOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "78211fb6cbc872f77cad3fc0b6cf647d923f4929" -uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" -version = "18.1.7+0" - -[[LZO_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "854a9c268c43b77b0a27f22d7fab8d33cdb3a731" -uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.2+1" - -[[LaTeXStrings]] -git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec" -uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.3.1" - -[[Latexify]] -deps = ["Format", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Requires"] -git-tree-sha1 = "ce5f5621cac23a86011836badfedf664a612cee4" -uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" -version = "0.16.5" - - [Latexify.extensions] - DataFramesExt = "DataFrames" - SparseArraysExt = "SparseArrays" - SymEngineExt = "SymEngine" - - [Latexify.weakdeps] - DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" - SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - SymEngine = "123dc426-2d89-5057-bbad-38513e3affd8" - -[[Lazy]] -deps = ["MacroTools"] -git-tree-sha1 = "1370f8202dac30758f3c345f9909b97f53d87d3f" -uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" -version = "0.15.1" - -[[LazyArtifacts]] -deps = ["Artifacts", "Pkg"] -uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" -version = "1.11.0" - -[[LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" -version = "0.6.4" - -[[LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "8.6.0+0" - -[[LibGit2]] -deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" -version = "1.11.0" - -[[LibGit2_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] -uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" -version = "1.7.2+0" - -[[LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "MbedTLS_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" -version = "1.11.0+1" - -[[Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -version = "1.11.0" - -[[Libffi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" -uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.2.2+1" - -[[Libgcrypt_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] -git-tree-sha1 = "9fd170c4bbfd8b935fdc5f8b7aa33532c991a673" -uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.11+0" - -[[Libglvnd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] -git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" -uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" -version = "1.6.0+0" - -[[Libgpg_error_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "fbb1f2bef882392312feb1ede3615ddc1e9b99ed" -uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.49.0+0" - -[[Libiconv_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" -uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.17.0+0" - -[[Libmount_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "0c4f9c4f1a50d8f35048fa0532dabbadf702f81e" -uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.40.1+0" - -[[Libtiff_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "b404131d06f7886402758c9ce2214b636eb4d54a" -uuid = "89763e89-9b03-5906-acba-b20f662cd828" -version = "4.7.0+0" - -[[Libuuid_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807" -uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.40.1+0" - -[[LiftedMaps]] -deps = ["LinearAlgebra", "LinearMaps"] -git-tree-sha1 = "68c65fe9d32407ddb13eb26ef96fa803d45369cd" -uuid = "d22a30c1-52ac-4762-a8c9-5838452405e0" -version = "0.5.1" - -[[LinearAlgebra]] -deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -version = "1.11.0" - -[[LinearElasticity_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "71e8ee0f9fe0e86a8f8c7f28361e5118eab2f93f" -uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" -version = "5.0.0+0" - -[[LinearMaps]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "ee79c3208e55786de58f8dcccca098ced79f743f" -uuid = "7a12625a-238d-50fd-b39a-03d52299707e" -version = "3.11.3" - - [LinearMaps.extensions] - LinearMapsChainRulesCoreExt = "ChainRulesCore" - LinearMapsSparseArraysExt = "SparseArrays" - LinearMapsStatisticsExt = "Statistics" - - [LinearMaps.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[LogExpFunctions]] -deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" -uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.28" - - [LogExpFunctions.extensions] - LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" - LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" - LogExpFunctionsInverseFunctionsExt = "InverseFunctions" - - [LogExpFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" -version = "1.11.0" - -[[METIS_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "1fd0a97409e418b78c53fac671cf4622efdf0f21" -uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" -version = "5.1.2+0" - -[[MKL_jll]] -deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] -git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f" -uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2024.2.0+0" - -[[MMG_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] -git-tree-sha1 = "70a59df96945782bb0d43b56d0fbfdf1ce2e4729" -uuid = "86086c02-e288-5929-a127-40944b0018b7" -version = "5.6.0+0" - -[[MPICH_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "7715e65c47ba3941c502bffb7f266a41a7f54423" -uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" -version = "4.2.3+0" - -[[MPIPreferences]] -deps = ["Libdl", "Preferences"] -git-tree-sha1 = "c105fe467859e7f6e9a852cb15cb4301126fac07" -uuid = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267" -version = "0.1.11" - -[[MPItrampoline_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "70e830dab5d0775183c99fc75e4c24c614ed7142" -uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" -version = "5.5.1+0" - -[[MacroTools]] -deps = ["Markdown", "Random"] -git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df" -uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.13" - -[[Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" -version = "1.11.0" - -[[MbedTLS]] -deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] -git-tree-sha1 = "c067a280ddc25f196b5e7df3877c6b226d390aaf" -uuid = "739be429-bea8-5141-9913-cc70e7f3736d" -version = "1.1.9" - -[[MbedTLS_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.6+0" - -[[Measures]] -git-tree-sha1 = "c13304c81eec1ed3af7fc20e75fb6b26092a1102" -uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e" -version = "0.3.2" - -[[MicrosoftMPI_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f12a29c4400ba812841c6ace3f4efbb6dbb3ba01" -uuid = "9237b28f-5490-5468-be7b-bb81f5f5e6cf" -version = "10.1.4+2" - -[[Missings]] -deps = ["DataAPI"] -git-tree-sha1 = "ec4f7fbeab05d7747bdf98eb74d130a2a2ed298d" -uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "1.2.0" - -[[Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" -version = "1.11.0" - -[[MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2023.12.12" - -[[Mustache]] -deps = ["Printf", "Tables"] -git-tree-sha1 = "3b2db451a872b20519ebb0cec759d3d81a1c6bcb" -uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" -version = "1.0.20" - -[[Mux]] -deps = ["AssetRegistry", "Base64", "HTTP", "Hiccup", "Pkg", "Sockets", "WebSockets"] -git-tree-sha1 = "82dfb2cead9895e10ee1b0ca37a01088456c4364" -uuid = "a975b10e-0019-58db-a62f-e48ff68538c9" -version = "0.7.6" - -[[NaNMath]] -deps = ["OpenLibm_jll"] -git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4" -uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" -version = "1.0.2" - -[[NestedUnitRanges]] -deps = ["AbstractTrees", "ArrayLayouts", "BlockArrays"] -git-tree-sha1 = "1cbdce42da2370fee5ef906ef24179f8c070e3b9" -uuid = "032820ab-dc03-4b49-91f4-7d58d4da98b3" -version = "0.2.1" - -[[NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" -version = "1.2.0" - -[[OCCT_jll]] -deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "bef34b68c20cc34475c5cb464ab48555e74f4c61" -uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" -version = "7.7.2+0" - -[[Observables]] -git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225" -uuid = "510215fc-4207-5dde-b226-833fc4488ee2" -version = "0.5.5" - -[[OffsetArrays]] -git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e" -uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.14.1" - - [OffsetArrays.extensions] - OffsetArraysAdaptExt = "Adapt" - - [OffsetArrays.weakdeps] - Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" - -[[Ogg_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "887579a3eb005446d514ab7aeac5d1d027658b8f" -uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" -version = "1.3.5+1" - -[[OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.27+1" - -[[OpenLibm_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" - -[[OpenMPI_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "e25c1778a98e34219a00455d6e4384e017ea9762" -uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" -version = "4.1.6+0" - -[[OpenSSL_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10" -uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.15+1" - -[[OpenSpecFun_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" -uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.5+0" - -[[Opus_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "6703a85cb3781bd5909d48730a67205f3f31a575" -uuid = "91d4177d-7536-5919-b921-800302f37372" -version = "1.3.3+0" - -[[OrderedCollections]] -git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.6.3" - -[[PCRE2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" -version = "10.42.0+1" - -[[Pango_jll]] -deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e127b609fb9ecba6f201ba7ab753d5a605d53801" -uuid = "36c8627f-9965-5494-a995-c6b170f724f3" -version = "1.54.1+0" - -[[Parameters]] -deps = ["OrderedCollections", "UnPack"] -git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" -uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" -version = "0.12.3" - -[[Parsers]] -deps = ["Dates", "PrecompileTools", "UUIDs"] -git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" -uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.8.1" - -[[Permutations]] -deps = ["Combinatorics", "LinearAlgebra", "Random"] -git-tree-sha1 = "f92b0a7b722b1ecfd5c0d77a7eda24b4eea5c56a" -uuid = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" -version = "0.4.22" - -[[Pidfile]] -deps = ["FileWatching", "Test"] -git-tree-sha1 = "2d8aaf8ee10df53d0dfb9b8ee44ae7c04ced2b03" -uuid = "fa939f87-e72e-5be4-a000-7fc836dbe307" -version = "1.3.0" - -[[Pipe]] -git-tree-sha1 = "6842804e7867b115ca9de748a0cf6b364523c16d" -uuid = "b98c9c47-44ae-5843-9183-064241ee97a0" -version = "1.3.0" - -[[Pixman_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] -git-tree-sha1 = "35621f10a7531bc8fa58f74610b1bfb70a3cfc6b" -uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.43.4+0" - -[[Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.11.0" -weakdeps = ["REPL"] - - [Pkg.extensions] - REPLExt = "REPL" - -[[PlotThemes]] -deps = ["PlotUtils", "Statistics"] -git-tree-sha1 = "6e55c6841ce3411ccb3457ee52fc48cb698d6fb0" -uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" -version = "3.2.0" - -[[PlotUtils]] -deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "Statistics"] -git-tree-sha1 = "7b1a9df27f072ac4c9c7cbe5efb198489258d1f5" -uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" -version = "1.4.1" - -[[Plotly]] -deps = ["Base64", "DelimitedFiles", "HTTP", "JSON", "PlotlyJS", "Reexport"] -git-tree-sha1 = "044a9194ae38a50cbdb34a05dc63bf68e4db95df" -uuid = "58dd65bb-95f3-509e-9936-c39a10fdeae7" -version = "0.4.1" - -[[PlotlyBase]] -deps = ["ColorSchemes", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "LaTeXStrings", "Logging", "Parameters", "Pkg", "REPL", "Requires", "Statistics", "UUIDs"] -git-tree-sha1 = "56baf69781fc5e61607c3e46227ab17f7040ffa2" -uuid = "a03496cd-edff-5a9b-9e67-9cda94a718b5" -version = "0.8.19" - -[[PlotlyJS]] -deps = ["Base64", "Blink", "DelimitedFiles", "JSExpr", "JSON", "Kaleido_jll", "Markdown", "Pkg", "PlotlyBase", "PlotlyKaleido", "REPL", "Reexport", "Requires", "WebIO"] -git-tree-sha1 = "f198c8a80c08987a2915156e6e6131e5d73b97f4" -uuid = "f0f68f2c-4968-5e81-91da-67840de0976a" -version = "0.18.14" - - [PlotlyJS.extensions] - CSVExt = "CSV" - DataFramesExt = ["DataFrames", "CSV"] - IJuliaExt = "IJulia" - JSON3Ext = "JSON3" - - [PlotlyJS.weakdeps] - CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" - DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" - IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" - JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" - -[[PlotlyKaleido]] -deps = ["Base64", "JSON", "Kaleido_jll"] -git-tree-sha1 = "3210de4d88af7ca5de9e26305758a59aabc48aac" -uuid = "f2990250-8cf9-495f-b13a-cce12b45703c" -version = "2.2.5" - -[[Plots]] -deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "JLFzf", "JSON", "LaTeXStrings", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "PrecompileTools", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "RelocatableFolders", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "TOML", "UUIDs", "UnicodeFun", "UnitfulLatexify", "Unzip"] -git-tree-sha1 = "45470145863035bb124ca51b320ed35d071cc6c2" -uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -version = "1.40.8" - - [Plots.extensions] - FileIOExt = "FileIO" - GeometryBasicsExt = "GeometryBasics" - IJuliaExt = "IJulia" - ImageInTerminalExt = "ImageInTerminal" - UnitfulExt = "Unitful" - - [Plots.weakdeps] - FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" - GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" - IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" - ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" - Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" - -[[PrecompileTools]] -deps = ["Preferences"] -git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" -uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" -version = "1.2.1" - -[[Preferences]] -deps = ["TOML"] -git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6" -uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.4.3" - -[[Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" -version = "1.11.0" - -[[Qt6Base_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Vulkan_Loader_jll", "Xorg_libSM_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_cursor_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "libinput_jll", "xkbcommon_jll"] -git-tree-sha1 = "492601870742dcd38f233b23c3ec629628c1d724" -uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56" -version = "6.7.1+1" - -[[Qt6Declarative_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6ShaderTools_jll"] -git-tree-sha1 = "e5dd466bf2569fe08c91a2cc29c1003f4797ac3b" -uuid = "629bc702-f1f5-5709-abd5-49b8460ea067" -version = "6.7.1+2" - -[[Qt6ShaderTools_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll"] -git-tree-sha1 = "1a180aeced866700d4bebc3120ea1451201f16bc" -uuid = "ce943373-25bb-56aa-8eca-768745ed7b5a" -version = "6.7.1+1" - -[[Qt6Wayland_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6Declarative_jll"] -git-tree-sha1 = "729927532d48cf79f49070341e1d918a65aba6b0" -uuid = "e99dba38-086e-5de3-a5b1-6e4c66e897c3" -version = "6.7.1+1" - -[[REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "StyledStrings", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" -version = "1.11.0" - -[[Random]] -deps = ["SHA"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -version = "1.11.0" - -[[RecipesBase]] -deps = ["PrecompileTools"] -git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" -uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "1.3.4" - -[[RecipesPipeline]] -deps = ["Dates", "NaNMath", "PlotUtils", "PrecompileTools", "RecipesBase"] -git-tree-sha1 = "45cf9fd0ca5839d06ef333c8201714e888486342" -uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c" -version = "0.6.12" - -[[Reexport]] -git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.2.2" - -[[RelocatableFolders]] -deps = ["SHA", "Scratch"] -git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864" -uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" -version = "1.0.1" - -[[Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.0" - -[[SCOTCH_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "7110b749766853054ce8a2afaa73325d72d32129" -uuid = "a8d0f55d-b80e-548d-aff6-1a04c175f0f9" -version = "6.1.3+0" - -[[SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" -version = "0.7.0" - -[[SauterSchwab3D]] -deps = ["FastGaussQuadrature", "GrundmannMoeller", "LinearAlgebra", "ShunnHamQuadrature", "StaticArrays"] -git-tree-sha1 = "87242fb25711b1f9eaa45506d8b5e6e0b50f086a" -uuid = "0a13313b-1c00-422e-8263-562364ed9544" -version = "0.1.4" - -[[SauterSchwabQuadrature]] -deps = ["FastGaussQuadrature", "LinearAlgebra", "StaticArrays", "TestItems"] -git-tree-sha1 = "b98948c567cbe250d774d01a07833b7a329ec511" -uuid = "535c7bfe-2023-5c1d-b712-654ef9d93a38" -version = "2.4.0" - -[[Scratch]] -deps = ["Dates"] -git-tree-sha1 = "3bac05bc7e74a75fd9cba4295cde4045d9fe2386" -uuid = "6c6a2e73-6563-6170-7368-637461726353" -version = "1.2.1" - -[[Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" -version = "1.11.0" - -[[SharedArrays]] -deps = ["Distributed", "Mmap", "Random", "Serialization"] -uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" -version = "1.11.0" - -[[Showoff]] -deps = ["Dates", "Grisu"] -git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" -uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" -version = "1.0.3" - -[[ShunnHamQuadrature]] -deps = ["LinearAlgebra", "StaticArrays"] -git-tree-sha1 = "dfa53166b13cd6f352d54c99a24124321ef95282" -uuid = "164309f2-5039-4884-b6c7-6da8aa5c66ad" -version = "0.1.0" - -[[Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" -version = "1.11.0" - -[[SortingAlgorithms]] -deps = ["DataStructures"] -git-tree-sha1 = "66e0a8e672a0bdfca2c3f5937efb8538b9ddc085" -uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.2.1" - -[[SparseArrays]] -deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -version = "1.11.0" - -[[Sparspak]] -deps = ["Libdl", "LinearAlgebra", "Logging", "OffsetArrays", "Printf", "SparseArrays", "Test"] -git-tree-sha1 = "342cf4b449c299d8d1ceaf00b7a49f4fbc7940e7" -uuid = "e56a9233-b9d6-4f03-8d0f-1825330902ac" -version = "0.3.9" - -[[SpecialFunctions]] -deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" -uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.4.0" - - [SpecialFunctions.extensions] - SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" - - [SpecialFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - -[[StaticArrays]] -deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "eeafab08ae20c62c44c8399ccb9354a04b80db50" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.7" - - [StaticArrays.extensions] - StaticArraysChainRulesCoreExt = "ChainRulesCore" - StaticArraysStatisticsExt = "Statistics" - - [StaticArrays.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[StaticArraysCore]] -git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682" -uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.3" - -[[Statistics]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "ae3bb1eb3bba077cd276bc5cfc337cc65c3075c0" -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -version = "1.11.1" -weakdeps = ["SparseArrays"] - - [Statistics.extensions] - SparseArraysExt = ["SparseArrays"] - -[[StatsAPI]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "1ff449ad350c9c4cbc756624d6f8a8c3ef56d3ed" -uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" -version = "1.7.0" - -[[StatsBase]] -deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "5cf7606d6cef84b543b483848d4ae08ad9832b21" -uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.34.3" - -[[StyledStrings]] -uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" -version = "1.11.0" - -[[SuiteSparse]] -deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] -uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" - -[[SuiteSparse_jll]] -deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] -uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.7.0+0" - -[[TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.3" - -[[TableTraits]] -deps = ["IteratorInterfaceExtensions"] -git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" -uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" -version = "1.0.1" - -[[Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] -git-tree-sha1 = "598cd7c1f68d1e205689b1c2fe65a9f85846f297" -uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.12.0" - -[[Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" -version = "1.10.0" - -[[TensorCore]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" -uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" -version = "0.1.1" - -[[Test]] -deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -version = "1.11.0" - -[[TestItems]] -git-tree-sha1 = "8621ba2637b49748e2dc43ba3d84340be2938022" -uuid = "1c621080-faea-4a02-84b6-bbd5e436b8fe" -version = "0.1.1" - -[[URIParser]] -deps = ["Unicode"] -git-tree-sha1 = "53a9f49546b8d2dd2e688d216421d050c9a31d0d" -uuid = "30578b45-9adc-5946-b283-645ec420af67" -version = "0.4.1" - -[[URIs]] -git-tree-sha1 = "67db6cc7b3821e19ebe75791a9dd19c9b1188f2b" -uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.5.1" - -[[UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" -version = "1.11.0" - -[[UnPack]] -git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" -uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" -version = "1.0.2" - -[[Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" -version = "1.11.0" - -[[UnicodeFun]] -deps = ["REPL"] -git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" -uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" -version = "0.4.1" - -[[Unitful]] -deps = ["Dates", "LinearAlgebra", "Random"] -git-tree-sha1 = "d95fe458f26209c66a187b1114df96fd70839efd" -uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" -version = "1.21.0" - - [Unitful.extensions] - ConstructionBaseUnitfulExt = "ConstructionBase" - InverseFunctionsUnitfulExt = "InverseFunctions" - - [Unitful.weakdeps] - ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[UnitfulLatexify]] -deps = ["LaTeXStrings", "Latexify", "Unitful"] -git-tree-sha1 = "975c354fcd5f7e1ddcc1f1a23e6e091d99e99bc8" -uuid = "45397f5d-5981-4c77-b2b3-fc36d6e9b728" -version = "1.6.4" - -[[Unzip]] -git-tree-sha1 = "ca0969166a028236229f63514992fc073799bb78" -uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d" -version = "0.2.0" - -[[Vulkan_Loader_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Wayland_jll", "Xorg_libX11_jll", "Xorg_libXrandr_jll", "xkbcommon_jll"] -git-tree-sha1 = "2f0486047a07670caad3a81a075d2e518acc5c59" -uuid = "a44049a8-05dd-5a78-86c9-5fde0876e88c" -version = "1.3.243+0" - -[[Wayland_jll]] -deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "7558e29847e99bc3f04d6569e82d0f5c54460703" -uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" -version = "1.21.0+1" - -[[Wayland_protocols_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "93f43ab61b16ddfb2fd3bb13b3ce241cafb0e6c9" -uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91" -version = "1.31.0+0" - -[[WebIO]] -deps = ["AssetRegistry", "Base64", "Distributed", "FunctionalCollections", "JSON", "Logging", "Observables", "Pkg", "Random", "Requires", "Sockets", "UUIDs", "WebSockets", "Widgets"] -git-tree-sha1 = "0eef0765186f7452e52236fa42ca8c9b3c11c6e3" -uuid = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" -version = "0.8.21" - -[[WebSockets]] -deps = ["Base64", "Dates", "HTTP", "Logging", "Sockets"] -git-tree-sha1 = "f91a602e25fe6b89afc93cf02a4ae18ee9384ce3" -uuid = "104b5d7c-a370-577a-8038-80a2059c5097" -version = "1.5.9" - -[[Widgets]] -deps = ["Colors", "Dates", "Observables", "OrderedCollections"] -git-tree-sha1 = "fcdae142c1cfc7d89de2d11e08721d0f2f86c98a" -uuid = "cc8bc4a8-27d6-5769-a93b-9d913e69aa62" -version = "0.6.6" - -[[WiltonInts84]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "9d61cac63c100e936194e5db8613e33572fd2bb8" -uuid = "a3e2863e-c0ee-5ff6-a523-307a4cdc8724" -version = "0.2.5" - -[[XML2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "1165b0443d0eca63ac1e32b8c0eb69ed2f4f8127" -uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.13.3+0" - -[[XSLT_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"] -git-tree-sha1 = "a54ee957f4c86b526460a720dbc882fa5edcbefc" -uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.41+0" - -[[XZ_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "ac88fb95ae6447c8dda6a5503f3bafd496ae8632" -uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" -version = "5.4.6+0" - -[[Xorg_libICE_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "326b4fea307b0b39892b3e85fa451692eda8d46c" -uuid = "f67eecfb-183a-506d-b269-f58e52b52d7c" -version = "1.1.1+0" - -[[Xorg_libSM_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libICE_jll"] -git-tree-sha1 = "3796722887072218eabafb494a13c963209754ce" -uuid = "c834827a-8449-5923-a945-d239c165b7dd" -version = "1.2.4+0" - -[[Xorg_libX11_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] -git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" -uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.8.6+0" - -[[Xorg_libXau_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" -uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.11+0" - -[[Xorg_libXcursor_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd" -uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" -version = "1.2.0+4" - -[[Xorg_libXdmcp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" -uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.4+0" - -[[Xorg_libXext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "d2d1a5c49fae4ba39983f63de6afcbea47194e85" -uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.6+0" - -[[Xorg_libXfixes_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" -uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" -version = "5.0.3+4" - -[[Xorg_libXft_jll]] -deps = ["Fontconfig_jll", "Libdl", "Pkg", "Xorg_libXrender_jll"] -git-tree-sha1 = "754b542cdc1057e0a2f1888ec5414ee17a4ca2a1" -uuid = "2c808117-e144-5220-80d1-69d4eaa9352c" -version = "2.3.3+1" - -[[Xorg_libXi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] -git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246" -uuid = "a51aa0fd-4e3c-5386-b890-e753decda492" -version = "1.7.10+4" - -[[Xorg_libXinerama_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] -git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" -uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" -version = "1.1.4+4" - -[[Xorg_libXrandr_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "34cea83cb726fb58f325887bf0612c6b3fb17631" -uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484" -version = "1.5.2+4" - -[[Xorg_libXrender_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "47e45cd78224c53109495b3e324df0c37bb61fbe" -uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.11+0" - -[[Xorg_libpthread_stubs_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9" -uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" -version = "0.1.1+0" - -[[Xorg_libxcb_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "bcd466676fef0878338c61e655629fa7bbc69d8e" -uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.17.0+0" - -[[Xorg_libxkbfile_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "730eeca102434283c50ccf7d1ecdadf521a765a4" -uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a" -version = "1.1.2+0" - -[[Xorg_xcb_util_cursor_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_jll", "Xorg_xcb_util_renderutil_jll"] -git-tree-sha1 = "04341cb870f29dcd5e39055f895c39d016e18ccd" -uuid = "e920d4aa-a673-5f3a-b3d7-f755a4d47c43" -version = "0.1.4+0" - -[[Xorg_xcb_util_image_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] -git-tree-sha1 = "0fab0a40349ba1cba2c1da699243396ff8e94b97" -uuid = "12413925-8142-5f55-bb0e-6d7ca50bb09b" -version = "0.4.0+1" - -[[Xorg_xcb_util_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll"] -git-tree-sha1 = "e7fd7b2881fa2eaa72717420894d3938177862d1" -uuid = "2def613f-5ad1-5310-b15b-b15d46f528f5" -version = "0.4.0+1" - -[[Xorg_xcb_util_keysyms_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] -git-tree-sha1 = "d1151e2c45a544f32441a567d1690e701ec89b00" -uuid = "975044d2-76e6-5fbe-bf08-97ce7c6574c7" -version = "0.4.0+1" - -[[Xorg_xcb_util_renderutil_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] -git-tree-sha1 = "dfd7a8f38d4613b6a575253b3174dd991ca6183e" -uuid = "0d47668e-0667-5a69-a72c-f761630bfb7e" -version = "0.3.9+1" - -[[Xorg_xcb_util_wm_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] -git-tree-sha1 = "e78d10aab01a4a154142c5006ed44fd9e8e31b67" -uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361" -version = "0.4.1+1" - -[[Xorg_xkbcomp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxkbfile_jll"] -git-tree-sha1 = "330f955bc41bb8f5270a369c473fc4a5a4e4d3cb" -uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4" -version = "1.4.6+0" - -[[Xorg_xkeyboard_config_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xkbcomp_jll"] -git-tree-sha1 = "691634e5453ad362044e2ad653e79f3ee3bb98c3" -uuid = "33bec58e-1273-512f-9401-5d533626f822" -version = "2.39.0+0" - -[[Xorg_xtrans_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77" -uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.5.0+0" - -[[Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.2.13+1" - -[[Zstd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "555d1076590a6cc2fdee2ef1469451f872d8b41b" -uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" -version = "1.5.6+1" - -[[eudev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "gperf_jll"] -git-tree-sha1 = "431b678a28ebb559d224c0b6b6d01afce87c51ba" -uuid = "35ca27e7-8b34-5b7f-bca9-bdc33f59eb06" -version = "3.2.9+0" - -[[fzf_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "936081b536ae4aa65415d869287d43ef3cb576b2" -uuid = "214eeab7-80f7-51ab-84ad-2988db7cef09" -version = "0.53.0+0" - -[[gmsh_jll]] -deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "1e7fe5c8dbe0e911931a18cdfbd2c7a1e01b68ef" -uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" -version = "4.13.1+0" - -[[gperf_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "3516a5630f741c9eecb3720b1ec9d8edc3ecc033" -uuid = "1a1c6b14-54f6-533d-8383-74cd7377aa70" -version = "3.1.1+0" - -[[libaec_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "46bf7be2917b59b761247be3f317ddf75e50e997" -uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" -version = "1.1.2+0" - -[[libaom_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1827acba325fdcdf1d2647fc8d5301dd9ba43a9d" -uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" -version = "3.9.0+0" - -[[libass_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "e17c115d55c5fbb7e52ebedb427a0dca79d4484e" -uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" -version = "0.15.2+0" - -[[libblastrampoline_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.11.0+0" - -[[libdecor_jll]] -deps = ["Artifacts", "Dbus_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pango_jll", "Wayland_jll", "xkbcommon_jll"] -git-tree-sha1 = "9bf7903af251d2050b467f76bdbe57ce541f7f4f" -uuid = "1183f4f0-6f2a-5f1a-908b-139f9cdfea6f" -version = "0.2.2+0" - -[[libevdev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "141fe65dc3efabb0b1d5ba74e91f6ad26f84cc22" -uuid = "2db6ffa8-e38f-5e21-84af-90c45d0032cc" -version = "1.11.0+0" - -[[libfdk_aac_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8a22cf860a7d27e4f3498a0fe0811a7957badb38" -uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" -version = "2.0.3+0" - -[[libinput_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "eudev_jll", "libevdev_jll", "mtdev_jll"] -git-tree-sha1 = "ad50e5b90f222cfe78aa3d5183a20a12de1322ce" -uuid = "36db933b-70db-51c0-b978-0f229ee0e533" -version = "1.18.0+0" - -[[libpng_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "b70c870239dc3d7bc094eb2d6be9b73d27bef280" -uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.44+0" - -[[libvorbis_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] -git-tree-sha1 = "490376214c4721cdaca654041f635213c6165cb3" -uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" -version = "1.3.7+2" - -[[mtdev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "814e154bdb7be91d78b6802843f76b6ece642f11" -uuid = "009596ad-96f7-51b1-9f1b-5ce2d5e8a71e" -version = "1.1.6+0" - -[[nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.59.0+0" - -[[oneTBB_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "7d0ea0f4895ef2f5cb83645fa689e52cb55cf493" -uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" -version = "2021.12.0+0" - -[[p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.4.0+2" - -[[x264_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4fea590b89e6ec504593146bf8b988b2c00922b2" -uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" -version = "2021.5.5+0" - -[[x265_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "ee567a171cce03570d77ad3a43e90218e38937a9" -uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" -version = "3.5.0+0" - -[[xkbcommon_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] -git-tree-sha1 = "9c304562909ab2bab0262639bd4f444d7bc2be37" -uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" -version = "1.4.1+1" diff --git a/examples/Project.toml b/examples/Project.toml index 6c64e382..0137e7e8 100644 --- a/examples/Project.toml +++ b/examples/Project.toml @@ -1,5 +1,8 @@ [deps] BEAST = "bb4162c7-ba94-5a20-af32-d8ec4428bdd1" CompScienceMeshes = "3e66a162-7b8c-5da0-b8f8-124ecd2c3ae1" +Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" Plotly = "58dd65bb-95f3-509e-9936-c39a10fdeae7" +PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5" +PlotlyDocumenter = "9b90f1cd-2639-4507-8b17-2fbe371ceceb" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" diff --git a/examples/efie.jl b/examples/efie.jl index aeb5223c..6f699e81 100644 --- a/examples/efie.jl +++ b/examples/efie.jl @@ -1,21 +1,71 @@ +# # EFIE +# +# This example demonstrates the modelling of scattering by a perfectly conducting +# sphere using the Electric Field Integral Equation (EFIE). +# + +using LinearAlgebra using CompScienceMeshes using BEAST +import PlotlyDocumenter # hide + +# We call upon GMsh to create a flat-faceted triangular mesh for the sphere. +# Subordinate to this mesh the space of lowest order Raviart-Thomas elements is +# constructed. For reproducibility of this example, a pre-constructed mesh is +# loaded from disk. + +Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) +X = raviartthomas(Γ); + +# The integral equation can be wrtiten down in terms of the single layer operaotr +# and a plane wave excitation. + +κ, η = 1.0, 1.0; +t = Maxwell3D.singlelayer(wavenumber=κ); +E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ); +e = (n × E) × n; + +# To write down the final variational formulation, placeholders for the test and +# trial functions are required. + +@hilbertspace j; +@hilbertspace k; +efie = @discretise t[k,j]==e[k] j∈X k∈X; + +# The system is assembled and solved by GMRES. When this processs terminated, the +# solution and the convergence history are returned. + +u, ch = BEAST.gmres_ch(efie; restart=1500); +@show ch + +# With the solution in hand, various secondary quatities can be computed. Here we +# query the far field pattern, the near field, and the norm of the solution at the +# barycenters of the triangles in the mesh. + +Φ, Θ = [0.0], range(0,stop=π,length=100); +pts = [point(cos(ϕ)*sin(θ), sin(ϕ)*sin(θ), cos(θ)) for ϕ in Φ for θ in Θ]; +ffd = potential(MWFarField3D(wavenumber=κ), pts, u, X); -# Γ = readmesh(joinpath(dirname(pathof(BEAST)),"../examples/sphere2.in")) -Γ = meshsphere(radius=1.0, h=0.1) -@show length(Γ) -X = raviartthomas(Γ) +fcr, geo = facecurrents(u, X); -κ, η = 1.0, 1.0 -t = Maxwell3D.singlelayer(wavenumber=κ) -E = Maxwell3D.planewave(direction=ẑ, polarization=x̂, wavenumber=κ) -e = (n × E) × n +ys = range(-2,stop=2,length=50); +zs = range(-4,stop=4,length=100); +gridpoints = [point(0,y,z) for y in ys, z in zs]; +Esc = potential(MWSingleLayerField3D(wavenumber = κ), gridpoints, u, X); +Ein = E.(gridpoints); +"" #hide -@hilbertspace j -@hilbertspace k -efie = @discretise t[k,j]==e[k] j∈X k∈X -u, ch = BEAST.gmres_ch(efie; restart=1500) +# The results can be exported or visualised using e.g. Plotly +import PlotlyBase as Plt +tr1 = Plt.scatter(x=Θ, y=norm.(ffd)) +pl1 = Plt.Plot(tr1) -include("utils/postproc.jl") -include("utils/plotresults.jl") +tr2 = Plt.heatmap(x=ys, y=zs, z=norm.(Esc-Ein), + colorscale="Viridis", zmin=0, zmax=2, showscale=false) +pl2 = Plt.Plot(tr2) +tr3 = CompScienceMeshes.patch(geo, norm.(fcr); + caxis=(0,2), colorscale="Viridis") +pl3 = Plt.Plot(tr3) +pl = [[pl1; pl2] pl3] +PlotlyDocumenter.to_documenter(pl) #hide \ No newline at end of file diff --git a/src/solvers/itsolver.jl b/src/solvers/itsolver.jl index 0f23e4be..ed4cfd37 100644 --- a/src/solvers/itsolver.jl +++ b/src/solvers/itsolver.jl @@ -102,7 +102,7 @@ LinearAlgebra.transpose(A::GMRESSolver) = GMRESSolver(transpose(A.linear_operato -function gmres_ch(eq::DiscreteEquation; maxiter=0, restart=0, tol=0) +function gmres_ch(eq::DiscreteEquation; maxiter=0, restart=0, tol=0, verbose=true) lhs = eq.equation.lhs rhs = eq.equation.rhs @@ -114,9 +114,9 @@ function gmres_ch(eq::DiscreteEquation; maxiter=0, restart=0, tol=0) Z = assemble(lhs, X, Y) if tol == 0 - invZ = GMRESSolver(Z, maxiter=maxiter, restart=restart) + invZ = GMRESSolver(Z; maxiter, restart, verbose) else - invZ = GMRESSolver(Z, maxiter=maxiter, restart=restart, reltol=tol) + invZ = GMRESSolver(Z; maxiter, restart, reltol=tol, verbose) end x, ch = solve(invZ, b) # x = invZ * b From d99d2e22b149da862504683b24704cf8e8a8a541 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 10 May 2025 00:55:01 +0000 Subject: [PATCH 497/528] CompatHelper: bump compat for SauterSchwab3D to 0.2, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index db138be8..ca8eef65 100644 --- a/Project.toml +++ b/Project.toml @@ -50,7 +50,7 @@ LiftedMaps = "0.5.1" LinearMaps = "3.7 - 3.9, 3.11.2" NestedUnitRanges = "0.2" Requires = "1" -SauterSchwab3D = "0.1" +SauterSchwab3D = "0.1, 0.2" SauterSchwabQuadrature = "2.4.0" SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1, 2" StaticArrays = "0.8.3, 0.9, 0.10, 0.11, 0.12, 1" From d92bebbcfaf31f40c0836de7c712659ac0ac2a8a Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Mon, 12 May 2025 17:09:01 +0200 Subject: [PATCH 498/528] localop assembly returns sparse --- src/maxwell/qlmwops.jl | 14 +++++++------- src/solvers/solver.jl | 7 ++++--- test/test_directproduct.jl | 2 +- test/test_local_assembly.jl | 26 ++++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/maxwell/qlmwops.jl b/src/maxwell/qlmwops.jl index 87e3d018..77361f34 100644 --- a/src/maxwell/qlmwops.jl +++ b/src/maxwell/qlmwops.jl @@ -62,10 +62,10 @@ end # Γ = meshsphere(radius=1.0, h=0.075) import Pkg - @show pathof(BEAST.CollisionDetection) - for d in Pkg.project().dependencies - @show d - end + # @show pathof(BEAST.CollisionDetection) + # for d in Pkg.project().dependencies + # @show d + # end δ = 0.025 γ = 1/δ @@ -75,7 +75,7 @@ end X2 = raviartthomas(Γ2) qs = BEAST.defaultquadstrat(t1, X1, X1) - @show qs + # @show qs @time Z11 = assemble(t1, X1, X1) @time W11 = assemble(t2, X1, X1) @@ -98,6 +98,6 @@ end Q1 = assemble(t1[k,j], j∈X1, k∈X1) Q2 = assemble(t1[k,j] + t1[k,j], j∈X1, k∈X1) - @test Q1.A.lmap isa SparseMatrixCSC - @test Q2.maps[1].A.lmap isa SparseMatrixCSC + @test Q1.lmap.A.lmap isa SparseMatrixCSC + @test Q2.maps[1].lmap.A.lmap isa SparseMatrixCSC end \ No newline at end of file diff --git a/src/solvers/solver.jl b/src/solvers/solver.jl index 2419d8d7..bdcf8b0a 100644 --- a/src/solvers/solver.jl +++ b/src/solvers/solver.jl @@ -233,7 +233,8 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; if spaceTimeBasis p = [numstages(temporalbasis(ch)) for ch in X.factors] - lincombv = ConvolutionOperators.LiftedConvOp[] + # lincombv = ConvolutionOperators.LiftedConvOp[] + lincombv = ConvolutionOperators.AbstractConvOp[] else p = 1 lincombv = LinearMap[] @@ -259,11 +260,11 @@ function assemble(bf::BilForm, X::DirectProductSpace, Y::DirectProductSpace; y = op[end](op[1:end-1]..., y) end - a = term.coeff * term.kernel + a = term.kernel # qs = quadstrat(a, x, y) z = materialize(a, x, y; quadstrat) - Smap = lift(z, Block(term.test_id), Block(term.trial_id), U, V) + Smap = term.coeff * lift(z, Block(term.test_id), Block(term.trial_id), U, V) T = promote_type(T, eltype(Smap)) push!(lincombv, Smap) end diff --git a/test/test_directproduct.jl b/test/test_directproduct.jl index 49f63deb..2fff26f4 100644 --- a/test/test_directproduct.jl +++ b/test/test_directproduct.jl @@ -31,6 +31,6 @@ for U in [Float32, Float64] local BilForm = BEAST.Variational.BilForm(:i, :j, bilterms) local BXX = BEAST.assemble(BilForm, X, X) - @assert BXX isa BEAST.LiftedMaps.LiftedMap + # @assert BXX.lmap isa BEAST.LiftedMaps.LiftedMap # @test typeof(assemble(BilForm, X, X)) == LinearMaps.LinearCombination{U, Vector{LinearMaps.LinearMap}} end \ No newline at end of file diff --git a/test/test_local_assembly.jl b/test/test_local_assembly.jl index ef015c1e..860a069f 100644 --- a/test/test_local_assembly.jl +++ b/test/test_local_assembly.jl @@ -45,3 +45,29 @@ fr2, st2 = BEAST.allocatestorage(Id, BC, RT, Val{:bandedstorage}, BEAST.LongDela BEAST.assemble_local_mixed!(Id, BC, RT, st2) Q2 = fr2() @test isapprox(Q1, Q2, atol=1e-8) + + +@testitem "localop assembly returns sparse" begin + using CompScienceMeshes + using LinearAlgebra + using LinearMaps + + Γ = meshsphere(radius=1.0, h=0.35) + X = raviartthomas(Γ) + @show numfunctions(X) + + Id = BEAST.Identity() + + @hilbertspace k + @hilbertspace j + + + Idlk = Id[k,j] + term = Idkj.terms[1] + dpsX = BEAST.DirectProductSpace([X]) + + Z1 = assemble(Idkj, dpsX, dpsX) + # T = typeof(Z1.lmap.A.lmap) + + @test Z1.lmap.A.lmap isa SparseMatrixCSC +end \ No newline at end of file From ee99c7b187c02b6ca895c3cf5dea38e096daa470 Mon Sep 17 00:00:00 2001 From: Kristof Cools Date: Thu, 15 May 2025 13:21:18 +0200 Subject: [PATCH 499/528] quadstrat is passed down to assemble! after multi-thread work balancing --- docs/src/tutorials/efie.md | 23 +++++++------------ examples/efie.jl | 19 +++++---------- src/BEAST.jl | 1 + src/helmholtz3d/timedomain/tdhh3dops.jl | 6 ++--- .../strategies/timedomain/nothingqstrat.jl | 2 ++ src/timedomain/tdintegralop.jl | 5 ++-- test/test_local_assembly.jl | 3 ++- 7 files changed, 25 insertions(+), 34 deletions(-) create mode 100644 src/quadrature/strategies/timedomain/nothingqstrat.jl diff --git a/docs/src/tutorials/efie.md b/docs/src/tutorials/efie.md index 3e6ffac2..a1abc7f1 100644 --- a/docs/src/tutorials/efie.md +++ b/docs/src/tutorials/efie.md @@ -80,26 +80,19 @@ Ein = E.(gridpoints); The results can be exported or visualised using e.g. Plotly ````julia -import PlotlyBase as Plt -tr1 = Plt.scatter(x=Θ, y=norm.(ffd)) -pl1 = Plt.Plot(tr1) - -tr2 = Plt.heatmap(x=ys, y=zs, z=norm.(Esc-Ein), - colorscale="Viridis", zmin=0, zmax=2, showscale=false) -pl2 = Plt.Plot(tr2) - -tr3 = CompScienceMeshes.patch(geo, norm.(fcr); - caxis=(0,2), colorscale="Viridis") -pl3 = Plt.Plot(tr3) -pl = [[pl1; pl2] pl3] +using PlotlyBase +plt = Plot(Layout(Subplots(rows=2, cols=2, specs=[Spec() Spec(rowspan=2); Spec(kind="mesh3d") missing]))) +add_trace!(plt, scatter(x=Θ, y=norm.(ffd)), row=1, col=1) +add_trace!(plt, heatmap(x=zs, y=ys, z=norm.(Esc-Ein)', colorscale="Viridis", zmin=0, zmax=2, showscale=false), row=1, col=2) +add_trace!(plt, patch(geo, norm.(fcr), caxis=(0, 2)), row=2, col=1) ```` ```@raw html -
+

PS>8Db()LeM?>+gA1ZnTn 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 z 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

PS>8Db()LeM?>+gA1ZnTn z+Jn8I-y1a&cn$Co*TSVyYR$Xrx+h~#Ww^Fj1&QjkihzAO-NZ!4FgV920S_ugck+1N zZ0R5L;5nwejLIO!pwb9;)&E!vI1z+FP0J#oNlHJ?n(RyJE277z;ZV5?#4X{8R`XMZ zY^lk>V$Y?2Vd~}|+IaLj1Hwq#8a0a?WA2~(uguk5{@x>G@e3C zZ$1Pg$2Qz*{4|ds#*S;k1RwNVkD9-}D*1owb%-Vy-o~tsd!AuCKh1gMxZP3XpH~X~ z*1Uk&fJJY+fvALNNWdxYe+@f+rfc8wcZ-B$#q$ODGE3rIdhNntn9La0S3~D`7{nu# ztj8k#odSiiK-=&4@WT$Ow;`{`%F7(*JLd{4H_Wt(U6i<55`$CP3fR6Fs?ll8P|h0- z3cxO)&d8qr*C#BG9ob39;7t;kgiCx2Ccz&kvcxC|(0=D+-Dd8S*vE}5Oj8IJW@C?C zFpO?weIJ>x!}WkySJmgK78eIMPPwx=P#4qm|G5B#+20+bou?6lw!a;MPYZ>8*-6yM z{JU%NWmkkhzB+axEEg$H8+mWScfG_3k6-qNin%jyT>LfN+xw6|yGphv^!Wwv7%s~n zp)@2S*ZX6+y~wVYG&;f^EZ%D8dkO6WnW~Sk4Rm3>>4<+6?mr@Bb+Va6nA0n?e z!oiGH>$npbc*w7PVrN7&-%4jk_D5BS0y2XOtSb|{WrZRP+MXO@f)_~=Evgs&k)Vw( zYfXtI9kMtQvGgN-5th-wH9kK#HVLL$1{~IPWL2)GPFQp$awr->w-5BSl3)a3F>rSVY=vk-(AWq8A2Uh8f&`o?iUkUD(MG$e=i5UUO3iqO)DOY zhm)@_V*fi5`2KeoR_XZ~-H1;1NAn7xR4uZ5VnpJ-iw7lIZ!yIiewRmw86K7D601%2 zJ6pm@GnTe&VFILy=7Rmm@T_+*ei7oMERMjDYq-1kw#+~ z4}H&(|G~I+A^)3E)Ec=|PWe6e@l09x%#z7^#pS%Fdo)6VL#_OZ*T6hFc?S+7qY(yw z+K%Wu=VYjem7Dz;_(gOOR~p3T@z8f64XpP zhC#Ng@s+xLn|0=lH}*CDd(U&7!^cUzvnhR|TB9F5Sz6=h7kE- zh-0v-uxP|5)z42bMe#$Qf!i!1^@H2Qmpofh9dG}EVDmp?HO+6e%{4dpl(6@)P_j*C zpj|#1rDe{Etg;MjWU=-rx(peVWSqCXemFSW^bhZf!Mf({UL3LPf@KYIq%jpCr&Qv`PEZ_iWUYjG_NTGpO4c(-tM!k`5|>Se_et7S0fOeW zc&X_r+QB_?(k5zMt8aN;ftM=G<4Beo2Ux?>S~yzvwDsGXbEQMw*rOEyg(qJR20Wa{ z`F*~WiPO{}EGZ(~+Z_H!Bl7rH3IS5DI%9YhGRO{bt7jTe2!xj+Z9bQck%6eg<72a_ zw*nZ8h@g&65X86OEf>X$h*(5Pvg^6xt@3~Dy1xIc+D&020{3VboEL*uVp*$M5t#gQW^ZGr z2ke*C?X0}aF{&U`E3qGCGUZ)Lh!GouE3ZM{qtZn-fSq5%- zqC3GpeU^9e%gbYJ0oUT6lxTF5dw%?@7!ilqHRhTDZES(zXnVW|x{NG0(>7qN^+&L)K6J=@%P5fNn%;lqvRxSe|_^JZfzwz_Cr*VcJ4@i2c?yZRJ#-%pa>0yeHP}+=)n-BJvsu z=}+LB4*o$n)~?G69>!gHiv2vmE2x~b<1JJHUaZP%0W1nlT5BNppJB)W%+_(w!OKYf zw@8CrBFlmATTx2l{=lllN1}JJ)b&a1CTmU`KGl)KIqIo?_BMjnGmhK8dBJ&h`YdxI=WnGdl+fPdaNQVU8hk}I^| zM56ZP6iYw+#Nqa-%Ca}$UEU5{yv(f1m)I(?u@&4b>#RPY0>G?_`O0yQ6?1hi8R>mp z8tw;ZPtEJatae}q5$J8jphRQf@>Od>k(Vyu?v%E9KkwWkXfSKri<>;}*TwO@Mw)E* zUlp63B5bMExrp4!2DT1(TaJ~vpYY~?yPCGh%+nM+erV;B-=!LwDGHl;eD*P^c{IA~ zw{1cih#+dxP{>@SnLgg6@@u7l>0@3NeVymX7TkYv#piuQlZW;4F5;oJ7#$A~GC_oj zGQ5q2w3gAX2WXCIfifBT?gdMk2rH{BZ2p~gHTd=IJvckK-VpJhM3n*e%wdRmcdw9iM%+_x1`{@~0da3G zA$|zm+>5EUrsyHy#m#-%-&o_r#sS?CtaSbH{q00|5yTmzCsc_>TqL1HLZJE;VLHV+ zRXxqW&FW`X-a|{ds|qC^EV~3c&(KgAT^!xns{}||oHmg|OK^?~fd)yk3DVWDbJtpm zucrX$h5mDYVdgsheOvc!16)<#+;vSw!1(H4xNkBIxn#b^?p`ENGqHkl=CnM@aFK&K z8-nxZH!M&cZXf)iVcdAEndoT@hLF4)E~j=|#TDlNW_uQI?#bRY?zcvJi*+X3GL))Qa$sJ6koEyA|KOhy_kPCtvF$5> zSnE`vBIG%8e3%_FM5F0Y>pQh+uSTC_euP0(r#(;HXZ$xRA}zChn^mwi5tbuO-{ci}1n0w484-c=-KTjUo$lJj z8}vn4*e+|}fn64b_(dBxP3vz;U3X2wk2$Zz>ZNXX0%DY9DI3in;gx`>OC4?7P?GoN5^# zCsIjTqHl&%Z{t-xl$bL3&DDy^9uZ4dTLA7kqo>pEHhO^){wH^gWF3)Hai29cOPwe! zR1?*Zec>|sjYQ(l-IvfE$j!k+;o+5q;n0NbvtajkfgpiTZR1l+UDV51O<2a;-GL@) zOyabX3xAQwN+&~#scyUicdZMdOALQ$oW%I((nyU?=ri(Yh_CsSNkDA9^~l^Bf_AN@ zn#uil+ywlM15qz@Wd5u~z#Qu+ZdlgwWJ8aSX#O)P+5T~6kZFa!Xc;rX#yxkVnD;Zu zLVtnl)Ztv~mBRX47JG+??VU0{o1RVgT*CLaI+}&jNJC>eQL`S+)e%s*<4|zpI zg)_Ost0r8cH3b7doeRA$T4xomccpMHCvrc>Kz6FCS)CMPLcnld!ln+(M=-yd(Rh@p z@M#cNTCan{%TZmIcSCxw|B2Rb`q-uY2BINO>y?>+4Xbjj#H;UQ0WmZoJOXJysBjaO zQu_98M~<&GtXNS(w$+NZZpK{m26LAI|rW;Tb*HKH}^?M7fyfn0^28tlL~|ygy)A9u*6A zXwRcYm#&wCC(4TETA2OC-jK(Ae;9}lSOn)dxU8Q3^HKb{TEYcM;?XOgA2Y>6aaSAJ>P34H_}FgvsI8|#ctcbGjKxOnZ{Hf+B((Auql8qFc*Hb&0*}Wfk5Fw z;{X?j?73{@6Y^07RNi+OQSVNN*zuHkC;zM8v!9UR;zgNuTEdY;!iGgSC^-k^>4}hG zmTt-!$@PGP=wPq)6z|t!NpY?g^U@!(p%^TGQ$$0Pa2c#%!Y564KWYF?cL1qPiZV#< zY6!m{e7mJG74%pg1a8yxA?kcTB$Bd^cF_?igh;k!`Ms);!jq5NG0}m0 zC+Y|gV*z^>&pnfNdHl${+`vy2hy~`atDjl_d+1(CxXZu?MV|s!HyAc?1*(*4-HC)$O0KwFy#P+^)COi$jiDIN>o8#jmdHxtTRYCQSW~TSw;yr`!(-y! z^!EuKlJmsia|asDIfJt(>}efyPcZ-u`?yIm#>RD zNoBdo!j2VGw|T*2q93;i9z1IX!o-zm3`IsTAY8Bw<&G|Ax>jUIoo`9yJqn3|)AO*3 z@ZDtD_$u3u2Hzi-$LvW2MciegeEtCy(yUJ#;FPDW0bU0WC|!7Z6Xs@(CAyWJ_djQY z(6;fQ$`}YW#Lq2xw=_gE-BgkHVRii(cP|yq$&XqmSFlrbR0Gs?@zfc@f(+m@mH9aN-2nDw;ghZP7mQ0yk~9uYqc+{);)ZJ> zL(I3s&XElau-RwCAvl*cEE~svh7^wI5pCdD*5-S(+Hd;vg*fC1FMhth=P;d?Byy@h zNzms1sP|a1`(-g_Vv8%d#E++LT1qNv!?0hoH8=}0(xHN5`*;;`o@v0b%_ZaAPBySK zR0o@Rhx9wxvcE*!wAT8AIGNTsR1Gj{LpN~6 zV0MSX#;M{@@5<+jhr!M<(dYh2LbCqAc#KF-Se8!ZiTO9E7U?=|VPVWzTKgGSJT*jB zC%$Ho{s+kvegFLcua7;m3LOs6nCOTIF*b%%UW2Yj<)t`>%hR~*50xcIVaO#N&=I2~mrP)8eOk26bIF+McUS(iN_z&joy%el0T-d~;ov;y|DF^d(9*H#w}J8|*fDJHCdafAz)A$dv@I4BuE)=m zpT58_(FybTY6k+;>cnVLZ;P0fz|B?e@1<%4eXU1 zC+4NdnUYi98^c9TYRy41GCUNAyH<4!3-(g;Ay8DSX!O|k?0j`odqGoLHM@4Z7>T7Z zYdU;03MW6M+%u)4)6+&i^iS_j+Xp(?X&ODglXbi+Co6(It=e=0JaC;xQ$Lfpy28N` z9QU03`LE511AhulqNJfdA*Yf(d!kb7`DKb_Jgn{ojwVUYLSr#~g}EM^Bp?9>W3^C6zT+3}DeeJiqd9&ha_=Keo(u={wg`5;6rgYZR)1 zKK7;pJfPDMxrOQQK@wS&?pi}aG9hekWtw@rBWi{h&G zUM$a6$xs119`G~R#gWX`_P%I`fHIq&0MsNR^;{%S|mT+@t9kz)v|zJK-3N=7fTJAYD7}kO;P?}(4Ay{!8=~{r$SDwN513no@Oe) z{0yPSyspr z=jA`tOB>QJOd)7O)KJ|x4*j{@`SCo7l;!a}&#$JH`7R6GCGrJY#PpS+3F}gt4UHPfTR!gb4Pq0qSvURv2r2P~c2UqbUdODq(y%c~lIe zrtAr914?M>=ko?tI8+F%KjwZON_HSVZgy})gS5mxg){J5ir!_K{Q>Jb{{D9DEc%?smml^bcn|a|smHh-5yv4d! z;xxc(%2fK&*izvZ!rG2d-?JEYN?xz%8f_~NzWme8!7#?bBH=S~Mf0l7G5nCvG`Zcd zR14|9T#=BBn`xL3?g*VBBa4E~+O#9G{^}!!wZDlS@3Rs zxiedlbN|KRrC=-|cpY9>CjNjR7}mx_Xf$2J=|w zypKozfL%by&HYK3d-xT=S1F-cx)HN(LwX)+i#Gu_3AfL;)O23+=HL_I??y*)>O`Jy zmFh{7d>cTLStbr}1U>$4*_iG?+_fD{ym#k8692cm&FQJo#Fgyj`g=tIH_beEUj~;U zvROX%>T$(a5?Py@xu#`x={gNZXY3byKjR+WlEy%4tki73H2aZa(1S7gkZ6SaxHTIC1f_J54JWB~hyU8l1mLu)O$Fu(-~PP@EOKm)`1Zay>$!4L zO2a4h@KBb>{27|!55fy^9V6MP$7&))^Bffux4Nkj)=CMrBfsD85U6e`-q& z9knN$CP3d_w|3L z$|}V$p^JO2Ei{UpVBIq*%K@2!?06Nrn^?KARF3fPOXke>r*yAbzTIt};P75bzJCZx zL{LmTDur;N3@+(|^wqXMm7WL=QJbHqv2g!Up2kul?LMlA)2Jw1a{jmeoO7@r;TDptFYI_< zY%a}19drM^I!(<>3+93+d4g-wUsip{3;ELMLx5iqT@X#%Q}1)$o10d4rcVq9jWqn- zbjOL^d4%vB)pZd_u%X>;YFyQ1ps$^z>Dich_q=~sXGsQ5bao()S1CP#cCU5PGJ_l z++5#G@bs^9yZG~#t33!CpB&pF#LJ081c3;H%c=d5&xd2*%6E{YOB~e#O1KY(u~aCM z|GeU4x6&@{W;3vp!zWN5ZZ96*c`k%qpm6e5HYj#YmrU;2+rT`4EGD5t3C>+dQS zqY+dS+UIT#Yrdd&sSOmLTIZsDq~*2?J!U1xq33F3O+Ey#`$iWSK$S8R*S+nc+rBaZ zQG=hz+q-pdUDegSdx0lkU5t%elu7?v3rv_K)za9FKOJCW>NJp19WaELSo z)l;(nq+k__JgO1)`oX)`)69cAJ;{;=N2=qm|93ChJUcwB<_Kk@q{ z?_)4$jg}rpGyF|7&$gku#~Wc*a$Z5m?^0dprOdzkB9>MeOQdqjE;}#R1 zr2NRbqt_=1Q2k2OiUshY8|Ip)2jCLHIO7prmB!3kHHVMyL{ch&zkyG37?yy8pV%Ub z#c|Y3q9U*}0H^YPLPHDH({z~**bWSIj)6d9<` zf9+8C+T^b9q<`VNTrb0cu6lm2`2V>8(Zw;ASG~s!V4u|)@%{~9l1<)4Oy#OO*02GC zR$8TIdR^1QXw{n@D>E}e`^AS^F>k-9G_9caPFICytJx?}@~?c8jw^WBo{+Xjs1t5K z-bUM5T>0&MKwwldE=FMzmcT(7G&|Pao`+3 z6;Ua>`yhEJ^$cSJf3%j1$JJrX3CcH#I3V!#m{K`m1W7_pzHKhluI8@z#<-kuU*Hsd z-D!-VAnl*VAl9+&zW*co7>KE7{Gh|OA@`IxDbyi+zt*ErmRS&O&A`4 z4>_T;CeGAF`_KnAHAw&6XsFJqz)iVAGc?e6i^tjsOG9uRIMc*Kq1vYTo5cNIK_%9C z?HEtFQ0Anw6L%wG4!?$2bd_JNwZ3jZ;Py%Qu4o$r8hfb_g7=a>detYZcn;pX*x+~I zD}n32Q8EliWrHU%X!A%qu^)b!2dh$iD-+1;ywA5+>V*yPmWb9sJW@XDTKf z-$DE~riex0EpdpE<1MGd$~VDSjrMdX(%2GAMLe2agqZOTusH}nCyWAM@z}qr6k1-W z-oFJf)BY`wFCr%R-L42sX1z`MM}MY=UKC(Pa(9#bZmsO7nxbaE6$kY~f601_%si4UO=O z{?_lT-Q_MbV~6QaB7>Y@-oJ@Jm`DcISEuh}1#|eCLg9u)Qg5z7@99LJhUuFU+bTLH zye!5m=-8zG!3_^b4+)(5A}Pq8&R0o08B%5~y~}m5sUn-0P6*)ZbgVN@*CrBqI(LCqgDN3OB5{y`RwE zXtU5es$8J$qpE+$+Ic@ijss|OY$*`qE2i&Rk7)3|6OyZy#B|PcXTugF_?z{g;3A%B zim7Hfy+hso9PM$8C_K+41m1q^)$gbYdp2};f1$QyT!eN0b<-v=nj`G?JqM@oWz8hg z4Bv}Uzq9gl;9j;!14&;rzrGkCiX1UR0Qi848l)%<%hVXGMT~E@2x|oWj1RyDn(9=p4ZBuUz&9?M!wp8k&kRrP~+Zuza zmA@gog#E&tOU07eL?=5m_QKOA1k85Pj z1NeGEm!SU1s3N+k2lv&*_aI_|Ga^DFsT>bgzjlTu5BPaE&^tODG=Esd7!z`hk}J)w zMgk`V884%Ve%2ynPezE3{D!y+BwCW~ zKA|v9dwV3?#}D92P!)w_m=jH-WEkB#rE@|+`kX8A0s|%(Imqk{F_---z?%Q*u>Qlg zC^J5Z!Lrb>JKz4e0}LH)g(REji_4G$1xPmQeniQpIcWgsl0HGZv7xRYr}s zt>Zb(&E(;+QOBtxNDQd)T~3Vu(EqS`Yv464+RFp2v9a| z_&_~_=34shi@7g9xuYZrgr7dXmH*$3QnuVAd>>$K>NxU{6L9|`xYVik>@<*#IG8BA zH%5nxyIWQ?=r8bIBsRnhXimo?Lc-I&1QR6XW@?Al%W+-*qY!ZvqNo%@UMRN|J<_Y5 z4{dR|945nSMA%mzYtC?3rvSzA4+XK8khuqcsRM^jAt>dta8B&{csL;zTXPuKG)Ajs zLsed?BHM)zOEop%(9$WV*rxfZB`Rk+yH`YGD{o$nM&NT|v2lV189dJB4*^Vg5HlR` zK$ztGR%>|(@>_u!Hr$yY*??GfS)Y7m4UBqmPSo-nJJFH`sS&HkO{{yeAsdzL6wRyL zYUZ>p)ZxkewB;zo?^^2BlXa90f8*ZWs{hLyf5S}{_g>(04~lnxXT*^Of*xZ}Q1s=o zdo^wO@`qd+3#y^&%~>+wNd8*oH)h01FQ?x**|ejG@X|QI`+_SQ7e9hpUY&DTNb1A+ zgRu_qY$*X16xZYJY-c<+$?p#a!+mMtxSRGek z%4(l|4SG?LcKt-`kR4to0MzP_K1N9}hSih#3sugXmO)f%!YRuBb93DiBweyIGU|h> zrtqL2rSYwtsIj1K+6MSVl)b}y(p~ff2mK%Bt6&Jw8?e8F9e?Q^@1tCae&zwdJM-WMttYSCDNv!xdWh~I7V6$#isnq(%D&5NlLw{a0J8*r z44Q;0FPWsdfAxNU@~A3vnrMKdC;Qau;m<}2W*r~}Y{Mw#>M_E1W19=y?{xn{WpC1W z$|;Hvp)GWQ_voKT`X3(thshrweE<=%Wz$ai9Y<76=`|}MlIpMpxs2~q*rKh}1Li%t ztKlhCNoD}(!Gn0F6g;A(N&92G@w7-U3!?VRojBh3S`ZmhbX2fx#M(;7U+}(=1 z+moKxd;fqpU-B*4-H|NOWzUX!#tEu85Z>Rs z&nE;H3(K_2+ly%Lf+;hwpPf-kz0^#Y3a4G zUCY$tAAc{Sjs_V77{My{7D`d1t&Gz=P`JU_fyF(v#kb+FH-oH_heSB}l}G$`YD3*l zec!0e?c?a(*=S@5D`T7HY5CgZB^;mri0`vW##Y$^< z!yi%xYFG|l7MvQKax#?uo_2>X`CKUGx+;oo%25S>q-S&Vw;j2Qk-SA1J61kyMPOI| z;D~vT4WD6x4%tL_j}(s_v{mALw*OR%&JxE7Qd?F+OR%U-;6>1?^XaH|==6e0HAh8< z9)?~>BfV8cOz?#kR{Se3ZNl&(gFj>9E+qvdBqA{giCRmvv~0;ddLTlS+wqG79;vj1 z{O?=or9_&3puCF53>vp@N^O*1n(>w@s?VUMxCn!W{(g6SjVZSG)j=6~imkCDtZ*ch zhH&z}g?s`YN8`V~wfUqFTKB68Tix_XawgI$0hz^3v?(1MkiM&BliV0yyfaNl- z@y-BCP+%;yRuc$CI>e_GHcAL}j$D3zfbd4j(_m6uo0BLsST*SS%)TY|{To@4RTq1n zuD=38nrty~L0bl*-Bs0lKi9lmoX&0IzxQ$fW=RA7^mg#KCSto$3p-C2d>B#ihz*gS zg-^72+W@4YlMxxbbj;Adb`1QCqC7+RYe$?xNr#3q*FVnpW$NL!N%Fb}uIYO4T-w8<{1@A=kC#0;Oq`;&NQG$jKcct0JLVe%&(yp7MI0-zNXivpkgE#F+b zJx!bpua>8Ev+3z-DaF?c2o8CtoMohPM`MfHyoA1Mt`Q~jLym({?EV8BA=8X~iw`VJ zsl8H}D_&f?`zWmRg1C)tJ0>#N8!k_wP5VS-+15d*6E@B74BI zMPZ7QVU=y;8egXfIe%E_y~d2vks-S+rkXn_Sg&N1F$=Ed>JYT5VGRtrY^^2k+I+}f zm3;6Kv*?eS&v!v#NNTOrMzA89=6x+ya%2Qg>2H>Sl@MH6MtQ;E&9#Nfm%Rc#fAMTL zRO;XA28Y|X#I672Il!#ZG*?1w!B30m;Cb}MeF~J5{U(`G^fwOoL*=|q zUfy~=0iQYHhCDfW^znDV1l<{tC0dS6!M*-T!@9}=5gJvUcs76RITIB7t}j+X0Q8DFbVs4gVfZ>VdxBniB@ouxSwkj)C|apRuf28=GlNP(5XIS!^^3wh$2pX(?hSU z70LiN&jBZDiS?O}X8=A@7aOahd@z9IZELEW2miD)eSn)aF_^dld~BW|6=0s~JDB$o z@6JQBRcnD+FNRY=M#nOpj+Pm0YYHSlvL1U=z29QYgd+0+EVROg(Z!gN=m3*9o^iDR&eV`=Gh#0ruo$81XJL8#v zE@4+EAx9#t?Mb&5Yq9)=q}cAnv(-iD~mnLmfMW^VxZ{5G;(jGy`#YKL?Ng z;DZ2%k!j#W4&`kkSPaB%l|2Y`z;}ayMHuzbCA^rGSw{S`&w2?DcP*)0H+GC;%mq1X5H!zc>MpDEF^%I> zW&1?a{A*u6*@M^3Z8)>{f9lMu;pKJ+F}StA%c$%OyGbQ-6({Sz?FTD(uNlHGpv^CG zL(r2A zWj>?XN_HL`UFQ4K#cEyq`)??t!KDk@pQG_jd^O;5Qsotg8E2`=xg_Qo?x~X#Bu_7K zWPn8TQ2}9dAAv?;N*=)ouv4QxsdsA|cv9-MC5}&e5NPo)Y1sg`Nw`d@J0E`?9{+GF z?t}&G*;rMEP8yO@j?~3!X9kCVm2!N)oK-~|WiYtOW1JaU!Io(AI4l@KY$v_Oi@~rd zZABtA+UHUUe}pSUhlZZ(rki@i!kZNw-1 zu+=Xjh>Ukm`!Qb(p6;844+;DjgBd3Q?^~k&fUs$=fR$<;R}mLfF#RVlAkMerY58$f z{e@*Wx+uLH>)(rwqmgLk=*=_CcYfXkb#1@4e-y;q__?e=d*m>uV9kqaQ9MF_CZDZm zL`4Hz)1HaQ?s1J5hXIcE2&6_?qoB&j35}=65ikk6@2(? ztWUFeSmx*I?@&AYrf%Y0FF!)-)yTKc%^&+Ox1 z0}vnZ+qifd<7gBM!~0y0Bn&DO_;89WM)avER57+7xXydoiHHmPOv}F!&P*IVt5R@8 z(Wo@8P(6F|JQ#VK8hQL`=JCJ1#Q{Wp?ML4{<*kl1q)8$xR?&*K*Px3!vi+MHGP(jW zjywFhc2E96&|=#Zu_#!F^spZ<1S*U`iIy0E@II|3Oi?RHY=v+Tz@WYLcCfhCe+&0t zV6=3>gAC_##In!gqc^LZ5cKzCuz%~O!WE>Gd&uAnwO62^W~y(kBBgdmR`x%um|OZ1 zh>DpNfi=XMK0x&)flad?onkWxK$O08z~N`N+z-rl|AVn!0eFYj7f}Wd_=8bUvboLH zalGHvEbosIhrXlX=YC|cF#xewJUo2o&G+!KwI@co$YV1&GVhQ zyE1#B!!x9wYwWwn6zfE9bEFSKL5#n+>5YVq;#Ix;K7Dw`RQ)b>VUy3*hXQ-+rHYgg zzyO~B*^=C_yMeUT+;%UISVVIA0tdD_gr~w--g}7W!zQ$f6?;o_3+#!dwF=Vz8jbL| z)udvcmU_o*_znI{1{c-i_r@l9ST5O4s5U+n>yrLL%blB=-}hH(%(*KEkEBo72Eh?T zT=iW`Wly5TA9j3WGc!$4~K2N3->#CP>m)y02#G3r&k>zSO@ z+m@5wu5v_l6E8+ACfk!JPdn#l!ff!}pVnOWp?=iQD|}dq62Jt_({wT>lLQBj zw|O%9&2OV{T=jQx;kD^6)<}w^$=J8E{t3Y%Agu6*)=Ff%w;j~JVw!B^QL&qKV8j^U z8n1w2zhW|wXi5JQoYE(z*G)W}TuoenbmSrNAd1KVjDI?I4;@Md#cE8UsH`sac} z@T}ZXLt#g^d}(*ECR)SrM_2S)J{L5Q%^0a7yj+%doVYhj=F^PS@+u6>@ z&>jyO=+0EoFTD1jl3_5yAh9e>8RLaFFKS)U8`?j5uoD|G<*64I&8chG;n*#ew)u%A z50MNdLGyTDj6o~TMJi8^oC&$z-q76Pe#nT;4>0n#zv1?N9OJw(s-|7T|(9nc>R zMx7rIZ_Y1w0_7kAV6^Y*o{OhbWbPvf|I}~9C=wWVpAAXhyNTK_@Jr+%Vc$K!lD<5iRXr-7n%18f{OZGOc+;~3s z;lB`&lmFiyUt5|5Tx*Y>JkGp19qtd@b|(m!6Mz@MS0vbet%%jh>;`846*`E}$JMjS z5Z(FEz47*gtAlY+`>X<|yU#p2d}h{U1sr^9DZdx(MS{Kk>Y*H@S|y@(xM~(YEaO2F zve|VdUfC_`er92Xo_zSAjt$;y)Ce)+3Sv%%8t*5t1womOq&DrkGy+?ew|bQ`z^k(s zZj^%Yy$Chg`Bm&H8@6>g`{vqP2hI#2`32n& zb<-IjzUfM&iQ6ArlRUY+ws`sPqtJkT1@EorhnTDO|8(_0ddvAS=Ov?XknSNzMyhKw z1^J8$C7~6mX;@eo=HQ|76X&&nl;Dty_rkigOci;gxaM&oDKbkT!ud)<_{^R)eVLjW z36kUr;c;aZTyM4dGZ?WL04@yLz;%STqta`t((2a zU%tYlPh7DteEja|ud!WdT4qBEM6|lB2*l*eW0`lCA>MoLWIZ77oHlFWFH!olK5U1% zETXq{XxQ2XBNMbDf6q?pv6o9*bR?wLUz|^dLzi2RdmeLqfwjoao4zdcN}y!LH-@j3 z3&6f7Hk` z!5dT)?wYq=Zb=b<_B$-3 zxlwC>5<$s`@KegBm3x-qWz)%0*GwUb@`TXZqQpL9a{NS#Hsr`T{-cdKiCb3g{K@H* zeVUa5) zMjPk^~Z zU*bMojuf`h%GnSjO(bF2 zST~*-w~;tr63`);2aOa{+W?^HuO$@VRy}q?UIY5VyasZy+f%z#)o&^-L4G4>P3n!~ zMzp0}$?m#J7U^&jwVB!n)ibdbv_cNJNZ%Kkq&jkr0+ge@+qU^h*rnuHl0M%@o94Ww zLVw5ad#U&(E>$5;9(dX%ad#zo$1hh&89q#Ev`|Dr!oodtO7zLyGP{yBYaG@p1!mJ- zXeBOWq%wep)*%;T84#`PU*G)d!79?%J-;=~f$iO%-qf~Bquxh-Pwh$}Z2#`cVMHbc zYlO0RVcw2s(6A5kDsC|fAf6rmnVI#mp=37yHY`QteYR!(ZV)BP%3Sl5V7xCs2Ji<+ z8Cs8o<+SU>VDXp214sEI`uHo8oS#zxm?Ee35sA=*35+jQBN0`tjw$^!Bp#K*sgA-% zhOgrg+*)#$e6~eze{`OS9=6z&I{gqZq!Y*#2o82*-*nVNbv%bBMWUKtY9CCN;pW&sK7(x)qBw!-u`UE_9nFkU4wjelaWzxdTh zs2AcclLWK&RV1QBHr)Re)3UC(1#*=NbxyN*E+c{=79{Mb{F3Pg$?{~%?nI{RnbGtp z>YUamj>~3$w985-FWph!(45Y z;_T0ge&=u73Af=@`1gt|pbvzl$YZGLe@*Py1OCME7{N%CUktUx9Zi{bd<7*IZ8;kJ zdTJ(dZ*;iq9qN?zpJu|(y3n9SRsj^&Fb-n?Em=ypUY)`D1=uri5@AzSL@+5P6p_Vn zt(or9$~T`uszv_mUsqYs&)iD$Y`2FM`CuYSn{TRZ4E=tr@w9V> z-i@0t_k;sA8x$p4U>X;^1Atr-Ik~Eho9MyJc8_;g>e`HR&t-nLk(M=Dfg<3Eyrh{W zA~s9Bxc0Y|p&Ydv5H|BeUFAy0zfW@`{q6KO(ax(#r{^Q#@;JW({ElI|oPwFp-cz4V zB|#3d%xPQug<`Lh#n2QuXC$V05|5BFM8^Te58r=tCli%3pf?xt!0 zdi=fT;IiO@L6qV3*{j(5OGDy4YP};7-+;NM1BgvNKTGVIIWcET^IB7)p58mVvg=Ha z)PqF2COeQ?z^&*4TdWJRPvgdt%tB&R1)qCt2`2+68Oe|z+ryAk?Ple*>c(P?!{w`8 zb5k#{iL>*AqB+@!+LA{vwO+{hU39+K{@>aiL67@s0ikypNeKuLeSb7L^m0xWgUji% z6}$S#q07%b>OV=yno=1l1xLu?k7aVF2mW}W1*PDd`@BPO);fh&9|Dl^I}Nr9Sx*X` zNz+_g3RoEEmjM1eRZ(__Y^@m(=T+j-1_yds6P+%NZE7-qHPI{kFxTUwaml4rm;Q*f zR6@_Rqoo8z?T<1X3KLi57{8lg=1DIo>~%ts_ppI*c9CfB1b(_7A1x`++MNA_0&krI zW5j2ds}?Q(`R$zs^W#IMyOc5iSn`0!8XpG4ehh*WZb-)=r8~xJ2m&{v=DuhIiQ=l> zdGo$(oJ)x(@zAU)gG@=~10jsqI)*-*u7)Mv0u`@fPbZf>xoHlu*gP!g=l~K2o89Wx zX!PwopBpTpaTuulP3!9P)*_Y*{7%KCwajUj=0!f+^%={L*^(aQCLpXA?2S%%rAR-< zRI0#;r+Ot(&Q6|d06BA-nBxLN?UOJV=iHV>?gg~^upGNIQ?CArXANXMZ@0BTt#%! z>nT~$@9lFC&AWY5CE56-OV0;U7#8i#E0<5|G2Kdp9UX{L!VZ|gyfSge%LZ@QlN{gA z^ag#HbvmE>NMXdey8^Zk7t7e8WsTka@;c5iEQuvcR&7ToPiMWyhg@}Dn@SV}s5q*D zD<5cqg}>+w0LMorO@BT0;ozXKg+TarOTzmlZ+U3_DZUI@^i$eS}-DDozZj^R(tsFG)fB7l)g`b=yGIxRf8fv&?7cZ^Utg z)Xy{cx^=1Dy}bj3UoF1gpDR_2AcV@Q4yaa#az~It%lP)!ro^jq(Zan+{v z_fBn&P{*Wgi?Nb1#!Y`mzapqWYKab0Oey?!RuT^ zw*GEh z5vn}J>t2LvX}U_80dP-`UR2B+1Mp4e4HHIF0#pi@Snj(r1X-i+r0NFlzZdPv5n3AD zm3YpZtGADLGW*Zro5|rx)T*!CH2A)M++w!Jy9YDPuU8om4zOq-QTD&;7A(SKjqece z8sNSshUP0;r3HX{5@6Bfa8B8nGOyS41k}b%a?|Wr?wFcKPFfJwy1X&~KExpkz*fEU z{g_nO@AcMrd^rYyd9m|QYFw`m-UTH_dF($^RM}QkECT|Rascpwhb(bOFoQ3I(*B*> z_rt;n7oO3-+MG7A^B!q@o&QXfjh~l|x39DEX|Tv-Q!>FPKobz{O|c8`E=VlK5De_G zBrp|N$tJ=Gkgd=NVNyWjtGg!^tMd$^9lzUW~!JRs9!)Wztc2x=Qsl%+t$@)d!|E)Kl^_;%L^G#%)mzCVl z1@#bJrul6gNOmHUGilHhMB*=7CXHW1i zVuB|S;4|p3J{JJLJ=Y&?Mi&WR#YHB-YWDWw_$9g8DW%sdhJ|X~wG|#7jeaKTpR8Cd zw8rC!=j$~n`p^sf4R&ixEwTh}IJCE1AnyBogj9)W)UHp74U=T;Zh7JL^Iaj+MrBl7 zG zbBNyc=a(`!1ON7+r?o)Ksuh+7)!^HMpZf@~4vNjJ#$$y{qq2fCgtr_7ggj7`4i#t?Yxmt=#g~Q@CVtr9*c_idaA=udeN*)| zw~3g0?lpZXRbb`l2YC^))M}5P_x$JN^q?tFswsu)pO0f}l^w*h9YP2$E>V>yfz=ST$kP=n zS~ZR3?u+4CNlq12gy_^G^lT`EJCgJn>>{nfQXk5uN(uH8XPV!{rm{vbfDNnmL*+*p(!Wx@vwmrnzKuY2oFYeiQ;8YBLo^1D14DxMtc{ zeIWGe9ujKZgH{Q`JafRELt)n0?rRzM>LW&QPFQWJFxWPQAB=K|UmmkrG3EZCjl=Pt z?B>v0^5uEePhO=^{-5SjYB1fLB)!}Uy(A$^`f23wOHlN!4{c?B`p3*zDVqk%J*UuR z0<1Y1{&E7Nt5mnrChsmc)&JeiDj*ySS9A1S7SxTueD;n_-9r~2`wir3cKZGzg!c+e zC|8#y=1JW54PePl`K!|*K_i!|+;?!q;=TKkydjd?AsOIrHDC{y=-6|D`VRoYT3s2R%`kVt(60Fi<^tcXgWRS#F&2 zWlZ)lM4V+JOAi8NKudwiNse6{FNPKa5I}Hj;Az|xv8i*~j?pLjKnkT}YRSLkzE!i* zq)8R7e&4~ufTBKXmHq>}Fm}PcH?ii~N3n<*6Yx#gGls7*y702L;vL57Z?!6gTb_bO z#coawpz(3@o?6Fk`B?o;#G;32+4z5PfGFeP76~c5B_)_g z>@W_J6`Yv<^;dz(o^RCcczen!zQfgD(dxcStoF`D`$iTdKiK3bwG%0fUby~_%r`=* zUUB7II%RopzXn>%IyBL{|gb|-+6^? z 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