Example 1: A non-linear diffusion reaction problem¶
In this example we solve a non-linear diffusion reaction problem, corresponding to the stationary case of the Fisher KPP equation. Specifically, the problem is described by the following equation:
with
being
Substituting the above expression in the Newton formula we obtain the following linearized problem to be solved for
Finally, observing that by definition of Newton update,
As always, fdaPDE requires the weak formulation of the differential problem above. Following the Galerkin approach, let
which we iteratively solve for
Implementation¶
The first step in any finite element code is the definition of the problem geometry. As we are going to iterate several times over the mesh is convinient to compute the cells once and cache them to obtain a fast re-access to the mesh. This option is enabled by activating the cell caching.
Triangulation<2, 2> unit_square = Triangulation<2, 2>::UnitSquare(60, cache_cells);
Once we have a discretization of the domain
FeSpace Vh(unit_square, P1<1>); // piecewise linear continuous scalar finite elements
TrialFunction u(Vh);
TestFunction v(Vh);
Because we are dealing with a non-linear problem, we need an object describing the current and previous solution FeFunction
type, which represents a scalar field
FeFunction u_prev(Vh), solution(Vh);
We now have all the ingredients to assemble our bilinear forms. First we notice that we can save some computational time observing that
being
auto a = integral(unit_square)(dot(grad(u), grad(v)) + (1 - u_prev) * u * v);
auto b = integral(unit_square)(-u_prev * u * v);
We then define the forcing term as a plain ScalarField
togheter with the forcing functional
ScalarField<2, decltype([](const PointT& p) {
return -9*std::pow(p[0], 4) - 12*p[0]*p[0]*p[1]*p[1] + 3*p[0]*p[0] +
2*p[1]*p[1] - 4*std::pow(p[1], 4) - 10;
})> f;
auto F = integral(unit_square, QS2DP4)(f * v);
Observe that we explicitly require an higher order quadrature specifying the quadrature formula QS2DP4
for the exact integration of order 4 polynomials as second argument of the integral
function. Finally, we define non-homegeneous Dirichlet boundary conditions
ScalarField<2, decltype([](const PointT& p) { return 3 * p[0] * p[0] + 2 * p[1] * p[1]; })> g;
auto& dof_handler = Vh.dof_handler();
dof_handler.set_dirichlet_constraint(/* on = */ BoundaryAll, /* data = */ g);
Recall that Dirichlet boundary conditions are implemented as constraints on the degrees of freedom of the linear system enforce_constraints
method.
We can now find an initial point for the Newton scheme. To this end, we solve the linerized problem with initial guess
u_prev = Eigen::Matrix<double, Dynamic, 1>::Zero(Vh.n_dofs()); // initial guess u = 0
Eigen::SparseMatrix<double> A = a.assemble();
Eigen::Matrix<double, Dynamic, 1> v_ = F.assemble();
dof_handler.enforce_constraints(A, v_);
// linear system solve A*u_prev = v_ using Cholesky factorization
Eigen::SimplicialLLT<Eigen::SparseMatrix<double>> lin_solver(A);
u_prev = lin_solver.solve(v_);
The code fragment above effectivelly assemble the discretization matrix A
for the bilinear form v_
of the forcing functional enforce_constaints
method of the dof_handler
object. Finally, observing that the bilinear form is SPD, solves the FEM linear system using a Cholesky factorization and sets
We can finally start looping until convergence, iteratively solving the recurrence
while (err > 1e-7) {
Eigen::SparseMatrix<double> A1 = a.assemble();
Eigen::SparseMatrix<double> A2 = b.assemble();
Eigen::Matrix<double, Dynamic, 1> v = v_ + A2 * u_prev.coeff();
A = A1 + A2;
dof_handler.enforce_constraints(A, v);
lin_solver.compute(A);
solution = lin_solver.solve(v);
// update error
err = (u_prev.coeff() - solution.coeff()).norm();
u_prev = solution;
}
The code just assembles A1
and A2
, updates the right hand side
The complete script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
|

Carlotta Gatti and Emanuele Tamburini. Solving nonlinear elliptic equations with femr. Advanced Programming for Scientific Computing project, 2023.