246#include "allheaders.h"
247#include "nonlinear_heat.h"
262 const QGauss<1> face_quadrature_formula(fe.degree+1);
271 const unsigned int dofs_per_cell = fe.dofs_per_cell;
272 const unsigned int n_q_points = quadrature_formula.size();
273 const unsigned int n_q_f_points = face_quadrature_formula.size();
281should be fairly straightforward. The variable `cell_rhs` holds the local (
for an element or a cell) residual. Note that we use the `
FEFaceValues<2>` as we will need to apply the Neuman boundary conditions on the right hand side of the domain.
283The lines below define the *Number type* we want to use to define our variables so that they are suitable
for automatic differentiation. Here, we are
using variables that will allow /automatic differentiation/. (The other option is symbolic differentiation
using SYMENGINE package. Here we are
using the automatic differentiation
using SACADO.)
287 using ADNumberType =
typename ADHelper::ad_type;
291The following lines are once again straightforward to understand.
295 std::vector<types::global_dof_index> local_dof_indices(
301 std::vector<double> consol(n_q_points);
304We only comment that the variable `consol` holds the values of the variable `converged_solution` at the gauss points of the current, active cell. Similarly, the variable `consol_grad` holds the values of the
gradients of the `converged_solution` at the gauss points. The variable `converged_solution` refers is solution at the previous time step (time step @f$s@f$).
307In following lines the variables `old_solution` and `old_solution_grad` are
308crucial. They define the variables of the appropriate number type
using which we will define our residual, so that it is suitable
for the automatic differentiation. `old_solution` is a solution variable of the appropriate number type with respect to which we need to define the residual and then
differentiate it to get the Jacobian. For ease of understanding, one may consider
this variable to be a special variable, that will look like
this
310 \text{old_solution} =
312 \psi_i (\zeta_1,\eta_1) d_i\\
313 \psi_i (\zeta_2,\eta_2) d_i\\
314 \psi_i (\zeta_3,\eta_3) d_i\\
315 \psi_i (\zeta_4,\eta_4) d_i
318where @f$\psi_i@f$ are the shape
functions and @f$d_i@f$ is some way of representing the primary variable in the problem (in
this case the temperature). Could be viewed as something like a symbol, which could be used to define
functions and then differentiated later. `old_solution` holds the values in the corresponding gauss points and the repeated indices in the previous equation indicate summation over the nodes. Similarly, the variable `old_solution_grad` holds the corresponding
gradients.
320 std::vector<ADNumberType> old_solution(
325 std::vector<Tensor<1, 2>> consol_grad(
327 std::vector<Tensor<1, 2,ADNumberType>> old_solution_grad(
342are straightforward to understand. The vector `residual` will hold the *numerical*
value of the residual and is hence initialized to be zero.
346 for (
const auto &cell: dof_handler.active_cell_iterators())
349 fe_values.reinit(cell);
350 cell->get_dof_indices(local_dof_indices);
351 const unsigned int n_independent_variables = local_dof_indices.size();
352 const unsigned int n_dependent_variables = dofs_per_cell;
355should be clear. In the following line we inform how many
independent and dependent variables we will have.
357 ADHelper ad_helper(n_independent_variables, n_dependent_variables);
360then, in the following line we tell the system, at which
point it should numerically evaluate the residual. This is the
point given by `evaluation_point`, which will contain the solution that is getting iterated to find the actual solution
for each time step.
362 ad_helper.register_dof_values(evaluation_point,local_dof_indices);
368 const std::vector<ADNumberType> &dof_values_ad = ad_helper.get_sensitive_dof_values();
369 fe_values[t].get_function_values_from_local_dof_values(dof_values_ad,
371 fe_values[t].get_function_gradients_from_local_dof_values(dof_values_ad,
375the actual storage of the values of the shape
functions and its derivatives at the gauss points is taking place in the variables `old_solution` and `old_solution_grad`.
379 fe_values[t].get_function_values(converged_solution, consol);
380 fe_values[t].get_function_gradients(converged_solution, consol_grad);
383the values of the `converged_solution` and its
gradients get stored in the variables `consol` and `con_sol_grad`.
387 for (
unsigned int q_index = 0; q_index < n_q_points; ++q_index)
389 for (
unsigned int i = 0; i < dofs_per_cell; ++i)
394 ADNumberType MijTjcurr = Cp*rho*fe_values[t].value(i,q_index)*old_solution[q_index];
395 ADNumberType MijTjprev = Cp*rho*fe_values[t].value(i,q_index)*consol[q_index];
396 ADNumberType k_curr = a +
b*old_solution[q_index] + c*
std::pow(old_solution[q_index],2);
397 ADNumberType k_prev = a +
b*consol[q_index] + c*
std::pow(consol[q_index],2);
398 ADNumberType Licurr = alpha * delta_t * (fe_values[t].gradient(i,q_index)*k_curr*old_solution_grad[q_index]);
399 ADNumberType Liprev = (1-alpha) * delta_t * (fe_values[t].
gradient(i,q_index)*k_prev*consol_grad[q_index]);
400 residual_ad[i] += (MijTjcurr+Licurr-MijTjprev+Liprev)*fe_values.JxW(q_index);
433 residual_ad[i]+= -delta_t*(-10)*fe_face_values[t].
value(i,q_point)*fe_face_values.JxW(q_point);
443 ad_helper.register_residual_vector(residual_ad);
446actually tells the `ad_helper` that
this is the residual it should use, and the line
448 ad_helper.compute_residual(cell_rhs);
451evaluates the residual at the appropriate
point, which is the `evaluation_point`. Then
453 cell->get_dof_indices(local_dof_indices);
455 for (
unsigned int i =0;i < dofs_per_cell; ++i)
456 residual(local_dof_indices[i])+= cell_rhs(i);
461are used to calculate the global `residual` from the local `cell_rhs`. Now `residual` is numerical and not of the special number type. Finally, lines
466 std::cout <<
" The Norm is :: = " << residual.l2_norm() << std::endl;
470are routine, in particular, it makes the global degrees of freedom, where the
dirichlet boundary conditions are applied to be zero.
473#### The compute_jacobian() function
475The `compute_jacobian()` only takes in the `evaluation_point` as an input and is identical to the
476`compute_residual()` except for some minor modification. In particular the line
478 ad_helper.compute_linearization(cell_matrix);
481computes the actual jacobian by performing the automatic differentiation and evaluates it at the `evaluation_point`. The remaining lines are routine and should be straightforward.
484### General ideas involved in solving coupled nonlinear equations using Newton Raphson's technique
485Consider that we have the equations
487 f_i^1(p_j^{{s+1}}, T_j^{{s+1}})=0 \\
488 f_i^2(p_j^{{s+1}}, T_j^{{s+1}})=0
490are coupled non-linear algebraic equations in the variables @f$p_j@f$ and @f$T_j@f$. In the problem we are trying to solve, we only have one unknown, but
this set of derivation clarifies what one should
do for coupled problems as well.
492We need to solve the
set equations immediately above
using Newton-Raphson
's technique. Now suppose we have 4 noded linear element, with values as @f$p_1^{{s+1}},p_2^{{s+1}},p_3^{{s+1}},p_4^{{s+1}}@f$ and @f$T_1^{{s+1}},T_2^{{s+1}},T_3^{{s+1}},T_4^{{s+1}}@f$, then actually @f$f_i^1@f$ and @f$f_i^2@f$ are
494 f_i^1(p_1^{{s+1}},p_2^{{s+1}},p_3^{{s+1}},p_4^{{s+1}},T_1^{{s+1}},T_2^{{s+1}},T_3^{{s+1}},T_4^{{s+1}})=0 \\
495 f_i^2(p_1^{{s+1}},p_2^{{s+1}},p_3^{{s+1}},p_4^{{s+1}},T_1^{{s+1}},T_2^{{s+1}},T_3^{{s+1}},T_4^{{s+1}})=0
497Also, note that @f$i@f$ goes from 1 to 4 for @f$f^1@f$ and from 1 to 4 for @f$f^2@f$. For the Newton Raphson's technique, we have to find the Jacobian. This is written explicitly below
for clear understanding.
501 \frac{\partial f^1_1}{\partial p^{s+1}_1} & \frac{\partial f^1_1}{\partial p^{s+1}_2} & \frac{\partial f^1_1}{\partial p^{s+1}_3} & \frac{\partial f^1_1}{\partial p^{s+1}_4} & \frac{\partial f^1_1}{\partial T^{s+1}_1} & \frac{\partial f^1_1}{\partial T^{s+1}_2}&\frac{\partial f^1_1}{\partial T^{s+1}_3}&\frac{\partial f^1_1}{\partial T^{s+1}_4}\\
503 \frac{\partial f^1_2}{\partial p^{s+1}_1} & \frac{\partial f^1_2}{\partial p^{s+1}_2} & \frac{\partial f^1_2}{\partial p^{s+1}_3} & \frac{\partial f^1_2}{\partial p^{s+1}_4} & \frac{\partial f^1_2}{\partial T^{s+1}_1} & \frac{\partial f^1_2}{\partial T^{s+1}_2}&\frac{\partial f^1_2}{\partial T^{s+1}_3}&\frac{\partial f^1_2}{\partial T^{s+1}_4}\\
505 \frac{\partial f^1_3}{\partial p^{s+1}_1} & \frac{\partial f^1_3}{\partial p^{s+1}_2} & \frac{\partial f^1_3}{\partial p^{s+1}_3} & \frac{\partial f^1_3}{\partial p^{s+1}_4} & \frac{\partial f^1_3}{\partial T^{s+1}_1} & \frac{\partial f^1_3}{\partial T^{s+1}_2}&\frac{\partial f^1_3}{\partial T^{s+1}_3}&\frac{\partial f^1_3}{\partial T^{s+1}_4}\\
507 \frac{\partial f^1_4}{\partial p^{s+1}_1} & \frac{\partial f^1_4}{\partial p^{s+1}_2} & \frac{\partial f^1_4}{\partial p^{s+1}_3} & \frac{\partial f^1_4}{\partial p^{s+1}_4} & \frac{\partial f^1_4}{\partial T^{s+1}_1} & \frac{\partial f^1_4}{\partial T^{s+1}_2}&\frac{\partial f^1_4}{\partial T^{s+1}_3}&\frac{\partial f^1_4}{\partial T^{s+1}_4}\\
508 %================================================================
509 \frac{\partial f^2_1}{\partial p^{s+1}_1} & \frac{\partial f^2_1}{\partial p^{s+1}_2} & \frac{\partial f^2_1}{\partial p^{s+1}_3} & \frac{\partial f^2_1}{\partial p^{s+1}_4} & \frac{\partial f^2_1}{\partial T^{s+1}_1} & \frac{\partial f^2_1}{\partial T^{s+1}_2}&\frac{\partial f^2_1}{\partial T^{s+1}_3}&\frac{\partial f^2_1}{\partial T^{s+1}_4}\\
511 \frac{\partial f^2_2}{\partial p^{s+1}_1} & \frac{\partial f^2_2}{\partial p^{s+1}_2} & \frac{\partial f^2_2}{\partial p^{s+1}_3} & \frac{\partial f^2_2}{\partial p^{s+1}_4} & \frac{\partial f^2_2}{\partial T^{s+1}_1} & \frac{\partial f^2_2}{\partial T^{s+1}_2}&\frac{\partial f^2_2}{\partial T^{s+1}_3}&\frac{\partial f^2_2}{\partial T^{s+1}_4}\\
513 \frac{\partial f^2_3}{\partial p^{s+1}_1} & \frac{\partial f^2_3}{\partial p^{s+1}_2} & \frac{\partial f^2_3}{\partial p^{s+1}_3} & \frac{\partial f^2_3}{\partial p^{s+1}_4} & \frac{\partial f^2_3}{\partial T^{s+1}_1} & \frac{\partial f^2_3}{\partial T^{s+1}_2}&\frac{\partial f^2_3}{\partial T^{s+1}_3}&\frac{\partial f^2_3}{\partial T^{s+1}_4}\\
515 \frac{\partial f^2_4}{\partial p^{s+1}_1} & \frac{\partial f^2_4}{\partial p^{s+1}_2} & \frac{\partial f^2_4}{\partial p^{s+1}_3} & \frac{\partial f^2_4}{\partial p^{s+1}_4} & \frac{\partial f^2_4}{\partial T^{s+1}_1} & \frac{\partial f^2_4}{\partial T^{s+1}_2}&\frac{\partial f^2_4}{\partial T^{s+1}_3}&\frac{\partial f^2_4}{\partial T^{s+1}_4}
518We use the Jacobian evaluated at the values of the variables (@f$p_j^{{s+1}}@f$, @f$T_j^{{s+1}}@f$) at the /previous iteration/ (denoted by the variable `present_solution` in the code) to obtain a
new value of the required vector. That is
542 \frac{\partial f^1_1}{\partial p^{s+1}_1} & \frac{\partial f^1_1}{\partial p^{s+1}_2} & \frac{\partial f^1_1}{\partial p^{s+1}_3} & \frac{\partial f^1_1}{\partial p^{s+1}_4} & \frac{\partial f^1_1}{\partial T^{s+1}_1} & \frac{\partial f^1_1}{\partial T^{s+1}_2}&\frac{\partial f^1_1}{\partial T^{s+1}_3}&\frac{\partial f^1_1}{\partial T^{s+1}_4}\\
544 \frac{\partial f^1_2}{\partial p^{s+1}_1} & \frac{\partial f^1_2}{\partial p^{s+1}_2} & \frac{\partial f^1_2}{\partial p^{s+1}_3} & \frac{\partial f^1_2}{\partial p^{s+1}_4} & \frac{\partial f^1_2}{\partial T^{s+1}_1} & \frac{\partial f^1_2}{\partial T^{s+1}_2}&\frac{\partial f^1_2}{\partial T^{s+1}_3}&\frac{\partial f^1_2}{\partial T^{s+1}_4}\\
546 \frac{\partial f^1_3}{\partial p^{s+1}_1} & \frac{\partial f^1_3}{\partial p^{s+1}_2} & \frac{\partial f^1_3}{\partial p^{s+1}_3} & \frac{\partial f^1_3}{\partial p^{s+1}_4} & \frac{\partial f^1_3}{\partial T^{s+1}_1} & \frac{\partial f^1_3}{\partial T^{s+1}_2}&\frac{\partial f^1_3}{\partial T^{s+1}_3}&\frac{\partial f^1_3}{\partial T^{s+1}_4}\\
548 \frac{\partial f^1_4}{\partial p^{s+1}_1} & \frac{\partial f^1_4}{\partial p^{s+1}_2} & \frac{\partial f^1_4}{\partial p^{s+1}_3} & \frac{\partial f^1_4}{\partial p^{s+1}_4} & \frac{\partial f^1_4}{\partial T^{s+1}_1} & \frac{\partial f^1_4}{\partial T^{s+1}_2}&\frac{\partial f^1_4}{\partial T^{s+1}_3}&\frac{\partial f^1_4}{\partial T^{s+1}_4}\\
549 %================================================================
550 \frac{\partial f^2_1}{\partial p^{s+1}_1} & \frac{\partial f^2_1}{\partial p^{s+1}_2} & \frac{\partial f^2_1}{\partial p^{s+1}_3} & \frac{\partial f^2_1}{\partial p^{s+1}_4} & \frac{\partial f^2_1}{\partial T^{s+1}_1} & \frac{\partial f^2_1}{\partial T^{s+1}_2}&\frac{\partial f^2_1}{\partial T^{s+1}_3}&\frac{\partial f^2_1}{\partial T^{s+1}_4}\\
552 \frac{\partial f^2_2}{\partial p^{s+1}_1} & \frac{\partial f^2_2}{\partial p^{s+1}_2} & \frac{\partial f^2_2}{\partial p^{s+1}_3} & \frac{\partial f^2_2}{\partial p^{s+1}_4} & \frac{\partial f^2_2}{\partial T^{s+1}_1} & \frac{\partial f^2_2}{\partial T^{s+1}_2}&\frac{\partial f^2_2}{\partial T^{s+1}_3}&\frac{\partial f^2_2}{\partial T^{s+1}_4}\\
554 \frac{\partial f^2_3}{\partial p^{s+1}_1} & \frac{\partial f^2_3}{\partial p^{s+1}_2} & \frac{\partial f^2_3}{\partial p^{s+1}_3} & \frac{\partial f^2_3}{\partial p^{s+1}_4} & \frac{\partial f^2_3}{\partial T^{s+1}_1} & \frac{\partial f^2_3}{\partial T^{s+1}_2}&\frac{\partial f^2_3}{\partial T^{s+1}_3}&\frac{\partial f^2_3}{\partial T^{s+1}_4}\\
556 \frac{\partial f^2_4}{\partial p^{s+1}_1} & \frac{\partial f^2_4}{\partial p^{s+1}_2} & \frac{\partial f^2_4}{\partial p^{s+1}_3} & \frac{\partial f^2_4}{\partial p^{s+1}_4} & \frac{\partial f^2_4}{\partial T^{s+1}_1} & \frac{\partial f^2_4}{\partial T^{s+1}_2}&\frac{\partial f^2_4}{\partial T^{s+1}_3}&\frac{\partial f^2_4}{\partial T^{s+1}_4}
569The @f$J_{pq}@f$ is evaluated with values obtained at the @f$k^{th}@f$ iteration. To avoid taking the inverse, we solve the following,
for the changes @f$\Delta p@f$ and @f$\Delta T@f$.
572 \frac{\partial f^1_1}{\partial p^{s+1}_1} & \frac{\partial f^1_1}{\partial p^{s+1}_2} & \frac{\partial f^1_1}{\partial p^{s+1}_3} & \frac{\partial f^1_1}{\partial p^{s+1}_4} & \frac{\partial f^1_1}{\partial T^{s+1}_1} & \frac{\partial f^1_1}{\partial T^{s+1}_2}&\frac{\partial f^1_1}{\partial T^{s+1}_3}&\frac{\partial f^1_1}{\partial T^{s+1}_4}\\
574 \frac{\partial f^1_2}{\partial p^{s+1}_1} & \frac{\partial f^1_2}{\partial p^{s+1}_2} & \frac{\partial f^1_2}{\partial p^{s+1}_3} & \frac{\partial f^1_2}{\partial p^{s+1}_4} & \frac{\partial f^1_2}{\partial T^{s+1}_1} & \frac{\partial f^1_2}{\partial T^{s+1}_2}&\frac{\partial f^1_2}{\partial T^{s+1}_3}&\frac{\partial f^1_2}{\partial T^{s+1}_4}\\
576 \frac{\partial f^1_3}{\partial p^{s+1}_1} & \frac{\partial f^1_3}{\partial p^{s+1}_2} & \frac{\partial f^1_3}{\partial p^{s+1}_3} & \frac{\partial f^1_3}{\partial p^{s+1}_4} & \frac{\partial f^1_3}{\partial T^{s+1}_1} & \frac{\partial f^1_3}{\partial T^{s+1}_2}&\frac{\partial f^1_3}{\partial T^{s+1}_3}&\frac{\partial f^1_3}{\partial T^{s+1}_4}\\
578 \frac{\partial f^1_4}{\partial p^{s+1}_1} & \frac{\partial f^1_4}{\partial p^{s+1}_2} & \frac{\partial f^1_4}{\partial p^{s+1}_3} & \frac{\partial f^1_4}{\partial p^{s+1}_4} & \frac{\partial f^1_4}{\partial T^{s+1}_1} & \frac{\partial f^1_4}{\partial T^{s+1}_2}&\frac{\partial f^1_4}{\partial T^{s+1}_3}&\frac{\partial f^1_4}{\partial T^{s+1}_4}\\
579 %================================================================
580 \frac{\partial f^2_1}{\partial p^{s+1}_1} & \frac{\partial f^2_1}{\partial p^{s+1}_2} & \frac{\partial f^2_1}{\partial p^{s+1}_3} & \frac{\partial f^2_1}{\partial p^{s+1}_4} & \frac{\partial f^2_1}{\partial T^{s+1}_1} & \frac{\partial f^2_1}{\partial T^{s+1}_2}&\frac{\partial f^2_1}{\partial T^{s+1}_3}&\frac{\partial f^2_1}{\partial T^{s+1}_4}\\
582 \frac{\partial f^2_2}{\partial p^{s+1}_1} & \frac{\partial f^2_2}{\partial p^{s+1}_2} & \frac{\partial f^2_2}{\partial p^{s+1}_3} & \frac{\partial f^2_2}{\partial p^{s+1}_4} & \frac{\partial f^2_2}{\partial T^{s+1}_1} & \frac{\partial f^2_2}{\partial T^{s+1}_2}&\frac{\partial f^2_2}{\partial T^{s+1}_3}&\frac{\partial f^2_2}{\partial T^{s+1}_4}\\
584 \frac{\partial f^2_3}{\partial p^{s+1}_1} & \frac{\partial f^2_3}{\partial p^{s+1}_2} & \frac{\partial f^2_3}{\partial p^{s+1}_3} & \frac{\partial f^2_3}{\partial p^{s+1}_4} & \frac{\partial f^2_3}{\partial T^{s+1}_1} & \frac{\partial f^2_3}{\partial T^{s+1}_2}&\frac{\partial f^2_3}{\partial T^{s+1}_3}&\frac{\partial f^2_3}{\partial T^{s+1}_4}\\
586 \frac{\partial f^2_4}{\partial p^{s+1}_1} & \frac{\partial f^2_4}{\partial p^{s+1}_2} & \frac{\partial f^2_4}{\partial p^{s+1}_3} & \frac{\partial f^2_4}{\partial p^{s+1}_4} & \frac{\partial f^2_4}{\partial T^{s+1}_1} & \frac{\partial f^2_4}{\partial T^{s+1}_2}&\frac{\partial f^2_4}{\partial T^{s+1}_3}&\frac{\partial f^2_4}{\partial T^{s+1}_4}
611 J_{pq}\{\Delta solution\}^{k+1}=\{function\}^{k}
613Then add
this to the
value of
626We keep doing
this, until the @f$L_2@f$-error of the vector is small. Determining whether the
norm is small can be done
using either
628 ||(function)^k||<\delta^0 ||function^0||
634where @f$tol@f$ is some small number. In the code, we use the
second of these two choices. The
final converged solution in the one that satisfies the
set of equations and is called by the variable `converged_solution` in the code. This iterative process is carried out
for every time step. The variable `present_solution` is given the
value of `converged_solution` at the beginning of the iteration of a given time step, so that it serves as a better
initial guess
for the nonlinear
set of equations
for the next time step.
637#### The
run() function
639The function which implements the nonlinear solver is the `run()` function for every time step. In particular the lines
642 additional_data.abs_tol = target_tolerance;
643 additional_data.max_iter = 100;
650 nonlinear_solver.residual =
653 compute_residual(evaluation_point, residual);
660 nonlinear_solver.setup_jacobian =
662 compute_jacobian(current_u);
668 nonlinear_solver.solve_with_jacobian = [&](
const Vector<double> &rhs,
670 const double tolerance) {
671 solve(rhs, dst, tolerance);
677 nonlinear_solver.solve(present_solution);
681Here, one needs to give how the residual is to be calculated and at what
point, how the jacobian is to be calculated and at what
point and
finally, how to solve the linear system occurring during each iteration. These are given as `
lambda`
functions.
686 additional_data.abs_tol = target_tolerance;
687 additional_data.max_iter = 100;
692setup the solver and give any basic data needed, such as the tolerance to converge or the maximum number of iterations it can
try.
696 nonlinear_solver.residual =
699 compute_residual(evaluation_point, residual);
703essentially, defines the function from where the residual will be calculated. Notice here that the `compute_residual()` function is called with `evaluation_point` and `residual`.
707 nonlinear_solver.setup_jacobian =
709 compute_jacobian(current_u);
713define the function from where the jacobian will be calculated. Note that it takes in a variable `current_u` which will be the `evaluation_point`
for the computing the jacobian. Thus, the actual points
for evaluating the jacobian and the residual need not be the same.
717 nonlinear_solver.solve_with_jacobian = [&](
const Vector<double> &rhs,
719 const double tolerance) {
720 solve(rhs, dst, tolerance);
724tells the solution needs to be done with the jacobian
using the function `solve()`.
728 nonlinear_solver.solve_with_jacobian = [&](
const Vector<double> &rhs,
730 const double tolerance) {
731 solve(rhs, dst, tolerance);
735actually calls the nonlinear solver.
737For details as to how these
lambda functions work together, please see @ref step_77
"step-77" and also the file `tests/trilinos/step-77-with-nox.cc`. Overall, the variable `present_solution` is presented as an
initial guess to the non-linear solver, which performs the iteration and gives the
final converged solution in the same variable. Hence,
this will be the converged solution
for the current step and
this assignment happens in the following line
739 converged_solution = present_solution;
742The variable `present_solution` is assigned the
value of `converged_solution` from the previous time step to serve as an
initial guess. This is done in line 45.
744 present_solution = converged_solution;
751The results are essentially the time evolution of the temperature throughout the domain. The
first of the pictures below shows the temperature distribution at the
final step, i.e. at time @f$t=5@f$. This should be very similar to the figure at the bottom on the page [here](https:
753![image](../code-gallery/nonlinear-heat_transfer_with_AD_NOX/doc/Images/contour.png)
755Contour plot of the temperature at the
final step
757![image](../code-gallery/nonlinear-heat_transfer_with_AD_NOX/doc/Images/Temp_evol.png)
759Evolution of temperature at a
point close to the right edge @f$\approx (0.49, 0.12)@f$
761In closing, we give some ideas as to how the residuals and the jacobians are actually calculated in a finite element setting.
764### Evaluating the function at previous iterations
766For solving the linear equation every iteration, we have to evaluate the right hand side
functions (@f$f_i^1@f$ and @f$f_i^2@f$) at the values @f$p@f$ and @f$T@f$ had at their previous iteration. For instance, in the current problem consider the
final equation above (we only have only one function @f$f_I@f$ in
this case), which is
768 M_{IJ}T_{J}^{s+1} + \alpha \Delta t L_I^{s+1} - M_{IJ}T_{J}^s + \Delta t \left( 1-\alpha \right) L_I^s - \Delta t Q_I = R_I^{s+1}
770Each term is written in the following manner. We write it
for only one term to illustrate the details.
771Consider @f$\alpha \Delta t L_I^{s+1} @f$
773\alpha \Delta t \int_{\Omega_e} \left( \psi_{I,i} \left( k (\psi_PT_P) \psi_{J,i}T_J \right) - \psi_I f \right)
dx dy = L_I^{s+1}
777 \alpha \Delta t \int_{\Omega_e} \left( \psi_{I,i} \left( k (\psi_PT_P^{s+1}) \psi_{J,i}T_J^{s+1} \right) - \psi_I f \right)
dx dy = L_I^{s+1}
779again, the term @f$\psi_{J,i} T_J^{{s+1}}@f$ is
nothing but the
value of
gradient of @f$T^{{s+1}}@f$ at whatever
point the
gradient of @f$\psi_{J}@f$ is evaluated in. We write
this gradient as @f$\nabla T^{{s+1}}(x,y)@f$. Similarly, @f$\psi_PT_P^{{s+1}}@f$ is the
value of the temperature at the
point where @f$\psi_P@f$ is evaluated and we call it @f$T^{{s+1}}@f$. While evaluating these terms to calculate the residual, we will use Gauss quadrature and
this evaluation of
this integral would become something like
781L_I^{{s+1}} = \Delta t \alpha \sum_{h} \nabla \psi_I (\xi_h,\eta_h) \cdot k(T^{{s+1}}) \nabla T^{{s+1}}(\xi_h,\eta_h) w_hJ_h
783where @f$h@f$ runs over the total number of quadrature points in the finite element and @f$w_hJ_h@f$ takes care of appropriate weights needed and other corrections needed to due to transforming the finite element onto a master element (See any finite element book
for details). All other terms can also be calculated in a similar manner to obtain the per-element residual from the
first equation in
this section. This has to assembled over all finite elements to get the global residual.
785Similar evaluations have to be done to obtain the Jacobian as well. In the current
case, the per element Jacobian is given by
787 J^e_{IQ} = \frac{\partial R_I^{s+1}}{\partial T_{Q}^{s+1}} = M_{IQ} + \alpha \Delta t \frac{\partial L_I^{s+1}}{\partial T_Q}
791 J_{IQ}^
e = \int_{\Omega_e}\rho C_p \psi_I \psi_J
dx dy +
792 \alpha \Delta t \int_{\Omega_e} \left( \psi_{I,i} (\psi_{J,i}T_J) (b\psi_Q + 2c (\psi_Y T_Y) \psi_Q) + \psi_{I,i}\psi_{Q,i}(a +
b (\psi_R T_R) + c \left( \psi_Y T_Y) \right)^2 \right)
dx dy
794which are evaluated
using gauss quadrature by summing the
functions after evaluation at the gauss points. Once again, the terms @f$\psi_{J,i}T_J@f$ and @f$\psi_R T_R@f$ are the values of the
gradients of the temperature and the temperature itself at the Gauss points.
800To understand
this application, @ref step_71
"step-71", @ref step_72
"step-72" and @ref step_77
"step-77" are needed.
803<a name=
"ann-mesh/README.md"></a>
804<h1>Annotated version of mesh/README.md</h1>
805The following copyright notice applies to the mesh files in
this directory:
818<a name=
"ann-include/allheaders.h"></a>
819<h1>Annotated version of include/allheaders.h</h1>
835 * #ifndef __ALLHEADERS_H_INCLUDED__
836 * #define __ALLHEADERS_H_INCLUDED__
839 * These are some of the header files
for this programme. Most of them
840 * are common from previous steps.
843 * #include <deal.II/base/quadrature_lib.h>
844 * #include <deal.II/base/function_lib.h>
845 * #include <deal.II/lac/generic_linear_algebra.h>
846 * #include <deal.II/base/function.h>
848 * #include <deal.II/dofs/dof_handler.h>
849 * #include <deal.II/dofs/dof_tools.h>
851 * #include <deal.II/fe/fe_values.h>
852 * #include <deal.II/fe/fe.h>
854 * #include <deal.II/grid/tria.h>
855 * #include <deal.II/grid/grid_generator.h>
857 * #include <deal.II/lac/dynamic_sparsity_pattern.h>
858 * #include <deal.II/lac/full_matrix.h>
859 * #include <deal.II/lac/precondition.h>
860 * #include <deal.II/lac/solver_cg.h>
861 * #include <deal.II/lac/sparse_matrix.h>
862 * #include <deal.II/lac/vector.h>
863 * #include <deal.II/grid/grid_tools.h>
865 * #include <deal.II/numerics/data_out.h>
866 * #include <deal.II/numerics/vector_tools.h>
867 * #include <deal.II/fe/fe_q.h>
868 * #include <deal.II/grid/grid_out.h>
869 * #include <deal.II/grid/grid_in.h>
871 * #include <deal.II/fe/fe_system.h>
873 * #include <deal.II/grid/tria_accessor.h>
874 * #include <deal.II/grid/tria_iterator.h>
875 * #include <deal.II/dofs/dof_handler.h>
876 * #include <deal.II/dofs/dof_accessor.h>
877 * #include <deal.II/dofs/dof_tools.h>
878 * #include <deal.II/fe/fe_values.h>
879 * #include <deal.II/fe/fe.h>
880 * #include <deal.II/numerics/matrix_tools.h>
881 * #include <deal.II/numerics/vector_tools.h>
882 * #include <deal.II/lac/vector.h>
883 * #include <deal.II/base/quadrature.h>
884 * #include <deal.II/distributed/tria.h>
885 * #include <deal.II/distributed/grid_refinement.h>
887 * #include <deal.II/lac/sparse_direct.h>
888 * #include <deal.II/base/timer.h>
889 * #include <deal.II/base/utilities.h>
891 * #include <deal.II/base/exceptions.h>
892 * #include <deal.II/base/geometric_utilities.h>
893 * #include <deal.II/base/conditional_ostream.h>
894 * #include <deal.II/base/mpi.h>
896 * #include <deal.II/grid/grid_tools.h>
897 * #include <deal.II/dofs/dof_renumbering.h>
898 * #include <deal.II/numerics/solution_transfer.h>
899 * #include <deal.II/base/index_set.h>
900 * #include <deal.II/lac/sparsity_tools.h>
901 * #include <deal.II/fe/fe_values_extractors.h>
904 * Automatic differentiation is carried out with TRILINOS Automatic differentiation scheme/
905 * This is invoked with the following include.
908 * #include <deal.II/differentiation/ad.h>
911 * We use Trilinos wrappers based NOX to solve the non-linear equations.
914 * #include <deal.II/trilinos/nox.h>
918 * #include <iostream>
925<a name=
"ann-include/nonlinear_heat.h"></a>
926<h1>Annotated version of include/nonlinear_heat.h</h1>
942 * #ifndef __MAIN_ALL_HEADER_H_INCLUDED__
943 * #define __MAIN_ALL_HEADER_H_INCLUDED__
944 * #include
"allheaders.h"
948 *
class nonlinear_heat
963 *
const double delta_t;
967 *
const double alpha;
971 *
const double tot_time;
1008 *
void setup_system(
unsigned int time_step);
1020 *
void output_results(
unsigned int prn)
const;
1025 *
void set_boundary_conditions(
double time);
1036 * std::unique_ptr<SparseDirectUMFPACK> matrix_factorization;
1053 *
class Initialcondition :
public Function<2>
1056 * Initialcondition():
Function<2>(1)
1064 * const unsigned
int component =0) const override;
1071 *
class Boundary_values_left:
public Function<2>
1074 * Boundary_values_left():
Function<2>(1)
1076 * virtual double
value(const
Point<2> & p,const unsigned
int component = 0) const override;
1082<a name=
"ann-nonlinear_heat.cc"></a>
1083<h1>Annotated version of nonlinear_heat.cc</h1>
1099 * #include
"allheaders.h"
1100 * #include
"nonlinear_heat.h"
1105 * nonlinear_heat nlheat;
1108 *
catch (std::exception &exc)
1110 * std::cerr << std::endl
1112 * <<
"----------------------------------------------------"
1114 * std::cerr <<
"Exception on processing: " << std::endl
1115 * << exc.what() << std::endl
1116 * <<
"Aborting!" << std::endl
1117 * <<
"----------------------------------------------------"
1124 * std::cerr << std::endl
1126 * <<
"----------------------------------------------------"
1128 * std::cerr <<
"Unknown exception!" << std::endl
1129 * <<
"Aborting!" << std::endl
1130 * <<
"----------------------------------------------------"
1140<a name=
"ann-source/boundary_values.cc"></a>
1141<h1>Annotated version of source/boundary_values.cc</h1>
1157 * #include
"nonlinear_heat.h"
1164 *
double Boundary_values_left::value(
const Point<2> & ,
const unsigned int )
const
1175 * nonlinear_heat nlheat;
1176 *
double total_time = nlheat.tot_time;
1177 *
return this->
get_time() * 100.0/total_time;
1184<a name=
"ann-source/compute_jacobian.cc"></a>
1185<h1>Annotated version of source/compute_jacobian.cc</h1>
1201 * #include
"allheaders.h"
1202 * #include
"nonlinear_heat.h"
1209 *
void nonlinear_heat::compute_jacobian(
const Vector<double> &evaluation_point)
1214 *
const QGauss<1> face_quadrature_formula(fe.degree+1);
1217 * quadrature_formula,
1226 *
const unsigned int dofs_per_cell = fe.dofs_per_cell;
1227 *
const unsigned int n_q_points = quadrature_formula.size();
1228 *
const unsigned int n_q_f_points = face_quadrature_formula.size();
1232 *
using ADNumberType =
typename ADHelper::ad_type;
1237 * system_matrix = 0.0;
1238 * std::vector<types::global_dof_index> local_dof_indices(
1241 * std::vector<double> consol(n_q_points);
1242 * std::vector<ADNumberType> old_solution(
1245 * std::vector<Tensor<1, 2>> consol_grad(
1247 * std::vector<Tensor<1, 2,ADNumberType>> old_solution_grad(
1250 *
for (
const auto &cell: dof_handler.active_cell_iterators())
1254 * fe_values.reinit(cell);
1255 * cell->get_dof_indices(local_dof_indices);
1256 *
const unsigned int n_independent_variables = local_dof_indices.size();
1257 *
const unsigned int n_dependent_variables = dofs_per_cell;
1258 * ADHelper ad_helper(n_independent_variables, n_dependent_variables);
1259 * ad_helper.register_dof_values(evaluation_point,local_dof_indices);
1260 *
const std::vector<ADNumberType> &dof_values_ad = ad_helper.get_sensitive_dof_values();
1261 * fe_values[t].get_function_values_from_local_dof_values(dof_values_ad,
1263 * fe_values[t].get_function_gradients_from_local_dof_values(dof_values_ad,
1264 * old_solution_grad);
1266 * fe_values[t].get_function_values(converged_solution, consol);
1267 * fe_values[t].get_function_gradients(converged_solution, consol_grad);
1268 * std::vector<ADNumberType> residual_ad(n_dependent_variables,
1269 * ADNumberType(0.0));
1270 *
for (
unsigned int q_index = 0; q_index < n_q_points; ++q_index)
1272 *
for (
unsigned int i = 0; i < dofs_per_cell; ++i)
1275 * ADNumberType MijTjcurr = Cp*rho*fe_values[t].value(i,q_index)*old_solution[q_index];
1276 * ADNumberType MijTjprev = Cp*rho*fe_values[t].value(i,q_index)*consol[q_index];
1277 * ADNumberType k_curr = a +
b*old_solution[q_index] + c*
std::pow(old_solution[q_index],2);
1278 * ADNumberType k_prev = a +
b*consol[q_index] + c*
std::pow(consol[q_index],2);
1279 * ADNumberType Licurr = alpha * delta_t * (fe_values[t].gradient(i,q_index)*k_curr*old_solution_grad[q_index]);
1280 * ADNumberType Liprev = (1-alpha) * delta_t * (fe_values[t].
gradient(i,q_index)*k_prev*consol_grad[q_index]);
1281 * residual_ad[i] += (MijTjcurr+Licurr-MijTjprev+Liprev)*fe_values.JxW(q_index);
1286 *
for (
unsigned int face_number = 0;face_number<GeometryInfo<2>::faces_per_cell; ++face_number)
1289 *
if (cell->face(face_number)->boundary_id() == 3)
1291 * fe_face_values.reinit(cell, face_number);
1292 *
for (
unsigned int q_point=0;q_point<n_q_f_points;++q_point)
1294 *
for (
unsigned int i =0;i<dofs_per_cell;++i)
1295 * residual_ad[i]+= -delta_t*(-10)*fe_face_values[t].value(i,q_point)*fe_face_values.JxW(q_point);
1301 * ad_helper.register_residual_vector(residual_ad);
1302 * ad_helper.compute_residual(cell_rhs);
1306 * ad_helper.compute_linearization(cell_matrix);
1307 * cell->get_dof_indices(local_dof_indices);
1312 *
for (
unsigned int i =0;i < dofs_per_cell; ++i){
1313 *
for(
unsigned int j = 0;j < dofs_per_cell;++j){
1314 * system_matrix.add(local_dof_indices[i],local_dof_indices[j],
cell_matrix(i,j));
1322 * std::map<types::global_dof_index, double> boundary_values;
1336 * std::cout <<
" Factorizing Jacobian matrix" << std::endl;
1337 * matrix_factorization = std::make_unique<SparseDirectUMFPACK>();
1338 * matrix_factorization->factorize(system_matrix);
1344<a name=
"ann-source/compute_residual.cc"></a>
1345<h1>Annotated version of source/compute_residual.cc</h1>
1361 * #include
"allheaders.h"
1362 * #include
"nonlinear_heat.h"
1377 *
const QGauss<1> face_quadrature_formula(fe.degree+1);
1379 * quadrature_formula,
1386 *
const unsigned int dofs_per_cell = fe.dofs_per_cell;
1387 *
const unsigned int n_q_points = quadrature_formula.size();
1388 *
const unsigned int n_q_f_points = face_quadrature_formula.size();
1400 *
using ADNumberType =
typename ADHelper::ad_type;
1409 * std::vector<types::global_dof_index> local_dof_indices(
1415 * std::vector<double> consol(n_q_points);
1416 * std::vector<ADNumberType> old_solution(
1421 * std::vector<Tensor<1, 2>> consol_grad(
1423 * std::vector<Tensor<1, 2,ADNumberType>> old_solution_grad(
1431 *
for (
const auto &cell: dof_handler.active_cell_iterators())
1434 * fe_values.reinit(cell);
1435 * cell->get_dof_indices(local_dof_indices);
1436 *
const unsigned int n_independent_variables = local_dof_indices.size();
1437 *
const unsigned int n_dependent_variables = dofs_per_cell;
1438 * ADHelper ad_helper(n_independent_variables, n_dependent_variables);
1443 * ad_helper.register_dof_values(evaluation_point,local_dof_indices);
1445 *
const std::vector<ADNumberType> &dof_values_ad = ad_helper.get_sensitive_dof_values();
1446 * fe_values[t].get_function_values_from_local_dof_values(dof_values_ad,
1448 * fe_values[t].get_function_gradients_from_local_dof_values(dof_values_ad,
1449 * old_solution_grad);
1454 * fe_values[t].get_function_values(converged_solution, consol);
1455 * fe_values[t].get_function_gradients(converged_solution, consol_grad);
1459 * std::vector<ADNumberType> residual_ad(n_dependent_variables,
1460 * ADNumberType(0.0));
1461 *
for (
unsigned int q_index = 0; q_index < n_q_points; ++q_index)
1463 *
for (
unsigned int i = 0; i < dofs_per_cell; ++i)
1468 * ADNumberType MijTjcurr = Cp*rho*fe_values[t].value(i,q_index)*old_solution[q_index];
1469 * ADNumberType MijTjprev = Cp*rho*fe_values[t].value(i,q_index)*consol[q_index];
1470 * ADNumberType k_curr = a +
b*old_solution[q_index] + c*
std::pow(old_solution[q_index],2);
1471 * ADNumberType k_prev = a +
b*consol[q_index] + c*
std::pow(consol[q_index],2);
1472 * ADNumberType Licurr = alpha * delta_t * (fe_values[t].gradient(i,q_index)*k_curr*old_solution_grad[q_index]);
1473 * ADNumberType Liprev = (1-alpha) * delta_t * (fe_values[t].
gradient(i,q_index)*k_prev*consol_grad[q_index]);
1474 * residual_ad[i] += (MijTjcurr+Licurr-MijTjprev+Liprev)*fe_values.JxW(q_index);
1481 *
for (
unsigned int face_number = 0;face_number<GeometryInfo<2>::faces_per_cell; ++face_number)
1484 *
if (cell->face(face_number)->boundary_id() == 3)
1486 * fe_face_values.reinit(cell, face_number);
1487 *
for (
unsigned int q_point=0;q_point<n_q_f_points;++q_point)
1489 *
for (
unsigned int i =0;i<dofs_per_cell;++i)
1493 * residual_ad[i]+= -delta_t*(-10)*fe_face_values[t].value(i,q_point)*fe_face_values.JxW(q_point);
1501 * ad_helper.register_residual_vector(residual_ad);
1507 * ad_helper.compute_residual(cell_rhs);
1508 * cell->get_dof_indices(local_dof_indices);
1510 *
for (
unsigned int i =0;i < dofs_per_cell; ++i)
1511 * residual(local_dof_indices[i])+= cell_rhs(i);
1518 * std::cout <<
" The Norm is :: = " << residual.l2_norm() << std::endl;
1523<a name=
"ann-source/initial_conditions.cc"></a>
1524<h1>Annotated version of source/initial_conditions.cc</h1>
1540 * #include
"nonlinear_heat.h"
1548 *
double Initialcondition::value(
const Point<2> & ,
const unsigned int )
const
1558<a name=
"ann-source/nonlinear_heat_cons_des.cc"></a>
1559<h1>Annotated version of source/nonlinear_heat_cons_des.cc</h1>
1575 * #include
"nonlinear_heat.h"
1581 * nonlinear_heat::nonlinear_heat ()
1597 * nonlinear_heat::~nonlinear_heat()
1599 * dof_handler.clear();
1606<a name=
"ann-source/output_results.cc"></a>
1607<h1>Annotated version of source/output_results.cc</h1>
1623 * #include
"nonlinear_heat.h"
1629 *
void nonlinear_heat::output_results(
unsigned int prn)
const
1633 * std::vector<std::string> solution_names;
1634 * solution_names.emplace_back (
"Temperature");
1635 * data_out.add_data_vector(converged_solution, solution_names);
1636 * data_out.build_patches();
1637 *
const std::string filename =
1639 * std::ofstream output(filename);
1640 * data_out.write_vtu(output);
1645<a name=
"ann-source/set_boundary_conditions.cc"></a>
1646<h1>Annotated version of source/set_boundary_conditions.cc</h1>
1662 * #include
"nonlinear_heat.h"
1663 * #include
"allheaders.h"
1669 *
void nonlinear_heat::set_boundary_conditions(
double time)
1671 * Boundary_values_left bl_left;
1672 * bl_left.set_time(time);
1673 * std::map<types::global_dof_index,double> boundary_values;
1679 *
for (
auto &boundary_value: boundary_values)
1680 * present_solution(boundary_value.
first) = boundary_value.
second;
1685<a name=
"ann-source/setup_system.cc"></a>
1686<h1>Annotated version of source/setup_system.cc</h1>
1702 * #include
"allheaders.h"
1703 * #include
"nonlinear_heat.h"
1709 *
void nonlinear_heat::setup_system(
unsigned int time_step)
1711 *
if (time_step ==0) {
1712 * dof_handler.distribute_dofs(fe);
1713 * converged_solution.reinit(dof_handler.n_dofs());
1714 * present_solution.reinit(dof_handler.n_dofs());
1719 * sparsity_pattern.copy_from(dsp);
1720 * system_matrix.reinit(sparsity_pattern);
1721 * matrix_factorization.reset();
1726<a name=
"ann-source/solve_and_run.cc"></a>
1727<h1>Annotated version of source/solve_and_run.cc</h1>
1743 * #include
"nonlinear_heat.h"
1752 * std::cout <<
" Solving linear system" << std::endl;
1753 * matrix_factorization->vmult(solution, rhs);
1756 *
void nonlinear_heat::run()
1760 * std::ifstream f(
"mesh/mesh.msh");
1761 * gridin.read_msh(f);
1765 *
unsigned int timestep_number = 0;
1766 *
unsigned int prn =0;
1769 *
while (time <=tot_time)
1773 * setup_system(timestep_number);
1776 * Initialcondition(),
1777 * present_solution);
1780 * Initialcondition(),
1781 * converged_solution);
1788 * present_solution = converged_solution;
1790 * std::cout<<
">>>>> Time now is: "<<time <<std::endl;
1791 * std::cout<<
">>>>> Time step is:"<<timestep_number<<std::endl;
1792 * set_boundary_conditions(time);
1795 *
const double target_tolerance = 1
e-3;
1801 * additional_data.abs_tol = target_tolerance;
1802 * additional_data.max_iter = 100;
1809 * nonlinear_solver.residual =
1812 * compute_residual(evaluation_point, residual);
1819 * nonlinear_solver.setup_jacobian =
1821 * compute_jacobian(current_u);
1827 * nonlinear_solver.solve_with_jacobian = [&](
const Vector<double> &rhs,
1829 *
const double tolerance) {
1830 * solve(rhs, dst, tolerance);
1836 * nonlinear_solver.solve(present_solution);
1841 * converged_solution = present_solution;
1843 *
if(timestep_number % 1 == 0) {
1845 * output_results(prn);
1848 * timestep_number++;
1849 * time=time+delta_t;
std::vector< bool > component_mask
void attach_dof_handler(const DoFHandler< dim, spacedim > &)
void attach_triangulation(Triangulation< dim, spacedim > &tria)
void refine_global(const unsigned int times=1)
__global__ void set(Number *val, const Number s, const size_type N)
void make_sparsity_pattern(const DoFHandler< dim, spacedim > &dof_handler, SparsityPatternBase &sparsity_pattern, const AffineConstraints< number > &constraints={}, const bool keep_constrained_dofs=true, const types::subdomain_id subdomain_id=numbers::invalid_subdomain_id)
@ update_values
Shape function values.
@ update_normal_vectors
Normal vectors.
@ update_JxW_values
Transformed quadrature weights.
@ update_gradients
Shape function gradients.
@ update_quadrature_points
Transformed quadrature points.
Expression differentiate(const Expression &f, const Expression &x)
void cell_matrix(FullMatrix< double > &M, const FEValuesBase< dim > &fe, const FEValuesBase< dim > &fetest, const ArrayView< const std::vector< double > > &velocity, const double factor=1.)
double norm(const FEValuesBase< dim > &fe, const ArrayView< const std::vector< Tensor< 1, dim > > > &Du)
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
VectorType::value_type * end(VectorType &V)
VectorType::value_type * begin(VectorType &V)
std::string int_to_string(const unsigned int value, const unsigned int digits=numbers::invalid_unsigned_int)
void run(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length, const unsigned int chunk_size)
int(& functions)(const void *v1, const void *v2)
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation