Skip to content

Commit c8eda85

Browse files
committed
Fast diagonalization demo
1 parent 7008cd7 commit c8eda85

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
Using fast diagonalization solvers in Firedrake
2+
===============================================
3+
4+
In this demo we show how to efficiently solve the Poisson equation using
5+
high-order tensor-product elements. This is done through a special basis,
6+
obtained from the fast diagonalization method (FDM). We first show that in this
7+
basis, the stiffness matrix is sparse, with as many zeros as a low-order method.
8+
We then combine this with an additive Schwarz method. Finally, we show how to
9+
do static condensation using fieldsplit.
10+
11+
Creating an Extruded mesh
12+
-------------------------
13+
14+
The fast diagonalization method produces a basis of discrete eigenfunctions.
15+
These are polynomials, and can be efficiently computed on tensor
16+
product-elements by solving an eigenproblem on the interval. Therefore, we will
17+
require quadrilateral or hexahedral meshes. Currently, the solver only supports
18+
extruded hexahedral meshes, so we must create an :func:`~ExtrudedMesh`. ::
19+
20+
from firedrake import *
21+
22+
base = UnitSquareMesh(8, 8, quadrilateral=True)
23+
mesh = ExtrudedMesh(base, 8)
24+
25+
Defining the problem: the Poisson equation
26+
------------------------------------------
27+
28+
Having defined the mesh we now need to set up our problem. The crucial step
29+
for fast diagonalization is a special choice of basis functions. We obtain them
30+
by passing `variant="fdm"` to the :func:`~FunctionSpace` constructor.
31+
We then define the Poisson problem in the usual way. ::
32+
33+
degree = 5
34+
V = FunctionSpace(mesh, "Q", degree, variant="fdm")
35+
36+
u = TrialFunction(V)
37+
v = TestFunction(V)
38+
39+
a = inner(grad(u), grad(v))*dx
40+
41+
bcs = [DirichletBC(V, 0, sub) for sub in ("on_boundary", "top", "bottom")]
42+
43+
To stress-test the solver, we prescribe a random cofunction as right-hand side ::
44+
45+
rg = RandomGenerator(PCG64(seed=123456789))
46+
L = rg.uniform(V.dual(), -1, 1)
47+
48+
We'll demonstrate a few different sets of solver parameters, so let's define a
49+
function that takes in set of parameters and uses them on a :class:`LinearVariationalSolver`. ::
50+
51+
def run_solve(parameters):
52+
uh = Function(V)
53+
problem = LinearVariationalProblem(a, L, uh, bcs=bcs)
54+
solver = LinearVariationalSolver(problem, solver_parameters=parameters)
55+
solver.solve()
56+
iterations = solver.snes.getLinearSolveIterations()
57+
print("Iterations", iterations)
58+
59+
Specifying the solver
60+
~~~~~~~~~~~~~~~~~~~~~
61+
62+
The solver will avoid the assembly of a matrix with dense O(p^d) x O(p^d)
63+
blocks, and apply a matrix-free conjugate gradients method with a
64+
preconditioner obtained by assembling a sparse matrix with O(dim(V)) nonzeros.
65+
This is done through a python type preconditioner :class:`~.FDMPC`. We define
66+
a function that enables us to compose :class:`~FDMPC` with an inner
67+
preconditioner. ::
68+
69+
def fdm_params(relax):
70+
return {
71+
"mat_type": "matfree",
72+
"ksp_type": "cg",
73+
"ksp_monitor": None,
74+
"pc_type": "python",
75+
"pc_python_type": "firedrake.FDMPC",
76+
"fdm": relax,
77+
}
78+
79+
Let's start with our first test. We'll confirm a working solve by
80+
using a direct method. ::
81+
82+
lu_params = {
83+
"pc_type": "cholesky",
84+
"pc_factor_mat_solver_type": "mumps",
85+
}
86+
87+
print('FDM + Cholesky')
88+
run_solve(fdm_params(lu_params))
89+
90+
Moving on to a more complicated solver, we'll employ a two-level with a Q1
91+
coarse space via :class:`~P1PC`. As the fine level relaxation we define an
92+
additive Scharz method on vertex-star patches implemented via
93+
:class:`~ASMExtrudedStarPC` as we have an extruded mesh::
94+
95+
asm_params = {
96+
"pc_type": "python",
97+
"pc_python_type": "firedrake.P1PC",
98+
"pmg_mg_coarse_mat_type": "aij",
99+
"pmg_mg_coarse": lu_params,
100+
"pmg_mg_levels": {
101+
"ksp_max_it": 1,
102+
"ksp_type": "chebyshev",
103+
"ksp_chebyshev_esteig": "0.125,0.625,0.125,1.125",
104+
"ksp_convergence_test": "skip",
105+
"pc_type": "python",
106+
"pc_python_type": "firedrake.ASMExtrudedStarPC",
107+
"sub_sub_pc_type": "cholesky",
108+
},
109+
}
110+
111+
print('FDM + ASM')
112+
run_solve(fdm_params(asm_params))
113+
114+
Static condensation
115+
-------------------
116+
117+
Finally, we construct :class:`~FDMPC` solver parameters using static
118+
condensation. The fast diagonalization basis diagonalizes the operator on cell
119+
interiors. So we define a solver that splits the interior and facet degrees of
120+
freedom via :class:`~FacetSplitPC` and fieldsplit options. We pass the option
121+
`fdm_static_condensation` to tell :class:`~FDMPC` to assemble a 2-by-2 block
122+
preconditioner where the lower-right block is replaced by the Schur complement
123+
resulting from eliminating the interior degrees of freedom. We use
124+
point-Jacobi to invert the diagonal, and we may apply the two-level additive
125+
Schwarz method on the facets. ::
126+
127+
def fdm_static_condensation_params(relax):
128+
return {
129+
"mat_type": "matfree",
130+
"ksp_type": "cg",
131+
"ksp_monitor": None,
132+
"pc_type": "python",
133+
"pc_python_type": "firedrake.FacetSplitPC",
134+
"facet_pc_type": "python",
135+
"facet_pc_python_type": "firedrake.FDMPC",
136+
"facet_fdm_static_condensation": True,
137+
"facet_fdm_pc_use_amat": False,
138+
"facet_fdm_pc_type": "fieldsplit",
139+
"facet_fdm_pc_fieldsplit_type": "symmetric_multiplicative",
140+
"facet_fdm_fieldsplit_ksp_type": "preonly",
141+
"facet_fdm_fieldsplit_0_pc_type": "jacobi",
142+
"facet_fdm_fieldsplit_1": relax,
143+
}
144+
145+
print('FDM + SC + ASM')
146+
run_solve(fdm_static_condensation_params(asm_params))
147+
148+
A runnable python version of this demo can be found :demo:`here
149+
<fast_diagonalization_poisson.py>`.

0 commit comments

Comments
 (0)