Skip to content

Commit 2afe568

Browse files
committed
Start writing tutorial on the Poisson equation
1 parent 49e3e5a commit 2afe568

File tree

7 files changed

+231
-15
lines changed

7 files changed

+231
-15
lines changed

assets/t006_poisson_dg/conv.png

15.7 KB
Loading

assets/t006_poisson_dg/error.png

80.2 KB
Loading

assets/t006_poisson_dg/jump_u.png

94.2 KB
Loading
28.6 KB
Loading

assets/t006_poisson_dg/uh.png

33.7 KB
Loading

docs/make.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ files = [
2626
"t002_validation",
2727
"t003_elasticity",
2828
"t0041_p_laplacian",
29-
"t004_hyperelasticity"]
29+
"t004_hyperelasticity",
30+
"t005_dg_discretization"]
3031

3132
for file in files
3233
file_jl = file*".jl"
@@ -40,7 +41,8 @@ pages = [
4041
"2 Code validation" => "pages/t002_validation.md",
4142
"3 Linear elasticity" => "pages/t003_elasticity.md",
4243
"4 p-Laplacian" => "pages/t0041_p_laplacian.md",
43-
"5 Hyper-elasticity" => "pages/t004_hyperelasticity.md"]
44+
"5 Hyper-elasticity" => "pages/t004_hyperelasticity.md",
45+
"6 Poisson equation (with DG)" => "pages/t005_dg_discretization.md"]
4446

