Skip to content

Commit af203a1

Browse files
authored
Update time-dependent problems. (#280)
* Fix nitsche * Update further tutorials. Add a small section on petsc LU factorization
1 parent f00a2b9 commit af203a1

File tree

9 files changed

+301
-93
lines changed

9 files changed

+301
-93
lines changed

chapter1/fundamentals_code.ipynb

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@
387387
"Using {py:meth}`problem.solve()<dolfinx.fem.petsc.LinearProblem.solve>` we solve the linear system of equations and\n",
388388
"return a {py:class}`Function<dolfinx.fem.Function>` containing the solution.\n",
389389
"\n",
390+
"(error-norm)=\n",
390391
"## Computing the error\n",
391392
"Finally, we want to compute the error to check the accuracy of the solution.\n",
392393
"We do this by comparing the finite element solution `u` with the exact solution.\n",
@@ -417,7 +418,7 @@
417418
"only assembles over the cells on the local process.\n",
418419
"This means that if we use 2 processes to solve our problem,\n",
419420
"we need to accumulate the local contributions to get the global error (on one or all processes).\n",
420-
"We can do this with the {func}`Comm.allreduce<mpi4py.MPI.Comm.allreduce>` function."
421+
"We can do this with the {py:meth}`Comm.allreduce<mpi4py.MPI.Comm.allreduce>` function."
421422
]
422423
},
423424
{
@@ -438,9 +439,10 @@
438439
"metadata": {},
439440
"source": [
440441
"Secondly, we compute the maximum error at any degree of freedom.\n",
441-
"As the finite element function $u$ can be expressed as a linear combination of basis functions $\\phi_j$, spanning the space $V$:\n",
442-
"$ u = \\sum_{j=1}^N U_j\\phi_j.$\n",
443-
"By writing `problem.solve()` we compute all the coefficients $U_1,\\dots, U_N$.\n",
442+
"As the finite element function $u$ can be expressed as a linear combination of basis functions $\\phi_j$,\n",
443+
"spanning the space $V$: $ u = \\sum_{j=1}^N U_j\\phi_j.$\n",
444+
"By writing {py:meth}`problem.solve()<dolfinx.fem.petsc.LinearProblem.sovle>`\n",
445+
"we compute all the coefficients $U_1,\\dots, U_N$.\n",
444446
"These values are known as the _degrees of freedom_ (dofs).\n",
445447
"We can access the degrees of freedom by accessing the underlying vector in `uh`.\n",
446448
"However, as a second order function space has more dofs than a linear function space,\n",
@@ -457,8 +459,7 @@
457459
"outputs": [],
458460
"source": [
459461
"error_max = numpy.max(numpy.abs(uD.x.array - uh.x.array))\n",
460-
"# Only print the error on one process\n",
461-
"if domain.comm.rank == 0:\n",
462+
"if domain.comm.rank == 0: # Only print the error on one process\n",
462463
" print(f\"Error_L2 : {error_L2:.2e}\")\n",
463464
" print(f\"Error_max : {error_max:.2e}\")"
464465
]

chapter1/fundamentals_code.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@
265265
# Using {py:meth}`problem.solve()<dolfinx.fem.petsc.LinearProblem.solve>` we solve the linear system of equations and
266266
# return a {py:class}`Function<dolfinx.fem.Function>` containing the solution.
267267
#
268+
# (error-norm)=
268269
# ## Computing the error
269270
# Finally, we want to compute the error to check the accuracy of the solution.
270271
# We do this by comparing the finite element solution `u` with the exact solution.
@@ -281,16 +282,17 @@
281282
# only assembles over the cells on the local process.
282283
# This means that if we use 2 processes to solve our problem,
283284
# we need to accumulate the local contributions to get the global error (on one or all processes).
284-
# We can do this with the {func}`Comm.allreduce<mpi4py.MPI.Comm.allreduce>` function.
285+
# We can do this with the {py:meth}`Comm.allreduce<mpi4py.MPI.Comm.allreduce>` function.
285286

286287
L2_error = fem.form(ufl.inner(uh - uex, uh - uex) * ufl.dx)
287288
error_local = fem.assemble_scalar(L2_error)
288289
error_L2 = numpy.sqrt(domain.comm.allreduce(error_local, op=MPI.SUM))
289290

290291
# Secondly, we compute the maximum error at any degree of freedom.
291-
# As the finite element function $u$ can be expressed as a linear combination of basis functions $\phi_j$, spanning the space $V$:
292-
# $ u = \sum_{j=1}^N U_j\phi_j.$
293-
# By writing `problem.solve()` we compute all the coefficients $U_1,\dots, U_N$.
292+
# As the finite element function $u$ can be expressed as a linear combination of basis functions $\phi_j$,
293+
# spanning the space $V$: $ u = \sum_{j=1}^N U_j\phi_j.$
294+
# By writing {py:meth}`problem.solve()<dolfinx.fem.petsc.LinearProblem.sovle>`
295+
# we compute all the coefficients $U_1,\dots, U_N$.
294296
# These values are known as the _degrees of freedom_ (dofs).
295297
# We can access the degrees of freedom by accessing the underlying vector in `uh`.
296298
# However, as a second order function space has more dofs than a linear function space,
@@ -299,8 +301,7 @@
299301
# we can compare the maximum values at any degree of freedom of the approximation space.
300302

301303
error_max = numpy.max(numpy.abs(uD.x.array - uh.x.array))
302-
# Only print the error on one process
303-
if domain.comm.rank == 0:
304+
if domain.comm.rank == 0: # Only print the error on one process
304305
print(f"Error_L2 : {error_L2:.2e}")
305306
print(f"Error_max : {error_max:.2e}")
306307

chapter1/membrane_code.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ def on_boundary(x):
246246
# This function returns a list of cells whose bounding box collide for each input point.
247247
# As different points might have different number of cells, the data is stored in
248248
# {py:class}`dolfinx.graph.AdjacencyList`, where one can access the cells for the
249-
# `i`th point by calling `links(i)<dolfinx.graph.AdjacencyList.links>`.
249+
# `i`th point by calling {py:meth}`links(i)<dolfinx.graph.AdjacencyList.links>`.
250250
# However, as the bounding box of a cell spans more of $\mathbb{R}^n$ than the actual cell,
251251
# we check that the actual cell collides with the input point using
252252
# {py:func}`dolfinx.geometry.compute_colliding_cells`,

chapter1/nitsche.ipynb

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
"# Weak imposition of Dirichlet conditions for the Poisson problem\n",
99
"Author: Jørgen S. Dokken\n",
1010
"\n",
11-
"In this section, we will go through how to solve the Poisson problem from the [Fundamentals](./fundamentals_code.ipynb) tutorial using Nitsche's method {cite}`Nitsche1971`.\n",
12-
"The idea of weak imposition is that we add additional terms to the variational formulation to impose the boundary condition, instead of modifying the matrix system using strong imposition (lifting).\n",
11+
"In this section, we will go through how to solve the Poisson problem from the\n",
12+
"[Fundamentals](./fundamentals_code.ipynb) tutorial using Nitsche's method {cite}`nitsche-Nitsche1971`.\n",
13+
"The idea of weak imposition is that we add additional terms to the variational formulation to\n",
14+
"impose the boundary condition, instead of modifying the matrix system using strong imposition (lifting).\n",
1315
"\n",
1416
"We start by importing the required modules and creating the mesh and function space for our solution"
1517
]
@@ -48,7 +50,12 @@
4850
"id": "2",
4951
"metadata": {},
5052
"source": [
51-
"Next, we create a function containing the exact solution (which will also be used in the Dirichlet boundary condition) and the corresponding source function for the right hand side. Note that we use `ufl.SpatialCoordinate` to define the exact solution, which in turn is interpolated into `uD` and used to create the source function `f`."
53+
"Next, we create a function containing the exact solution (which will also be used in the Dirichlet boundary condition)\n",
54+
"and the corresponding source function for the right hand side. Note that we use {py:class}`ufl.SpatialCoordinate`\n",
55+
"to define the exact solution, which in turn is interpolated into {py:class}`uD<dolfinx.fem.Function>`\n",
56+
"by wrapping the {py:class}`ufl-expression<ufl.core.expr.Expr>` as a {py:class}`dolfinx.fem.Expression`.\n",
57+
"For the source function, we use the symbolic differentiation capabilities of UFL to\n",
58+
"compute the negative Laplacian of the exact solution, which we can use directly in the variational formulation."
5259
]
5360
},
5461
{
@@ -72,21 +79,34 @@
7279
"source": [
7380
"As opposed to the first tutorial, we now have to have another look at the variational form.\n",
7481
"We start by integrating the problem by parts, to obtain\n",
82+
"\n",
83+
"$$\n",
7584
"\\begin{align}\n",
76-
" \\int_{\\Omega} \\nabla u \\cdot \\nabla v~\\mathrm{d}x - \\int_{\\partial\\Omega}\\nabla u \\cdot n v~\\mathrm{d}s = \\int_{\\Omega} f v~\\mathrm{d}x.\n",
85+
" \\int_{\\Omega} \\nabla u \\cdot \\nabla v~\\mathrm{d}x\n",
86+
" - \\int_{\\partial\\Omega}\\nabla u \\cdot n v~\\mathrm{d}s = \\int_{\\Omega} f v~\\mathrm{d}x.\n",
7787
"\\end{align}\n",
88+
"$$\n",
89+
"\n",
7890
"As we are not using strong enforcement, we do not set the trace of the test function to $0$ on the outer boundary.\n",
7991
"Instead, we add the following two terms to the variational formulation\n",
92+
"\n",
93+
"$$\n",
8094
"\\begin{align}\n",
81-
" -\\int_{\\partial\\Omega} \\nabla v \\cdot n (u-u_D)~\\mathrm{d}s + \\frac{\\alpha}{h} \\int_{\\partial\\Omega} (u-u_D)v~\\mathrm{d}s.\n",
95+
" -\\int_{\\partial\\Omega} \\nabla v \\cdot n (u-u_D)~\\mathrm{d}s\n",
96+
" + \\frac{\\alpha}{h} \\int_{\\partial\\Omega} (u-u_D)v~\\mathrm{d}s.\n",
8297
"\\end{align}\n",
98+
"$$\n",
99+
"\n",
83100
"where the first term enforces symmetry to the bilinear form, while the latter term enforces coercivity.\n",
84101
"$u_D$ is the known Dirichlet condition, and $h$ is the diameter of the circumscribed sphere of the mesh element.\n",
85102
"We create bilinear and linear form, $a$ and $L$\n",
103+
"\n",
104+
"$$\n",
86105
"\\begin{align}\n",
87106
" a(u, v) &= \\int_{\\Omega} \\nabla u \\cdot \\nabla v~\\mathrm{d}x + \\int_{\\partial\\Omega}-(n \\cdot\\nabla u) v - (n \\cdot \\nabla v) u + \\frac{\\alpha}{h} uv~\\mathrm{d}s,\\\\\n",
88107
" L(v) &= \\int_{\\Omega} fv~\\mathrm{d}x + \\int_{\\partial\\Omega} -(n \\cdot \\nabla v) u_D + \\frac{\\alpha}{h} u_Dv~\\mathrm{d}s\n",
89-
"\\end{align}"
108+
"\\end{align}\n",
109+
"$$"
90110
]
91111
},
92112
{
@@ -112,7 +132,8 @@
112132
"id": "6",
113133
"metadata": {},
114134
"source": [
115-
"As we now have the variational form, we can solve the linear problem"
135+
"As we now have the variational form, we can solve the arising\n",
136+
"{py:class}`linear problem<dolfinx.fem.petsc.LinearProblem>`"
116137
]
117138
},
118139
{
@@ -176,7 +197,9 @@
176197
"id": "12",
177198
"metadata": {},
178199
"source": [
179-
"We observe that as we weakly impose the boundary condition, we no longer fullfill the equation to machine precision at the mesh vertices. We also plot the solution using `pyvista`"
200+
"We observe that as we weakly impose the boundary condition,\n",
201+
"we no longer fullfill the equation to machine precision at the mesh vertices.\n",
202+
"We also plot the solution using {py:mod}`pyvista`"
180203
]
181204
},
182205
{
@@ -206,7 +229,9 @@
206229
"metadata": {},
207230
"source": [
208231
"```{bibliography}\n",
209-
" :filter: cited and ({\"chapter1/nitsche\"} >= docnames)\n",
232+
" :filter: cited\n",
233+
" :labelprefix:\n",
234+
" :keyprefix: nitsche-\n",
210235
"```"
211236
]
212237
}

