begin
using ExtendableFEMBase
using ExtendableGrids
using ExtendableSparse
using GridVisualize
using PlutoVista
GridVisualize.default_plotter!(PlutoVista)
end
PlutoVista
Tutorial notebook: Poisson problem
This notebook demonstrates how to implement the Poisson problem with the low level structures provided by ExtendableFEMBase. The Poisson problem with homogeneous Dirichlet boundary data seeks \(u\) such that
$$\begin{aligned} - \mu \Delta u = f. \end{aligned}$$
The weak formulation seeks \(u \in V := H^1_0(\Omega)\) such that
$$\begin{aligned} \mu (\nabla u, \nabla v) = (f, v) \quad \text{for all } v \in V \end{aligned}$$
begin
## PDE data
μ = 1.0
f = x -> x[1] - x[2]
## discretization parameters
nref = 9
order = 1
end
1
tricontour(xgrid[Coordinates],xgrid[CellNodes],sol.entries[1:num_nodes(xgrid)]; levels = 5, resolution = (500,500))
begin
## call low level solver
sol = solve_poisson_lowlevel(FES, μ, f)
end
FEVector information ==================== block | ndofs | min / max | FEType (name/tag) [ 1] | 263169 | -1.21e-02/1.21e-02 | H1Pk{1,2,1} (#1)
function solve_poisson_lowlevel(FES, μ, f)
Solution = FEVector(FES)
FES = Solution[1].FES
A = FEMatrix(FES, FES)
b = FEVector(FES)
println("Assembling operators...")
@time assemble!(A.entries, b.entries, FES, f, μ)
## fix boundary dofs
println("Assembling boundary data...")
@time begin
BFaceDofs::Adjacency{Int32} = FES[ExtendableFEMBase.BFaceDofs]
nbfaces::Int = num_sources(BFaceDofs)
AM::ExtendableSparseMatrix{Float64,Int64} = A.entries
dof_j::Int = 0
for bface = 1 : nbfaces
for j = 1 : num_targets(BFaceDofs,1)
dof_j = BFaceDofs[j, bface]
AM[dof_j,dof_j] = 1e60
b.entries[dof_j] = 0
end
end
end
ExtendableSparse.flush!(A.entries)
## solve
println("Solving linear system...")
@time copyto!(Solution.entries, A.entries \ b.entries)
return Solution
end
solve_poisson_lowlevel (generic function with 1 method)
begin
## create finite element space
FEType = H1Pk{1,2,order}
## prepare finite element space and dofmaps
println("Creating FESpace...")
@time FES = FESpace{FEType}(xgrid)
FES
end
FESpace information =================== name = H1Pk{1,2,1} FEType = H1Pk{1,2,1} FEClass = ExtendableFEMBase.AbstractH1FiniteElement ndofs = 263169 DofMaps ==========
begin
## create grid
X = LinRange(0,1,2^nref+1)
Y = LinRange(0,1,2^nref+1)
println("Creating grid...")
@time xgrid = simplexgrid(X,Y)
println("Preparing FaceNodes...")
@time xgrid[FaceNodes]
println("Preparing CellVolumes...")
@time xgrid[CellVolumes]
xgrid
end
ExtendableGrids.ExtendableGrid{Float64, Int32} dim = 2 nnodes = 263169 ncells = 524288 nbfaces = 2048
function assemble!(A::ExtendableSparseMatrix, b::Vector, FES, f, μ = 1)
xgrid = FES.xgrid
EG = xgrid[UniqueCellGeometries][1]
FEType = eltype(FES)
L2G = L2GTransformer(EG, xgrid, ON_CELLS)
## dofmap
CellDofs = FES[ExtendableFEMBase.CellDofs]
## quadrature formula
qf = QuadratureRule{Float64, EG}(2*(get_polynomialorder(FEType, EG)-1))
weights::Vector{Float64} = qf.w
xref::Vector{Vector{Float64}} = qf.xref
nweights::Int = length(weights)
## FE basis evaluator
FEBasis_∇ = FEEvaluator(FES, Gradient, qf)
∇vals = FEBasis_∇.cvals
FEBasis_id = FEEvaluator(FES, Identity, qf)
idvals = FEBasis_id.cvals
cellvolumes = xgrid[CellVolumes]
## ASSEMBLY LOOP
function barrier(EG, L2G::L2GTransformer)
## barrier function to avoid allocations by EG dispatch
ndofs4cell::Int = get_ndofs(ON_CELLS, FEType, EG)
Aloc = zeros(Float64, ndofs4cell, ndofs4cell)
ncells::Int = num_cells(xgrid)
dof_j::Int, dof_k::Int = 0, 0
x::Vector{Float64} = zeros(Float64, 2)
for cell = 1 : ncells
## update FE basis evaluators
FEBasis_∇.citem[] = cell
update_basis!(FEBasis_∇)
## assemble local stiffness matrix
for j = 1 : ndofs4cell, k = j : ndofs4cell
temp = 0
for qp = 1 : nweights
temp += weights[qp] * dot(view(∇vals,:,j,qp), view(∇vals,:,k,qp))
end
Aloc[j,k] = temp
end
Aloc .*= μ * cellvolumes[cell]
## add local matrix to global matrix
for j = 1 : ndofs4cell
dof_j = CellDofs[j, cell]
for k = j : ndofs4cell
dof_k = CellDofs[k, cell]
if abs(Aloc[j,k]) > 1e-15
# write into sparse matrix, only lines with allocations
rawupdateindex!(A, +, Aloc[j,k], dof_j, dof_k)
if k > j
rawupdateindex!(A, +, Aloc[j,k], dof_k, dof_j)
end
end
end
end
fill!(Aloc, 0)
## assemble right-hand side
update_trafo!(L2G, cell)
for j = 1 : ndofs4cell
## right-hand side
temp = 0
for qp = 1 : nweights
## get global x for quadrature point
eval_trafo!(x, L2G, xref[qp])
## evaluate (f(x), v_j(x))
temp += weights[qp] * idvals[1, j, qp] * f(x)
end
## write into global vector
dof_j = CellDofs[j, cell]
b[dof_j] += temp * cellvolumes[cell]
end
end
end
barrier(EG, L2G)
flush!(A)
end
assemble! (generic function with 2 methods)
Built with Julia 1.11.1 and
ExtendableFEMBase 0.8.1ExtendableGrids 1.11.0
ExtendableSparse 1.6.0
GridVisualize 1.8.2
PlutoVista 1.0.1