4547
makedocs(
4648
sitename = "Gridap tutorials",

src/t005_dg_discretization.jl

Lines changed: 227 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,103 @@
1+
# # Tutorial 6: Poisson equation (with DG)
2+
#
3+
#md # [![](https://mybinder.org/badge_logo.svg)](@__BINDER_ROOT_URL__/notebooks/t005_dg_discretization.ipynb)
4+
#md # [![](https://img.shields.io/badge/show-nbviewer-579ACA.svg)](@__NBVIEWER_ROOT_URL__/notebooks/t005_dg_discretization.ipynb)
5+
#
6+
# ## Learning outcomes
7+
#
8+
# - How to solve a simple with a Discontinuous Galerkin (DG) discretization
9+
# - How to build discontinuous FE spaces
10+
# - How to integrate quantities on the mesh skeleton
11+
# - How to compute jumps and averages of quantities on the mesh skeleton
12+
#
13+
#
14+
# ## Problem statement
15+
#
16+
# The goal of this tutorial is to solve a simple PDE using a Discontinuous Galerkin (DG) formulation. For simplicity, we take the Poisson equation on the unit multi dimensional cube $\Omega \doteq (0,1)^d$, with $d=2$ and $d=3$, as a model problem:
17+
#
18+
#
19+
# ```math
20+
# \left\lbrace
21+
# \begin{aligned}
22+
# -\Delta u = f \ \text{in} \ \Omega\\
23+
# u = g \ \text{on}\ \partial\Omega,\\
24+
# \end{aligned}
25+
# \right.
26+
# ```
27+
# where $f$ is the source term and $g$ is the Dirichlet boundary value.
28+
#
29+
# We are going to solve two version of this problem. In a first stage, we take $d=3$ and consider a manufactured solution, namely $u(x) = 3 x_1 + x_2 + 2 x_3$, that belongs to the FE interpolation that we will build below. In this case, we expect to compute a numerical solution with an approximation error close to the machine precision. On the other hand, we will perform a convergence test for the 2D case ($d=2$). To this, end we will consider a manufactured solution that cannot by represented exactly by the interpolation, namely $u(x)=x_2 \sin(2 \pi\ x_1)$. Our goal is to confirm that the convergence order of the discretization error is the optimal one.
30+
#
31+
# ## Numerical Scheme
32+
#
33+
# In contrast to previous tutorials, we consider a DG formulation to approximate the problem. For the sake of simplicity, we take the well know (symmetric) interior penalty method. For this formulation, the approximation space is made of discontinuous piece-wise polynomials, namely
34+
#
35+
# ```math
36+
# V \doteq \{ v\in L^2(\Omega):\ v|_{T}\in Q_p(T) \text{ for all } T\in\mathcal{T} \},
37+
# ```
38+
# where $\mathcal{T}$ is the set of all cells $T$ of the FE mesh, and $Q_p(T)$ is a polynomial space of degree $p$ defined on a generic cell $T$. For simplicity, we consider Cartesian meshes in this tutorial. In this case, the space $Q_p(T)$ is made of multi-variate polynomials up to degree $p$ in each spatial coordinate.
39+
#
40+
#
41+
#
42+
#
43+
# In order to write the weak form of the problem, we need to introduce the set of interior and boundary facets associated with the FE mesh, denoted here as $\mathcal{F}_\Gamma$ and $\mathcal{F}_{\partial\Omega}$ respectively. In addition, for a given function $v\in V$ restricted to the interior facets $\mathcal{F}_\Gamma$, we need to define the well known jump and mean value operators:
44+
# ```math
45+
# \lbrack\!\lbrack v\ n \rbrack\!\rbrack \doteq v^+\ n^+ + v^- n^-, \text{ and } \{\! \!\{\nabla v\}\! \!\} \doteq \dfrac{ \nabla v^+ + \nabla v^-}{2},
46+
# ```
47+
# with $v^+$, and $v^-$ being the restrictions of $v\in V$ to the cells $T^+$, $T^-$ that share a generic interior facet in $\mathcal{F}_\Gamma$, and $n^+$, and $n^-$ are the facet outward unit normals from either the perspective of $T^+$ and $T^-$ respectively.
48+
#
49+
# With this notation, the weak form associated with the interior penalty formulation reads: find $u\in V$ such that $a(v,u) = b(v)$ for all $v\in V$. The bilinear $a(\cdot,\cdot)$ and linear form $b(\cdot)$ have contributions associated with the bulk of $\Omega$, and the boundary and interior facets $\mathcal{F}_{\partial\Omega}$, $\mathcal{F}_\Gamma$, namely
50+
# ``` math
51+
# \begin{aligned}
52+
# a(v,u) &= a_{\Omega}(v,u) + a_{\partial\Omega}(v,u) + a_{\Gamma}(v,u),\\
53+
# b(v) &= b_{\Omega}(v) + b_{\partial\Omega}(v),
54+
# \end{aligned}
55+
# ```
56+
# which are defined as
57+
# ```math
58+
# a_{\Omega}(v,u) \doteq \sum_{T\in\mathcal{T}} \int_{T} \nabla v \cdot \nabla u \ {\rm d}T, \quad b_{\Omega}(v) \doteq \int_{\Omega} v\ f \ {\rm d}\Omega,
59+
# ```
60+
# for the volume
61+
# ```math
62+
# \begin{aligned}
63+
# a_{\partial\Omega}(v,u) &\doteq \sum_{F\in\mathcal{F}_{\partial\Omega}} \dfrac{\gamma}{|F|} \int_{F} v\ u \ {\rm d}F - \sum_{F\in\mathcal{F}_{\partial\Omega}} \int_{F} v\ (\nabla u \cdot n) \ {\rm d}F - \sum_{F\in\mathcal{F}_{\partial\Omega}} \int_{F} (\nabla v \cdot n)\ u \ {\rm d}F, \\
64+
# b_{\partial\Omega} &\doteq \sum_{F\in\mathcal{F}_{\partial\Omega}} \dfrac{\gamma}{|F|} \int_{F} v\ g \ {\rm d}F - \sum_{F\in\mathcal{F}_{\partial\Omega}} \int_{F} (\nabla v \cdot n)\ g \ {\rm d}F,
65+
# \end{aligned}
66+
# ```
67+
# for the boundary facets and
68+
# ```math
69+
# a_{\Gamma}(v,u) \doteq \sum_{F\in\mathcal{F}_{\Gamma}} \dfrac{\gamma}{|F|} \int_{F} \lbrack\!\lbrack v\ n \rbrack\!\rbrack\cdot \lbrack\!\lbrack u\ n \rbrack\!\rbrack \ {\rm d}F - \sum_{F\in\mathcal{F}_{\Gamma}} \int_{F} \lbrack\!\lbrack v\ n \rbrack\!\rbrack\cdot \{\! \!\{\nabla u\}\! \!\} \ {\rm d}F - \sum_{F\in\mathcal{F}_{\Gamma}} \int_{F} \{\! \!\{\nabla v\}\! \!\}\cdot \lbrack\!\lbrack u\ n \rbrack\!\rbrack \ {\rm d}F,
70+
# ```
71+
# for the interior facets. In previous expressions, $|F|$ denotes the diameter of the face $F$ (in our Cartesian grid, this is equivalent to the characteristic mesh size $h$), and $\gamma$ is a stabilization parameter that should be chosen large enough such that the bilinear form $a(\cdot,\cdot)$ is stable and continuous. Here, we take $\gamma = p\ (p+1)$.
72+
#
73+
# ## 3D manufactured solution
174

275
using Gridap
376
import Gridap:
477

5-
u(x) = x[1] + x[2]
6-
∇u(x) = VectorValue(1.0,1.0)
78+
u(x) = 3*x[1] + x[2] + 2*x[3]
79+
∇u(x) = VectorValue(3.0,1.0,2.0)
780
(::typeof(u)) = ∇u
881
f(x) = 0.0
982
g(x) = u(x)
1083

1184
L = 1.0
12-
limits = (0.0, L, 0.0, L)
13-
ncellx = 4
14-
model = CartesianDiscreteModel(domain=limits, partition=(ncellx,ncellx))
85+
limits = (0.0, L, 0.0, L, 0.0, L)
86+
n = 4
87+
model = CartesianDiscreteModel(domain=limits, partition=(n,n,n))
1588

16-
h = L / ncellx
17-
18-
γ = 10
89+
h = L / n
1990

2091
order = 3
21-
fespace = DLagrangianFESpace(Float64,model,order)
92+
93+
fespace = FESpace(
94+
reffe=:Lagrangian,
95+
conformity = :L2,
96+
valuetype = Float64,
97+
model = model,
98+
order = order)
99+
100+
γ = order*(order+1)
22101

23102
V = TestFESpace(fespace)
24103
U = TrialFESpace(fespace)
@@ -28,12 +107,17 @@ quad = CellQuadrature(trian,order=2*order)
28107

29108
btrian = BoundaryTriangulation(model)
30109
bquad = CellQuadrature(btrian,order=2*order)
31-
nb = NormalVector(btrian)
32110

33111
strian = SkeletonTriangulation(model)
34112
squad = CellQuadrature(strian,order=2*order)
113+
114+
# ![](../assets/t006_poisson_dg/skeleton_trian.png)
115+
116+
nb = NormalVector(btrian)
35117
ns = NormalVector(strian)
36118

119+
writevtk(strian,"strian")
120+
37121
a_Ω(v,u) = inner((v), (u))
38122
b_Ω(v) = inner(v,f)
39123
t_Ω = AffineFETerm(a_Ω,b_Ω,trian,quad)
@@ -42,20 +126,150 @@ a_∂Ω(v,u) = (γ/h) * inner(v,u) - inner(v, ∇(u)*nb ) - inner(∇(v)*nb, u)
42126
b_∂Ω(v) =/h) * inner(v,g) - inner((v)*nb, g)
43127
t_∂Ω = AffineFETerm(a_∂Ω,b_∂Ω,btrian,bquad)
44128

45-
a_Γ(v,u) =/h) * inner( jump(v*ns), jump(u*ns))
46-
- inner( jump(v*ns), mean((u)) ) - inner( mean((v)), jump(u*ns) )
129+
a_Γ(v,u) =/h) * inner( jump(v*ns), jump(u*ns)) -
130+
inner( jump(v*ns), mean((u)) ) - inner( mean((v)), jump(u*ns) )
47131
t_Γ = LinearFETerm(a_Γ,strian,squad)
48132

49133
op = LinearFEOperator(V,U,t_Ω,t_∂Ω,t_Γ)
50134

51135
uh = solve(op)
52136

137+
uh_Γ = restrict(uh,strian)
138+
139+
writevtk(strian,"jumps",
140+
cellfields=["jump_u"=>jump(uh_Γ), "jump_gradn_u"=> jump((uh_Γ)*ns)])
141+
142+
# ![](../assets/t006_poisson_dg/jump_u.png)
143+
53144
e = u - uh
54145

55-
#writevtk(trian,"trian",cellfields=["uh"=>uh,"e"=>e])
146+
writevtk(trian,"trian",cellfields=["uh"=>uh,"e"=>e])
147+
148+
# ![](../assets/t006_poisson_dg/error.png)
56149

57150
l2(u) = inner(u,u)
58151
h1(u) = a_Ω(u,u) + l2(u)
59152