chapter1/nitsche.py

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
# # Weak imposition of Dirichlet conditions for the Poisson problem
1717
# Author: Jørgen S. Dokken
1818
#
19-
# In this section, we will go through how to solve the Poisson problem from the [Fundamentals](./fundamentals_code.ipynb) tutorial using Nitsche's method {cite}`Nitsche1971`.
20-
# The idea of weak imposition is that we add additional terms to the variational formulation to impose the boundary condition, instead of modifying the matrix system using strong imposition (lifting).
19+
# In this section, we will go through how to solve the Poisson problem from the
20+
# [Fundamentals](./fundamentals_code.ipynb) tutorial using Nitsche's method {cite}`nitsche-Nitsche1971`.
21+
# The idea of weak imposition is that we add additional terms to the variational formulation to
22+
# impose the boundary condition, instead of modifying the matrix system using strong imposition (lifting).
2123
#
2224
# We start by importing the required modules and creating the mesh and function space for our solution
2325

@@ -44,7 +46,12 @@
4446
V = fem.functionspace(domain, ("Lagrange", 1))
4547
# -
4648

47-
# Next, we create a function containing the exact solution (which will also be used in the Dirichlet boundary condition) and the corresponding source function for the right hand side. Note that we use `ufl.SpatialCoordinate` to define the exact solution, which in turn is interpolated into `uD` and used to create the source function `f`.
49+
# Next, we create a function containing the exact solution (which will also be used in the Dirichlet boundary condition)
50+
# and the corresponding source function for the right hand side. Note that we use {py:class}`ufl.SpatialCoordinate`
51+
# to define the exact solution, which in turn is interpolated into {py:class}`uD<dolfinx.fem.Function>`
52+
# by wrapping the {py:class}`ufl-expression<ufl.core.expr.Expr>` as a {py:class}`dolfinx.fem.Expression`.
53+
# For the source function, we use the symbolic differentiation capabilities of UFL to
54+
# compute the negative Laplacian of the exact solution, which we can use directly in the variational formulation.
4855

4956
uD = fem.Function(V)
5057
x = SpatialCoordinate(domain)
@@ -54,21 +61,34 @@
5461

5562
# As opposed to the first tutorial, we now have to have another look at the variational form.
5663
# We start by integrating the problem by parts, to obtain
64+
#
65+
# $$
5766
# \begin{align}
58-
# \int_{\Omega} \nabla u \cdot \nabla v~\mathrm{d}x - \int_{\partial\Omega}\nabla u \cdot n v~\mathrm{d}s = \int_{\Omega} f v~\mathrm{d}x.
67+
# \int_{\Omega} \nabla u \cdot \nabla v~\mathrm{d}x
68+
# - \int_{\partial\Omega}\nabla u \cdot n v~\mathrm{d}s = \int_{\Omega} f v~\mathrm{d}x.
5969
# \end{align}
70+
# $$
71+
#
6072
# As we are not using strong enforcement, we do not set the trace of the test function to $0$ on the outer boundary.
6173
# Instead, we add the following two terms to the variational formulation
74+
#
75+
# $$
6276
# \begin{align}
63-
# -\int_{\partial\Omega} \nabla v \cdot n (u-u_D)~\mathrm{d}s + \frac{\alpha}{h} \int_{\partial\Omega} (u-u_D)v~\mathrm{d}s.
77+
# -\int_{\partial\Omega} \nabla v \cdot n (u-u_D)~\mathrm{d}s
78+
# + \frac{\alpha}{h} \int_{\partial\Omega} (u-u_D)v~\mathrm{d}s.
6479
# \end{align}
80+
# $$
81+
#
6582
# where the first term enforces symmetry to the bilinear form, while the latter term enforces coercivity.
6683
# $u_D$ is the known Dirichlet condition, and $h$ is the diameter of the circumscribed sphere of the mesh element.
6784
# We create bilinear and linear form, $a$ and $L$
85+
#
86+
# $$
6887
# \begin{align}
6988
# a(u, v) &= \int_{\Omega} \nabla u \cdot \nabla v~\mathrm{d}x + \int_{\partial\Omega}-(n \cdot\nabla u) v - (n \cdot \nabla v) u + \frac{\alpha}{h} uv~\mathrm{d}s,\\
7089
# L(v) &= \int_{\Omega} fv~\mathrm{d}x + \int_{\partial\Omega} -(n \cdot \nabla v) u_D + \frac{\alpha}{h} u_Dv~\mathrm{d}s
7190
# \end{align}
91+
# $$
7292

7393
u = TrialFunction(V)
7494
v = TestFunction(V)
@@ -80,7 +100,8 @@
80100
L = inner(f, v) * dx
81101
L += -inner(n, grad(v)) * uD * ds + alpha / h * inner(uD, v) * ds
82102

83-
# As we now have the variational form, we can solve the linear problem
103+
# As we now have the variational form, we can solve the arising
104+
# {py:class}`linear problem<dolfinx.fem.petsc.LinearProblem>`
84105

85106
problem = LinearProblem(a, L, petsc_options_prefix="nitsche_poisson")
86107
uh = problem.solve()
@@ -102,7 +123,9 @@
102123
if domain.comm.rank == 0:
103124
print(f"Error_max : {error_max:.2e}")
104125

105-
# We observe that as we weakly impose the boundary condition, we no longer fullfill the equation to machine precision at the mesh vertices. We also plot the solution using `pyvista`
126+
# We observe that as we weakly impose the boundary condition,
127+
# we no longer fullfill the equation to machine precision at the mesh vertices.
128+
# We also plot the solution using {py:mod}`pyvista`
106129

107130
# +
108131
import pyvista
@@ -120,5 +143,7 @@
120143
# -
121144

122145
# ```{bibliography}
123-
# :filter: cited and ({"chapter1/nitsche"} >= docnames)
146+
# :filter: cited
147+
# :labelprefix:
148+
# :keyprefix: nitsche-
124149
# ```

0 commit comments

Comments
 (0)