374 *
#ifndef AUXILIARY_FUNCTIONS
375 *
#define AUXILIARY_FUNCTIONS
385 * Comparison of
numbers with a given tolerance.
388 *
template <
typename T>
389 *
bool isapprox(
const T &a,
const T &b,
const double tol = 1e-10)
396 * Fill the std::vector with the values from the range [interval_begin, interval_end].
399 *
template <
typename T>
400 *
void linspace(T interval_begin, T interval_end, std::vector<T> &arr)
402 *
const size_t SIZE = arr.size();
403 *
const T step = (interval_end - interval_begin) /
static_cast<T>(SIZE - 1);
404 *
for (
size_t i = 0; i < SIZE; ++i)
406 *
arr[i] = interval_begin + i * step;
412 * Check the file existence.
415 *
inline bool file_exists(
const std::string &filename)
417 *
std::ifstream f(filename.c_str());
425<a name=
"ann-IntegrateSystem.h"></a>
426<h1>Annotated version of IntegrateSystem.h</h1>
442 *
#ifndef INTEGRATE_SYSTEM
443 *
#define INTEGRATE_SYSTEM
445 *
#include <
boost/numeric/odeint.hpp>
447 *
#include <iostream>
451 *
template <
typename state_T,
typename time_T>
452 *
void SaveSolutionIntoFile(
const std::vector<state_T>& x_vec,
const std::vector<time_T>& t_vec, std::string filename=
"output_ode_sol.txt")
454 *
if (!x_vec.empty() && !t_vec.empty())
456 *
std::ofstream output(filename);
457 *
output << std::setprecision(16);
459 *
size_t dim = x_vec[0].size();
460 *
for (
size_t i = 0; i < t_vec.size(); ++i)
462 *
output << std::fixed << t_vec[i];
463 *
for (
size_t j = 0; j < dim; ++j)
465 *
output << std::scientific <<
" " << x_vec[i][j];
473 *
std::cout <<
"Solution is not saved into file.\n";
479 * type of RK integrator
482 *
enum class Integrator_Type
494 *
template <
typename state_type>
495 *
class Push_back_state_time
498 *
std::vector<state_type>& m_states;
499 *
std::vector<double>& m_times;
501 *
Push_back_state_time(std::vector<state_type>& states, std::vector<double>& times)
502 *
: m_states(states), m_times(times)
505 *
void operator() (
const state_type& x,
double t)
507 *
m_states.push_back(x);
508 *
m_times.push_back(t);
515 * Integrate system at specified points.
518 *
template <
typename ODE_obj_T,
typename state_type,
typename Iterable_type>
519 *
void IntegrateSystemAtTimePoints(
520 *
std::vector<state_type>& x_vec, std::vector<double>& t_vec,
const Iterable_type& iterable_time_span,
521 *
const ODE_obj_T& ode_system_obj,
522 *
state_type& x,
const double dt,
523 *
Integrator_Type integrator_type=Integrator_Type::dopri5,
bool save_to_file_flag=
false,
524 *
const double abs_er_tol=1.0e-15,
const double rel_er_tol=1.0e-15
527 *
using namespace boost::numeric::odeint;
529 *
if (integrator_type == Integrator_Type::dopri5)
531 *
typedef runge_kutta_dopri5< state_type > error_stepper_type;
532 *
integrate_times( make_controlled< error_stepper_type >(abs_er_tol, rel_er_tol),
533 *
ode_system_obj, x, iterable_time_span.begin(), iterable_time_span.end(), dt, Push_back_state_time< state_type >(x_vec, t_vec) );
535 *
else if (integrator_type == Integrator_Type::cash_karp54)
537 *
typedef runge_kutta_cash_karp54< state_type > error_stepper_type;
538 *
integrate_times( make_controlled< error_stepper_type >(abs_er_tol, rel_er_tol),
539 *
ode_system_obj, x, iterable_time_span.begin(), iterable_time_span.end(), dt, Push_back_state_time< state_type >(x_vec, t_vec) );
543 *
typedef runge_kutta_fehlberg78< state_type > error_stepper_type;
544 *
integrate_times( make_controlled< error_stepper_type >(abs_er_tol, rel_er_tol),
545 *
ode_system_obj, x, iterable_time_span.begin(), iterable_time_span.end(), dt, Push_back_state_time< state_type >(x_vec, t_vec) );
548 *
if (save_to_file_flag)
550 *
SaveSolutionIntoFile(x_vec, t_vec);
559<a name=
"ann-LimitSolution.cc"></a>
560<h1>Annotated version of LimitSolution.cc</h1>
576 *
#include
"LimitSolution.h"
578 *
namespace TravelingWave
581 *
LimitSolution::LimitSolution(
const Parameters ¶meters,
const double ilambda_0,
const double iu_0,
const double iT_0,
const double iroot_sign)
582 *
: params(parameters)
583 *
, problem(params.problem)
584 *
, wave_speed(problem.wave_speed_init)
585 *
, lambda_0(ilambda_0)
588 *
, root_sign(iroot_sign)
590 *
calculate_constants_A_B();
593 *
double LimitSolution::omega_func(
const double lambda,
const double T)
const
595 *
return problem.k * (1. -
lambda) *
std::exp(-problem.theta / T);
598 *
void LimitSolution::operator() (
const state_type &x , state_type &dxdt ,
const double )
600 *
dxdt[0] = -1. / wave_speed * omega_func(x[0], T_func(x[0]));
603 *
double LimitSolution::u_func(
const double lambda)
const
605 *
double coef = 2 * (wave_speed - 1) / problem.epsilon - 1;
606 *
return (coef + root_sign *
std::sqrt(coef * coef - 4 * (problem.q * lambda + B - 2 * A / problem.epsilon))) / 2;
609 *
double LimitSolution::T_func(
const double lambda)
const
611 *
return u_func(lambda) + problem.q *
lambda + B;
614 *
void LimitSolution::calculate_constants_A_B()
617 *
A = u_0 * (1 - wave_speed) + problem.epsilon * (u_0 * u_0 + T_0) / 2;
620 *
void LimitSolution::set_wave_speed(
double iwave_speed)
622 *
wave_speed = iwave_speed;
623 *
calculate_constants_A_B();
626 *
void LimitSolution::calculate_u_T_omega()
628 *
if (!t_vec.empty() && !lambda_vec.empty())
630 *
u_vec.resize(lambda_vec.size());
631 *
T_vec.resize(lambda_vec.size());
632 *
omega_vec.resize(lambda_vec.size());
633 *
for (
unsigned int i = 0; i < lambda_vec.size(); ++i)
635 *
u_vec[i].resize(1);
636 *
T_vec[i].resize(1);
637 *
omega_vec[i].resize(1);
639 *
u_vec[i][0] = u_func(lambda_vec[i][0]);
640 *
T_vec[i][0] = T_func(lambda_vec[i][0]);
641 *
omega_vec[i][0] = omega_func(lambda_vec[i][0], T_vec[i][0]);
646 *
std::cout <<
"t_vec or lambda_vec vector is empty!" << std::endl;
654<a name=
"ann-LimitSolution.h"></a>
655<h1>Annotated version of LimitSolution.h</h1>
671 *
#ifndef LIMIT_SOLUTION
672 *
#define LIMIT_SOLUTION
674 *
#include
"Parameters.h"
675 *
#include <iostream>
678 *
namespace TravelingWave
680 *
typedef std::vector< double > state_type;
682 *
class LimitSolution
685 *
LimitSolution(
const Parameters ¶meters,
const double ilambda_0,
const double iu_0,
const double iT_0,
const double root_sign = 1.);
687 *
void operator() (
const state_type &x , state_type &dxdt ,
const double );
688 *
void calculate_u_T_omega();
689 *
void set_wave_speed(
double iwave_speed);
691 *
std::vector<double> t_vec;
692 *
std::vector<state_type> omega_vec;
693 *
std::vector<state_type> lambda_vec;
694 *
std::vector<state_type> u_vec;
695 *
std::vector<state_type> T_vec;
698 *
double omega_func(
const double lambda,
const double T)
const;
699 *
double u_func(
const double lambda)
const;
700 *
double T_func(
const double lambda)
const;
702 *
void calculate_constants_A_B();
704 *
const Parameters ¶ms;
705 *
const Problem &problem;
708 *
const double lambda_0, u_0, T_0;
711 *
const double root_sign;
721<a name=
"ann-LinearInterpolator.h"></a>
722<h1>Annotated version of LinearInterpolator.h</h1>
738 *
#ifndef LINEAR_INTERPOLATOR
739 *
#define LINEAR_INTERPOLATOR
742 *
#include <algorithm>
747 * Linear interpolation
class
750 *
template <
typename Number_Type>
751 *
class LinearInterpolator
754 *
LinearInterpolator(
const std::vector<Number_Type> &ix_points,
const std::vector<Number_Type> &iy_points);
755 *
Number_Type
value(
const Number_Type x)
const;
758 *
const std::vector<Number_Type> x_points;
759 *
const std::vector<Number_Type> y_points;
762 *
template <
typename Number_Type>
763 *
LinearInterpolator<Number_Type>::LinearInterpolator(
const std::vector<Number_Type> &ix_points,
const std::vector<Number_Type> &iy_points)
764 *
: x_points(ix_points)
765 *
, y_points(iy_points)
768 *
template <
typename Number_Type>
769 *
Number_Type LinearInterpolator<Number_Type>::value(
const Number_Type x)
const
771 *
Number_Type res = 0.;
773 *
auto lower = std::lower_bound(x_points.begin(), x_points.end(), x);
774 *
unsigned int right_index = 0;
775 *
unsigned int left_index = 0;
776 *
if (lower == x_points.begin())
780 *
else if (lower == x_points.end())
782 *
res = y_points[x_points.size()-1];
786 *
right_index = lower - x_points.begin();
787 *
left_index = right_index - 1;
789 *
Number_Type y_2 = y_points[right_index];
790 *
Number_Type y_1 = y_points[left_index];
791 *
Number_Type x_2 = x_points[right_index];
792 *
Number_Type x_1 = x_points[left_index];
794 *
res = (y_2 - y_1) / (x_2 - x_1) * (x - x_1) + y_1;
804<a name=
"ann-Parameters.cc"></a>
805<h1>Annotated version of Parameters.cc</h1>
821 *
#include
"Parameters.h"
823 *
namespace TravelingWave
830 *
add_parameter(
"delta", delta = 0.01);
831 *
add_parameter(
"epsilon", epsilon = 0.1);
832 *
add_parameter(
"Prandtl number", Pr = 0.75);
833 *
add_parameter(
"Lewis number", Le = 1.0);
834 *
add_parameter(
"Constant of reaction rate", k = 1.0);
835 *
add_parameter(
"Activation energy", theta = 1.65);
836 *
add_parameter(
"Heat release", q = 1.7);
837 *
add_parameter(
"Ignition Temperature", T_ign = 1.0);
838 *
add_parameter(
"Type of the wave (deflagration / detonation)", wave_type = 1);
840 *
add_parameter(
"Type of boundary condition for the temperature at the right boundary", T_r_bc_type = 1);
842 *
add_parameter(
"T_left", T_left = 5.3);
843 *
add_parameter(
"T_right", T_right = 0.9);
844 *
add_parameter(
"u_left", u_left = -0.2);
845 *
add_parameter(
"u_right", u_right = 0.);
847 *
add_parameter(
"Initial guess for the wave speed", wave_speed_init = 1.2);
850 *
FiniteElements::FiniteElements()
853 *
add_parameter(
"Polynomial degree", poly_degree = 1);
854 *
add_parameter(
"Number of quadrature points", quadrature_points_number = 3);
860 *
add_parameter(
"Interval left boundary", interval_left = -50.0);
861 *
add_parameter(
"Interval right boundary", interval_right = 20.0);
862 *
add_parameter<unsigned int>(
"Refinements number", refinements_number = 10);
863 *
add_parameter(
"Adaptive mesh refinement", adaptive = 1);
869 *
add_parameter(
"Tolerance", tol = 1e-10);
876<a name=
"ann-Parameters.h"></a>
877<h1>Annotated version of Parameters.h</h1>
896 *
#include <deal.II/base/parameter_acceptor.h>
898 *
namespace TravelingWave
908 *
double k,
theta, q;
912 *
double T_left, T_right;
913 *
double u_left, u_right;
915 *
double wave_speed_init;
922 *
unsigned int poly_degree;
923 *
unsigned int quadrature_points_number;
930 *
double interval_left;
931 *
double interval_right;
932 *
unsigned int refinements_number;
957<a name=
"ann-Solution.cc"></a>
958<h1>Annotated version of Solution.cc</h1>
974 *
#include
"Solution.h"
976 *
namespace TravelingWave
981 *
SolutionStruct::SolutionStruct() {}
982 *
SolutionStruct::SolutionStruct(
const std::vector<double> &ix,
const std::vector<double> &iu,
983 *
const std::vector<double> &iT,
const std::vector<double> &ilambda,
double iwave_speed)
988 *
, wave_speed(iwave_speed)
990 *
SolutionStruct::SolutionStruct(
const std::vector<double> &ix,
const std::vector<double> &iu,
991 *
const std::vector<double> &iT,
const std::vector<double> &ilambda)
992 *
: SolutionStruct(ix, iu, iT, ilambda, 0.)
995 *
void SolutionStruct::reinit(
const unsigned int number_of_elements)
1003 *
x.resize(number_of_elements);
1004 *
u.resize(number_of_elements);
1005 *
T.resize(number_of_elements);
1006 *
lambda.resize(number_of_elements);
1009 *
void SolutionStruct::save_to_file(std::string filename =
"sol") const
1011 *
const std::string file_for_solution = filename +
".txt";
1012 *
std::ofstream output(file_for_solution);
1014 *
output << std::scientific << std::setprecision(16);
1015 *
for (
unsigned int i = 0; i < x.size(); ++i)
1017 *
output << std::fixed << x[i];
1018 *
output << std::scientific <<
" " << u[i] <<
" " <<
T[i] <<
" " <<
lambda[i] <<
"\n";
1022 *
std::ofstream file_for_wave_speed_output(
"wave_speed-" + file_for_solution);
1023 *
file_for_wave_speed_output << std::scientific << std::setprecision(16);
1024 *
file_for_wave_speed_output << wave_speed << std::endl;
1025 *
file_for_wave_speed_output.close();
1029 *
Interpolant::Interpolant(
const std::vector<double> &ix_points,
const std::vector<double> &iy_points)
1030 *
: interpolant(ix_points, iy_points)
1033 *
double Interpolant::value(
const Point<1> &p,
const unsigned int )
const
1036 *
double res = interpolant.value(x);
1042 *
template <
typename InterpolantType>
1043 *
SolutionVectorFunction<InterpolantType>::SolutionVectorFunction(InterpolantType iu_interpolant, InterpolantType iT_interpolant, InterpolantType ilambda_interpolant)
1045 *
, u_interpolant(iu_interpolant)
1046 *
, T_interpolant(iT_interpolant)
1047 *
, lambda_interpolant(ilambda_interpolant)
1050 *
template <
typename InterpolantType>
1051 *
double SolutionVectorFunction<InterpolantType>::value(
const Point<1> &p,
const unsigned int component)
const
1054 *
if (component == 0) { res = u_interpolant.value(p); }
1055 *
else if (component == 1) { res = T_interpolant.value(p); }
1056 *
else if (component == 2) { res = lambda_interpolant.value(p); }
1061 *
template class SolutionVectorFunction<Interpolant>;
1067<a name=
"ann-Solution.h"></a>
1068<h1>Annotated version of Solution.h</h1>
1087 *
#include <deal.II/base/function.h>
1089 *
#include
"LinearInterpolator.h"
1093 *
#include <fstream>
1094 *
#include <iostream>
1095 *
#include <iomanip>
1098 *
namespace TravelingWave
1100 *
using namespace dealii;
1104 * The structure
for keeping the solution: arrays of coordinates @f$\xi@f$, solution @f$u@f$, @f$T@f$, @f$\lambda@f$, and the wave speed @f$c@f$.
1107 *
struct SolutionStruct
1110 *
SolutionStruct(
const std::vector<double> &ix,
const std::vector<double> &iu,
1111 *
const std::vector<double> &iT,
const std::vector<double> &ilambda,
const double iwave_speed);
1112 *
SolutionStruct(
const std::vector<double> &ix,
const std::vector<double> &iu,
1113 *
const std::vector<double> &iT,
const std::vector<double> &ilambda);
1115 *
void reinit(
const unsigned int number_of_elements);
1117 *
void save_to_file(std::string filename)
const;
1119 *
std::vector<double> x;
1120 *
std::vector<double> u;
1121 *
std::vector<double>
T;
1122 *
std::vector<double>
lambda;
1124 *
double wave_speed;
1129 * Interpolation
class
1132 *
class Interpolant :
public Function<1>
1135 *
Interpolant(
const std::vector<double> &ix_points,
const std::vector<double> &iy_points);
1136 *
virtual double value(
const Point<1> &p,
const unsigned int component = 0)
const override;
1139 *
LinearInterpolator<double> interpolant;
1147 *
template <
typename InterpolantType>
1148 *
class SolutionVectorFunction :
public Function<1>
1151 *
SolutionVectorFunction(InterpolantType iu_interpolant, InterpolantType iT_interpolant, InterpolantType ilambda_interpolant);
1152 *
virtual double value(
const Point<1> &p,
const unsigned int component = 0)
const override;
1155 *
InterpolantType u_interpolant;
1156 *
InterpolantType T_interpolant;
1157 *
InterpolantType lambda_interpolant;
1166<a name=
"ann-TravelingWaveSolver.cc"></a>
1167<h1>Annotated version of TravelingWaveSolver.cc</h1>
1183 *
#include
"TravelingWaveSolver.h"
1185 *
namespace TravelingWave
1187 *
using namespace dealii;
1191 * Constructor of the
class that takes parameters of the problem and an
initial guess
for Newton's iterations.
1194 *
TravelingWaveSolver::TravelingWaveSolver(
const Parameters ¶meters,
const SolutionStruct &initial_guess_input)
1195 *
: params(parameters)
1196 *
, problem(params.problem)
1197 *
, number_of_quadrature_points((params.fe.quadrature_points_number > 0) ? params.fe.quadrature_points_number : (params.fe.poly_degree + 1))
1198 *
, triangulation_uploaded(
false)
1199 *
, fe(
FE_Q<1>(params.fe.poly_degree), 1,
1200 *
FE_Q<1>(params.fe.poly_degree), 1,
1201 *
FE_Q<1>(params.fe.poly_degree), 1)
1202 *
, dof_handler(triangulation)
1203 *
, current_wave_speed(0.)
1204 *
, initial_guess(initial_guess_input)
1209 *
Table with values of some parameters to be written to the standard output before calculations.
1213 *
table.add_value(
"Parameter name",
"number of quadrature points");
1214 *
table.add_value(
"value", number_of_quadrature_points);
1216 *
table.add_value(
"Parameter name",
"delta");
1217 *
table.add_value(
"value", params.problem.delta);
1219 *
table.add_value(
"Parameter name",
"epsilon");
1220 *
table.add_value(
"value", params.problem.epsilon);
1222 *
table.add_value(
"Parameter name",
"params.problem.wave_speed_init");
1223 *
table.add_value(
"value", params.problem.wave_speed_init);
1225 *
table.add_value(
"Parameter name",
"initial_guess.wave_speed");
1226 *
table.add_value(
"value", initial_guess.wave_speed);
1228 *
table.add_value(
"Parameter name",
"T_left");
1229 *
table.add_value(
"value", params.problem.T_left);
1231 *
table.set_precision(
"value", 2);
1232 *
table.set_scientific(
"value",
true);
1234 *
std::cout <<
"\n";
1236 *
std::cout <<
"\n";
1241 *
A function that takes a triangulation and assigns it to the member variable <code>triangulation </code>.
1244 *
void TravelingWaveSolver::set_triangulation(
const Triangulation<1> &itriangulation)
1246 *
triangulation.clear();
1247 *
triangulation.copy_triangulation(itriangulation);
1248 *
triangulation_uploaded =
true;
1253 * Here we find the indices of the degrees of freedom, associated with the boundary vertices, and the degree of freedom, associated with the vertex with coordinate @f$\xi = 0@f$, and corresponding to
temperature.
1256 *
void TravelingWaveSolver::find_boundary_and_centering_dof_numbers()
1258 *
for (
const auto &cell : dof_handler.active_cell_iterators())
1260 *
for (
const auto &v_ind : cell->vertex_indices())
1262 *
if (isapprox(cell->vertex(v_ind)[0], params.mesh.interval_left))
1264 *
boundary_and_centering_dof_numbers[
"u_left"] = cell->vertex_dof_index(v_ind, 0);
1265 *
boundary_and_centering_dof_numbers[
"T_left"] = cell->vertex_dof_index(v_ind, 1);
1266 *
boundary_and_centering_dof_numbers[
"lambda_left"] = cell->vertex_dof_index(v_ind, 2);
1268 *
else if (isapprox(cell->vertex(v_ind)[0], params.mesh.interval_right))
1270 *
boundary_and_centering_dof_numbers[
"u_right"] = cell->vertex_dof_index(v_ind, 0);
1271 *
boundary_and_centering_dof_numbers[
"T_right"] = cell->vertex_dof_index(v_ind, 1);
1272 *
boundary_and_centering_dof_numbers[
"lambda_right"] = cell->vertex_dof_index(v_ind, 2);
1274 *
else if (isapprox(cell->vertex(v_ind)[0], 0.))
1276 *
boundary_and_centering_dof_numbers[
"T_zero"] = cell->vertex_dof_index(v_ind, 1);
1284 * Set solution values, corresponding to Dirichlet boundary conditions and the centering condition @f$T(0) = T_{\mathrm{ign}}@f$.
1287 *
void TravelingWaveSolver::set_boundary_and_centering_values()
1289 *
current_solution[boundary_and_centering_dof_numbers[
"u_right"]] = problem.u_right;
1291 *
current_solution[boundary_and_centering_dof_numbers[
"T_left"]] = problem.T_left;
1292 *
if (problem.T_r_bc_type == 1)
1294 *
current_solution[boundary_and_centering_dof_numbers[
"T_right"]] = problem.T_right;
1296 *
current_solution[boundary_and_centering_dof_numbers[
"T_zero"]] = problem.T_ign;
1298 *
current_solution[boundary_and_centering_dof_numbers[
"lambda_right"]] = 0.;
1302 *
void TravelingWaveSolver::setup_system(
const bool initial_step)
1306 *
dof_handler.distribute_dofs(fe);
1308 *
std::cout <<
"Number of dofs : " << dof_handler.n_dofs() << std::endl;
1310 *
extended_solution_dim = dof_handler.n_dofs() + 1;
1312 *
find_boundary_and_centering_dof_numbers();
1316 * Boundary condition constraints
for @f$du@f$, @f$dT@f$ and @f$d\lambda@f$.
1319 *
zero_boundary_constraints.clear();
1323 * Dirichlet homogeneous boundary condition
for @f$du@f$ at the right boundary.
1326 *
zero_boundary_constraints.add_line(boundary_and_centering_dof_numbers[
"u_right"]);
1330 * Dirichlet homogeneous boundary condition
for @f$dT@f$ at the left boundary.
1333 *
zero_boundary_constraints.add_line(boundary_and_centering_dof_numbers[
"T_left"]);
1336 * For the
temperature at the left boundary there are two possibilities:
1339 *
if (problem.T_r_bc_type == 1)
1341 *
std::cout <<
"Dirichlet condition for the temperature at the right boundary." << std::endl;
1342 *
zero_boundary_constraints.add_line(boundary_and_centering_dof_numbers[
"T_right"]);
1346 *
std::cout <<
"Neumann condition for the temperature at the right boundary." << std::endl;
1351 * Dirichlet homogeneous boundary condition
for @f$d\lambda@f$ at the right boundary. (At the left boundary we consider the homogeneous Neumann boundary condition
for @f$d\lambda@f$.)
1354 *
zero_boundary_constraints.add_line(boundary_and_centering_dof_numbers[
"lambda_right"]);
1356 *
zero_boundary_constraints.close();
1360 * We create extended
dynamic sparsity pattern with an additional row and an additional column.
1365 *
std::vector<types::global_dof_index> dofs_on_this_cell;
1366 *
dofs_on_this_cell.reserve(dof_handler.get_fe_collection().max_dofs_per_cell());
1368 *
for (
const auto &cell : dof_handler.active_cell_iterators())
1370 *
const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();
1372 *
cell->get_dof_indices(dofs_on_this_cell);
1374 *
zero_boundary_constraints.add_entries_local_to_global(dofs_on_this_cell,
1381 * Adding elements to the last column.
1384 *
for (
unsigned int i = 0; i < extended_solution_dim; ++i)
1386 *
dsp.add(i, extended_solution_dim - 1);
1390 * Adding
one element to the last row, corresponding to the
T(0).
1393 *
dsp.add(extended_solution_dim - 1, boundary_and_centering_dof_numbers[
"T_zero"]);
1401 *
sparsity_pattern_extended.copy_from(dsp);
1402 *
jacobian_matrix_extended.reinit(sparsity_pattern_extended);
1403 *
jacobian_matrix_extended_factorization.reset();
1405 *
current_solution_extended.reinit(extended_solution_dim);
1409 *
current_solution.reinit(dof_handler.n_dofs());
1415 *
void TravelingWaveSolver::set_initial_guess()
1417 *
current_wave_speed = initial_guess.wave_speed;
1421 * The
initial condition is a discrete
set of coordinates @f$\xi@f$ and values of
functions @f$u@f$, @f$T@f$ and @f$\lambda@f$. From the three sets we create three continuous
functions using interpolation, which then form
one continuous vector function of <code> SolutionVectorFunction </code> type.
1424 *
Interpolant u_interpolant(initial_guess.x, initial_guess.u);
1425 *
Interpolant T_interpolant(initial_guess.x, initial_guess.T);
1426 *
Interpolant lambda_interpolant(initial_guess.x, initial_guess.lambda);
1428 *
SolutionVectorFunction init_guess_func(u_interpolant, T_interpolant, lambda_interpolant);
1432 *
set_boundary_and_centering_values();
1434 *
for (
unsigned int i = 0; i < extended_solution_dim - 1; ++i)
1436 *
current_solution_extended(i) = current_solution(i);
1438 *
current_solution_extended(extended_solution_dim - 1) = current_wave_speed;
1443 * Heaviside function.
1446 *
double TravelingWaveSolver::Heaviside_func(
double x)
const
1459 *
void TravelingWaveSolver::compute_and_factorize_jacobian(
const Vector<double> &evaluation_point_extended)
1465 *
for (
unsigned int i = 0; i < dof_handler.n_dofs(); ++i)
1467 *
evaluation_point(i) = evaluation_point_extended(i);
1470 *
const double wave_speed = evaluation_point_extended(extended_solution_dim - 1);
1472 *
std::cout <<
"Computing Jacobian matrix ... " << std::endl;
1474 *
const QGauss<1> quadrature_formula(number_of_quadrature_points);
1476 *
jacobian_matrix_extended = 0;
1479 *
quadrature_formula,
1484 *
const unsigned int n_q_points = quadrature_formula.size();
1489 *
std::vector<types::global_dof_index> local_dof_indices(
dofs_per_cell);
1495 *
std::vector<double> current_velocity_values(
n_q_points);
1496 *
std::vector<double> current_temperature_values(
n_q_points);
1497 *
std::vector<double> current_lambda_values(
n_q_points);
1499 *
std::vector<Tensor<1, 1>> current_velocity_gradients(
n_q_points);
1500 *
std::vector<Tensor<1, 1>> current_temperature_gradients(
n_q_points);
1501 *
std::vector<Tensor<1, 1>> current_lambda_gradients(
n_q_points);
1510 *
for (
const auto &cell : dof_handler.active_cell_iterators())
1513 *
row_last_element_vector = 0;
1515 *
fe_values.reinit(cell);
1517 *
fe_values[velocity].get_function_values(evaluation_point, current_velocity_values);
1518 *
fe_values[
temperature].get_function_values(evaluation_point, current_temperature_values);
1519 *
fe_values[
lambda].get_function_values(evaluation_point, current_lambda_values);
1521 *
fe_values[velocity].get_function_gradients(evaluation_point, current_velocity_gradients);
1522 *
fe_values[
temperature].get_function_gradients(evaluation_point, current_temperature_gradients);
1523 *
fe_values[
lambda].get_function_gradients(evaluation_point, current_lambda_gradients);
1525 *
auto kappa_1 = [=,
this](
double T,
double lambda){
1526 *
return problem.k * (1 -
lambda) *
std::exp(-problem.theta / T) * (
1527 *
problem.theta / (
T *
T) * Heaviside_func(T - problem.T_ign)
1531 *
auto kappa_2 = [=,
this](
double T,
double ){
1532 *
return -problem.k *
std::exp(-problem.theta / T) * Heaviside_func(T - problem.T_ign);
1535 *
for (
unsigned int q = 0; q <
n_q_points; ++q)
1539 *
phi_u[k] = fe_values[velocity].value(k, q);
1540 *
grad_phi_u[k] = fe_values[velocity].gradient(k, q);
1542 *
grad_phi_T[k] = fe_values[
temperature].gradient(k, q);
1543 *
phi_lambda[k] = fe_values[
lambda].value(k, q);
1544 *
grad_phi_lambda[k] = fe_values[
lambda].gradient(k, q);
1547 *
const double del_Pr_eps = (problem.Pr * 4 * problem.delta / (3 * problem.epsilon));
1548 *
const double del_Le = (problem.delta / problem.Le);
1556 *
del_Pr_eps * (-grad_phi_u[i] * grad_phi_u[j])
1558 *
- (1 - wave_speed + problem.epsilon * current_velocity_values[q]) * grad_phi_u[j][0]
1559 *
- problem.epsilon * current_velocity_gradients[q][0] * phi_u[j]
1560 *
- problem.epsilon / 2. * grad_phi_T[j][0]
1563 *
+ problem.delta * (-grad_phi_T[i] * grad_phi_T[j])
1565 *
- wave_speed * grad_phi_u[j][0]
1566 *
+ wave_speed * grad_phi_T[j][0]
1567 *
+ problem.q * kappa_1(current_temperature_values[q], current_lambda_values[q]) * phi_T[j]
1568 *
+ problem.q * kappa_2(current_temperature_values[q], current_lambda_values[q]) * phi_lambda[j]
1571 *
+ del_Le * (-grad_phi_lambda[i] * grad_phi_lambda[j])
1572 *
+ phi_lambda[i] * (
1573 *
kappa_1(current_temperature_values[q], current_lambda_values[q]) * phi_T[j]
1574 *
+ wave_speed * grad_phi_lambda[j][0]
1575 *
+ kappa_2(current_temperature_values[q], current_lambda_values[q]) * phi_lambda[j]
1578 *
) * fe_values.JxW(q);
1582 *
row_last_element_vector(i) += (
1583 *
(phi_u[i] * current_velocity_gradients[q][0])
1584 *
+ (phi_T[i] * current_temperature_gradients[q][0])
1585 *
- (phi_T[i] * current_velocity_gradients[q][0])
1586 *
+ (phi_lambda[i] * current_lambda_gradients[q][0])
1587 *
) * fe_values.JxW(q);
1592 *
cell->get_dof_indices(local_dof_indices);
1594 *
for (
const unsigned int i : fe_values.dof_indices())
1596 *
for (
const unsigned int j : fe_values.dof_indices())
1598 *
jacobian_matrix_extended.add(local_dof_indices[i],
1599 *
local_dof_indices[j],
1605 * Adding elements to the last column.
1608 *
jacobian_matrix_extended.add(local_dof_indices[i],
1609 *
extended_solution_dim - 1,
1610 *
row_last_element_vector(i));
1617 * Global dof indices of dofs
for @f$dT@f$ and @f$d\lambda@f$, associated with vertex @f$\xi = 0@f$.
1624 * Approximating the derivative of @f$T@f$ at @f$\xi = 0@f$ as done in @ref step_14
"step-14".
1627 *
double T_point_derivative(0.);
1628 *
double T_at_zero_point(0.);
1629 *
double lambda_at_zero_point(0.);
1631 *
double derivative_evaluation_point = 0.;
1635 *
quadrature_formula,
1641 *
const unsigned int n_q_points = quadrature_formula.size();
1642 *
std::vector<double> current_temperature_values(
n_q_points);
1643 *
std::vector<Tensor<1, 1>> current_temperature_gradients(
n_q_points);
1644 *
std::vector<double> current_lambda_values(
n_q_points);
1646 *
unsigned int derivative_evaluation_point_hits = 0;
1648 *
for (
const auto &cell : dof_handler.active_cell_iterators())
1650 *
for (
const auto &vertex : cell->vertex_indices())
1652 *
if (isapprox(cell->vertex(vertex)[0], derivative_evaluation_point))
1654 *
T_zero_point_dof_ind = cell->vertex_dof_index(vertex, 1);
1655 *
lambda_zero_point_dof_ind = cell->vertex_dof_index(vertex, 2);
1657 *
fe_values.reinit(cell);
1658 *
fe_values[
temperature].get_function_values(current_solution, current_temperature_values);
1659 *
fe_values[
temperature].get_function_gradients(current_solution, current_temperature_gradients);
1660 *
fe_values[
lambda].get_function_values(current_solution, current_lambda_values);
1662 *
unsigned int q_point = 0;
1665 *
if (isapprox(fe_values.quadrature_point(q_point)[0], derivative_evaluation_point))
1671 *
T_at_zero_point = current_temperature_values[q_point];
1672 *
lambda_at_zero_point = current_lambda_values[q_point];
1674 *
T_point_derivative += current_temperature_gradients[q_point][0];
1675 *
++derivative_evaluation_point_hits;
1681 *
T_point_derivative /=
static_cast<double>(derivative_evaluation_point_hits);
1686 * Here we add to the
matrix the terms that appear after integrating the terms with the Dirac delta function (which we skipped inside the loop).
1689 *
double term_with_delta_func(0.);
1690 *
term_with_delta_func = problem.k *
std::exp(-problem.theta / T_at_zero_point) * (1 - lambda_at_zero_point) /
std::abs(T_point_derivative);
1691 *
jacobian_matrix_extended.add(T_zero_point_dof_ind, T_zero_point_dof_ind, problem.q * term_with_delta_func);
1692 *
jacobian_matrix_extended.add(lambda_zero_point_dof_ind, T_zero_point_dof_ind, term_with_delta_func);
1696 * Add 1 to the position <code> T_zero_point_dof_ind </code> of the last row of the
matrix.
1699 *
jacobian_matrix_extended.add(extended_solution_dim - 1, T_zero_point_dof_ind, 1.);
1701 *
zero_boundary_constraints.condense(jacobian_matrix_extended);
1707 *
std::cout <<
"Factorizing Jacobian matrix" << std::endl;
1709 *
jacobian_matrix_extended_factorization = std::make_unique<SparseDirectUMFPACK>();
1710 *
jacobian_matrix_extended_factorization->factorize(jacobian_matrix_extended);
1720 *
std::cout <<
"Computing residual vector ... " << std::endl;
1725 *
for (
unsigned int i = 0; i < dof_handler.n_dofs(); ++i)
1727 *
evaluation_point(i) = evaluation_point_extended(i);
1730 *
const double wave_speed = evaluation_point_extended(extended_solution_dim - 1);
1732 *
const QGauss<1> quadrature_formula(number_of_quadrature_points);
1734 *
quadrature_formula,
1739 *
const unsigned int n_q_points = quadrature_formula.size();
1742 *
std::vector<types::global_dof_index> local_dof_indices(
dofs_per_cell);
1748 *
std::vector<double> current_velocity_values(
n_q_points);
1749 *
std::vector<Tensor<1, 1>> current_velocity_gradients(
n_q_points);
1750 *
std::vector<double> current_temperature_values(
n_q_points);
1751 *
std::vector<Tensor<1, 1>> current_temperature_gradients(
n_q_points);
1752 *
std::vector<double> current_lambda_values(
n_q_points);
1753 *
std::vector<Tensor<1, 1>> current_lambda_gradients(
n_q_points);
1762 *
for (
const auto &cell : dof_handler.active_cell_iterators())
1766 *
fe_values.reinit(cell);
1768 *
fe_values[velocity].get_function_values(evaluation_point, current_velocity_values);
1769 *
fe_values[velocity].get_function_gradients(evaluation_point, current_velocity_gradients);
1770 *
fe_values[
temperature].get_function_values(evaluation_point, current_temperature_values);
1771 *
fe_values[
temperature].get_function_gradients(evaluation_point, current_temperature_gradients);
1772 *
fe_values[
lambda].get_function_values(evaluation_point, current_lambda_values);
1773 *
fe_values[
lambda].get_function_gradients(evaluation_point, current_lambda_gradients);
1775 *
auto omega = [=,
this](
double T,
double lambda){
1776 *
return problem.k * (1 -
lambda) *
std::exp(-problem.theta / T) * Heaviside_func(T - problem.T_ign);
1779 *
for (
unsigned int q = 0; q <
n_q_points; ++q)
1783 *
phi_u[k] = fe_values[velocity].value(k, q);
1784 *
grad_phi_u[k] = fe_values[velocity].gradient(k, q);
1786 *
grad_phi_T[k] = fe_values[
temperature].gradient(k, q);
1787 *
phi_lambda[k] = fe_values[
lambda].value(k, q);
1788 *
grad_phi_lambda[k] = fe_values[
lambda].gradient(k, q);
1791 *
double del_Pr_eps = (problem.Pr * 4 * problem.delta / (3 * problem.epsilon));
1792 *
double del_Le = (problem.delta / problem.Le);
1798 *
del_Pr_eps * (-grad_phi_u[i] * current_velocity_gradients[q])
1800 *
- current_velocity_gradients[q][0] * (1 - wave_speed + problem.epsilon * current_velocity_values[q])
1801 *
- problem.epsilon / 2. * current_temperature_gradients[q][0]
1804 *
+ problem.delta * (-grad_phi_T[i] * current_temperature_gradients[q])
1806 *
wave_speed * (current_temperature_gradients[q][0] - current_velocity_gradients[q][0])
1807 *
+ problem.q * omega(current_temperature_values[q], current_lambda_values[q])
1810 *
+ del_Le * (-grad_phi_lambda[i] * current_lambda_gradients[q])
1811 *
+ phi_lambda[i] * (
1812 *
wave_speed * current_lambda_gradients[q][0] + omega(current_temperature_values[q], current_lambda_values[q])
1815 *
) * fe_values.JxW(q);
1820 *
cell->get_dof_indices(local_dof_indices);
1822 *
for (
const unsigned int i : fe_values.dof_indices())
1828 *
residual(extended_solution_dim - 1) = 0.;
1830 *
zero_boundary_constraints.condense(
residual);
1832 *
double residual_norm =
residual.l2_norm();
1834 *
std::cout << std::defaultfloat;
1835 *
std::cout <<
"norm of residual = " << residual_norm << std::endl;
1837 *
return residual_norm;
1842 * Split the solution vector into two parts:
one part is the solution @f$u@f$, @f$T@f$ and @f$\lambda@f$, and another part is the wave speed.
1845 *
void TravelingWaveSolver::split_extended_solution_vector()
1847 *
for (
unsigned int i = 0; i < extended_solution_dim - 1; ++i)
1849 *
current_solution(i) = current_solution_extended(i);
1852 *
current_wave_speed = current_solution_extended(extended_solution_dim - 1);
1860 *
std::cout <<
"Solving linear system ... " << std::endl;
1862 *
jacobian_matrix_extended_factorization->vmult(solution_extended, rhs);
1864 *
zero_boundary_constraints.distribute(solution_extended);
1874 *
void TravelingWaveSolver::refine_mesh()
1876 *
Vector<float> estimated_error_per_cell(triangulation.n_active_cells());
1885 *
estimated_error_per_cell,
1886 *
fe.component_mask(lambda)
1890 *
estimated_error_per_cell,
1894 *
triangulation.prepare_coarsening_and_refinement();
1897 *
solution_transfer.prepare_for_coarsening_and_refinement(current_solution);
1899 *
triangulation.execute_coarsening_and_refinement();
1901 *
setup_system(
false);
1904 *
current_solution.reinit(dof_handler.n_dofs());
1905 *
solution_transfer.interpolate(current_solution);
1908 *
solution_transfer.interpolate(current_solution, tmp);
1909 *
current_solution = std::move(tmp);
1912 *
set_boundary_and_centering_values();
1914 *
for (
unsigned int i = 0; i < extended_solution_dim - 1; ++i)
1916 *
current_solution_extended(i) = current_solution(i);
1918 *
current_solution_extended(extended_solution_dim - 1) = current_wave_speed;
1923 *
double TravelingWaveSolver::run_newton_iterations(
const double target_tolerance)
1926 *
double residual_norm = 0.;
1929 *
additional_data.function_tolerance = target_tolerance;
1934 *
x.reinit(extended_solution_dim);
1938 *
residual_norm = compute_residual(evaluation_point,
residual);
1944 *
compute_and_factorize_jacobian(evaluation_point);
1950 *
this->solve(rhs, solution, tolerance);
1955 *
nonlinear_solver.solve(current_solution_extended);
1958 *
return residual_norm;
1964 * Output the solution (@f$u@f$, @f$T@f$ and @f$\lambda@f$) and the wave speed into two separate files with
double precision. The files can be read by
gnuplot.
1967 *
void TravelingWaveSolver::output_with_double_precision(
const Vector<double> &solution,
const double wave_speed,
const std::string filename)
1971 *
const std::string file_for_solution = filename +
".txt";
1972 *
std::ofstream output(file_for_solution);
1974 *
for (
const auto &cell : dof_handler.active_cell_iterators())
1976 *
for (
const auto &v_ind : cell->vertex_indices())
1978 *
double u = solution(cell->vertex_dof_index(v_ind, 0));
1979 *
double T = solution(cell->vertex_dof_index(v_ind, 1));
1980 *
double lambda = solution(cell->vertex_dof_index(v_ind, 2));
1982 *
output << std::scientific << std::setprecision(16);
1983 *
output << cell->vertex(v_ind)[0];
1985 *
output << std::scientific << std::setprecision(16);
1986 *
output << std::scientific <<
" " << u <<
" " <<
T <<
" " <<
lambda <<
"\n";
1993 *
std::ofstream file_for_wave_speed_output(
"wave_speed-" + file_for_solution);
1994 *
file_for_wave_speed_output << std::scientific << std::setprecision(16);
1995 *
file_for_wave_speed_output << wave_speed << std::endl;
1996 *
file_for_wave_speed_output.close();
2001 * Copy the solution into the <code> SolutionStruct </code> object, that stores the solution in an ordered manner.
2004 *
void TravelingWaveSolver::get_solution(SolutionStruct &solution)
const
2008 * To obtain an ordered solution array, we first create a
set consisting of the elements <code> {x, u,
T,
lambda} </code> in which the sorting is done by coordinate, and then
copy the contents of the
set into the arrays of the <code> SolutionStruct </code>
object.
2011 *
auto comp = [](
const std::vector<double> &a,
const std::vector<double> &
b) {
2012 *
return a[0] <
b[0];
2014 *
std::set<std::vector<double>,
decltype(comp)> solution_set(comp);
2015 *
for (
const auto &cell : dof_handler.active_cell_iterators())
2017 *
for (
const auto &v_ind : cell->vertex_indices())
2019 *
double x = cell->vertex(v_ind)[0];
2020 *
double u = current_solution(cell->vertex_dof_index(v_ind, 0));
2021 *
double T = current_solution(cell->vertex_dof_index(v_ind, 1));
2022 *
double lambda = current_solution(cell->vertex_dof_index(v_ind, 2));
2023 *
solution_set.insert({x, u,
T,
lambda});
2027 *
solution.x.clear();
2028 *
solution.u.clear();
2029 *
solution.T.clear();
2030 *
solution.lambda.clear();
2032 *
solution.x.reserve(solution_set.size());
2033 *
solution.u.reserve(solution_set.size());
2034 *
solution.T.reserve(solution_set.size());
2035 *
solution.lambda.reserve(solution_set.size());
2037 *
for (
auto it = solution_set.begin(); it != solution_set.end(); ++it)
2039 *
solution.x.push_back((*it)[0]);
2040 *
solution.u.push_back((*it)[1]);
2041 *
solution.T.push_back((*it)[2]);
2042 *
solution.lambda.push_back((*it)[3]);
2045 *
solution.wave_speed = current_wave_speed;
2050 *
void TravelingWaveSolver::get_triangulation(
Triangulation<1> &otriangulation)
const
2052 *
otriangulation.clear();
2053 *
otriangulation.copy_triangulation(triangulation);
2057 *
void TravelingWaveSolver::run(
const std::string filename,
const bool save_solution_to_file)
2059 *
const int mesh_refinement_type = params.mesh.adaptive;
2060 *
const unsigned int n_refinements = params.mesh.refinements_number;
2061 *
const double tol = params.solver.tol;
2063 *
if (triangulation_uploaded ==
false)
2067 * We create two triangulations:
one to the left and
one to the right of
zero coordinate. After that we
merge them to obtain
one triangulation, which contains
zero point.
2072 *
triangulation_left,
2073 *
static_cast<unsigned int>(
std::abs( 0. - params.mesh.interval_left )),
2074 *
params.mesh.interval_left, 0.
2079 *
triangulation_right,
2080 *
static_cast<unsigned int>(
std::abs( params.mesh.interval_right - 0. )),
2081 *
0., params.mesh.interval_right
2088 *
if (triangulation_uploaded ==
false)
2090 *
if (mesh_refinement_type == 1)
2092 *
triangulation.refine_global(1);
2094 *
else if (mesh_refinement_type == 0)
2096 *
triangulation.refine_global(n_refinements);
2100 *
setup_system(
true);
2101 *
set_initial_guess();
2103 *
if (save_solution_to_file)
2105 *
output_with_double_precision(current_solution, current_wave_speed,
"solution_initial_data");
2108 *
if (mesh_refinement_type == 1)
2110 *
double residual_norm = 0.;
2113 *
residual_norm = compute_residual(current_solution_extended, tmp_residual);
2116 *
unsigned int refinement_cycle = 0;
2117 *
while ((residual_norm > tol) && (refinement_cycle < n_refinements))
2119 *
computing_timer.reset();
2120 *
std::cout <<
"Mesh refinement step " << refinement_cycle << std::endl;
2122 *
if (refinement_cycle != 0) { refine_mesh(); }
2124 *
const double target_tolerance = 0.1 *
std::pow(0.1, refinement_cycle);
2125 *
std::cout <<
" Target_tolerance: " << target_tolerance << std::endl;
2127 *
residual_norm = run_newton_iterations(target_tolerance);
2128 *
split_extended_solution_vector();
2131 *
std::cout << std::scientific << std::setprecision(16);
2132 *
std::cout <<
"current_wave_speed = " << current_wave_speed << std::endl;
2133 *
std::cout << std::defaultfloat;
2136 *
computing_timer.print_summary();
2138 *
++refinement_cycle;
2140 *
if (save_solution_to_file)
2142 *
output_with_double_precision(current_solution, current_wave_speed, filename);
2146 *
else if (mesh_refinement_type == 0)
2148 *
run_newton_iterations(tol);
2149 *
split_extended_solution_vector();
2151 *
if (save_solution_to_file)
2153 *
output_with_double_precision(current_solution, current_wave_speed, filename);
2157 *
std::cout << std::scientific << std::setprecision(16);
2158 *
std::cout <<
"current_wave_speed = " << current_wave_speed << std::endl;
2159 *
std::cout << std::defaultfloat;
2162 *
computing_timer.print_summary();
2173<a name=
"ann-TravelingWaveSolver.h"></a>
2174<h1>Annotated version of TravelingWaveSolver.h</h1>
2190 *
#ifndef TRAVELING_WAVE_SOLVER
2191 *
#define TRAVELING_WAVE_SOLVER
2193 *
#include <deal.II/base/timer.h>
2194 *
#include <deal.II/base/function.h>
2195 *
#include <deal.II/base/table_handler.h>
2196 *
#include <deal.II/grid/tria.h>
2197 *
#include <deal.II/grid/grid_generator.h>
2198 *
#include <deal.II/grid/grid_refinement.h>
2199 *
#include <deal.II/grid/grid_out.h>
2200 *
#include <deal.II/dofs/dof_handler.h>
2201 *
#include <deal.II/dofs/dof_tools.h>
2202 *
#include <deal.II/fe/fe_q.h>
2203 *
#include <deal.II/fe/fe_system.h>
2204 *
#include <deal.II/fe/fe_values.h>
2205 *
#include <deal.II/lac/vector.h>
2206 *
#include <deal.II/lac/full_matrix.h>
2207 *
#include <deal.II/lac/sparse_matrix.h>
2208 *
#include <deal.II/lac/sparse_direct.h>
2209 *
#include <deal.II/lac/dynamic_sparsity_pattern.h>
2210 *
#include <deal.II/numerics/vector_tools.h>
2211 *
#include <deal.II/numerics/error_estimator.h>
2212 *
#include <deal.II/numerics/solution_transfer.h>
2213 *
#include <deal.II/numerics/data_out.h>
2214 *
#include <deal.II/sundials/kinsol.h>
2216 *
#include
"Parameters.h"
2217 *
#include
"Solution.h"
2218 *
#include
"AuxiliaryFunctions.h"
2221 *
#include <algorithm>
2223 *
#include <fstream>
2224 *
#include <iostream>
2225 *
#include <iomanip>
2230 * Namespace of the program
2233 *
namespace TravelingWave
2235 *
using namespace dealii;
2239 * The
main class for construction of the traveling wave solutions.
2242 *
class TravelingWaveSolver
2245 *
TravelingWaveSolver(
const Parameters ¶meters,
const SolutionStruct &initial_guess_input);
2249 *
void run(
const std::string filename=
"solution",
const bool save_solution_to_file=
true);
2250 *
void get_solution(SolutionStruct &solution)
const;
2254 *
void setup_system(
const bool initial_step);
2255 *
void find_boundary_and_centering_dof_numbers();
2256 *
void set_boundary_and_centering_values();
2258 *
void set_initial_guess();
2260 *
double Heaviside_func(
double x)
const;
2262 *
void compute_and_factorize_jacobian(
const Vector<double> &evaluation_point_extended);
2264 *
void split_extended_solution_vector();
2267 *
void refine_mesh();
2268 *
double run_newton_iterations(
const double target_tolerance=1e-5);
2270 *
void output_with_double_precision(
const Vector<double> &solution,
const double wave_speed,
const std::string filename=
"solution");
2274 * The
dimension of the finite element solution increased by
one to account
for the
value corresponding to the wave speed.
2277 *
unsigned int extended_solution_dim;
2278 *
std::map<std::string, unsigned int> boundary_and_centering_dof_numbers;
2282 * Parameters of the problem, taken from a .prm file.
2285 *
const Parameters ¶ms;
2286 *
const Problem &problem;
2288 *
unsigned int number_of_quadrature_points;
2293 * The flag indicating whether the triangulation was uploaded externally or created within the <code>
run </code> member function.
2296 *
bool triangulation_uploaded;
2302 * Constraints
for Dirichlet boundary conditions.
2309 *
std::unique_ptr<SparseDirectUMFPACK> jacobian_matrix_extended_factorization;
2313 * Finite element solution of the problem.
2320 * Value of the wave speed @f$c@f$.
2323 *
double current_wave_speed;
2327 * Solution with an additional term, corresponding to the variable wave_speed.
2334 * Initial guess
for Newton
's iterations.
2337 * SolutionStruct initial_guess;
2339 * TimerOutput computing_timer;
2342 * } // namespace TravelingWave
2348<a name="ann-calculate_profile.cc"></a>
2349<h1>Annotated version of calculate_profile.cc</h1>
2355 * /* -----------------------------------------------------------------------------
2357 * * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
2358 * * Copyright (C) 2024 by Shamil Magomedov
2360 * * This file is part of the deal.II code gallery.
2362 * * -----------------------------------------------------------------------------
2365 * #include "TravelingWaveSolver.h"
2366 * #include "calculate_profile.h"
2368 * namespace TravelingWave
2370 * using namespace dealii;
2374 * Computation of the limit case (ideal) solution, corresponding to @f$\delta = 0@f$, by solving the ODE. The output is the part of the solution to the left of zero. Here u_0, T_0, lambda_0 are the values of the medium state to the right of zero.
2377 * void compute_limit_sol_left_part(const Parameters ¶meters,
2378 * const double wave_speed,
2381 * const double lambda_0,
2382 * SolutionStruct &LimitSol,
2383 * const double root_sign)
2385 * LimitSolution limit_sol(parameters, lambda_0, u_0, T_0, root_sign);
2386 * limit_sol.set_wave_speed(wave_speed);
2391 * We take more integration points to better resolve the transition layer.
2394 * std::vector<double> t_span(static_cast<unsigned int>(std::abs( 0. - parameters.mesh.interval_left )));
2395 * double finer_mesh_starting_value = -9.1;
2396 * linspace(parameters.mesh.interval_left, finer_mesh_starting_value, t_span);
2397 * std::vector<double> fine_grid(10000);
2398 * linspace(finer_mesh_starting_value + 1e-4, 0., fine_grid);
2399 * t_span.insert(t_span.end(), fine_grid.begin(), fine_grid.end());
2403 * Reverse the order of the elements (because we need to perform back in time integration).
2406 * std::reverse(t_span.begin(), t_span.end());
2408 * state_type lambda_val(1);
2409 * lambda_val[0] = lambda_0; // initial value
2410 * IntegrateSystemAtTimePoints(limit_sol.lambda_vec, limit_sol.t_vec, t_span,
2413 * -1e-2, Integrator_Type::dopri5);
2416 * limit_sol.calculate_u_T_omega();
2420 * Reverse the order of elements
2423 * std::reverse(limit_sol.t_vec.begin(), limit_sol.t_vec.end());
2424 * std::reverse(limit_sol.lambda_vec.begin(), limit_sol.lambda_vec.end());
2425 * std::reverse(limit_sol.u_vec.begin(), limit_sol.u_vec.end());
2426 * std::reverse(limit_sol.T_vec.begin(), limit_sol.T_vec.end());
2427 * std::reverse(limit_sol.omega_vec.begin(), limit_sol.omega_vec.end());
2429 * SaveSolutionIntoFile(limit_sol.lambda_vec, limit_sol.t_vec, "solution_lambda_limit.txt");
2430 * SaveSolutionIntoFile(limit_sol.u_vec, limit_sol.t_vec, "solution_u_limit.txt");
2431 * SaveSolutionIntoFile(limit_sol.T_vec, limit_sol.t_vec, "solution_T_limit.txt");
2432 * SaveSolutionIntoFile(limit_sol.omega_vec, limit_sol.t_vec, "solution_omega_limit.txt");
2434 * LimitSol.reinit(limit_sol.t_vec.size());
2435 * LimitSol.wave_speed = wave_speed;
2436 * for (unsigned int i=0; i < limit_sol.t_vec.size(); ++i)
2438 * LimitSol.x[i] = limit_sol.t_vec[i];
2439 * LimitSol.u[i] = limit_sol.u_vec[i][0];
2440 * LimitSol.T[i] = limit_sol.T_vec[i][0];
2441 * LimitSol.lambda[i] = limit_sol.lambda_vec[i][0];
2448 * Construction of an initial guess for detonation wave solution. The ODE is solved for the ideal system with @f$\delta = 0@f$.
2451 * void compute_initial_guess_detonation(const Parameters ¶ms, SolutionStruct &initial_guess, const double root_sign)
2453 * const Problem &problem = params.problem;
2454 * double current_wave_speed(problem.wave_speed_init);
2456 * { // Here we compute the exact value of the wave speed c for the detonation case. We can do this because we have the Dirichlet boundary conditions T_l, T_r and u_r. Exact values of u_l and c are obtained using the integral relations.
2457 * double DeltaT = problem.T_left - problem.T_right;
2458 * double qDT = problem.q - DeltaT;
2459 * current_wave_speed = 1. + problem.epsilon * (problem.u_right - (qDT * qDT + DeltaT) / (2 * qDT));
2462 * double u_0 = problem.u_right;
2463 * double T_0 = problem.T_right;
2464 * double lambda_0 = 0.;
2466 * compute_limit_sol_left_part(params, current_wave_speed, u_0, T_0, lambda_0, initial_guess, root_sign);
2468 * initial_guess.wave_speed = current_wave_speed;
2470 * for (int i = initial_guess.x.size() - 1; i > - 1; --i)
2472 * if (isapprox(initial_guess.x[i], 0.))
2474 * initial_guess.u[i] = problem.u_right;
2475 * initial_guess.T[i] = problem.T_ign;
2476 * initial_guess.lambda[i] = 0.;
2483 * Adding the points to the right part of the interval (w.r.t. @f$\xi = 0@f$).
2486 * unsigned int number_of_additional_points = 5;
2487 * for (unsigned int i = 0; i < number_of_additional_points; ++i)
2489 * initial_guess.x.push_back(params.mesh.interval_right / (std::pow(2., number_of_additional_points - 1 - i)));
2490 * initial_guess.u.push_back(problem.u_right);
2491 * initial_guess.T.push_back(problem.T_right);
2492 * initial_guess.lambda.push_back(0.);
2500 * Construction of a piecewise constant initial guess for deflagration wave solution.
2503 * void compute_initial_guess_deflagration(const Parameters ¶ms, SolutionStruct &initial_guess)
2505 * const Problem &problem = params.problem;
2506 * double current_wave_speed(problem.wave_speed_init);
2508 * double del_Pr_eps = (problem.Pr * 4 * problem.delta / (3 * problem.epsilon));
2510 * auto u_init_guess_func = [&](double x) {
2513 * return problem.u_left;
2517 * return problem.u_right;
2521 * auto T_init_guess_func = [&](double x) {
2524 * return problem.T_left;
2526 * else if (isapprox(x, 0.))
2528 * return problem.T_ign;
2532 * return problem.T_right;
2536 * auto lambda_init_guess_func = [=](double x) {
2539 * return -std::exp(x * std::abs(1 - current_wave_speed) / del_Pr_eps) + 1;
2547 * unsigned int multiplier_for_number_of_points = 7;
2548 * unsigned int number_of_points = multiplier_for_number_of_points * static_cast<unsigned int>(std::trunc(std::abs( params.mesh.interval_right - params.mesh.interval_left )));
2549 * std::vector<double> x_span(number_of_points);
2550 * linspace(params.mesh.interval_left, params.mesh.interval_right, x_span);
2552 * std::vector<double> u_init_arr(number_of_points);
2553 * std::vector<double> T_init_arr(number_of_points);
2554 * std::vector<double> lambda_init_arr(number_of_points);
2556 * for (unsigned int i = 0; i < number_of_points; ++i)
2558 * u_init_arr[i] = u_init_guess_func(x_span[i]);
2559 * T_init_arr[i] = T_init_guess_func(x_span[i]);
2560 * lambda_init_arr[i] = lambda_init_guess_func(x_span[i]);
2563 * initial_guess.x = x_span;
2564 * initial_guess.u = u_init_arr;
2565 * initial_guess.T = T_init_arr;
2566 * initial_guess.lambda = lambda_init_arr;
2567 * initial_guess.wave_speed = current_wave_speed;
2574 * Compute the traveling-wave profile. The continuation method can be switched on by setting the argument <code> continuation_for_delta </code> as <code> true </code>.
2577 * void calculate_profile(Parameters& parameters,
2578 * const bool continuation_for_delta /* Compute with the continuation. */,
2579 * const double delta_start /* The starting value of delta for the continuation method. */,
2580 * const unsigned int number_of_continuation_points)
2582 * SolutionStruct sol;
2584 * if (parameters.problem.wave_type == 1) // detonation wave
2586 * compute_initial_guess_detonation(parameters, sol);
2588 * else if (parameters.problem.wave_type == 0) // deflagration wave
2590 * compute_initial_guess_deflagration(parameters, sol);
2593 * if (continuation_for_delta == false)
2595 * TravelingWaveSolver wave(parameters, sol);
2596 * std::string filename = "solution_delta-" + Utilities::to_string(parameters.problem.delta) + "_eps-"
2597 * + Utilities::to_string(parameters.problem.epsilon);
2598 * wave.run(filename);
2599 * wave.get_solution(sol);
2601 * else // Run with continuation_for_delta.
2603 * double delta_target = parameters.problem.delta;
2604 * parameters.problem.delta = delta_start;
2606 * std::vector<double> delta_span(number_of_continuation_points);
2610 * Generate a sequence of delta values being uniformly distributed in log10 scale.
2614 * double delta_start_log10 = std::log10(delta_start);
2615 * double delta_target_log10 = std::log10(delta_target);
2617 * std::vector<double> delta_log_span(delta_span.size());
2618 * linspace(delta_start_log10, delta_target_log10, delta_log_span);
2620 * for (unsigned int i = 0; i < delta_span.size(); ++i)
2622 * delta_span[i] = std::pow(10, delta_log_span[i]);
2626 * Triangulation<1> refined_triangulation;
2627 * bool first_iter_flag = true;
2629 * for (double delta : delta_span)
2631 * parameters.problem.delta = delta;
2632 * std::string filename = "solution_delta-" + Utilities::to_string(parameters.problem.delta) + "_eps-"
2633 * + Utilities::to_string(parameters.problem.epsilon);
2635 * TravelingWaveSolver wave(parameters, sol);
2637 * if (first_iter_flag)
2639 * first_iter_flag = false;
2643 * wave.set_triangulation(refined_triangulation);
2646 * wave.run(filename);
2647 * wave.get_solution(sol);
2648 * wave.get_triangulation(refined_triangulation);
2659 * unsigned int sol_length = sol.x.size();
2660 * double u_r = sol.u[sol_length-1]; // Dirichlet boundary condition
2661 * double T_r = sol.T[sol_length-1]; // Dirichlet condition only for detonation case
2662 * double u_l = sol.u[0];
2663 * double T_l = sol.T[0]; // Dirichlet boundary condition
2664 * double wave_speed = sol.wave_speed;
2666 * std::cout << "Error estimates:" << std::endl;
2667 * double DeltaT = T_l - T_r;
2668 * double qDT = parameters.problem.q - DeltaT;
2670 * double wave_speed_formula = 1. + parameters.problem.epsilon * (u_r - (qDT * qDT + DeltaT) / (2 * qDT));
2671 * std::cout << std::setw(18) << std::left << "For wave speed" << " : " << std::setw(5) << wave_speed - wave_speed_formula << std::endl;
2673 * double u_l_formula = DeltaT + u_r - parameters.problem.q;
2674 * std::cout << std::setw(18) << std::left << "For u_l" << " : " << std::setw(5) << u_l - u_l_formula << std::endl;
2679 * } // namespace TravelingWave
2683<a name="ann-calculate_profile.h"></a>
2684<h1>Annotated version of calculate_profile.h</h1>
2690 * /* -----------------------------------------------------------------------------
2692 * * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
2693 * * Copyright (C) 2024 by Shamil Magomedov
2695 * * This file is part of the deal.II code gallery.
2697 * * -----------------------------------------------------------------------------
2700 * #ifndef CALCULATE_PROFILE
2701 * #define CALCULATE_PROFILE
2703 * #include "Parameters.h"
2704 * #include "Solution.h"
2705 * #include "LimitSolution.h"
2706 * #include "IntegrateSystem.h"
2707 * #include "AuxiliaryFunctions.h"
2709 * namespace TravelingWave
2711 * void compute_limit_sol_left_part(const Parameters ¶meters,
2712 * const double wave_speed,
2715 * const double lambda_0,
2716 * SolutionStruct &LimitSol,
2717 * const double root_sign = 1.);
2719 * void compute_initial_guess_detonation(const Parameters ¶ms, SolutionStruct &initial_guess, const double root_sign = 1.);
2720 * void compute_initial_guess_deflagration(const Parameters ¶ms, SolutionStruct &initial_guess);
2722 * void calculate_profile(Parameters& parameters,
2723 * const bool continuation_for_delta=false /* Compute with the continuation. */,
2724 * const double delta_start=0.01 /* The starting value of delta for the continuation method. */,
2725 * const unsigned int number_of_continuation_points=10);
2727 * } // namespace TravelingWave
2733<a name="ann-main.cc"></a>
2734<h1>Annotated version of main.cc</h1>
2740 * /* -----------------------------------------------------------------------------
2742 * * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
2743 * * Copyright (C) 2024 by Shamil Magomedov
2745 * * This file is part of the deal.II code gallery.
2747 * * -----------------------------------------------------------------------------
2750 * #include "calculate_profile.h"
2752 * int main(int argc, char *argv[])
2757 * using namespace TravelingWave;
2759 * Parameters parameters;
2761 * std::string prm_filename = "ParametersList.prm";
2766 * Check if file argv[1] exists.
2769 * if (file_exists(argv[1]))
2771 * prm_filename = argv[1];
2775 * std::string errorMessage = "File \"" + std::string(argv[1]) + "\" is not found.";
2776 * throw std::runtime_error(errorMessage);
2783 * Check if the file "ParametersList.prm" exists in the current or in the parent directory.
2786 * if (!(file_exists(prm_filename) || file_exists("../" + prm_filename)))
2788 * std::string errorMessage = "File \"" + prm_filename + "\" is not found.";
2789 * throw std::runtime_error(errorMessage);
2793 * if (!file_exists(prm_filename))
2795 * prm_filename = "../" + prm_filename;
2800 * std::cout << "Reading parameters... " << std::flush;
2801 * ParameterAcceptor::initialize(prm_filename);
2802 * std::cout << "done" << std::endl;
2804 * calculate_profile(parameters, /* With continuation_for_delta */ false, 0.1, 3);
2807 * catch (std::exception &exc)
2809 * std::cerr << std::endl
2811 * << "----------------------------------------------------"
2813 * std::cerr << "Exception on processing: " << std::endl
2814 * << exc.what() << std::endl
2815 * << "Aborting!" << std::endl
2816 * << "----------------------------------------------------"
2822 * std::cerr << std::endl
2824 * << "----------------------------------------------------"
2826 * std::cerr << "Unknown exception!" << std::endl
2827 * << "Aborting!" << std::endl
2828 * << "----------------------------------------------------"
2838<a name="ann-plot.py"></a>
2839<h1>Annotated version of plot.py</h1>
2841## -----------------------------------------------------------------------------
2843## SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
2844## Copyright (C) 2024 by Shamil Magomedov
2846## This file is part of the deal.II code gallery.
2848## -----------------------------------------------------------------------------
2852import matplotlib.pyplot as plt
2858 # 'lines.marker
' : 'x
',
2860 'lines.markersize
' : 4,
2861 'lines.linewidth
' : 1,
2862 'axes.labelsize
': 16,
2863 # 'textfontsize
': 12,
2865 'legend.fontsize
': 16,
2866 'xtick.labelsize
': 14,
2867 'ytick.labelsize
': 14,
2868 'text.usetex
': True,
2869 'figure.figsize
': [9,6],
2873plt.rcParams.update(plot_params)
2876if len(sys.argv) > 1:
2878 filename = sys.argv[1]
2880 if os.path.exists(filename):
2881 data = np.loadtxt(filename, np.float64)
2882 data_unique = np.unique(data, axis=0)
2883 data_unique = np.array(sorted(data_unique, key=lambda x : x[0]))
2884 x = data_unique[:, 0]
2885 u_sol = data_unique[:, 1]
2886 T_sol = data_unique[:, 2]
2887 lambda_sol = data_unique[:, 3]
2889 fig, ax = plt.subplots(nrows=1, ncols=1)
2891 ax.scatter(x, u_sol, label=r"@f$u@f$", color='blue
')
2892 ax.scatter(x, T_sol, label=r"@f$T@f$", color='red
')
2893 ax.scatter(x, lambda_sol, label=r"@f$\lambda@f$", color='green
')
2896 # Plot of limit solutions for the detonation case. Uncomment, if needed.
2897 #===============================================================#
2900 path_to_solution_files = os.path.split(filename)[0]
2901 u_limit_path = os.path.join(path_to_solution_files,
'solution_u_limit.txt')
2902 T_limit_path = os.path.join(path_to_solution_files,
'solution_T_limit.txt')
2903 lambda_limit_path = os.path.join(path_to_solution_files,
'solution_lambda_limit.txt')
2905 if os.path.exists(u_limit_path):
2906 u_limit = np.loadtxt(u_limit_path, np.float64)
2907 ax.plot(u_limit[:, 0], u_limit[:, 1], label=r
"@f$u_{\mathrm{lim}}@f$", color=
'blue')
2908 ax.plot([0, x[-1]], [u_sol[-1], u_sol[-1]], color=
'blue')
2910 print(
"No such file:", u_limit_path)
2912 if os.path.exists(T_limit_path):
2913 T_limit = np.loadtxt(T_limit_path, np.float64)
2914 ax.plot(T_limit[:, 0], T_limit[:, 1], label=r
"@f$T_{\mathrm{lim}}@f$", color=
'red')
2915 ax.plot([0, x[-1]], [T_sol[-1], T_sol[-1]], color=
'red')
2917 print(
"No such file:", T_limit_path)
2919 if os.path.exists(lambda_limit_path):
2920 lambda_limit = np.loadtxt(lambda_limit_path, np.float64)
2921 ax.plot(lambda_limit[:, 0], lambda_limit[:, 1], label=r
"@f$\lambda_{\mathrm{lim}}@f$", color=
'green')
2922 ax.plot([0, x[-1]], [lambda_sol[-1], lambda_sol[-1]], color=
'green')
2924 print(
"No such file:", lambda_limit_path)
2928 #===============================================================#
2931 ax.set_xlabel(r
"@f$\xi@f$")
2932 ax.set_ylabel(r
"@f$u, T, \lambda@f$")
2935 # plt.savefig("fast_deflagration_delta_0.01.png", bbox_inches='tight', dpi=500)
2936 # plt.savefig('slow_deflagration_delta_0.01.png', bbox_inches='tight', dpi=500)
2937 # plt.savefig('detonation_delta_0.01.png', bbox_inches='tight', dpi=500)
2941 print(
"No such file:", filename)
* * * struct InterferenceTaperTransform *
* * for(const auto &cell :triangulation.active_cell_iterators())
* * int main(int argc, char **argv)
* x_component_mask set(0, true)
* * Point< dim > operator()(const Point< dim > &p) const *
static void estimate(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const Quadrature< dim - 1 > &quadrature, const std::map< types::boundary_id, const Function< spacedim, Number > * > &neumann_bc, const ReadVector< Number > &solution, Vector< float > &error, const ComponentMask &component_mask={}, const Function< spacedim > *coefficients=nullptr, const unsigned int n_threads=numbers::invalid_unsigned_int, const types::subdomain_id subdomain_id=numbers::invalid_subdomain_id, const types::material_id material_id=numbers::invalid_material_id, const Strategy strategy=cell_diameter_over_24)
#define DEAL_II_VERSION_GTE(major, minor, subminor)
const unsigned int DoFAccessor< structdim, dim, spacedim, level_dof_access >::dimension
const bool IsBlockVector< VectorType >::value
@ update_values
Shape function values.
@ update_JxW_values
Transformed quadrature weights.
@ update_gradients
Shape function gradients.
@ update_quadrature_points
Transformed quadrature points.
void subdivided_hyper_cube(Triangulation< dim, spacedim > &tria, const unsigned int repetitions, const double left=0., const double right=1., const bool colorize=false)
void merge_triangulations(const Triangulation< dim, spacedim > &triangulation_1, const Triangulation< dim, spacedim > &triangulation_2, Triangulation< dim, spacedim > &result, const double duplicated_vertex_tolerance=1.0e-12, const bool copy_manifold_ids=false, const bool copy_boundary_ids=false)
void refine_and_coarsen_fixed_number(Triangulation< dim, spacedim > &triangulation, const Vector< Number > &criteria, const double top_fraction_of_cells, const double bottom_fraction_of_cells, const unsigned int max_n_cells=std::numeric_limits< unsigned int >::max())
@ matrix
Contents is actually a matrix.
constexpr types::blas_int zero
constexpr types::blas_int one
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.)
void cell_residual(Vector< double > &result, const FEValuesBase< dim > &fe, const std::vector< Tensor< 1, dim > > &input, const ArrayView< const std::vector< double > > &velocity, double factor=1.)
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > epsilon(const Tensor< 2, dim, Number > &Grad_u)
* const Number temperature
T scatter(const MPI_Comm comm, const std::vector< T > &objects_to_send, const unsigned int root_process=0)
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)
constexpr TableIndices< 2 > merge(const TableIndices< 2 > &previous_indices, const unsigned int new_index, const unsigned int position)
void copy(const T *begin, const T *end, U *dest)
int(& functions)(const void *v1, const void *v2)
void reinit(MatrixBlock< MatrixType > &v, const BlockSparsityPattern &p)
::VectorizedArray< Number, width > exp(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
unsigned int global_dof_index
* const unsigned int dofs_per_cell
* const unsigned int n_q_points