60153
el2 = sqrt(sum( integrate(l2(e),trian,quad) ))
61154
eh1 = sqrt(sum( integrate(h1(e),trian,quad) ))
155+
156+
#src @show el2
157+
#src @show eh1
158+
159+
const k = 2*pi
160+
u(x) = sin(k*x[1]) * x[2]
161+
∇u(x) = VectorValue(k*cos(k*x[1])*x[2], sin(k*x[1]))
162+
f(x) = (k^2)*sin(k*x[1])*x[2]
163+
(::typeof(u)) = ∇u
164+
165+
function run(n,order)
166+
167+
#Setup model
168+
L = 1.0
169+
h = L / n
170+
limits = (0.0, L, 0.0, L)
171+
partition = (n,n)
172+
model = CartesianDiscreteModel(domain=limits, partition=partition)
173+
174+
#Setup FE spaces
175+
fespace = FESpace(
176+
reffe=:Lagrangian,
177+
conformity = :L2,
178+
valuetype = Float64,
179+
model = model,
180+
order = order)
181+
V = TestFESpace(fespace)
182+
U = TrialFESpace(fespace)
183+
184+
#Setup integration meshes
185+
trian = Triangulation(model)
186+
btrian = BoundaryTriangulation(model)
187+
strian = SkeletonTriangulation(model)
188+
189+
#Setup quadratures
190+
quad = CellQuadrature(trian,order=2*order)
191+
squad = CellQuadrature(strian,order=2*order)
192+
bquad = CellQuadrature(btrian,order=2*order)
193+
194+
#Setup normal vectors
195+
nb = NormalVector(btrian)
196+
ns = NormalVector(strian)
197+
198+
#Setup weak form (volume)
199+
a_Ω(v,u) = inner((v), (u))
200+
b_Ω(v) = inner(v,f)
201+
202+
#Setup weak form (boundary)
203+
γ = order*(order+1)
204+
a_∂Ω(v,u) =/h) * inner(v,u) - inner(v, (u)*nb ) - inner((v)*nb, u)
205+
b_∂Ω(v) =/h) * inner(v,g) - inner((v)*nb, g)
206+
207+
#Setup weak form (skeleton)
208+
a_Γ(v,u) =/h) * inner( jump(v*ns), jump(u*ns)) -
209+
inner( jump(v*ns), mean((u)) ) - inner( mean((v)), jump(u*ns) )
210+
211+
#Setup FE problem
212+
t_Ω = AffineFETerm(a_Ω,b_Ω,trian,quad)
213+
t_Γ = LinearFETerm(a_Γ,strian,squad)
214+
t_∂Ω = AffineFETerm(a_∂Ω,b_∂Ω,btrian,bquad)
215+
op = LinearFEOperator(V,U,t_Ω,t_∂Ω,t_Γ)
216+
217+
#Solve
218+
uh = solve(op)
219+
220+
#Measure discretization error
221+
e = u - uh
222+
l2(u) = inner(u,u)
223+
h1(u) = a_Ω(u,u) + l2(u)
224+
el2 = sqrt(sum( integrate(l2(e),trian,quad) ))
225+
eh1 = sqrt(sum( integrate(h1(e),trian,quad) ))
226+
227+
(el2, eh1, h)
228+
229+
end
230+
231+
function conv_test(ns,order)
232+
233+
el2s = Float64[]
234+
eh1s = Float64[]
235+
hs = Float64[]
236+
237+
for n in ns
238+
@show n
239+
el2, eh1, h = run(n,order)
240+
push!(el2s,el2)
241+
push!(eh1s,eh1)
242+
push!(hs,h)
243+
end
244+
245+
(el2s, eh1s, hs)
246+
247+
end
248+
249+
250+
el2s, eh1s, hs = conv_test([8,16,32,64],3)
251+
252+
using Plots
253+
254+
plot(hs,[el2s eh1s],
255+
xaxis=:log, yaxis=:log,
256+
label=["L2" "H1"],
257+
shape=:auto,
258+
xlabel="h",ylabel="error norm")
259+
260+
#src savefig("conv.png")
261+
262+
function slope(hs,errors)
263+
x = log10.(hs)
264+
y = log10.(errors)
265+
linreg = hcat(fill!(similar(x), 1), x) \ y
266+
linreg[2]
267+
end
268+
269+
slope(hs,el2s)
270+
271+
slope(hs,eh1s)
272+
273+
#md # ![](../assets/t006_poisson_dg/conv.png)
274+
275+

0 commit comments

Comments
 (0)