DiFfRG
Loading...
Searching...
No Matches
cg.hh
Go to the documentation of this file.
1#pragma once
2
3// standard library
4#include <sstream>
5
6// DiFfRG
8
9namespace DiFfRG
10{
11 namespace CG
12 {
13 using namespace dealii;
14 using std::array;
15
16 template <typename... T> auto fe_tie(T &&...t)
17 {
18 return named_tuple<std::tuple<T &...>, "fe_functions", "fe_derivatives", "fe_hessians", "extractors",
19 "variables">(std::tie(t...));
20 }
21
22 template <typename... T> auto i_tie(T &&...t)
23 {
24 return named_tuple<std::tuple<T &...>, "fe_functions", "fe_derivatives", "fe_hessians">(std::tie(t...));
25 }
26
27 namespace internal
28 {
35 template <typename Discretization> struct ScratchData {
36 static constexpr uint dim = Discretization::dim;
38
39 ScratchData(const Mapping<dim> &mapping, const FiniteElement<dim> &fe, const Quadrature<dim> &quadrature,
40 const Quadrature<dim - 1> &quadrature_face,
41 const UpdateFlags update_flags = update_values | update_gradients | update_quadrature_points |
42 update_JxW_values | update_hessians,
43 const UpdateFlags interface_update_flags = update_values | update_gradients |
44 update_quadrature_points | update_JxW_values |
45 update_normal_vectors | update_hessians)
46 : n_components(fe.n_components()), fe_values(mapping, fe, quadrature, update_flags),
47 fe_interface_values(mapping, fe, quadrature_face, interface_update_flags)
48 {
49 solution.resize(quadrature.size(), Vector<NumberType>(n_components));
50 solution_grad.resize(quadrature.size(), std::vector<Tensor<1, dim, NumberType>>(n_components));
51 solution_hess.resize(quadrature.size(), std::vector<Tensor<2, dim, NumberType>>(n_components));
52 solution_dot.resize(quadrature.size(), Vector<NumberType>(n_components));
53 solution_interface[0].resize(quadrature_face.size(), Vector<NumberType>(n_components));
54 solution_interface[1].resize(quadrature_face.size(), Vector<NumberType>(n_components));
55 solution_grad_interface[0].resize(quadrature_face.size(),
56 std::vector<Tensor<1, dim, NumberType>>(n_components));
57 solution_grad_interface[1].resize(quadrature_face.size(),
58 std::vector<Tensor<1, dim, NumberType>>(n_components));
59 solution_hess_interface[0].resize(quadrature_face.size(),
60 std::vector<Tensor<2, dim, NumberType>>(n_components));
61 solution_hess_interface[1].resize(quadrature_face.size(),
62 std::vector<Tensor<2, dim, NumberType>>(n_components));
63 }
64
66 : n_components(scratch_data.fe_values.get_fe().n_components()),
67 fe_values(scratch_data.fe_values.get_mapping(), scratch_data.fe_values.get_fe(),
68 scratch_data.fe_values.get_quadrature(), scratch_data.fe_values.get_update_flags()),
69 fe_interface_values(scratch_data.fe_interface_values.get_mapping(),
70 scratch_data.fe_interface_values.get_fe(),
71 scratch_data.fe_interface_values.get_quadrature(),
72 scratch_data.fe_interface_values.get_update_flags())
73 {
74 const uint q_size = scratch_data.fe_values.get_quadrature().size();
75 const uint q_face_size = scratch_data.fe_interface_values.get_quadrature().size();
76
77 solution.resize(q_size, Vector<NumberType>(n_components));
78 solution_grad.resize(q_size, std::vector<Tensor<1, dim, NumberType>>(n_components));
79 solution_hess.resize(q_size, std::vector<Tensor<2, dim, NumberType>>(n_components));
80 solution_dot.resize(q_size, Vector<NumberType>(n_components));
81 solution_interface[0].resize(q_face_size, Vector<NumberType>(n_components));
82 solution_interface[1].resize(q_face_size, Vector<NumberType>(n_components));
83 solution_grad_interface[0].resize(q_face_size, std::vector<Tensor<1, dim, NumberType>>(n_components));
84 solution_grad_interface[1].resize(q_face_size, std::vector<Tensor<1, dim, NumberType>>(n_components));
85 solution_hess_interface[0].resize(q_face_size, std::vector<Tensor<2, dim, NumberType>>(n_components));
86 solution_hess_interface[1].resize(q_face_size, std::vector<Tensor<2, dim, NumberType>>(n_components));
87 }
88
90
91 FEValues<dim> fe_values;
92 FEInterfaceValues<dim> fe_interface_values;
93
94 std::vector<Vector<NumberType>> solution;
95 std::vector<std::vector<Tensor<1, dim, NumberType>>> solution_grad;
96 std::vector<std::vector<Tensor<2, dim, NumberType>>> solution_hess;
97 std::vector<Vector<NumberType>> solution_dot;
98 array<std::vector<Vector<NumberType>>, 2> solution_interface;
99 array<std::vector<std::vector<Tensor<1, dim, NumberType>>>, 2> solution_grad_interface;
100 array<std::vector<std::vector<Tensor<2, dim, NumberType>>>, 2> solution_hess_interface;
101 };
102
103 template <typename NumberType> struct CopyData_R {
104 Vector<NumberType> cell_residual;
105 std::vector<types::global_dof_index> local_dof_indices;
106
107 template <class Iterator> void reinit(const Iterator &cell, uint dofs_per_cell)
108 {
109 cell_residual.reinit(dofs_per_cell);
110 local_dof_indices.resize(dofs_per_cell);
111 cell->get_dof_indices(local_dof_indices);
112 }
113 };
114
115 template <typename NumberType> struct CopyData_J {
116 FullMatrix<NumberType> cell_jacobian;
117 FullMatrix<NumberType> extractor_cell_jacobian;
118 std::vector<types::global_dof_index> local_dof_indices;
119
120 template <class Iterator> void reinit(const Iterator &cell, uint dofs_per_cell, uint n_extractors)
121 {
122 cell_jacobian.reinit(dofs_per_cell, dofs_per_cell);
123 if (n_extractors > 0) extractor_cell_jacobian.reinit(dofs_per_cell, n_extractors);
124 local_dof_indices.resize(dofs_per_cell);
125 cell->get_dof_indices(local_dof_indices);
126 }
127 };
128
129 template <typename NumberType> struct CopyData_I {
131 std::array<uint, 2> cell_indices;
132 std::array<double, 2> values;
133 };
134 std::vector<CopyFaceData_I> face_data;
135 double value;
137 };
138 } // namespace internal
139
145 template <typename Discretization_, typename Model_> class Assembler : public FEMAssembler<Discretization_, Model_>
146 {
148
149 public:
150 using Discretization = Discretization_;
151 using Model = Model_;
154
156 static constexpr uint dim = Discretization::dim;
157
159 : Base(discretization, model, json),
160 quadrature(fe.degree + 1 + json.get_uint("/discretization/overintegration")),
161 quadrature_face(fe.degree + 1 + json.get_uint("/discretization/overintegration"))
162 {
163 static_assert(Components::count_fe_subsystems() == 1, "A CG model cannot have multiple submodels!");
164 reinit();
165 }
166
167 virtual void reinit_vector(VectorType &vec) const override { vec.reinit(dof_handler.n_dofs()); }
168
169 virtual void reinit() override
170 {
171 Timer timer;
172
173 Base::reinit();
174
175 // Mass sparsity pattern
176 {
177 DynamicSparsityPattern dsp(dof_handler.n_dofs());
178 DoFTools::make_sparsity_pattern(dof_handler, dsp, discretization.get_constraints(),
179 /*keep_constrained_dofs = */ true);
180 sparsity_pattern_mass.copy_from(dsp);
182 MatrixCreator::create_mass_matrix(dof_handler, quadrature, mass_matrix,
183 (const Function<dim, NumberType> *const)nullptr,
184 discretization.get_constraints());
185 }
186 // Jacobian sparsity pattern
187 {
188 DynamicSparsityPattern dsp(dof_handler.n_dofs());
189 DoFTools::make_sparsity_pattern(dof_handler, dsp, discretization.get_constraints(),
190 /*keep_constrained_dofs = */ true);
191 sparsity_pattern_jacobian.copy_from(dsp);
192 }
193 timings_reinit.push_back(timer.wall_time());
194
195 // Boundary dofs
196 std::vector<IndexSet> component_boundary_dofs(Components::count_fe_functions());
197 for (uint c = 0; c < Components::count_fe_functions(); ++c) {
198 ComponentMask component_mask(Components::count_fe_functions(), false);
199 component_mask.set(c, true);
200 component_boundary_dofs[c] = DoFTools::extract_boundary_dofs(dof_handler, component_mask);
201 }
202 std::vector<std::vector<Point<dim>>> component_boundary_points(Components::count_fe_functions());
203 for (uint c = 0; c < Components::count_fe_functions(); ++c) {
204 component_boundary_points[c].resize(component_boundary_dofs[c].n_elements());
205 for (uint i = 0; i < component_boundary_dofs[c].n_elements(); ++i)
206 component_boundary_points[c][i] =
207 discretization.get_support_point(component_boundary_dofs[c].nth_index_in_set(i));
208 }
209
210 auto &constraints = discretization.get_constraints();
211 constraints.clear();
212 DoFTools::make_hanging_node_constraints(dof_handler, constraints);
213 model.affine_constraints(constraints, component_boundary_dofs, component_boundary_points);
214 constraints.close();
215 }
216
217 virtual void rebuild_jacobian_sparsity() override
218 {
219 // Jacobian sparsity pattern
220 DynamicSparsityPattern dsp(dof_handler.n_dofs());
221 DoFTools::make_sparsity_pattern(dof_handler, dsp, discretization.get_constraints(),
222 /*keep_constrained_dofs = */ true);
223 for (uint row = 0; row < dsp.n_rows(); ++row)
224 dsp.add_row_entries(row, extractor_dof_indices);
225 sparsity_pattern_jacobian.copy_from(dsp);
226 }
227
228 virtual const SparsityPattern &get_sparsity_pattern_jacobian() const override
229 {
231 }
232 virtual const SparseMatrix<NumberType> &get_mass_matrix() const override { return mass_matrix; }
233
241 virtual void refinement_indicator(Vector<double> &indicator, const VectorType &solution_global) override
242 {
243 using Iterator = typename DoFHandler<dim>::active_cell_iterator;
245 using CopyData = internal::CopyData_I<NumberType>;
246
247 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
248 scratch_data.fe_values.reinit(t_cell);
249 const auto &fe_v = scratch_data.fe_values;
250 copy_data.cell_index = t_cell->active_cell_index();
251 copy_data.value = 0;
252
253 const auto &JxW = fe_v.get_JxW_values();
254 const auto &q_points = fe_v.get_quadrature_points();
255 const auto &q_indices = fe_v.quadrature_point_indices();
256
257 auto &solution = scratch_data.solution;
258 auto &solution_grad = scratch_data.solution_grad;
259 auto &solution_hess = scratch_data.solution_hess;
260 fe_v.get_function_values(solution_global, solution);
261 fe_v.get_function_gradients(solution_global, solution_grad);
262 fe_v.get_function_hessians(solution_global, solution_hess);
263
264 double local_indicator = 0.;
265 for (const auto &q_index : q_indices) {
266 const auto &x_q = q_points[q_index];
267 const std::vector<double> nothing = {};
268 model.cell_indicator(local_indicator, x_q,
269 i_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index]));
270 copy_data.value += JxW[q_index] * local_indicator;
271 }
272 };
273 const auto copier = [&](const CopyData &c) { indicator[c.cell_index] += c.value; };
274
275 Scratch scratch_data(mapping, fe, quadrature, quadrature_face);
276 CopyData copy_data;
277 MeshWorker::AssembleFlags assemble_flags = MeshWorker::assemble_own_cells;
278
279 MeshWorker::mesh_loop(dof_handler.begin_active(), dof_handler.end(), cell_worker, copier, scratch_data,
280 copy_data, assemble_flags, nullptr, nullptr, threads, batch_size);
281 }
282
283 virtual void mass(VectorType &mass, const VectorType &solution_global, const VectorType &solution_global_dot,
284 NumberType weight) override
285 {
286 using Iterator = typename DoFHandler<dim>::active_cell_iterator;
288 using CopyData = internal::CopyData_R<NumberType>;
289 const auto &constraints = discretization.get_constraints();
290
291 const auto cell_worker = [&](const Iterator &cell, Scratch &scratch_data, CopyData &copy_data) {
292 scratch_data.fe_values.reinit(cell);
293 const auto &fe_v = scratch_data.fe_values;
294 const uint n_dofs = fe_v.get_fe().n_dofs_per_cell();
295
296 copy_data.reinit(cell, n_dofs);
297 const auto &JxW = fe_v.get_JxW_values();
298 const auto &q_points = fe_v.get_quadrature_points();
299 const auto &q_indices = fe_v.quadrature_point_indices();
300
301 auto &solution = scratch_data.solution;
302 auto &solution_dot = scratch_data.solution_dot;
303 fe_v.get_function_values(solution_global, solution);
304 fe_v.get_function_values(solution_global_dot, solution_dot);
305
306 array<NumberType, Components::count_fe_functions()> mass{};
307 for (const auto &q_index : q_indices) {
308 const auto &x_q = q_points[q_index];
309 model.mass(mass, x_q, solution[q_index], solution_dot[q_index]);
310
311 for (uint i = 0; i < n_dofs; ++i) {
312 const auto component_i = fe_v.get_fe().system_to_component_index(i).first;
313 copy_data.cell_residual(i) += weight * JxW[q_index] *
314 fe_v.shape_value_component(i, q_index, component_i) *
315 mass[component_i]; // +phi_i(x_q) * mass(x_q, u_q)
316 }
317 }
318 };
319 const auto copier = [&](const CopyData &c) {
320 constraints.distribute_local_to_global(c.cell_residual, c.local_dof_indices, mass);
321 };
322
323 Scratch scratch_data(mapping, fe, quadrature, quadrature_face);
324 CopyData copy_data;
325 MeshWorker::AssembleFlags flags = MeshWorker::assemble_own_cells;
326
327 MeshWorker::mesh_loop(dof_handler.begin_active(), dof_handler.end(), cell_worker, copier, scratch_data,
328 copy_data, flags, nullptr, nullptr, threads, batch_size);
329 }
330
331 virtual void residual(VectorType &residual, const VectorType &solution_global, NumberType weight,
332 const VectorType &solution_global_dot, NumberType weight_mass,
333 const VectorType &variables = VectorType()) override
334 {
335 using Iterator = typename DoFHandler<dim>::active_cell_iterator;
337 using CopyData = internal::CopyData_R<NumberType>;
338 const auto &constraints = discretization.get_constraints();
339
340 // Find the EoM and extract whatever data is needed for the model.
341 std::array<NumberType, Components::count_extractors()> __extracted_data{{}};
342 if constexpr (Components::count_extractors() > 0)
343 this->extract(__extracted_data, solution_global, variables, true, false, true);
344 const auto &extracted_data = __extracted_data;
345
346 const auto cell_worker = [&](const Iterator &cell, Scratch &scratch_data, CopyData &copy_data) {
347 scratch_data.fe_values.reinit(cell);
348 const auto &fe_v = scratch_data.fe_values;
349 const uint n_dofs = fe_v.get_fe().n_dofs_per_cell();
350
351 copy_data.reinit(cell, n_dofs);
352 const auto &JxW = fe_v.get_JxW_values();
353 const auto &q_points = fe_v.get_quadrature_points();
354 const auto &q_indices = fe_v.quadrature_point_indices();
355
356 auto &solution = scratch_data.solution;
357 auto &solution_grad = scratch_data.solution_grad;
358 auto &solution_hess = scratch_data.solution_hess;
359 auto &solution_dot = scratch_data.solution_dot;
360 fe_v.get_function_values(solution_global, solution);
361 fe_v.get_function_gradients(solution_global, solution_grad);
362 fe_v.get_function_hessians(solution_global, solution_hess);
363 fe_v.get_function_values(solution_global_dot, solution_dot);
364
365 array<Tensor<1, dim, NumberType>, Components::count_fe_functions()> flux{};
366 array<NumberType, Components::count_fe_functions()> source{};
367 array<NumberType, Components::count_fe_functions()> mass{};
368 for (const auto &q_index : q_indices) {
369 const auto &x_q = q_points[q_index];
370 model.flux(
371 flux, x_q,
372 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
373 model.source(
374 source, x_q,
375 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
376 model.mass(mass, x_q, solution[q_index], solution_dot[q_index]);
377
378 for (uint i = 0; i < n_dofs; ++i) {
379 const auto component_i = fe.system_to_component_index(i).first;
380 copy_data.cell_residual(i) += JxW[q_index] * weight * // dx *
381 (-scalar_product(fe_v.shape_grad_component(i, q_index, component_i),
382 flux[component_i]) // -dphi_i(x_q) * flux(x_q, u_q)
383 + fe_v.shape_value_component(i, q_index, component_i) *
384 source[component_i]); // -phi_i(x_q) * source(x_q, u_q)
385 copy_data.cell_residual(i) += weight_mass * JxW[q_index] *
386 fe_v.shape_value_component(i, q_index, component_i) *
387 mass[component_i]; // +phi_i(x_q) * mass(x_q, u_q)
388 }
389 }
390 };
391 const auto boundary_worker = [&](const Iterator &cell, const uint &face_no, Scratch &scratch_data,
392 CopyData &copy_data) {
393 scratch_data.fe_interface_values.reinit(cell, face_no);
394 const auto &fe_fv = scratch_data.fe_interface_values.get_fe_face_values(0);
395 const uint n_dofs = fe_fv.get_fe().n_dofs_per_cell();
396
397 const auto &JxW = fe_fv.get_JxW_values();
398 const auto &q_points = fe_fv.get_quadrature_points();
399 const auto &q_indices = fe_fv.quadrature_point_indices();
400 const std::vector<Tensor<1, dim>> &normals = fe_fv.get_normal_vectors();
401
402 auto &solution = scratch_data.solution_interface[0];
403 auto &solution_grad = scratch_data.solution_grad_interface[0];
404 auto &solution_hess = scratch_data.solution_hess_interface[0];
405 fe_fv.get_function_values(solution_global, solution);
406 fe_fv.get_function_gradients(solution_global, solution_grad);
407 fe_fv.get_function_hessians(solution_global, solution_hess);
408
409 array<Tensor<1, dim, NumberType>, Components::count_fe_functions()> numflux{};
410 for (const auto &q_index : q_indices) {
411 const auto &x_q = q_points[q_index];
412 model.boundary_numflux(
413 numflux, normals[q_index], x_q,
414 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
415
416 for (uint i = 0; i < n_dofs; ++i) {
417 const auto component_i = fe.system_to_component_index(i).first;
418 copy_data.cell_residual(i) +=
419 weight * JxW[q_index] * // dx
420 (fe_fv.shape_value_component(i, q_index, component_i) *
421 scalar_product(numflux[component_i], normals[q_index])); // phi_i(x_q) * numflux(x_q, u_q) * n(x_q)
422 }
423 }
424 };
425 const auto copier = [&](const CopyData &c) {
426 constraints.distribute_local_to_global(c.cell_residual, c.local_dof_indices, residual);
427 };
428
429 Scratch scratch_data(mapping, fe, quadrature, quadrature_face);
430 CopyData copy_data;
431 MeshWorker::AssembleFlags flags = MeshWorker::assemble_own_cells | MeshWorker::assemble_boundary_faces;
432
433 Timer timer;
434 MeshWorker::mesh_loop(dof_handler.begin_active(), dof_handler.end(), cell_worker, copier, scratch_data,
435 copy_data, flags, boundary_worker, nullptr, threads, batch_size);
436 timings_residual.push_back(timer.wall_time());
437 }
438
439 virtual void jacobian_mass(SparseMatrix<NumberType> &jacobian, const VectorType &solution_global,
440 const VectorType &solution_global_dot, NumberType alpha, NumberType beta) override
441 {
442 using Iterator = typename DoFHandler<dim>::active_cell_iterator;
444 using CopyData = internal::CopyData_J<NumberType>;
445 const auto &constraints = discretization.get_constraints();
446
447 const auto cell_worker = [&](const Iterator &cell, Scratch &scratch_data, CopyData &copy_data) {
448 scratch_data.fe_values.reinit(cell);
449 const auto &fe_v = scratch_data.fe_values;
450 const uint n_dofs = fe_v.get_fe().n_dofs_per_cell();
451
452 copy_data.reinit(cell, n_dofs, Components::count_extractors());
453 const auto &JxW = fe_v.get_JxW_values();
454 const auto &q_points = fe_v.get_quadrature_points();
455 const auto &q_indices = fe_v.quadrature_point_indices();
456 auto &solution = scratch_data.solution;
457 auto &solution_dot = scratch_data.solution_dot;
458
459 SimpleMatrix<NumberType, Components::count_fe_functions()> j_mass;
460 SimpleMatrix<NumberType, Components::count_fe_functions()> j_mass_dot;
461
462 fe_v.get_function_values(solution_global, solution);
463 fe_v.get_function_values(solution_global_dot, solution_dot);
464 for (const auto &q_index : q_indices) {
465 const auto &x_q = q_points[q_index];
466 model.template jacobian_mass<0>(j_mass, x_q, solution[q_index], solution_dot[q_index]);
467 model.template jacobian_mass<1>(j_mass_dot, x_q, solution[q_index], solution_dot[q_index]);
468
469 for (uint i = 0; i < n_dofs; ++i) {
470 const auto component_i = fe.system_to_component_index(i).first;
471 for (uint j = 0; j < n_dofs; ++j) {
472 const auto component_j = fe.system_to_component_index(j).first;
473 copy_data.cell_jacobian(i, j) +=
474 JxW[q_index] * fe_v.shape_value_component(j, q_index, component_j) *
475 fe_v.shape_value_component(i, q_index, component_i) *
476 (alpha * j_mass_dot(component_i, component_j) + beta * j_mass(component_i, component_j));
477 }
478 }
479 }
480 };
481 const auto copier = [&](const CopyData &c) {
482 constraints.distribute_local_to_global(c.cell_jacobian, c.local_dof_indices, jacobian);
483 };
484
485 Scratch scratch_data(mapping, fe, quadrature, quadrature_face);
486 CopyData copy_data;
487 MeshWorker::AssembleFlags flags = MeshWorker::assemble_own_cells;
488
489 Timer timer;
490 MeshWorker::mesh_loop(dof_handler.begin_active(), dof_handler.end(), cell_worker, copier, scratch_data,
491 copy_data, flags, nullptr, nullptr, threads, batch_size);
492 timings_jacobian.push_back(timer.wall_time());
493 }
494
495 virtual void jacobian(SparseMatrix<NumberType> &jacobian, const VectorType &solution_global, NumberType weight,
496 const VectorType &solution_global_dot, NumberType alpha, NumberType beta,
497 const VectorType &variables = VectorType()) override
498 {
499 using Iterator = typename DoFHandler<dim>::active_cell_iterator;
501 using CopyData = internal::CopyData_J<NumberType>;
502 const auto &constraints = discretization.get_constraints();
503
504 // Find the EoM and extract whatever data is needed for the model.
505 std::array<NumberType, Components::count_extractors()> extracted_data{{}};
506 if constexpr (Components::count_extractors() > 0) {
507 this->extract(extracted_data, solution_global, variables, true, true, true);
508 if (this->jacobian_extractors(this->extractor_jacobian, solution_global, variables))
510 }
511
512 const auto cell_worker = [&](const Iterator &cell, Scratch &scratch_data, CopyData &copy_data) {
513 scratch_data.fe_values.reinit(cell);
514 const auto &fe_v = scratch_data.fe_values;
515 const uint n_dofs = fe_v.get_fe().n_dofs_per_cell();
516
517 copy_data.reinit(cell, n_dofs, Components::count_extractors());
518 const auto &JxW = fe_v.get_JxW_values();
519 const auto &q_points = fe_v.get_quadrature_points();
520 const auto &q_indices = fe_v.quadrature_point_indices();
521
522 auto &solution = scratch_data.solution;
523 auto &solution_dot = scratch_data.solution_dot;
524 auto &solution_grad = scratch_data.solution_grad;
525 auto &solution_hess = scratch_data.solution_hess;
526
527 fe_v.get_function_values(solution_global, solution);
528 fe_v.get_function_gradients(solution_global, solution_grad);
529 fe_v.get_function_hessians(solution_global, solution_hess);
530 fe_v.get_function_values(solution_global_dot, solution_dot);
531
532 SimpleMatrix<Tensor<1, dim, NumberType>, Components::count_fe_functions()> j_flux;
533 SimpleMatrix<Tensor<1, dim, Tensor<1, dim, NumberType>>, Components::count_fe_functions()> j_grad_flux;
534 SimpleMatrix<Tensor<1, dim, Tensor<2, dim, NumberType>>, Components::count_fe_functions()> j_hess_flux;
535 SimpleMatrix<Tensor<1, dim, NumberType>, Components::count_fe_functions(), Components::count_extractors()>
536 j_extr_flux;
537 SimpleMatrix<NumberType, Components::count_fe_functions()> j_source;
538 SimpleMatrix<Tensor<1, dim, NumberType>, Components::count_fe_functions()> j_grad_source;
539 SimpleMatrix<Tensor<2, dim, NumberType>, Components::count_fe_functions()> j_hess_source;
540 SimpleMatrix<NumberType, Components::count_fe_functions(), Components::count_extractors()> j_extr_source;
541 SimpleMatrix<NumberType, Components::count_fe_functions()> j_mass;
542 SimpleMatrix<NumberType, Components::count_fe_functions()> j_mass_dot;
543
544 for (const auto &q_index : q_indices) {
545 const auto &x_q = q_points[q_index];
546 model.template jacobian_flux<0, 0>(
547 j_flux, x_q,
548 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
549 model.template jacobian_source<0, 0>(
550 j_source, x_q,
551 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
552 model.template jacobian_flux_grad<1>(
553 j_grad_flux, x_q,
554 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
555 model.template jacobian_source_grad<1>(
556 j_grad_source, x_q,
557 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
558 model.template jacobian_flux_hess<2>(
559 j_hess_flux, x_q,
560 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
561 model.template jacobian_source_hess<2>(
562 j_hess_source, x_q,
563 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
564 if constexpr (Components::count_extractors() > 0) {
565 model.template jacobian_flux_extr<3>(
566 j_extr_flux, x_q,
567 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
568 model.template jacobian_source_extr<3>(
569 j_extr_source, x_q,
570 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
571 }
572 model.template jacobian_mass<0>(j_mass, x_q, solution[q_index], solution_dot[q_index]);
573 model.template jacobian_mass<1>(j_mass_dot, x_q, solution[q_index], solution_dot[q_index]);
574
575 tbb::parallel_for(
576 tbb::blocked_range2d<uint>(0, n_dofs, 0, n_dofs), [&](const tbb::blocked_range2d<uint> &range) {
577 for (uint i = range.rows().begin(); i < range.rows().end(); ++i) {
578 const auto component_i = fe_v.get_fe().system_to_component_index(i).first;
579 for (uint j = range.cols().begin(); j < range.cols().end(); ++j) {
580 const auto component_j = fe.system_to_component_index(j).first;
581 // scalar contribution
582 copy_data.cell_jacobian(i, j) +=
583 weight * JxW[q_index] *
584 fe_v.shape_value_component(j, q_index, component_j) * // dx * phi_j * (
585 (-scalar_product(fe_v.shape_grad_component(i, q_index, component_i),
586 j_flux(component_i, component_j)) // -dphi_i * jflux
587 + fe_v.shape_value_component(i, q_index, component_i) *
588 j_source(component_i, component_j)); // -phi_i * jsource)
589 // gradient contribution
590 copy_data.cell_jacobian(i, j) +=
591 weight * JxW[q_index] *
592 scalar_product(fe_v.shape_grad_component(j, q_index, component_j), // dx * phi_j *
593 -scalar_product(fe_v.shape_grad_component(i, q_index, component_i),
594 j_grad_flux(component_i, component_j)) // -dphi_i * jflux
595 + fe_v.shape_value_component(i, q_index, component_i) *
596 j_grad_source(component_i, component_j)); // -phi_i * jsource
597 // hessian contribution
598 copy_data.cell_jacobian(i, j) +=
599 weight * JxW[q_index] *
600 scalar_product(fe_v.shape_hessian_component(j, q_index, component_j),
601 -scalar_product(fe_v.shape_grad_component(i, q_index, component_i),
602 j_hess_flux(component_i, component_j)) +
603 fe_v.shape_value_component(i, q_index, component_i) *
604 j_hess_source(component_i, component_j));
605 // mass contribution
606 copy_data.cell_jacobian(i, j) +=
607 JxW[q_index] * fe_v.shape_value_component(j, q_index, component_j) *
608 fe_v.shape_value_component(i, q_index, component_i) *
609 (alpha * j_mass_dot(component_i, component_j) + beta * j_mass(component_i, component_j));
610 }
611 }
612 });
613
614 // extractor contribution
615 if constexpr (Components::count_extractors() > 0) {
616 for (uint i = 0; i < n_dofs; ++i) {
617 const auto component_i = fe.system_to_component_index(i).first;
618 for (uint e = 0; e < Components::count_extractors(); ++e)
619 copy_data.extractor_cell_jacobian(i, e) +=
620 weight * JxW[q_index] * // dx * phi_j * (
621 (-scalar_product(fe_v.shape_grad_component(i, q_index, component_i),
622 j_extr_flux(component_i, e)) // -dphi_i * jflux
623 + fe_v.shape_value_component(i, q_index, component_i) *
624 j_extr_source(component_i, e)); // -phi_i * jsource)
625 }
626 }
627 }
628 };
629
630 const auto boundary_worker = [&](const Iterator &cell, const uint &face_no, Scratch &scratch_data,
631 CopyData &copy_data) {
632 scratch_data.fe_interface_values.reinit(cell, face_no);
633 const auto &fe_fv = scratch_data.fe_interface_values.get_fe_face_values(0);
634 const uint n_dofs = fe_fv.get_fe().n_dofs_per_cell();
635
636 const auto &JxW = fe_fv.get_JxW_values();
637 const auto &q_points = fe_fv.get_quadrature_points();
638 const auto &q_indices = fe_fv.quadrature_point_indices();
639 const std::vector<Tensor<1, dim>> &normals = fe_fv.get_normal_vectors();
640
641 auto &solution = scratch_data.solution_interface[0];
642 auto &solution_grad = scratch_data.solution_grad_interface[0];
643 auto &solution_hess = scratch_data.solution_hess;
644 fe_fv.get_function_values(solution_global, solution);
645 fe_fv.get_function_gradients(solution_global, solution_grad);
646 fe_fv.get_function_hessians(solution_global, solution_hess);
647
648 SimpleMatrix<Tensor<1, dim, NumberType>, Components::count_fe_functions()> j_boundary_numflux;
649 SimpleMatrix<Tensor<1, dim, Tensor<1, dim, NumberType>>, Components::count_fe_functions()>
650 j_grad_boundary_numflux;
651 SimpleMatrix<Tensor<1, dim, Tensor<2, dim, NumberType>>, Components::count_fe_functions()>
652 j_hess_boundary_numflux;
653 SimpleMatrix<Tensor<1, dim, NumberType>, Components::count_fe_functions(), Components::count_extractors()>
654 j_extr_boundary_numflux;
655 for (const auto &q_index : q_indices) {
656 const auto &x_q = q_points[q_index];
657 model.template jacobian_boundary_numflux<0, 0>(
658 j_boundary_numflux, normals[q_index], x_q,
659 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
660 model.template jacobian_boundary_numflux_grad<1>(
661 j_grad_boundary_numflux, normals[q_index], x_q,
662 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
663 model.template jacobian_boundary_numflux_hess<2>(
664 j_hess_boundary_numflux, normals[q_index], x_q,
665 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
666 if constexpr (Components::count_extractors() > 0) {
667 model.template jacobian_boundary_numflux_extr<3>(
668 j_extr_boundary_numflux, normals[q_index], x_q,
669 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data, variables));
670 }
671
672 tbb::parallel_for(
673 tbb::blocked_range2d<uint>(0, n_dofs, 0, n_dofs), [&](const tbb::blocked_range2d<uint> &range) {
674 for (uint i = range.rows().begin(); i < range.rows().end(); ++i) {
675 const auto component_i = fe.system_to_component_index(i).first;
676 for (uint j = range.cols().begin(); j < range.cols().end(); ++j) {
677 const auto component_j = fe.system_to_component_index(j).first;
678
679 // scalar contribution
680 copy_data.cell_jacobian(i, j) +=
681 weight * JxW[q_index] *
682 fe_fv.shape_value_component(j, q_index, component_j) * // dx * phi_j(x_q)
683 (fe_fv.shape_value_component(i, q_index, component_i) *
684 scalar_product(j_boundary_numflux(component_i, component_j),
685 normals[q_index])); // phi_i(x_q) * j_numflux(x_q, u_q) * n(x_q)
686 // gradient contribution
687 copy_data.cell_jacobian(i, j) +=
688 weight * JxW[q_index] *
689 scalar_product(
690 fe_fv.shape_grad_component(j, q_index, component_j), // dx * phi_j(x_q)
691 fe_fv.shape_value_component(i, q_index, component_i) *
692 scalar_product(j_grad_boundary_numflux(component_i, component_j),
693 normals[q_index])); // phi_i(x_q) * j_numflux(x_q, u_q) * n(x_q)
694 // hessian contribution
695 copy_data.cell_jacobian(i, j) +=
696 weight * JxW[q_index] *
697 scalar_product(
698 fe_fv.shape_hessian_component(j, q_index, component_j), // dx * phi_j(x_q)
699 fe_fv.shape_value_component(i, q_index, component_i) *
700 scalar_product(j_hess_boundary_numflux(component_i, component_j),
701 normals[q_index])); // phi_i(x_q) * j_numflux(x_q, u_q) * n(x_q)
702 }
703 }
704 });
705
706 // extractor contribution
707 if constexpr (Components::count_extractors() > 0) {
708 for (uint i = 0; i < n_dofs; ++i) {
709 const auto component_i = fe.system_to_component_index(i).first;
710 for (uint e = 0; e < Components::count_extractors(); ++e)
711 copy_data.extractor_cell_jacobian(i, e) +=
712 weight * JxW[q_index] * // dx * phi_j(x_q)
713 (fe_fv.shape_value_component(i, q_index, component_i) *
714 scalar_product(j_extr_boundary_numflux(component_i, e),
715 normals[q_index])); // phi_i(x_q) * j_numflux(x_q, u_q) * n(x_q)
716 }
717 }
718 }
719 };
720
721 const auto copier = [&](const CopyData &c) {
722 constraints.distribute_local_to_global(c.cell_jacobian, c.local_dof_indices, jacobian);
723 if constexpr (Components::count_extractors() > 0) {
724 FullMatrix<NumberType> extractor_dependence(c.local_dof_indices.size(), extractor_dof_indices.size());
725 c.extractor_cell_jacobian.mmult(extractor_dependence, this->extractor_jacobian);
726 constraints.distribute_local_to_global(extractor_dependence, c.local_dof_indices, extractor_dof_indices,
727 jacobian);
728 }
729 };
730
731 Scratch scratch_data(mapping, fe, quadrature, quadrature_face);
732 CopyData copy_data;
733 MeshWorker::AssembleFlags flags = MeshWorker::assemble_own_cells | MeshWorker::assemble_boundary_faces;
734
735 Timer timer;
736 MeshWorker::mesh_loop(dof_handler.begin_active(), dof_handler.end(), cell_worker, copier, scratch_data,
737 copy_data, flags, boundary_worker, nullptr, threads, batch_size);
738 timings_jacobian.push_back(timer.wall_time());
739 }
740
741 void log(const std::string logger)
742 {
743 std::stringstream ss;
744 ss << "CG Assembler: " << std::endl;
745 ss << " Reinit: " << average_time_reinit() * 1000 << "ms (" << num_reinits() << ")" << std::endl;
746 ss << " Residual: " << average_time_residual_assembly() * 1000 << "ms (" << num_residuals() << ")"
747 << std::endl;
748 ss << " Jacobian: " << average_time_jacobian_assembly() * 1000 << "ms (" << num_jacobians() << ")"
749 << std::endl;
750 spdlog::get(logger)->info(ss.str());
751 }
752
753 double average_time_reinit() const
754 {
755 double t = 0.;
756 double n = timings_reinit.size();
757 for (const auto &t_ : timings_reinit)
758 t += t_ / n;
759 return t;
760 }
761 uint num_reinits() const { return timings_reinit.size(); }
762
764 {
765 double t = 0.;
766 double n = timings_residual.size();
767 for (const auto &t_ : timings_residual)
768 t += t_ / n;
769 return t;
770 }
771 uint num_residuals() const { return timings_residual.size(); }
772
774 {
775 double t = 0.;
776 double n = timings_jacobian.size();
777 for (const auto &t_ : timings_jacobian)
778 t += t_ / n;
779 return t;
780 }
781 uint num_jacobians() const { return timings_jacobian.size(); }
782
783 protected:
785 using Base::dof_handler;
786 using Base::fe;
787 using Base::mapping;
788 using Base::model;
789
790 QGauss<dim> quadrature;
791 QGauss<dim - 1> quadrature_face;
792 using Base::batch_size;
793 using Base::threads;
794
795 SparsityPattern sparsity_pattern_mass;
797 SparseMatrix<NumberType> mass_matrix;
798
799 std::vector<double> timings_reinit;
800 std::vector<double> timings_residual;
801 std::vector<double> timings_jacobian;
802
804 };
805 } // namespace CG
806} // namespace DiFfRG
The basic assembler that can be used for any standard CG scheme with flux and source.
Definition cg.hh:146
double average_time_residual_assembly()
Definition cg.hh:763
std::vector< double > timings_jacobian
Definition cg.hh:801
Model & model
Definition common.hh:333
double average_time_jacobian_assembly()
Definition cg.hh:773
const Mapping< dim > & mapping
Definition common.hh:336
uint num_reinits() const
Definition cg.hh:761
virtual void mass(VectorType &mass, const VectorType &solution_global, const VectorType &solution_global_dot, NumberType weight) override
Definition cg.hh:283
const FiniteElement< dim > & fe
Definition common.hh:334
double average_time_reinit() const
Definition cg.hh:753
virtual void refinement_indicator(Vector< double > &indicator, const VectorType &solution_global) override
refinement indicator for adaptivity. Only calls the model's cell_indicator function,...
Definition cg.hh:241
static constexpr uint dim
Definition cg.hh:156
Model_ Model
Definition cg.hh:151
uint threads
Definition common.hh:338
typename Discretization::Components Components
Definition cg.hh:155
typename Discretization::NumberType NumberType
Definition cg.hh:152
std::vector< double > timings_residual
Definition cg.hh:800
virtual void rebuild_jacobian_sparsity() override
Definition cg.hh:217
std::vector< double > timings_reinit
Definition cg.hh:799
virtual void reinit() override
Reinitialize the assembler. This is necessary if the mesh has changed, e.g. after a mesh refinement.
Definition cg.hh:169
SparsityPattern sparsity_pattern_mass
Definition cg.hh:795
QGauss< dim > quadrature
Definition cg.hh:790
Assembler(Discretization &discretization, Model &model, const JSONValue &json)
Definition cg.hh:158
typename Discretization::VectorType VectorType
Definition cg.hh:153
const DoFHandler< dim > & dof_handler
Definition common.hh:335
virtual const SparsityPattern & get_sparsity_pattern_jacobian() const override
Obtain the sparsity pattern of the jacobian matrix.
Definition cg.hh:228
virtual void residual(VectorType &residual, const VectorType &solution_global, NumberType weight, const VectorType &solution_global_dot, NumberType weight_mass, const VectorType &variables=VectorType()) override
Definition cg.hh:331
SparsityPattern sparsity_pattern_jacobian
Definition cg.hh:796
QGauss< dim - 1 > quadrature_face
Definition cg.hh:791
virtual const SparseMatrix< NumberType > & get_mass_matrix() const override
Obtain the mass matrix.
Definition cg.hh:232
Discretization & discretization
Definition common.hh:332
virtual void jacobian(SparseMatrix< NumberType > &jacobian, const VectorType &solution_global, NumberType weight, const VectorType &solution_global_dot, NumberType alpha, NumberType beta, const VectorType &variables=VectorType()) override
Definition cg.hh:495
SparseMatrix< NumberType > mass_matrix
Definition cg.hh:797
uint batch_size
Definition common.hh:339
Discretization_ Discretization
Definition cg.hh:150
std::vector< types::global_dof_index > extractor_dof_indices
Definition common.hh:350
void log(const std::string logger)
Definition cg.hh:741
uint num_residuals() const
Definition cg.hh:771
virtual void jacobian_mass(SparseMatrix< NumberType > &jacobian, const VectorType &solution_global, const VectorType &solution_global_dot, NumberType alpha, NumberType beta) override
Definition cg.hh:439
virtual void reinit_vector(VectorType &vec) const override
Definition cg.hh:167
uint num_jacobians() const
Definition cg.hh:781
NumberType_ NumberType
Definition cg.hh:34
Components_ Components
Definition cg.hh:33
Vector< NumberType > VectorType
Definition cg.hh:35
static constexpr uint dim
Definition cg.hh:38
The basic assembler that can be used for any standard CG scheme with flux and source.
Definition common.hh:39
Model & model
Definition common.hh:333
const Mapping< dim > & mapping
Definition common.hh:336
FullMatrix< NumberType > extractor_jacobian
Definition common.hh:346
const FiniteElement< dim > & fe
Definition common.hh:334
void extract(std::array< NumberType, Components::count_extractors()> &data, const VectorType &solution_global, const VectorType &variables, bool search_EoM, bool set_EoM, bool postprocess) const
Definition common.hh:195
bool jacobian_extractors(FullMatrix< NumberType > &extractor_jacobian, const VectorType &solution_global, const VectorType &variables)
Definition common.hh:232
uint threads
Definition common.hh:338
virtual void reinit() override
Reinitialize the assembler. This is necessary if the mesh has changed, e.g. after a mesh refinement.
Definition common.hh:105
const DoFHandler< dim > & dof_handler
Definition common.hh:335
Discretization & discretization
Definition common.hh:332
uint batch_size
Definition common.hh:339
std::vector< types::global_dof_index > extractor_dof_indices
Definition common.hh:350
static constexpr int nothing
Definition common.hh:41
A wrapper around the boost json value class.
Definition json.hh:19
Definition quadrature.hh:50
size_t size() const
A simple NxM-matrix class, which is used for cell-wise Jacobians.
Definition tuples.hh:153
auto fe_tie(T &&...t)
Definition cg.hh:16
auto i_tie(T &&...t)
Definition cg.hh:22
Definition complex_math.hh:14
unsigned int uint
Definition utils.hh:22
std::array< double, 2 > values
Definition cg.hh:132
std::array< uint, 2 > cell_indices
Definition cg.hh:131
double value
Definition cg.hh:135
uint cell_index
Definition cg.hh:136
std::vector< CopyFaceData_I > face_data
Definition cg.hh:134
FullMatrix< NumberType > extractor_cell_jacobian
Definition cg.hh:117
std::vector< types::global_dof_index > local_dof_indices
Definition cg.hh:118
FullMatrix< NumberType > cell_jacobian
Definition cg.hh:116
void reinit(const Iterator &cell, uint dofs_per_cell, uint n_extractors)
Definition cg.hh:120
void reinit(const Iterator &cell, uint dofs_per_cell)
Definition cg.hh:107
Vector< NumberType > cell_residual
Definition cg.hh:104
std::vector< types::global_dof_index > local_dof_indices
Definition cg.hh:105
Class to hold data for each assembly thread, i.e. FEValues for cells, interfaces, as well as pre-allo...
Definition cg.hh:35
std::vector< Vector< NumberType > > solution
Definition cg.hh:94
array< std::vector< std::vector< Tensor< 1, dim, NumberType > > >, 2 > solution_grad_interface
Definition cg.hh:99
FEValues< dim > fe_values
Definition cg.hh:91
static constexpr uint dim
Definition cg.hh:36
array< std::vector< std::vector< Tensor< 2, dim, NumberType > > >, 2 > solution_hess_interface
Definition cg.hh:100
std::vector< std::vector< Tensor< 1, dim, NumberType > > > solution_grad
Definition cg.hh:95
FEInterfaceValues< dim > fe_interface_values
Definition cg.hh:92
ScratchData(const ScratchData< Discretization > &scratch_data)
Definition cg.hh:65
const uint n_components
Definition cg.hh:89
ScratchData(const Mapping< dim > &mapping, const FiniteElement< dim > &fe, const Quadrature< dim > &quadrature, const Quadrature< dim - 1 > &quadrature_face, const UpdateFlags update_flags=update_values|update_gradients|update_quadrature_points|update_JxW_values|update_hessians, const UpdateFlags interface_update_flags=update_values|update_gradients|update_quadrature_points|update_JxW_values|update_normal_vectors|update_hessians)
Definition cg.hh:39
array< std::vector< Vector< NumberType > >, 2 > solution_interface
Definition cg.hh:98
std::vector< std::vector< Tensor< 2, dim, NumberType > > > solution_hess
Definition cg.hh:96
std::vector< Vector< NumberType > > solution_dot
Definition cg.hh:97
typename Discretization::NumberType NumberType
Definition cg.hh:37
A class to store a tuple with elements that can be accessed by name. The names are stored as FixedStr...
Definition tuples.hh:27