Coverage for pySDC / projects / StroemungsRaum / problem_classes / HeatEquation_2D_FEniCS.py: 100%
57 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-27 07:06 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-27 07:06 +0000
1import logging
3import dolfin as df
4import numpy as np
6from pySDC.core.problem import Problem
7from pySDC.implementations.datatype_classes.fenics_mesh import fenics_mesh, rhs_fenics_mesh
10class fenics_heat2D_mass(Problem):
11 r"""
12 Example implementing the forced two-dimensional heat equation with Dirichlet boundary conditions
14 .. math::
15 \frac{\partial u}{\partial t} = \nu \Delta u + f
17 for :math:`x \in \Omega:=[0,1]x[0,1]`, where the forcing term :math:`f` is defined by
19 .. math::
20 f(x, y, t) = -\sin(\pi x)\sin(\pi y) (\sin(t) - 2\nu \pi^2 \cos(t)).
22 For initial conditions with constant c and
24 .. math::
25 u(x, y, 0) = \sin(\pi x)\sin(\pi y) + c
27 the exact solution of the problem is given by
29 .. math::
30 u(x, y, t) = \sin(\pi x)\sin(\pi y)\cos(t) + c.
32 In this class the spatial part is solved using ``FEniCS`` [1]_. Hence, the problem is reformulated to
33 the *weak formulation*
35 .. math:
36 \int_\Omega u_t v\,dx = - \nu \int_\Omega \nabla u \nabla v\,dx + \int_\Omega f v\,dx.
38 The part containing the forcing term is treated explicitly, where it is interpolated in the function
39 space. The other part will be treated in an implicit way.
41 Parameters
42 ----------
43 c_nvars : int, optional
44 Spatial resolution.
45 t0 : float, optional
46 Starting time.
47 family : str, optional
48 Indicates the family of elements used to create the function space
49 for the trail and test functions. The default is ``'CG'``, which are the class
50 of Continuous Galerkin, a *synonym* for the Lagrange family of elements, see [2]_.
51 order : int, optional
52 Defines the order of the elements in the function space.
53 nu : float, optional
54 Diffusion coefficient :math:`\nu`.
55 c: float, optional
56 Constant for the Dirichlet boundary condition :math: `c`
58 Attributes
59 ----------
60 V : FunctionSpace
61 Defines the function space of the trial and test functions.
62 M : scalar, vector, matrix or higher rank tensor
63 Denotes the expression :math:`\int_\Omega u_t v\,dx`.
64 K : scalar, vector, matrix or higher rank tensor
65 Denotes the expression :math:`- \nu \int_\Omega \nabla u \nabla v\,dx`.
66 g : Expression
67 The forcing term :math:`f` in the heat equation.
68 bc : DirichletBC
69 Denotes the Dirichlet boundary conditions.
71 References
72 ----------
73 .. [1] The FEniCS Project Version 1.5. M. S. Alnaes, J. Blechta, J. Hake, A. Johansson, B. Kehlet, A. Logg,
74 C. Richardson, J. Ring, M. E. Rognes, G. N. Wells. Archive of Numerical Software (2015).
75 .. [2] Automated Solution of Differential Equations by the Finite Element Method. A. Logg, K.-A. Mardal, G. N.
76 Wells and others. Springer (2012).
77 """
79 dtype_u = fenics_mesh
80 dtype_f = rhs_fenics_mesh
82 def __init__(self, c_nvars=64, t0=0.0, family='CG', order=2, nu=0.1, c=0.0):
84 # define the Dirichlet boundary
85 def Boundary(x, on_boundary):
86 return on_boundary
88 # set solver and form parameters
89 df.parameters["form_compiler"]["optimize"] = True
90 df.parameters["form_compiler"]["cpp_optimize"] = True
91 df.parameters['allow_extrapolation'] = True
93 # set mesh and refinement (for multilevel)
94 mesh = df.UnitSquareMesh(c_nvars, c_nvars)
96 # define function space for future reference
97 self.V = df.FunctionSpace(mesh, family, order)
98 tmp = df.Function(self.V)
99 self.logger.debug('DoFs on this level:', len(tmp.vector()[:]))
101 # invoke super init, passing number of dofs, dtype_u and dtype_f
102 super().__init__(self.V)
103 self._makeAttributeAndRegister('c_nvars', 't0', 'family', 'order', 'nu', 'c', localVars=locals(), readOnly=True)
105 # Stiffness term (Laplace)
106 u = df.TrialFunction(self.V)
107 v = df.TestFunction(self.V)
108 a_K = -1.0 * df.inner(df.nabla_grad(u), self.nu * df.nabla_grad(v)) * df.dx
110 # Mass term
111 a_M = u * v * df.dx
113 self.M = df.assemble(a_M)
114 self.K = df.assemble(a_K)
116 # set boundary values
117 self.u_D = df.Expression('sin(a*x[0]) * sin(a*x[1]) * cos(t) + c', c=self.c, a=np.pi, t=t0, degree=self.order)
118 self.bc = df.DirichletBC(self.V, self.u_D, Boundary)
119 self.bc_hom = df.DirichletBC(self.V, df.Constant(0), Boundary)
121 self.fix_bc_for_residual = True
123 # set forcing term as expression
124 self.g = df.Expression(
125 '-sin(a*x[0]) * sin(a*x[1]) * (sin(t) - 2*b*a*a*cos(t))',
126 a=np.pi,
127 b=self.nu,
128 t=self.t0,
129 degree=self.order,
130 )
132 def solve_system(self, rhs, factor, u0, t):
133 r"""
134 Dolfin's linear solver for :math:`(M - factor A) \vec{u} = \vec{rhs}`.
136 Parameters
137 ----------
138 rhs : dtype_f
139 Right-hand side for the nonlinear system.
140 factor : float
141 Abbrev. for the node-to-node stepsize (or any other factor required).
142 u0 : dtype_u
143 Initial guess for the iterative solver (not used here so far).
144 t : float
145 Current time.
147 Returns
148 -------
149 u : dtype_u
150 Solution.
151 """
153 u = self.dtype_u(u0)
154 T = self.M - factor * self.K
155 b = self.dtype_u(rhs)
157 self.u_D.t = t
159 self.bc.apply(T, b.values.vector())
160 self.bc.apply(b.values.vector())
162 df.solve(T, u.values.vector(), b.values.vector())
164 return u
166 def eval_f(self, u, t):
167 """
168 Routine to evaluate both parts of the right-hand side.
170 Parameters
171 ----------
172 u : dtype_u
173 Current values of the numerical solution.
174 t : float
175 Current time at which the numerical solution is computed.
177 Returns
178 -------
179 f : dtype_f
180 The right-hand side divided into two parts.
181 """
183 f = self.dtype_f(self.V)
185 self.K.mult(u.values.vector(), f.impl.values.vector())
187 self.g.t = t
188 f.expl = self.apply_mass_matrix(self.dtype_u(df.interpolate(self.g, self.V)))
189 return f
191 def apply_mass_matrix(self, u):
192 r"""
193 Routine to apply mass matrix.
195 Parameters
196 ----------
197 u : dtype_u
198 Current values of the numerical solution.
200 Returns
201 -------
202 me : dtype_u
203 The product :math:`M \vec{u}`.
204 """
206 me = self.dtype_u(self.V)
207 self.M.mult(u.values.vector(), me.values.vector())
209 return me
211 def u_exact(self, t):
212 r"""
213 Routine to compute the exact solution at time :math:`t`.
215 Parameters
216 ----------
217 t : float
218 Time of the exact solution.
220 Returns
221 -------
222 me : dtype_u
223 Exact solution.
224 """
225 u0 = df.Expression('sin(a*x[0]) * sin(a*x[1]) * cos(t) + c', c=self.c, a=np.pi, t=t, degree=self.order)
226 me = self.dtype_u(df.interpolate(u0, self.V), val=self.V)
228 return me
230 def fix_residual(self, res):
231 """
232 Applies homogeneous Dirichlet boundary conditions to the residual
234 Parameters
235 ----------
236 res : dtype_u
237 Residual
238 """
239 self.bc_hom.apply(res.values.vector())
240 return None