/home/runner/work/DiFfRG_current/DiFfRG_current/DiFfRG/include/DiFfRG/discretization/FEM/assembler/ldg.hh Source File#

DiFfRG: /home/runner/work/DiFfRG_current/DiFfRG_current/DiFfRG/include/DiFfRG/discretization/FEM/assembler/ldg.hh Source File
DiFfRG
ldg.hh
Go to the documentation of this file.
1#pragma once
2
3// standard library
4#include <memory>
5#include <sstream>
6
7// external libraries
8#include <deal.II/base/multithread_info.h>
9#include <deal.II/base/quadrature_lib.h>
10#include <deal.II/base/timer.h>
11#include <deal.II/dofs/dof_handler.h>
12#include <deal.II/dofs/dof_tools.h>
13#include <deal.II/fe/fe_interface_values.h>
14#include <deal.II/fe/fe_values.h>
15#include <deal.II/lac/block_sparse_matrix.h>
16#include <deal.II/lac/block_vector.h>
17#include <deal.II/lac/full_matrix.h>
18#include <deal.II/lac/vector.h>
19#include <deal.II/lac/vector_memory.h>
20#include <deal.II/meshworker/mesh_loop.h>
21#include <deal.II/numerics/matrix_tools.h>
22#include <deal.II/numerics/vector_tools.h>
23#include <tbb/tbb.h>
24
25// DiFfRG
30
31namespace DiFfRG
32{
33 namespace LDG
34 {
35 using namespace dealii;
36 using std::array, std::vector, std::unique_ptr;
37
38 template <typename Discretization_, typename Model_>
39 class LDGAssemblerBase : public AbstractAssembler<typename Discretization_::VectorType,
40 typename Discretization_::SparseMatrixType, Discretization_::dim>
41 {
42 public:
43 using Discretization = Discretization_;
44 using Model = Model_;
47
49 static constexpr uint dim = Discretization::dim;
50
53 dof_handler(discretization.get_dof_handler()), mapping(discretization.get_mapping()),
54 threads(json.get_uint("/discretization/threads")), batch_size(json.get_uint("/discretization/batch_size")),
55 EoM_cell(*(dof_handler.active_cell_iterators().end())),
56 old_EoM_cell(*(dof_handler.active_cell_iterators().end())),
57 EoM_abs_tol(json.get_double("/discretization/EoM_abs_tol")),
58 EoM_max_iter(json.get_uint("/discretization/EoM_max_iter"))
59 {
60 if (this->threads == 0) this->threads = dealii::MultithreadInfo::n_threads() / 2;
61 spdlog::get("log")->info("FEM: Using {} threads for assembly.", threads);
62 }
63
64 virtual IndexSet get_differential_indices() const override
65 {
66 ComponentMask component_mask(model.template differential_components<dim>());
67 return DoFTools::extract_dofs(dof_handler, component_mask);
68 }
69
70 const auto &get_discretization() const { return discretization; }
72
73 virtual void reinit() override
74 {
75 // Boundary dofs
76 std::vector<IndexSet> component_boundary_dofs(Components::count_fe_functions());
77 for (uint c = 0; c < Components::count_fe_functions(); ++c) {
78 ComponentMask component_mask(Components::count_fe_functions(), false);
79 component_mask.set(c, true);
80 component_boundary_dofs[c] = DoFTools::extract_boundary_dofs(dof_handler, component_mask);
81 }
82 std::vector<std::vector<Point<dim>>> component_boundary_points(Components::count_fe_functions());
83 for (uint c = 0; c < Components::count_fe_functions(); ++c) {
84 component_boundary_points[c].resize(component_boundary_dofs[c].n_elements());
85 for (uint i = 0; i < component_boundary_dofs[c].n_elements(); ++i)
86 component_boundary_points[c][i] =
87 discretization.get_support_point(component_boundary_dofs[c].nth_index_in_set(i));
88 }
89
90 auto &constraints = discretization.get_constraints();
91 constraints.clear();
92 DoFTools::make_hanging_node_constraints(dof_handler, constraints);
93 model.affine_constraints(constraints, component_boundary_dofs, component_boundary_points);
94 constraints.close();
95 }
96
97 virtual void rebuild_jacobian_sparsity() = 0;
98
99 virtual void set_time(double t) override { model.set_time(t); }
100
101 virtual void refinement_indicator(Vector<double> & /*indicator*/, const VectorType & /*solution*/) = 0;
102
104 {
105 double t = 0.;
106 double n = timings_variable_residual.size();
107 for (const auto &t_ : timings_variable_residual)
108 t += t_ / n;
109 return t;
110 }
112
114 {
115 double t = 0.;
116 double n = timings_variable_jacobian.size();
117 for (const auto &t_ : timings_variable_jacobian)
118 t += t_ / n;
119 return t;
120 }
122
123 protected:
126 const FiniteElement<dim> &fe;
127 const DoFHandler<dim> &dof_handler;
128 const Mapping<dim> &mapping;
129
132
133 mutable typename DoFHandler<dim>::cell_iterator EoM_cell;
134 typename DoFHandler<dim>::cell_iterator old_EoM_cell;
135 const double EoM_abs_tol;
137 mutable Point<dim> EoM;
138 FullMatrix<NumberType> extractor_jacobian;
139 FullMatrix<NumberType> extractor_jacobian_u;
140 FullMatrix<NumberType> extractor_jacobian_du;
141 FullMatrix<NumberType> extractor_jacobian_ddu;
142 std::vector<types::global_dof_index> extractor_dof_indices;
143
144 std::vector<double> timings_variable_residual;
145 std::vector<double> timings_variable_jacobian;
146 };
147
148 namespace internal
149 {
154 template <typename Discretization> struct ScratchData {
155 static constexpr uint dim = Discretization::dim;
157 static constexpr uint n_fe_subsystems = Discretization::Components::count_fe_subsystems();
158 using Iterator = typename DoFHandler<dim>::active_cell_iterator;
159 using t_Iterator = typename Triangulation<dim>::active_cell_iterator;
160
161 ScratchData(const Mapping<dim> &mapping, const vector<const DoFHandler<dim> *> &dofh,
162 const Quadrature<dim> &quadrature, const Quadrature<dim - 1> &quadrature_face,
163 const UpdateFlags update_flags = update_values | update_gradients | update_quadrature_points |
164 update_JxW_values,
165 const UpdateFlags interface_update_flags = update_values | update_gradients |
166 update_quadrature_points | update_JxW_values |
167 update_normal_vectors)
168 {
169 AssertThrow(dofh.size() >= n_fe_subsystems,
170 StandardExceptions::ExcDimensionMismatch(dofh.size(), n_fe_subsystems));
171
172 for (uint i = 0; i < n_fe_subsystems; ++i) {
173 const auto &fe = dofh[i]->get_fe();
174 fe_values[i] = std::make_unique<FEValues<dim>>(mapping, fe, quadrature, update_flags);
176 std::make_unique<FEInterfaceValues<dim>>(mapping, fe, quadrature_face, interface_update_flags);
178 std::make_unique<FEFaceValues<dim>>(mapping, fe, quadrature_face, interface_update_flags);
179
180 n_components[i] = fe.n_components();
181 solution[i].resize(quadrature.size(), Vector<NumberType>(n_components[i]));
182 solution_interface[0][i].resize(quadrature_face.size(), Vector<NumberType>(n_components[i]));
183 solution_interface[1][i].resize(quadrature_face.size(), Vector<NumberType>(n_components[i]));
184
185 const uint n_dofs_per_cell = fe.n_dofs_per_cell();
186 comp[i].resize(n_dofs_per_cell);
187 for (uint d = 0; d < n_dofs_per_cell; ++d)
188 comp[i][d] = fe.system_to_component_index(d).first;
189
190 cell[i] = dofh[i]->begin_active();
191 ncell[i] = dofh[i]->begin_active();
192 }
193 solution_dot.resize(quadrature.size(), Vector<NumberType>(n_components[0]));
194 }
195
197 {
198 for (uint i = 0; i < n_fe_subsystems; ++i) {
199 const auto &old_fe = scratch_data.fe_values[i];
200 const auto &old_fe_i = scratch_data.fe_interface_values[i];
201 const auto &old_fe_b = scratch_data.fe_boundary_values[i];
202
203 fe_values[i] = unique_ptr<FEValues<dim>>(new FEValues<dim>(
204 old_fe->get_mapping(), old_fe->get_fe(), old_fe->get_quadrature(), old_fe->get_update_flags()));
205 fe_interface_values[i] = unique_ptr<FEInterfaceValues<dim>>(new FEInterfaceValues<dim>(
206 old_fe_i->get_mapping(), old_fe_i->get_fe(), old_fe_i->get_quadrature(), old_fe_i->get_update_flags()));
207 fe_boundary_values[i] = unique_ptr<FEFaceValues<dim>>(new FEFaceValues<dim>(
208 old_fe_b->get_mapping(), old_fe_b->get_fe(), old_fe_b->get_quadrature(), old_fe_b->get_update_flags()));
209
210 n_components[i] = scratch_data.n_components[i];
211 comp[i] = scratch_data.comp[i];
212 solution[i].resize(scratch_data.solution[i].size(), Vector<NumberType>(n_components[i]));
213 solution_interface[0][i].resize(scratch_data.solution_interface[0][i].size(),
214 Vector<NumberType>(n_components[i]));
215 solution_interface[1][i].resize(scratch_data.solution_interface[1][i].size(),
216 Vector<NumberType>(n_components[i]));
217
218 cell[i] = scratch_data.cell[i];
219 ncell[i] = scratch_data.ncell[i];
220 }
221 solution_dot.resize(scratch_data.solution_dot.size(), Vector<NumberType>(n_components[0]));
222 }
223
224 const auto &new_fe_values(const t_Iterator &t_cell)
225 {
226 for (uint i = 0; i < n_fe_subsystems; ++i) {
227 cell[i]->copy_from(*t_cell);
228 fe_values[i]->reinit(cell[i]);
229 }
230 return fe_values;
231 }
232 const auto &new_fe_interface_values(const t_Iterator &t_cell, uint f, uint sf, const t_Iterator &t_ncell,
233 uint nf, unsigned int nsf)
234 {
235 for (uint i = 0; i < n_fe_subsystems; ++i) {
236 cell[i]->copy_from(*t_cell);
237 ncell[i]->copy_from(*t_ncell);
238 fe_interface_values[i]->reinit(cell[i], f, sf, ncell[i], nf, nsf);
239 }
240 return fe_interface_values;
241 }
242 const auto &new_fe_boundary_values(const t_Iterator &t_cell, uint face_no)
243 {
244 for (uint i = 0; i < n_fe_subsystems; ++i) {
245 cell[i]->copy_from(*t_cell);
246 fe_boundary_values[i]->reinit(cell[i], face_no);
247 }
248 return fe_boundary_values;
249 }
250
251 array<uint, n_fe_subsystems> n_components;
252 array<Iterator, n_fe_subsystems> cell;
253 array<Iterator, n_fe_subsystems> ncell;
254
255 array<unique_ptr<FEValues<dim>>, n_fe_subsystems> fe_values;
256 array<unique_ptr<FEInterfaceValues<dim>>, n_fe_subsystems> fe_interface_values;
257 array<unique_ptr<FEFaceValues<dim>>, n_fe_subsystems> fe_boundary_values;
258
259 array<std::vector<uint>, n_fe_subsystems> comp;
260
261 array<vector<Vector<NumberType>>, n_fe_subsystems> solution;
262 vector<Vector<NumberType>> solution_dot;
263 array<array<vector<Vector<NumberType>>, n_fe_subsystems>, 2> solution_interface;
264 };
265
266 template <typename NumberType> struct CopyData_R {
268 Vector<NumberType> cell_residual;
269 std::vector<types::global_dof_index> joint_dof_indices;
270 };
271
272 Vector<NumberType> cell_residual;
273 Vector<NumberType> cell_mass;
274 std::vector<types::global_dof_index> local_dof_indices;
275 std::vector<CopyDataFace_R> face_data;
276
277 template <class Iterator> void reinit(const Iterator &cell, uint dofs_per_cell)
278 {
279 cell_residual.reinit(dofs_per_cell);
280 cell_mass.reinit(dofs_per_cell);
281 local_dof_indices.resize(dofs_per_cell);
282 cell->get_dof_indices(local_dof_indices);
283 face_data.clear();
284 face_data.reserve(6);
285 }
286
287 template <int dim> CopyDataFace_R &new_face_data(const FEInterfaceValues<dim> &fe_iv)
288 {
289 face_data.emplace_back();
290 auto &copy_data_face = face_data.back();
291 copy_data_face.cell_residual.reinit(fe_iv.n_current_interface_dofs());
292 copy_data_face.joint_dof_indices = fe_iv.get_interface_dof_indices();
293 return copy_data_face;
294 }
295 };
296
297 template <typename NumberType> struct CopyData_J {
299 FullMatrix<NumberType> cell_jacobian;
300 std::vector<types::global_dof_index> joint_dof_indices_from;
301 std::vector<types::global_dof_index> joint_dof_indices_to;
302 };
303
304 FullMatrix<NumberType> cell_jacobian;
305 std::vector<types::global_dof_index> local_dof_indices_from;
306 std::vector<types::global_dof_index> local_dof_indices_to;
307 std::vector<CopyDataFace_J> face_data;
308
309 template <class Iterator>
310 void reinit(const Iterator &cell_from, const Iterator &cell_to, uint dofs_per_cell_from, uint dofs_per_cell_to)
311 {
312 cell_jacobian.reinit(dofs_per_cell_to, dofs_per_cell_from);
313 local_dof_indices_from.resize(dofs_per_cell_from);
314 local_dof_indices_to.resize(dofs_per_cell_to);
315 cell_from->get_dof_indices(local_dof_indices_from);
316 cell_to->get_dof_indices(local_dof_indices_to);
317 }
318
319 template <int dim>
320 CopyDataFace_J &new_face_data(const FEInterfaceValues<dim> &fe_iv_from, const FEInterfaceValues<dim> &fe_iv_to)
321 {
322 auto &copy_data_face = face_data.emplace_back();
323 copy_data_face.cell_jacobian.reinit(fe_iv_to.n_current_interface_dofs(),
324 fe_iv_from.n_current_interface_dofs());
325 copy_data_face.joint_dof_indices_from = fe_iv_from.get_interface_dof_indices();
326 copy_data_face.joint_dof_indices_to = fe_iv_to.get_interface_dof_indices();
327 return copy_data_face;
328 }
329 };
330
331 template <typename NumberType, uint n_fe_subsystems> struct CopyData_J_full {
333 array<FullMatrix<NumberType>, n_fe_subsystems> cell_jacobian;
334 FullMatrix<NumberType> extractor_cell_jacobian;
335 array<vector<types::global_dof_index>, n_fe_subsystems> joint_dof_indices;
336 };
337
338 array<FullMatrix<NumberType>, n_fe_subsystems> cell_jacobian;
339 FullMatrix<NumberType> cell_mass_jacobian;
340 FullMatrix<NumberType> extractor_cell_jacobian;
341 array<vector<types::global_dof_index>, n_fe_subsystems> local_dof_indices;
342 vector<CopyDataFace_J> face_data;
343
345
346 template <class Iterator> void reinit(const array<Iterator, n_fe_subsystems> &cell, const uint n_extractors)
347 {
348 const uint n_dofs = cell[0]->get_fe().n_dofs_per_cell();
349 dofs_per_cell = n_dofs;
350 for (uint i = 0; i < n_fe_subsystems; ++i) {
351 const uint from_n_dofs = cell[i]->get_fe().n_dofs_per_cell();
352 if (i == 0) cell_mass_jacobian.reinit(n_dofs, from_n_dofs);
353 cell_jacobian[i].reinit(n_dofs, from_n_dofs);
354 local_dof_indices[i].resize(from_n_dofs);
355 cell[i]->get_dof_indices(local_dof_indices[i]);
356 }
357 if (n_extractors > 0) extractor_cell_jacobian.reinit(dofs_per_cell, n_extractors);
358 face_data.clear();
359 face_data.reserve(6);
360 }
361
362 template <int dim>
363 CopyDataFace_J &new_face_data(const array<unique_ptr<FEInterfaceValues<dim>>, n_fe_subsystems> &fe_iv,
364 const uint n_extractors)
365 {
366 auto &copy_data_face = face_data.emplace_back();
367 for (uint i = 0; i < n_fe_subsystems; ++i) {
368 copy_data_face.cell_jacobian[i].reinit(fe_iv[0]->n_current_interface_dofs(),
369 fe_iv[i]->n_current_interface_dofs());
370 copy_data_face.joint_dof_indices[i] = fe_iv[i]->get_interface_dof_indices();
371 }
372 if (n_extractors > 0)
373 copy_data_face.extractor_cell_jacobian.reinit(fe_iv[0]->n_current_interface_dofs(), n_extractors);
374 return copy_data_face;
375 }
376 };
377
378 template <typename NumberType> struct CopyData_I {
380 std::array<uint, 2> cell_indices;
381 std::array<double, 2> values;
382 };
383 std::vector<CopyFaceData_I> face_data;
384 double value;
386 };
387 } // namespace internal
388
395 template <typename Discretization_, typename Model_>
396 class Assembler : public LDGAssemblerBase<Discretization_, Model_>
397 {
399
400 public:
401 using Discretization = Discretization_;
402 using Model = Model_;
405
407 static constexpr uint dim = Discretization::dim;
408 static constexpr uint stencil = Components::count_fe_subsystems();
409
410 private:
411 template <typename... T> auto fe_conv(std::tuple<T &...> &t) const
412 {
413 if constexpr (stencil == 2)
415 else if constexpr (stencil == 3)
417 t);
418 else if constexpr (stencil == 4)
419 return named_tuple<std::tuple<T &...>,
421 else
422 throw std::runtime_error("Only <= 3 LDG subsystems are supported.");
423 }
424
425 template <typename... T> auto fe_more_conv(std::tuple<T &...> &t) const
426 {
427 if constexpr (stencil == 2)
428 return named_tuple<std::tuple<T &...>, StringSet<"fe_functions", "LDG1", "fe_derivatives", "fe_hessians",
429 "extractors", "variables">>(t);
430 else if constexpr (stencil == 3)
431 return named_tuple<std::tuple<T &...>, StringSet<"fe_functions", "LDG1", "LDG2", "fe_derivatives",
432 "fe_hessians", "extractors", "variables">>(t);
433 else if constexpr (stencil == 4)
434 return named_tuple<std::tuple<T &...>, StringSet<"fe_functions", "LDG1", "LDG2", "LDG3", "fe_derivatives",
435 "fe_hessians", "extractors", "variables">>(t);
436 else
437 throw std::runtime_error("Only <= 3 LDG subsystems are supported.");
438 }
439
440 template <typename... T> auto ref_conv(std::tuple<T &...> &t) const
441 {
442 if constexpr (stencil == 2)
443 return named_tuple<std::tuple<T &...>, StringSet<"fe_functions", "LDG1">>(t);
444 else if constexpr (stencil == 3)
445 return named_tuple<std::tuple<T &...>, StringSet<"fe_functions", "LDG1", "LDG2">>(t);
446 else if constexpr (stencil == 4)
448 else
449 throw std::runtime_error("Only <= 3 LDG subsystems are supported.");
450 }
451
452 public:
454 : Base(discretization, model, json),
455 quadrature(fe.degree + 1 + json.get_uint("/discretization/overintegration")),
456 quadrature_face(fe.degree + 1 + json.get_uint("/discretization/overintegration")),
457 dof_handler_list(discretization.get_dof_handler_list())
458 {
459 static_assert(Components::count_fe_subsystems() > 1, "LDG must have a submodel with index 1.");
460 reinit();
461 }
462
463 virtual void reinit_vector(VectorType &vec) const override { vec.reinit(dof_handler.n_dofs()); }
464
471 virtual void attach_data_output(DataOutput<dim, VectorType> &data_out, const VectorType &solution,
472 const VectorType &variables, const VectorType &dt_solution = VectorType(),
473 const VectorType &residual = VectorType()) override
474 {
475 rebuild_ldg_vectors(solution);
476 readouts(data_out, solution, variables);
477
478 const auto fe_function_names = Components::FEFunction_Descriptor::get_names_vector();
479 std::vector<std::string> fe_function_names_residual;
480 for (const auto &name : fe_function_names)
481 fe_function_names_residual.push_back(name + "_residual");
482 std::vector<std::string> fe_function_names_dot;
483 for (const auto &name : fe_function_names)
484 fe_function_names_dot.push_back(name + "_dot");
485
486 auto &fe_out = data_out.fe_output();
487 fe_out.attach(*dof_handler_list[0], solution, fe_function_names);
488 if (dt_solution.size() > 0) fe_out.attach(dof_handler, dt_solution, fe_function_names_dot);
489 if (residual.size() > 0) fe_out.attach(dof_handler, residual, fe_function_names_residual);
490 for (uint k = 1; k < Components::count_fe_subsystems(); ++k) {
492 fe_out.attach(*dof_handler_list[k], sol_vector_vec_tmp[k], "LDG" + std::to_string(k));
493 }
494 }
495
496 virtual void reinit() override
497 {
498 const auto init_mass = [&](uint i) {
499 // build the sparsity of the mass matrix and mass matrix of all ldg levels
500 auto dofs_per_component = DoFTools::count_dofs_per_fe_component(*(dof_handler_list[i]));
501
502 if (i == 0) {
503 auto n_fe = dofs_per_component.size();
504 for (uint j = 1; j < n_fe; ++j)
505 if (dofs_per_component[j] != dofs_per_component[0])
506 throw std::runtime_error("For LDG the FE basis of all systems must be equal!");
507
508 BlockDynamicSparsityPattern dsp(n_fe, n_fe);
509 for (uint i = 0; i < n_fe; ++i)
510 for (uint j = 0; j < n_fe; ++j)
511 dsp.block(i, j).reinit(dofs_per_component[0], dofs_per_component[0]);
512 dsp.collect_sizes();
513 DoFTools::make_sparsity_pattern(*(dof_handler_list[0]), dsp, discretization.get_constraints(0), true);
514 sparsity_pattern_mass.copy_from(dsp);
515
516 // i do not understand why this is needed.
519
520 MatrixCreator::create_mass_matrix(*(dof_handler_list[0]), quadrature, mass_matrix,
521 (Function<dim, NumberType> *)nullptr, discretization.get_constraints(0));
523 } else {
524 sol_vector[i].reinit(dofs_per_component);
525 sol_vector_tmp[i].reinit(dofs_per_component);
526 ldg_matrix_built[i] = false;
527 jacobian_tmp_built[i] = false;
528 }
529 };
530
531 const auto init_jacobian = [&](uint i) {
532 // build the jacobian and subjacobians
533 if (i == 0) {
535 true);
536 for (uint k = 1; k < Components::count_fe_subsystems(); ++k)
538 } else {
540 j_ug[i].reinit(sparsity_pattern_ug[i]);
541 }
542 };
543
544 auto init_ldg = [&](uint i) {
545 // build the subjacobian sparsity patterns of all matrices that contribute to the jacobian = uu + ug*gu
547 j_gu[i].reinit(sparsity_pattern_gu[i]);
548
549 // these are the "in-between" dependencies of the ldg levels
551 j_wg[i].reinit(sparsity_pattern_wg[i]);
552 j_wg_tmp[i].reinit(sparsity_pattern_wg[i]);
553 };
554
555 Timer timer;
556
557 Base::reinit();
558
559 vector<std::thread> threads;
560 for (uint i = 0; i < Components::count_fe_subsystems(); ++i)
561 threads.emplace_back(std::thread(init_mass, i));
562 for (uint i = 0; i < Components::count_fe_subsystems(); ++i)
563 threads.emplace_back(std::thread(init_jacobian, i));
564 for (uint i = 1; i < Components::count_fe_subsystems(); ++i)
565 threads.emplace_back(std::thread(init_ldg, i));
566 for (auto &t : threads)
567 t.join();
568
569 timings_reinit.push_back(timer.wall_time());
570 }
571
572 virtual void rebuild_jacobian_sparsity() override
573 {
575 for (uint k = 1; k < Components::count_fe_subsystems(); ++k)
577 }
578
579 virtual void refinement_indicator(Vector<double> &indicator, const VectorType &solution_global) override
580 {
581 using Iterator = typename Triangulation<dim>::active_cell_iterator;
583 using CopyData = internal::CopyData_I<NumberType>;
584
585 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
586 const auto &fe_v = scratch_data.new_fe_values(t_cell);
587 copy_data.cell_index = t_cell->active_cell_index();
588 copy_data.value = 0;
589
590 const auto &JxW = fe_v[0]->get_JxW_values();
591 const auto &q_points = fe_v[0]->get_quadrature_points();
592 const auto &q_indices = fe_v[0]->quadrature_point_indices();
593
594 auto &solution = scratch_data.solution;
595 fe_v[0]->get_function_values(solution_global, solution[0]);
596 for (uint i = 1; i < Components::count_fe_subsystems(); ++i)
597 fe_v[i]->get_function_values(sol_vector[i], solution[i]);
598
599 double local_indicator = 0.;
600 for (const auto &q_index : q_indices) {
601 const auto &x_q = q_points[q_index];
602 auto sol_q = local_sol_q(solution, q_index);
603 model.cell_indicator(local_indicator, x_q, ref_conv(sol_q));
604
605 copy_data.value += JxW[q_index] * local_indicator;
606 }
607 };
608 const auto face_worker = [&](const Iterator &t_cell, const uint &f, const uint &sf, const Iterator &t_ncell,
609 const uint &nf, const unsigned int &nsf, Scratch &scratch_data,
610 CopyData &copy_data) {
611 const auto &fe_iv = scratch_data.new_fe_interface_values(t_cell, f, sf, t_ncell, nf, nsf);
612
613 auto &copy_data_face = copy_data.face_data.emplace_back();
614 copy_data_face.cell_indices[0] = t_cell->active_cell_index();
615 copy_data_face.cell_indices[1] = t_ncell->active_cell_index();
616 copy_data_face.values[0] = 0;
617 copy_data_face.values[1] = 0;
618
619 const auto &JxW = fe_iv[0]->get_JxW_values();
620 const auto &q_points = fe_iv[0]->get_quadrature_points();
621 const auto &q_indices = fe_iv[0]->quadrature_point_indices();
622 const std::vector<Tensor<1, dim>> &normals = fe_iv[0]->get_normal_vectors();
623 array<double, 2> local_indicator{};
624
625 auto &solution = scratch_data.solution_interface;
626 fe_iv[0]->get_fe_face_values(0).get_function_values(solution_global, solution[0][0]);
627 fe_iv[0]->get_fe_face_values(1).get_function_values(solution_global, solution[1][0]);
628 for (uint i = 1; i < Components::count_fe_subsystems(); ++i) {
629 fe_iv[i]->get_fe_face_values(0).get_function_values(sol_vector[i], solution[0][i]);
630 fe_iv[i]->get_fe_face_values(1).get_function_values(sol_vector[i], solution[1][i]);
631 }
632
633 for (const auto &q_index : q_indices) {
634 const auto &x_q = q_points[q_index];
635 auto sol_q_s = local_sol_q(solution[0], q_index);
636 auto sol_q_n = local_sol_q(solution[1], q_index);
637 model.face_indicator(local_indicator, normals[q_index], x_q, ref_conv(sol_q_s), ref_conv(sol_q_n));
638
639 copy_data_face.values[0] += JxW[q_index] * local_indicator[0] * (1. + t_cell->at_boundary());
640 copy_data_face.values[1] += JxW[q_index] * local_indicator[1] * (1. + t_ncell->at_boundary());
641 }
642 };
643 const auto copier = [&](const CopyData &c) {
644 for (auto &cdf : c.face_data)
645 for (uint j = 0; j < 2; ++j)
646 indicator[cdf.cell_indices[j]] += cdf.values[j];
647 indicator[c.cell_index] += c.value;
648 };
649
650 const UpdateFlags update_flags = update_values | update_quadrature_points | update_JxW_values;
651 Scratch scratch_data(mapping, dof_handler_list, quadrature, quadrature_face, update_flags);
652 CopyData copy_data;
653 MeshWorker::AssembleFlags assemble_flags =
654 MeshWorker::assemble_own_cells | MeshWorker::assemble_own_interior_faces_once;
655
656 rebuild_ldg_vectors(solution_global);
657 MeshWorker::mesh_loop(dof_handler.begin_active(), dof_handler.end(), cell_worker, copier, scratch_data,
658 copy_data, assemble_flags, nullptr, face_worker, threads, batch_size);
659 }
660
661 virtual const BlockSparsityPattern &get_sparsity_pattern_jacobian() const override
662 {
664 }
665 virtual const BlockSparseMatrix<NumberType> &get_mass_matrix() const override { return mass_matrix; }
666
674 virtual void mass(VectorType &residual, const VectorType &solution_global, const VectorType &solution_global_dot,
675 NumberType weight) override
676 {
677 using Iterator = typename Triangulation<dim>::active_cell_iterator;
679 using CopyData = internal::CopyData_R<NumberType>;
680 const auto &constraints = discretization.get_constraints();
681
682 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
683 const auto &fe_v = scratch_data.new_fe_values(t_cell);
684 const uint n_dofs = fe_v[0]->get_fe().n_dofs_per_cell();
685 copy_data.reinit(scratch_data.cell[0], n_dofs);
686
687 const auto &JxW = fe_v[0]->get_JxW_values();
688 const auto &q_points = fe_v[0]->get_quadrature_points();
689 const auto &q_indices = fe_v[0]->quadrature_point_indices();
690
691 auto &solution = scratch_data.solution;
692 auto &solution_dot = scratch_data.solution_dot;
693 fe_v[0]->get_function_values(solution_global, solution[0]);
694 fe_v[0]->get_function_values(solution_global_dot, solution_dot);
695
696 const auto &comp_0 = scratch_data.comp[0];
697 array<NumberType, Components::count_fe_functions(0)> mass{};
698 for (const auto &q_index : q_indices) {
699 const auto &x_q = q_points[q_index];
700 model.mass(mass, x_q, solution[0][q_index], solution_dot[q_index]);
701
702 for (uint i = 0; i < n_dofs; ++i) {
703 const auto component_i = comp_0[i];
704 copy_data.cell_residual(i) += weight * JxW[q_index] * // dx
705 fe_v[0]->shape_value_component(i, q_index, component_i) *
706 mass[component_i]; // phi_i(x_q) * mass(x_q, u_q)
707 }
708 }
709 };
710 const auto copier = [&](const CopyData &c) {
711 constraints.distribute_local_to_global(c.cell_residual, c.local_dof_indices, residual);
712 };
713
714 const UpdateFlags update_flags = update_values | update_quadrature_points | update_JxW_values;
715 const MeshWorker::AssembleFlags assemble_flags = MeshWorker::assemble_own_cells;
716 Scratch scratch_data(mapping, dof_handler_list, quadrature, quadrature_face, update_flags);
717 CopyData copy_data;
718
719 MeshWorker::mesh_loop(dof_handler.begin_active(), dof_handler.end(), cell_worker, copier, scratch_data,
720 copy_data, assemble_flags, nullptr, nullptr, threads, batch_size);
721 }
722
730 virtual void residual(VectorType &residual, const VectorType &solution_global, NumberType weight,
731 const VectorType &solution_global_dot, NumberType weight_mass,
732 const VectorType &variables = VectorType()) override
733 {
734 using Iterator = typename Triangulation<dim>::active_cell_iterator;
736 using CopyData = internal::CopyData_R<NumberType>;
737 const auto &constraints = discretization.get_constraints();
738
739 // Find the EoM and extract whatever data is needed for the model.
740 std::array<NumberType, Components::count_extractors()> __extracted_data{{}};
741 if constexpr (Components::count_extractors() > 0)
742 this->extract(__extracted_data, solution_global, variables, true, false, true);
743 const auto &extracted_data = __extracted_data;
744
745 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
746 const auto &fe_v = scratch_data.new_fe_values(t_cell);
747 const uint n_dofs = fe_v[0]->get_fe().n_dofs_per_cell();
748 copy_data.reinit(scratch_data.cell[0], n_dofs);
749
750 const auto &JxW = fe_v[0]->get_JxW_values();
751 const auto &q_points = fe_v[0]->get_quadrature_points();
752 const auto &q_indices = fe_v[0]->quadrature_point_indices();
753
754 auto &solution = scratch_data.solution;
755 auto &solution_dot = scratch_data.solution_dot;
756 fe_v[0]->get_function_values(solution_global, solution[0]);
757 fe_v[0]->get_function_values(solution_global_dot, solution_dot);
758 for (uint i = 1; i < Components::count_fe_subsystems(); ++i)
759 fe_v[i]->get_function_values(sol_vector[i], solution[i]);
760
761 const auto &comp_0 = scratch_data.comp[0];
762 array<NumberType, Components::count_fe_functions(0)> mass{};
763 array<Tensor<1, dim, NumberType>, Components::count_fe_functions(0)> flux{};
764 array<NumberType, Components::count_fe_functions(0)> source{};
765 for (const auto &q_index : q_indices) {
766 const auto &x_q = q_points[q_index];
767 auto sol_q = std::tuple_cat(local_sol_q(solution, q_index), std::tie(extracted_data, variables));
768 model.mass(mass, x_q, solution[0][q_index], solution_dot[q_index]);
769 model.flux(flux, x_q, fe_conv(sol_q));
770 model.source(source, x_q, fe_conv(sol_q));
771
772 for (uint i = 0; i < n_dofs; ++i) {
773 const auto component_i = comp_0[i];
774 copy_data.cell_residual(i) += weight * JxW[q_index] * // dx
775 (-scalar_product(fe_v[0]->shape_grad_component(i, q_index, component_i),
776 flux[component_i]) // -dphi_i(x_q) * flux(x_q, u_q)
777 + fe_v[0]->shape_value_component(i, q_index, component_i) *
778 (source[component_i])); // -phi_i(x_q) * source(x_q, u_q)
779 copy_data.cell_mass(i) += weight_mass * JxW[q_index] * // dx
780 fe_v[0]->shape_value_component(i, q_index, component_i) *
781 mass[component_i]; // phi_i(x_q) * mass(x_q, u_q)
782 }
783 }
784 };
785 const auto boundary_worker = [&](const Iterator &t_cell, const uint &face_no, Scratch &scratch_data,
786 CopyData &copy_data) {
787 const auto &fe_fv = scratch_data.new_fe_boundary_values(t_cell, face_no);
788 const uint n_dofs = fe_fv[0]->get_fe().n_dofs_per_cell();
789
790 const auto &JxW = fe_fv[0]->get_JxW_values();
791 const auto &q_points = fe_fv[0]->get_quadrature_points();
792 const auto &q_indices = fe_fv[0]->quadrature_point_indices();
793 const auto &normals = fe_fv[0]->get_normal_vectors();
794
795 auto &solution = scratch_data.solution;
796 fe_fv[0]->get_function_values(solution_global, solution[0]);
797 for (uint i = 1; i < Components::count_fe_subsystems(); ++i)
798 fe_fv[i]->get_function_values(sol_vector[i], solution[i]);
799
800 const auto &comp_0 = scratch_data.comp[0];
801 array<Tensor<1, dim, NumberType>, Components::count_fe_functions(0)> numflux{};
802 for (const auto &q_index : q_indices) {
803 const auto &x_q = q_points[q_index];
804 auto sol_q = std::tuple_cat(local_sol_q(solution, q_index), std::tie(extracted_data, variables));
805 model.boundary_numflux(numflux, normals[q_index], x_q, fe_conv(sol_q));
806
807 for (uint i = 0; i < n_dofs; ++i) {
808 const auto component_i = comp_0[i];
809 copy_data.cell_residual(i) +=
810 weight * JxW[q_index] * // weight * dx
811 (fe_fv[0]->shape_value_component(i, q_index, component_i) *
812 scalar_product(numflux[component_i], normals[q_index])); // phi_i(x_q) * numflux(x_q, u_q) * n(x_q)
813 }
814 }
815 };
816 const auto face_worker = [&](const Iterator &t_cell, const uint &f, const uint &sf, const Iterator &t_ncell,
817 const uint &nf, const unsigned int &nsf, Scratch &scratch_data,
818 CopyData &copy_data) {
819 const auto &fe_iv = scratch_data.new_fe_interface_values(t_cell, f, sf, t_ncell, nf, nsf);
820 const uint n_dofs = fe_iv[0]->n_current_interface_dofs();
821 auto &copy_data_face = copy_data.new_face_data(*(fe_iv[0]));
822
823 const auto &JxW = fe_iv[0]->get_JxW_values();
824 const auto &q_points = fe_iv[0]->get_quadrature_points();
825 const auto &q_indices = fe_iv[0]->quadrature_point_indices();
826 const auto &normals = fe_iv[0]->get_normal_vectors();
827
828 auto &solution = scratch_data.solution_interface;
829 fe_iv[0]->get_fe_face_values(0).get_function_values(solution_global, solution[0][0]);
830 fe_iv[0]->get_fe_face_values(1).get_function_values(solution_global, solution[1][0]);
831 for (uint i = 1; i < Components::count_fe_subsystems(); ++i) {
832 fe_iv[i]->get_fe_face_values(0).get_function_values(sol_vector[i], solution[0][i]);
833 fe_iv[i]->get_fe_face_values(1).get_function_values(sol_vector[i], solution[1][i]);
834 }
835
836 // Pre-compute component indices for interface DoFs
837 std::vector<uint> iface_comp_0(n_dofs);
838 for (uint i = 0; i < n_dofs; ++i) {
839 const auto &cd_i = fe_iv[0]->interface_dof_to_dof_indices(i);
840 iface_comp_0[i] = cd_i[0] == numbers::invalid_unsigned_int
841 ? fe_iv[0]->get_fe().system_to_component_index(cd_i[1]).first
842 : fe_iv[0]->get_fe().system_to_component_index(cd_i[0]).first;
843 }
844
845 array<Tensor<1, dim, NumberType>, Components::count_fe_functions(0)> numflux{};
846 for (const auto &q_index : q_indices) {
847 const auto &x_q = q_points[q_index];
848 auto sol_q_s = std::tuple_cat(local_sol_q(solution[0], q_index), std::tie(extracted_data, variables));
849 auto sol_q_n = std::tuple_cat(local_sol_q(solution[1], q_index), std::tie(extracted_data, variables));
850 model.numflux(numflux, normals[q_index], x_q, fe_conv(sol_q_s), fe_conv(sol_q_n));
851
852 for (uint i = 0; i < n_dofs; ++i) {
853 const auto component_i = iface_comp_0[i];
854 copy_data_face.cell_residual(i) +=
855 weight * JxW[q_index] * // weight * dx
856 (fe_iv[0]->jump_in_shape_values(i, q_index, component_i) *
857 scalar_product(numflux[component_i],
858 normals[q_index])); // [[phi_i(x_q)]] * numflux(x_q, u_q) * n(x_q)
859 }
860 }
861 };
862 const auto copier = [&](const CopyData &c) {
863 constraints.distribute_local_to_global(c.cell_residual, c.local_dof_indices, residual);
864 constraints.distribute_local_to_global(c.cell_mass, c.local_dof_indices, residual);
865 for (auto &cdf : c.face_data)
866 constraints.distribute_local_to_global(cdf.cell_residual, cdf.joint_dof_indices, residual);
867 };
868
869 const UpdateFlags update_flags =
870 update_values | update_gradients | update_quadrature_points | update_JxW_values;
871 const MeshWorker::AssembleFlags assemble_flags = MeshWorker::assemble_own_cells |
872 MeshWorker::assemble_boundary_faces |
873 MeshWorker::assemble_own_interior_faces_once;
874 Scratch scratch_data(mapping, dof_handler_list, quadrature, quadrature_face, update_flags);
875 CopyData copy_data;
876
877 Timer timer;
878
879 rebuild_ldg_vectors(solution_global);
880 MeshWorker::mesh_loop(dof_handler.begin_active(), dof_handler.end(), cell_worker, copier, scratch_data,
881 copy_data, assemble_flags, boundary_worker, face_worker, threads, batch_size);
882
883 timings_residual.push_back(timer.wall_time());
884 }
885
886 virtual void jacobian_mass(BlockSparseMatrix<NumberType> &jacobian, const VectorType &solution_global,
887 const VectorType &solution_global_dot, NumberType alpha = 1.,
888 NumberType beta = 1.) override
889 {
890 using Iterator = typename Triangulation<dim>::active_cell_iterator;
892 using CopyData = internal::CopyData_J_full<NumberType, Components::count_fe_subsystems()>;
893 const auto &constraints = discretization.get_constraints();
894
895 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
896 const auto &fe_v = scratch_data.new_fe_values(t_cell);
897 const uint to_n_dofs = fe_v[0]->get_fe().n_dofs_per_cell();
898 copy_data.reinit(scratch_data.cell, Components::count_extractors());
899
900 const auto &JxW = fe_v[0]->get_JxW_values();
901 const auto &q_points = fe_v[0]->get_quadrature_points();
902 const auto &q_indices = fe_v[0]->quadrature_point_indices();
903
904 auto &solution = scratch_data.solution;
905 auto &solution_dot = scratch_data.solution_dot;
906
907 fe_v[0]->get_function_values(solution_global, solution[0]);
908 fe_v[0]->get_function_values(solution_global_dot, solution_dot);
909
910 const auto &comp_0 = scratch_data.comp[0];
911 SimpleMatrix<NumberType, Components::count_fe_functions(0)> j_mass;
912 SimpleMatrix<NumberType, Components::count_fe_functions(0)> j_mass_dot;
913
914 const uint from_n_dofs = fe_v[0]->get_fe().n_dofs_per_cell();
915 for (const auto &q_index : q_indices) {
916 const auto &x_q = q_points[q_index];
917
918 model.template jacobian_mass<0>(j_mass, x_q, solution[0][q_index], solution_dot[q_index]);
919 model.template jacobian_mass<1>(j_mass_dot, x_q, solution[0][q_index], solution_dot[q_index]);
920
921 for (uint i = 0; i < to_n_dofs; ++i) {
922 const auto component_i = comp_0[i];
923 for (uint j = 0; j < from_n_dofs; ++j) {
924 const auto component_j = comp_0[j];
925 copy_data.cell_jacobian[0](i, j) +=
926 JxW[q_index] * fe_v[0]->shape_value_component(j, q_index, component_j) * // weight * dx * phi_j(x_q)
927 fe_v[0]->shape_value_component(i, q_index, component_i) *
928 (alpha * j_mass_dot(component_i, component_j) +
929 beta * j_mass(component_i, component_j)); // -phi_i(x_q) * jsource(x_q, u_q)
930 }
931 }
932 }
933 };
934 const auto copier = [&](const CopyData &c) {
935 constraints.distribute_local_to_global(c.cell_jacobian[0], c.local_dof_indices[0], jacobian);
936 };
937
938 const UpdateFlags update_flags = update_values | update_quadrature_points | update_JxW_values;
939 const MeshWorker::AssembleFlags assemble_flags = MeshWorker::assemble_own_cells;
940 Scratch scratch_data(mapping, dof_handler_list, quadrature, quadrature_face, update_flags);
941 CopyData copy_data;
942
943 MeshWorker::mesh_loop(dof_handler.begin_active(), dof_handler.end(), cell_worker, copier, scratch_data,
944 copy_data, assemble_flags, nullptr, nullptr, threads, batch_size);
945 }
946
954 virtual void jacobian(BlockSparseMatrix<NumberType> &jacobian, const VectorType &solution_global,
955 NumberType weight, const VectorType &solution_global_dot, NumberType alpha, NumberType beta,
956 const VectorType &variables = VectorType()) override
957 {
958 if (is_close(weight, 0.))
959 throw std::runtime_error("Please call jacobian_mass instead of jacobian for weight == 0).");
960 using Iterator = typename Triangulation<dim>::active_cell_iterator;
962 using CopyData = internal::CopyData_J_full<NumberType, Components::count_fe_subsystems()>;
963 const auto &constraints = discretization.get_constraints();
964
965 // Find the EoM and extract whatever data is needed for the model.
966 std::array<NumberType, Components::count_extractors()> __extracted_data{{}};
967 if constexpr (Components::count_extractors() > 0) {
968 this->extract(__extracted_data, solution_global, variables, true, true, true);
969 if (this->jacobian_extractors(this->extractor_jacobian, solution_global, variables))
971 }
972 const auto &extracted_data = __extracted_data;
973
974 bool exception = false;
975
976 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
977 const auto &fe_v = scratch_data.new_fe_values(t_cell);
978 const uint to_n_dofs = fe_v[0]->get_fe().n_dofs_per_cell();
979 copy_data.reinit(scratch_data.cell, Components::count_extractors());
980
981 const auto &JxW = fe_v[0]->get_JxW_values();
982 const auto &q_points = fe_v[0]->get_quadrature_points();
983 const auto &q_indices = fe_v[0]->quadrature_point_indices();
984
985 auto &solution = scratch_data.solution;
986 auto &solution_dot = scratch_data.solution_dot;
987
988 fe_v[0]->get_function_values(solution_global, solution[0]);
989 fe_v[0]->get_function_values(solution_global_dot, solution_dot);
990 for (uint i = 1; i < Components::count_fe_subsystems(); ++i)
991 fe_v[i]->get_function_values(sol_vector[i], solution[i]);
992
993 SimpleMatrix<NumberType, Components::count_fe_functions(0)> j_mass;
994 SimpleMatrix<NumberType, Components::count_fe_functions(0)> j_mass_dot;
996 auto j_source = jacobian_tuple<NumberType, Model>();
997 SimpleMatrix<Tensor<1, dim, NumberType>, Components::count_fe_functions(), Components::count_extractors()>
998 j_extr_flux;
999 SimpleMatrix<NumberType, Components::count_fe_functions(), Components::count_extractors()> j_extr_source;
1001 if (jacobian_tmp_built[k] && model.get_components().jacobians_constant(0, k)) return;
1002
1003 const uint from_n_dofs = fe_v[k]->get_fe().n_dofs_per_cell();
1004 for (const auto &q_index : q_indices) {
1005 const auto &x_q = q_points[q_index];
1006 auto sol_q = std::tuple_cat(local_sol_q(solution, q_index), std::tie(extracted_data, variables));
1007
1008 if constexpr (k == 0) {
1009 this->model.template jacobian_mass<0>(j_mass, x_q, solution[0][q_index], solution_dot[q_index]);
1010 this->model.template jacobian_mass<1>(j_mass_dot, x_q, solution[0][q_index], solution_dot[q_index]);
1011 if constexpr (Components::count_extractors() > 0) {
1012 this->model.template jacobian_flux_source_extr<stencil>(j_extr_flux, j_extr_source, x_q,
1013 fe_conv(sol_q));
1014 }
1015 }
1016 model.template jacobian_flux_source<k, 0>(std::get<k>(j_flux), std::get<k>(j_source), x_q,
1017 fe_conv(sol_q));
1018
1019 if (!std::get<k>(j_flux).is_finite() || !std::get<k>(j_source).is_finite()) exception = true;
1020
1021 const auto &comp_0 = scratch_data.comp[0];
1022 const auto &comp_k = scratch_data.comp[k];
1023 for (uint i = 0; i < to_n_dofs; ++i) {
1024 const auto component_i = comp_0[i];
1025 for (uint j = 0; j < from_n_dofs; ++j) {
1026 const auto component_j = comp_k[j];
1027
1028 copy_data.cell_jacobian[k](i, j) +=
1029 JxW[q_index] *
1030 fe_v[k]->shape_value_component(j, q_index, component_j) * // weight * dx * phi_j(x_q)
1031 (-scalar_product(fe_v[0]->shape_grad_component(i, q_index, component_i),
1032 std::get<k>(j_flux)(component_i, component_j)) // -dphi_i(x_q) * jflux(x_q, u_q)
1033 + fe_v[0]->shape_value_component(i, q_index, component_i) *
1034 std::get<k>(j_source)(component_i, component_j)); // -phi_i(x_q) * jsource(x_q, u_q)
1035 if constexpr (k == 0) {
1036 copy_data.cell_mass_jacobian(i, j) +=
1037 JxW[q_index] *
1038 fe_v[0]->shape_value_component(j, q_index, component_j) * // weight * dx * phi_j(x_q)
1039 fe_v[0]->shape_value_component(i, q_index, component_i) *
1040 (alpha / weight * j_mass_dot(component_i, component_j) +
1041 beta / weight * j_mass(component_i, component_j)); // -phi_i(x_q) * jsource(x_q, u_q)
1042 }
1043 }
1044
1045 // extractor contribution
1046 if constexpr (k == 0)
1047 if constexpr (Components::count_extractors() > 0)
1048 for (uint e = 0; e < Components::count_extractors(); ++e)
1049 copy_data.extractor_cell_jacobian(i, e) +=
1050 weight * JxW[q_index] * // dx * phi_j * (
1051 (-scalar_product(fe_v[0]->shape_grad_component(i, q_index, component_i),
1052 j_extr_flux(component_i, e)) // -dphi_i * jflux
1053 + fe_v[0]->shape_value_component(i, q_index, component_i) *
1054 j_extr_source(component_i, e)); // -phi_i * jsource)
1055 }
1056 }
1057 });
1058 };
1059 const auto boundary_worker = [&](const Iterator &t_cell, const uint &face_no, Scratch &scratch_data,
1060 CopyData &copy_data) {
1061 const auto &fe_fv = scratch_data.new_fe_boundary_values(t_cell, face_no);
1062 const uint to_n_dofs = fe_fv[0]->get_fe().n_dofs_per_cell();
1063
1064 const auto &JxW = fe_fv[0]->get_JxW_values();
1065 const auto &q_points = fe_fv[0]->get_quadrature_points();
1066 const auto &q_indices = fe_fv[0]->quadrature_point_indices();
1067 const auto &normals = fe_fv[0]->get_normal_vectors();
1068 auto &solution = scratch_data.solution;
1069
1070 fe_fv[0]->get_function_values(solution_global, solution[0]);
1071 for (uint i = 1; i < Components::count_fe_subsystems(); ++i)
1072 fe_fv[i]->get_function_values(sol_vector[i], solution[i]);
1073
1074 auto j_boundary_numflux = jacobian_tuple<Tensor<1, dim>, Model>();
1076 if (jacobian_tmp_built[k] && model.get_components().jacobians_constant(0, k)) return;
1077 for (const auto &q_index : q_indices) {
1078 const auto &x_q = q_points[q_index];
1079 auto sol_q = std::tuple_cat(local_sol_q(solution, q_index), std::tie(extracted_data, variables));
1080
1081 const uint from_n_dofs = fe_fv[k]->get_fe().n_dofs_per_cell();
1082
1083 model.template jacobian_boundary_numflux<k, 0>(std::get<k>(j_boundary_numflux), normals[q_index], x_q,
1084 fe_conv(sol_q));
1085
1086 if (!std::get<k>(j_boundary_numflux).is_finite()) exception = true;
1087
1088 const auto &comp_0 = scratch_data.comp[0];
1089 const auto &comp_k = scratch_data.comp[k];
1090 for (uint i = 0; i < to_n_dofs; ++i) {
1091 const auto component_i = comp_0[i];
1092 for (uint j = 0; j < from_n_dofs; ++j) {
1093 const auto component_j = comp_k[j];
1094
1095 copy_data.cell_jacobian[k](i, j) +=
1096 JxW[q_index] *
1097 fe_fv[k]->shape_value_component(j, q_index, component_j) * // weight * dx * phi_j(x_q)
1098 (fe_fv[0]->shape_value_component(i, q_index, component_i) *
1099 scalar_product(std::get<k>(j_boundary_numflux)(component_i, component_j),
1100 normals[q_index])); // phi_i(x_q) * j_numflux(x_q, u_q) * n(x_q)
1101 }
1102 }
1103 }
1104 });
1105 };
1106 const auto face_worker = [&](const Iterator &t_cell, const uint &f, const uint &sf, const Iterator &t_ncell,
1107 const uint &nf, const unsigned int &nsf, Scratch &scratch_data,
1108 CopyData &copy_data) {
1109 const auto &fe_iv = scratch_data.new_fe_interface_values(t_cell, f, sf, t_ncell, nf, nsf);
1110 const uint to_n_dofs = fe_iv[0]->n_current_interface_dofs();
1111 auto &copy_data_face = copy_data.new_face_data(fe_iv, Components::count_extractors());
1112
1113 const auto &JxW = fe_iv[0]->get_JxW_values();
1114 const auto &q_points = fe_iv[0]->get_quadrature_points();
1115 const auto &q_indices = fe_iv[0]->quadrature_point_indices();
1116 const auto &normals = fe_iv[0]->get_normal_vectors();
1117
1118 auto &solution = scratch_data.solution_interface;
1119 fe_iv[0]->get_fe_face_values(0).get_function_values(solution_global, solution[0][0]);
1120 fe_iv[0]->get_fe_face_values(1).get_function_values(solution_global, solution[1][0]);
1121 for (uint i = 1; i < Components::count_fe_subsystems(); ++i) {
1122 fe_iv[i]->get_fe_face_values(0).get_function_values(sol_vector[i], solution[0][i]);
1123 fe_iv[i]->get_fe_face_values(1).get_function_values(sol_vector[i], solution[1][i]);
1124 }
1125
1126 auto j_numflux = jacobian_2_tuple<Tensor<1, dim>, Model>();
1128 if (jacobian_tmp_built[k] && model.get_components().jacobians_constant(0, k)) return;
1129
1130 const uint from_n_dofs = fe_iv[k]->n_current_interface_dofs();
1131
1132 // Pre-compute interface DoF component indices and face numbers
1133 std::vector<uint> iface_comp_0(to_n_dofs);
1134 std::vector<uint> iface_face_0(to_n_dofs);
1135 for (uint i = 0; i < to_n_dofs; ++i) {
1136 const auto &cd_i = fe_iv[0]->interface_dof_to_dof_indices(i);
1137 iface_face_0[i] = cd_i[0] == numbers::invalid_unsigned_int ? 1 : 0;
1138 iface_comp_0[i] = fe_iv[0]->get_fe().system_to_component_index(cd_i[iface_face_0[i]]).first;
1139 }
1140 std::vector<uint> iface_comp_k(from_n_dofs);
1141 std::vector<uint> iface_face_k(from_n_dofs);
1142 std::vector<uint> iface_dof_k(from_n_dofs);
1143 for (uint j = 0; j < from_n_dofs; ++j) {
1144 const auto &cd_j = fe_iv[k]->interface_dof_to_dof_indices(j);
1145 iface_face_k[j] = cd_j[0] == numbers::invalid_unsigned_int ? 1 : 0;
1146 iface_dof_k[j] = cd_j[iface_face_k[j]];
1147 iface_comp_k[j] = fe_iv[k]->get_fe().system_to_component_index(iface_dof_k[j]).first;
1148 }
1149
1150 for (const auto &q_index : q_indices) {
1151 const auto &x_q = q_points[q_index];
1152 auto sol_q_s = std::tuple_cat(local_sol_q(solution[0], q_index), std::tie(extracted_data, variables));
1153 auto sol_q_n = std::tuple_cat(local_sol_q(solution[1], q_index), std::tie(extracted_data, variables));
1154
1155 model.template jacobian_numflux<k, 0>(std::get<k>(j_numflux), normals[q_index], x_q, fe_conv(sol_q_s),
1156 fe_conv(sol_q_n));
1157
1158 if (!std::get<k>(j_numflux)[0].is_finite() || !std::get<k>(j_numflux)[1].is_finite()) exception = true;
1159
1160 for (uint i = 0; i < to_n_dofs; ++i) {
1161 const auto component_i = iface_comp_0[i];
1162 for (uint j = 0; j < from_n_dofs; ++j) {
1163 const auto component_j = iface_comp_k[j];
1164 const uint face_no_j = iface_face_k[j];
1165
1166 copy_data_face.cell_jacobian[k](i, j) +=
1167 JxW[q_index] *
1168 fe_iv[k]->get_fe_face_values(face_no_j).shape_value_component(
1169 iface_dof_k[j], q_index, component_j) * // weight * dx * phi_j(x_q)
1170 (fe_iv[0]->jump_in_shape_values(i, q_index, component_i) *
1171 scalar_product(std::get<k>(j_numflux)[face_no_j](component_i, component_j),
1172 normals[q_index])); // [[phi_i(x_q)]] * j_numflux(x_q, u_q)
1173 }
1174 }
1175 }
1176 });
1177 };
1178 const auto copier = [&](const CopyData &c) {
1179 try {
1180 constraints.distribute_local_to_global(c.cell_jacobian[0], c.local_dof_indices[0], jacobian);
1181 constraints.distribute_local_to_global(c.cell_mass_jacobian, c.local_dof_indices[0], jacobian);
1182 for (auto &cdf : c.face_data) {
1183 constraints.distribute_local_to_global(cdf.cell_jacobian[0], cdf.joint_dof_indices[0], jacobian);
1184 if constexpr (Components::count_extractors() > 0) {
1185 FullMatrix<NumberType> extractor_dependence(cdf.joint_dof_indices[0].size(),
1186 extractor_dof_indices.size());
1187 cdf.extractor_cell_jacobian.mmult(extractor_dependence, this->extractor_jacobian);
1188 constraints.distribute_local_to_global(extractor_dependence, cdf.joint_dof_indices[0],
1190 }
1191 }
1192 if constexpr (Components::count_extractors() > 0) {
1193 FullMatrix<NumberType> extractor_dependence(c.local_dof_indices[0].size(), extractor_dof_indices.size());
1194 c.extractor_cell_jacobian.mmult(extractor_dependence, this->extractor_jacobian);
1195 constraints.distribute_local_to_global(extractor_dependence, c.local_dof_indices[0],
1197 }
1198
1199 // LDG things
1200
1201 for (uint i = 1; i < Components::count_fe_subsystems(); ++i) {
1202 if (jacobian_tmp_built[i] && model.get_components().jacobians_constant(0, i)) continue;
1203 j_ug[i].add(c.local_dof_indices[0], c.local_dof_indices[i], c.cell_jacobian[i]);
1204 }
1205 for (auto &cdf : c.face_data) {
1206 for (uint i = 1; i < Components::count_fe_subsystems(); ++i) {
1207 if (jacobian_tmp_built[i] && model.get_components().jacobians_constant(0, i)) continue;
1208 j_ug[i].add(cdf.joint_dof_indices[0], cdf.joint_dof_indices[i], cdf.cell_jacobian[i]);
1209 }
1210 }
1211 } catch (...) {
1212 exception = true;
1213 }
1214 };
1215
1216 const UpdateFlags update_flags =
1217 update_values | update_gradients | update_quadrature_points | update_JxW_values;
1218 const MeshWorker::AssembleFlags assemble_flags = MeshWorker::assemble_own_cells |
1219 MeshWorker::assemble_boundary_faces |
1220 MeshWorker::assemble_own_interior_faces_once;
1221 Scratch scratch_data(mapping, dof_handler_list, quadrature, quadrature_face, update_flags);
1222 CopyData copy_data;
1223
1224 Timer timer;
1225
1227 if (!ldg_matrix_built[k] || !model.get_components().jacobians_constant(k, k - 1))
1228 rebuild_ldg_jacobian<k>(solution_global);
1229 if (!jacobian_tmp_built[k] || !model.get_components().jacobians_constant(0, k)) j_ug[k] = 0;
1230 });
1231 rebuild_ldg_vectors(solution_global);
1232
1233 MeshWorker::mesh_loop(dof_handler.begin_active(), dof_handler.end(), cell_worker, copier, scratch_data,
1234 copy_data, assemble_flags, boundary_worker, face_worker, threads, batch_size);
1235
1236 if (exception) throw std::runtime_error("Infinity encountered in jacobian construction");
1237
1238 tbb::parallel_for(
1239 tbb::blocked_range<uint>(1, Components::count_fe_subsystems()), [&](tbb::blocked_range<uint> rk) {
1240 for (uint k = rk.begin(); k < rk.end(); ++k) {
1241 if (!jacobian_tmp_built[k] || !model.get_components().jacobians_constant(0, k)) {
1242 jacobian_tmp[k] = 0;
1243 tbb::parallel_for(
1244 tbb::blocked_range<uint>(0, Components::count_fe_functions(0)), [&](tbb::blocked_range<uint> r) {
1245 for (uint q = r.begin(); q < r.end(); ++q)
1246 for (const auto &c : model.get_components().ldg_couplings(k, 0))
1247 j_ug[k].block(q, c[0]).mmult(jacobian_tmp[k].block(q, c[1]), j_gu[k].block(c[0], c[1]),
1248 Vector<NumberType>(), false);
1249 });
1250 jacobian_tmp_built[k] = true;
1251 }
1252 }
1253 });
1254
1255 tbb::parallel_for(
1256 tbb::blocked_range<uint>(0, Components::count_fe_functions(0)), [&](tbb::blocked_range<uint> rc1) {
1257 tbb::parallel_for(tbb::blocked_range<uint>(0, Components::count_fe_functions(0)),
1258 [&](tbb::blocked_range<uint> rc2) {
1259 for (uint c1 = rc1.begin(); c1 < rc1.end(); ++c1)
1260 for (uint c2 = rc2.begin(); c2 < rc2.end(); ++c2) {
1261 for (uint k = 1; k < Components::count_fe_subsystems(); ++k)
1262 jacobian.block(c1, c2).add(NumberType(1.), jacobian_tmp[k].block(c1, c2));
1263 jacobian.block(c1, c2) *= weight;
1264 }
1265 });
1266 });
1267
1268 timings_jacobian.push_back(timer.wall_time());
1269 }
1270
1271 void log(const std::string logger)
1272 {
1273 std::stringstream ss;
1274 ss << "LDG Assembler: " << std::endl;
1275 ss << " Reinit: " << average_time_reinit() * 1000 << "ms (" << num_reinits() << ")" << std::endl;
1276 ss << " Residual: " << average_time_residual_assembly() * 1000 << "ms (" << num_residuals() << ")"
1277 << std::endl;
1278 ss << " Jacobian: " << average_time_jacobian_assembly() * 1000 << "ms (" << num_jacobians() << ")"
1279 << std::endl;
1280 spdlog::get(logger)->info(ss.str());
1281 }
1282
1283 double average_time_reinit() const
1284 {
1285 double t = 0.;
1286 double n = timings_reinit.size();
1287 for (const auto &t_ : timings_reinit)
1288 t += t_ / n;
1289 return t;
1290 }
1291 uint num_reinits() const { return timings_reinit.size(); }
1292
1294 {
1295 double t = 0.;
1296 double n = timings_residual.size();
1297 for (const auto &t_ : timings_residual)
1298 t += t_ / n;
1299 return t;
1300 }
1301 uint num_residuals() const { return timings_residual.size(); }
1302
1304 {
1305 double t = 0.;
1306 double n = timings_jacobian.size();
1307 for (const auto &t_ : timings_jacobian)
1308 t += t_ / n;
1309 return t;
1310 }
1311 uint num_jacobians() const { return timings_jacobian.size(); }
1312
1313 protected:
1315 using Base::dof_handler;
1316 using Base::fe;
1317 using Base::mapping;
1318 using Base::model;
1319
1320 QGauss<dim> quadrature;
1322 using Base::batch_size;
1323 using Base::threads;
1324
1325 std::vector<const DoFHandler<dim> *> dof_handler_list;
1326
1327 mutable array<BlockVector<NumberType>, Components::count_fe_subsystems()> sol_vector;
1328 mutable array<BlockVector<NumberType>, Components::count_fe_subsystems()> sol_vector_tmp;
1329 mutable array<Vector<NumberType>, Components::count_fe_subsystems()> sol_vector_vec_tmp;
1330
1331 BlockSparsityPattern sparsity_pattern_jacobian;
1332 BlockSparsityPattern sparsity_pattern_mass;
1333 array<BlockSparsityPattern, Components::count_fe_subsystems()> sparsity_pattern_ug;
1334 array<BlockSparsityPattern, Components::count_fe_subsystems()> sparsity_pattern_gu;
1335 array<BlockSparsityPattern, Components::count_fe_subsystems()> sparsity_pattern_wg;
1336
1337 array<BlockSparseMatrix<NumberType>, Components::count_fe_subsystems()> jacobian_tmp;
1338
1339 BlockSparseMatrix<NumberType> mass_matrix;
1340 SparseMatrix<NumberType> component_mass_matrix_inverse;
1341 array<BlockSparseMatrix<NumberType>, Components::count_fe_subsystems()> j_ug;
1342 mutable array<BlockSparseMatrix<NumberType>, Components::count_fe_subsystems()> j_gu;
1343 mutable array<BlockSparseMatrix<NumberType>, Components::count_fe_subsystems()> j_wg;
1344 mutable array<BlockSparseMatrix<NumberType>, Components::count_fe_subsystems()> j_wg_tmp;
1345
1346 std::vector<double> timings_reinit;
1347 std::vector<double> timings_residual;
1348 std::vector<double> timings_jacobian;
1349
1350 mutable array<bool, Components::count_fe_subsystems()> ldg_matrix_built;
1351 mutable array<bool, Components::count_fe_subsystems()> jacobian_tmp_built;
1352
1353 using Base::EoM_abs_tol;
1354 using Base::EoM_max_iter;
1356
1357 void rebuild_ldg_vectors(const VectorType &sol) const
1358 {
1360 if (!model.get_components().jacobians_constant(k, k - 1)) {
1361 if (k == 1)
1362 build_ldg_vector<k - 1, k>(sol, sol_vector[k], sol_vector_tmp[k]);
1363 else
1364 build_ldg_vector<k - 1, k>(sol_vector[k - 1], sol_vector[k], sol_vector_tmp[k]);
1365 } else {
1367
1368 j_gu[k].vmult(sol_vector[k], sol);
1369 }
1370 });
1371 }
1372
1373 template <int k> void rebuild_ldg_jacobian(const VectorType &sol) const
1374 {
1375 static_assert(k > 0);
1376 ldg_matrix_built[k] = true;
1377 if constexpr (k == 1)
1379 else {
1380 if (!ldg_matrix_built[k - 1]) rebuild_ldg_jacobian<k - 1>(sol);
1381 build_ldg_jacobian<k - 1, k>(sol_vector[k - 1], j_wg[k], j_wg_tmp[k]);
1382 for (const auto &c : model.get_components().ldg_couplings(k, 0))
1383 for (const auto &b : model.get_components().ldg_couplings(k, k - 1))
1384 if (b[0] == c[0])
1385 j_wg[k]
1386 .block(b[0], b[1])
1387 .mmult(j_gu[k].block(c[0], c[1]), j_gu[k - 1].block(b[1], c[1]), Vector<NumberType>(), false);
1388 }
1389 }
1390
1400 template <int from, int to, typename VectorType, typename VectorTypeldg>
1401 void build_ldg_vector(const VectorType &solution_global, VectorTypeldg &ldg_vector,
1402 VectorTypeldg &ldg_vector_tmp) const
1403 {
1404 static_assert(to - from == 1, "can only build LDG from last level!");
1405 using Iterator = typename Triangulation<dim>::active_cell_iterator;
1407 using CopyData = internal::CopyData_R<NumberType>;
1408 const auto &constraints = discretization.get_constraints(to);
1409
1410 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
1411 const auto &fe_v = scratch_data.new_fe_values(t_cell);
1412 const uint to_n_dofs = fe_v[to]->get_fe().n_dofs_per_cell();
1413 copy_data.reinit(scratch_data.cell[to], to_n_dofs);
1414
1415 const auto &JxW = fe_v[to]->get_JxW_values();
1416 const auto &q_points = fe_v[to]->get_quadrature_points();
1417 const auto &q_indices = fe_v[to]->quadrature_point_indices();
1418 auto &solution = scratch_data.solution;
1419
1420 fe_v[from]->get_function_values(solution_global, solution[from]);
1421
1422 array<Tensor<1, dim, NumberType>, Components::count_fe_functions(to)> flux{};
1423 array<NumberType, Components::count_fe_functions(to)> source{};
1424 for (const auto &q_index : q_indices) {
1425 const auto &x_q = q_points[q_index];
1426 model.template ldg_flux<to>(flux, x_q, solution[from][q_index]);
1427 model.template ldg_source<to>(source, x_q, solution[from][q_index]);
1428
1429 for (uint i = 0; i < to_n_dofs; ++i) {
1430 const auto component_i = fe_v[to]->get_fe().system_to_component_index(i).first;
1431 copy_data.cell_residual(i) += JxW[q_index] * // dx
1432 (-scalar_product(fe_v[to]->shape_grad_component(i, q_index, component_i),
1433 flux[component_i]) // -dphi_i(x_q) * flux(x_q, u_q)
1434 + fe_v[to]->shape_value_component(i, q_index, component_i) *
1435 source[component_i]); // -phi_i(x_q) * source(x_q, u_q)
1436 }
1437 }
1438 };
1439 const auto boundary_worker = [&](const Iterator &t_cell, const uint &face_no, Scratch &scratch_data,
1440 CopyData &copy_data) {
1441 const auto &fe_fv = scratch_data.new_fe_boundary_values(t_cell, face_no);
1442 const uint to_n_dofs = fe_fv[to]->get_fe().n_dofs_per_cell();
1443
1444 const auto &JxW = fe_fv[to]->get_JxW_values();
1445 const auto &q_points = fe_fv[to]->get_quadrature_points();
1446 const auto &q_indices = fe_fv[to]->quadrature_point_indices();
1447 const std::vector<Tensor<1, dim>> &normals = fe_fv[from]->get_normal_vectors();
1448 auto &solution = scratch_data.solution;
1449
1450 fe_fv[from]->get_function_values(solution_global, solution[from]);
1451
1452 array<Tensor<1, dim, NumberType>, Components::count_fe_functions(to)> numflux{};
1453 for (const auto &q_index : q_indices) {
1454 const auto &x_q = q_points[q_index];
1455 model.template ldg_boundary_numflux<to>(numflux, normals[q_index], x_q, solution[from][q_index]);
1456
1457 for (uint i = 0; i < to_n_dofs; ++i) {
1458 const auto component_i = fe_fv[to]->get_fe().system_to_component_index(i).first;
1459 copy_data.cell_residual(i) +=
1460 JxW[q_index] * // weight * dx
1461 (fe_fv[to]->shape_value_component(i, q_index, component_i) *
1462 scalar_product(numflux[component_i], normals[q_index])); // phi_i(x_q) * numflux(x_q, u_q) * n(x_q)
1463 }
1464 }
1465 };
1466 const auto face_worker = [&](const Iterator &t_cell, const uint &f, const uint &sf, const Iterator &t_ncell,
1467 const uint &nf, const unsigned int &nsf, Scratch &scratch_data,
1468 CopyData &copy_data) {
1469 const auto &fe_iv = scratch_data.new_fe_interface_values(t_cell, f, sf, t_ncell, nf, nsf);
1470 const uint to_n_dofs = fe_iv[to]->n_current_interface_dofs();
1471 auto &copy_data_face = copy_data.new_face_data(*(fe_iv[to]));
1472
1473 const auto &JxW = fe_iv[to]->get_JxW_values();
1474 const auto &q_points = fe_iv[to]->get_quadrature_points();
1475 const auto &q_indices = fe_iv[to]->quadrature_point_indices();
1476 // normals are facing outwards!
1477 const std::vector<Tensor<1, dim>> &normals = fe_iv[to]->get_normal_vectors();
1478 auto &solution = scratch_data.solution_interface;
1479
1480 fe_iv[from]->get_fe_face_values(0).get_function_values(solution_global, solution[0][from]);
1481 fe_iv[from]->get_fe_face_values(1).get_function_values(solution_global, solution[1][from]);
1482
1483 array<Tensor<1, dim, NumberType>, Components::count_fe_functions(to)> numflux{};
1484 for (const auto &q_index : q_indices) {
1485 const auto &x_q = q_points[q_index];
1486 model.template ldg_numflux<to>(numflux, normals[q_index], x_q, solution[0][from][q_index],
1487 solution[1][from][q_index]);
1488
1489 for (uint i = 0; i < to_n_dofs; ++i) {
1490 const auto &cd_i = fe_iv[to]->interface_dof_to_dof_indices(i);
1491 const auto component_i = cd_i[0] == numbers::invalid_unsigned_int
1492 ? fe_iv[to]->get_fe().system_to_component_index(cd_i[1]).first
1493 : fe_iv[to]->get_fe().system_to_component_index(cd_i[0]).first;
1494 copy_data_face.cell_residual(i) +=
1495 JxW[q_index] * // weight * dx
1496 (fe_iv[to]->jump_in_shape_values(i, q_index, component_i) *
1497 scalar_product(numflux[component_i],
1498 normals[q_index])); // [[phi_i(x_q)]] * numflux(x_q, u_q) * n(x_q)
1499 }
1500 }
1501 };
1502 const auto copier = [&](const CopyData &c) {
1503 constraints.distribute_local_to_global(c.cell_residual, c.local_dof_indices, ldg_vector_tmp);
1504 for (auto &cdf : c.face_data)
1505 constraints.distribute_local_to_global(cdf.cell_residual, cdf.joint_dof_indices, ldg_vector_tmp);
1506 };
1507
1508 const MeshWorker::AssembleFlags assemble_flags = MeshWorker::assemble_own_cells |
1509 MeshWorker::assemble_boundary_faces |
1510 MeshWorker::assemble_own_interior_faces_once;
1511 Scratch scratch_data(mapping, dof_handler_list, quadrature, quadrature_face);
1512 CopyData copy_data;
1513
1514 ldg_vector_tmp = 0;
1515 MeshWorker::mesh_loop(dof_handler.begin_active(), dof_handler.end(), cell_worker, copier, scratch_data,
1516 copy_data, assemble_flags, boundary_worker, face_worker, threads, batch_size);
1517
1518 for (uint i = 0; i < Components::count_fe_functions(to); ++i)
1519 component_mass_matrix_inverse.vmult(ldg_vector.block(i), ldg_vector_tmp.block(i));
1520 }
1521
1531 template <int from, int to, typename VectorType>
1532 void build_ldg_jacobian(const VectorType &solution_global, BlockSparseMatrix<NumberType> &ldg_jacobian,
1533 BlockSparseMatrix<NumberType> &ldg_jacobian_tmp) const
1534 {
1535 static_assert(to - from == 1, "can only build LDG from last level!");
1536 using Iterator = typename Triangulation<dim>::active_cell_iterator;
1538 using CopyData = internal::CopyData_J<NumberType>;
1539
1540 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
1541 const auto &fe_v = scratch_data.new_fe_values(t_cell);
1542 const uint to_n_dofs = fe_v[to]->get_fe().n_dofs_per_cell();
1543 const uint from_n_dofs = fe_v[from]->get_fe().n_dofs_per_cell();
1544 copy_data.reinit(scratch_data.cell[from], scratch_data.cell[to], from_n_dofs, to_n_dofs);
1545
1546 const auto &JxW = fe_v[to]->get_JxW_values();
1547 const auto &q_points = fe_v[to]->get_quadrature_points();
1548 const auto &q_indices = fe_v[to]->quadrature_point_indices();
1549 auto &solution = scratch_data.solution;
1550
1551 fe_v[from]->get_function_values(solution_global, solution[from]);
1552
1553 SimpleMatrix<Tensor<1, dim>, Components::count_fe_functions(to), Components::count_fe_functions(from)> j_flux;
1554 SimpleMatrix<NumberType, Components::count_fe_functions(to), Components::count_fe_functions(from)> j_source;
1555 for (const auto &q_index : q_indices) {
1556 const auto &x_q = q_points[q_index];
1557 model.template jacobian_flux_source<from, to>(j_flux, j_source, x_q, solution[from][q_index]);
1558
1559 for (uint i = 0; i < to_n_dofs; ++i) {
1560 const auto component_i = fe_v[to]->get_fe().system_to_component_index(i).first;
1561 for (uint j = 0; j < from_n_dofs; ++j) {
1562 const auto component_j = fe_v[from]->get_fe().system_to_component_index(j).first;
1563 copy_data.cell_jacobian(i, j) +=
1564 JxW[q_index] *
1565 fe_v[from]->shape_value_component(j, q_index, component_j) * // weight * dx * phi_j(x_q)
1566 (-scalar_product(fe_v[to]->shape_grad_component(i, q_index, component_i),
1567 j_flux(component_i, component_j)) // -dphi_i(x_q) * jflux(x_q, u_q)
1568 + fe_v[to]->shape_value_component(i, q_index, component_i) *
1569 j_source(component_i, component_j)); // -phi_i(x_q) * jsource(x_q, u_q)
1570 }
1571 }
1572 }
1573 };
1574 const auto boundary_worker = [&](const Iterator &t_cell, const uint &face_no, Scratch &scratch_data,
1575 CopyData &copy_data) {
1576 const auto &fe_fv = scratch_data.new_fe_boundary_values(t_cell, face_no);
1577 const uint to_n_dofs = fe_fv[to]->get_fe().n_dofs_per_cell();
1578 const uint from_n_dofs = fe_fv[from]->get_fe().n_dofs_per_cell();
1579
1580 const auto &JxW = fe_fv[to]->get_JxW_values();
1581 const auto &q_points = fe_fv[to]->get_quadrature_points();
1582 const auto &q_indices = fe_fv[to]->quadrature_point_indices();
1583 const std::vector<Tensor<1, dim>> &normals = fe_fv[to]->get_normal_vectors();
1584 auto &solution = scratch_data.solution;
1585
1586 fe_fv[from]->get_function_values(solution_global, solution[from]);
1587
1588 SimpleMatrix<Tensor<1, dim>, Components::count_fe_functions(to), Components::count_fe_functions(from)>
1589 j_boundary_numflux;
1590 for (const auto &q_index : q_indices) {
1591 const auto &x_q = q_points[q_index];
1592 model.template jacobian_boundary_numflux<from, to>(j_boundary_numflux, normals[q_index], x_q,
1593 solution[from][q_index]);
1594
1595 for (uint i = 0; i < to_n_dofs; ++i) {
1596 const auto component_i = fe_fv[to]->get_fe().system_to_component_index(i).first;
1597 for (uint j = 0; j < from_n_dofs; ++j) {
1598 const auto component_j = fe_fv[from]->get_fe().system_to_component_index(j).first;
1599 copy_data.cell_jacobian(i, j) +=
1600 JxW[q_index] *
1601 fe_fv[from]->shape_value_component(j, q_index, component_j) * // weight * dx * phi_j(x_q)
1602 (fe_fv[to]->shape_value_component(i, q_index, component_i) *
1603 scalar_product(j_boundary_numflux(component_i, component_j),
1604 normals[q_index])); // phi_i(x_q) * j_numflux(x_q, u_q) * n(x_q)
1605 }
1606 }
1607 }
1608 };
1609 const auto face_worker = [&](const Iterator &t_cell, const uint &f, const uint &sf, const Iterator &t_ncell,
1610 const uint &nf, const unsigned int &nsf, Scratch &scratch_data,
1611 CopyData &copy_data) {
1612 const auto &fe_iv = scratch_data.new_fe_interface_values(t_cell, f, sf, t_ncell, nf, nsf);
1613 const uint to_n_dofs = fe_iv[to]->n_current_interface_dofs();
1614 const uint from_n_dofs = fe_iv[from]->n_current_interface_dofs();
1615 auto &copy_data_face = copy_data.new_face_data(*(fe_iv[from]), *(fe_iv[to]));
1616
1617 const auto &JxW = fe_iv[to]->get_JxW_values();
1618 const auto &q_points = fe_iv[to]->get_quadrature_points();
1619 const auto &q_indices = fe_iv[to]->quadrature_point_indices();
1620 const std::vector<Tensor<1, dim>> &normals = fe_iv[to]->get_normal_vectors();
1621 auto &solution = scratch_data.solution_interface;
1622
1623 fe_iv[from]->get_fe_face_values(0).get_function_values(solution_global, solution[0][from]);
1624 fe_iv[from]->get_fe_face_values(1).get_function_values(solution_global, solution[1][from]);
1625
1626 array<SimpleMatrix<Tensor<1, dim>, Components::count_fe_functions(to), Components::count_fe_functions(from)>,
1627 2>
1628 j_numflux;
1629 for (const auto &q_index : q_indices) {
1630 const auto &x_q = q_points[q_index];
1631 model.template jacobian_numflux<from, to>(j_numflux, normals[q_index], x_q, solution[0][from][q_index],
1632 solution[1][from][q_index]);
1633
1634 for (uint i = 0; i < to_n_dofs; ++i) {
1635 const auto &cd_i = fe_iv[to]->interface_dof_to_dof_indices(i);
1636 const uint face_no_i = cd_i[0] == numbers::invalid_unsigned_int ? 1 : 0;
1637 const auto &component_i = fe_iv[to]->get_fe().system_to_component_index(cd_i[face_no_i]).first;
1638 for (uint j = 0; j < from_n_dofs; ++j) {
1639 const auto &cd_j = fe_iv[from]->interface_dof_to_dof_indices(j);
1640 const uint face_no_j = cd_j[0] == numbers::invalid_unsigned_int ? 1 : 0;
1641 const auto &component_j = fe_iv[from]->get_fe().system_to_component_index(cd_j[face_no_j]).first;
1642
1643 copy_data_face.cell_jacobian(i, j) +=
1644 JxW[q_index] *
1645 fe_iv[from]->get_fe_face_values(face_no_j).shape_value_component(
1646 cd_j[face_no_j], q_index, component_j) * // weight * dx * phi_j(x_q)
1647 (fe_iv[to]->jump_in_shape_values(i, q_index, component_i) *
1648 scalar_product(j_numflux[face_no_j](component_i, component_j),
1649 normals[q_index])); // [[phi_i(x_q)]] * j_numflux(x_q, u_q)
1650 }
1651 }
1652 }
1653 };
1654 const auto copier = [&](const CopyData &c) {
1655 ldg_jacobian_tmp.add(c.local_dof_indices_to, c.local_dof_indices_from, c.cell_jacobian);
1656 for (auto &cdf : c.face_data)
1657 ldg_jacobian_tmp.add(cdf.joint_dof_indices_to, cdf.joint_dof_indices_from, cdf.cell_jacobian);
1658 };
1659
1660 const MeshWorker::AssembleFlags assemble_flags = MeshWorker::assemble_own_cells |
1661 MeshWorker::assemble_boundary_faces |
1662 MeshWorker::assemble_own_interior_faces_once;
1663 Scratch scratch_data(mapping, dof_handler_list, quadrature, quadrature_face);
1664 CopyData copy_data;
1665
1666 ldg_jacobian_tmp = 0;
1667 ldg_jacobian = 0;
1668 MeshWorker::mesh_loop(dof_handler.begin_active(), dof_handler.end(), cell_worker, copier, scratch_data,
1669 copy_data, assemble_flags, boundary_worker, face_worker, threads, batch_size);
1670 for (const auto &c : model.get_components().ldg_couplings(to, from))
1671 component_mass_matrix_inverse.mmult(ldg_jacobian.block(c[0], c[1]), ldg_jacobian_tmp.block(c[0], c[1]),
1672 Vector<NumberType>(), false);
1673 }
1674
1683 void build_ldg_sparsity(BlockSparsityPattern &sparsity_pattern, const DoFHandler<dim> &to_dofh,
1684 const DoFHandler<dim> &from_dofh, const int stencil = 1,
1685 bool add_extractor_dofs = false) const
1686 {
1687 const auto &triangulation = discretization.get_triangulation();
1688 auto to_dofs_per_component = DoFTools::count_dofs_per_fe_component(to_dofh);
1689 auto from_dofs_per_component = DoFTools::count_dofs_per_fe_component(from_dofh);
1690 auto to_n_fe = to_dofs_per_component.size();
1691 auto from_n_fe = from_dofs_per_component.size();
1692 for (uint j = 1; j < from_dofs_per_component.size(); ++j)
1693 if (from_dofs_per_component[j] != from_dofs_per_component[0])
1694 throw std::runtime_error("For LDG the FE basis of all systems must be equal!");
1695 for (uint j = 1; j < to_dofs_per_component.size(); ++j)
1696 if (to_dofs_per_component[j] != to_dofs_per_component[0])
1697 throw std::runtime_error("For LDG the FE basis of all systems must be equal!");
1698
1699 BlockDynamicSparsityPattern dsp(to_n_fe, from_n_fe);
1700 for (uint i = 0; i < to_n_fe; ++i)
1701 for (uint j = 0; j < from_n_fe; ++j)
1702 dsp.block(i, j).reinit(to_dofs_per_component[i], from_dofs_per_component[j]);
1703 dsp.collect_sizes();
1704
1705 const auto to_dofs_per_cell = to_dofh.get_fe().dofs_per_cell;
1706 const auto from_dofs_per_cell = from_dofh.get_fe().dofs_per_cell;
1707
1708 for (const auto &t_cell : triangulation.active_cell_iterators()) {
1709 std::vector<types::global_dof_index> to_dofs(to_dofs_per_cell);
1710 std::vector<types::global_dof_index> from_dofs(from_dofs_per_cell);
1711 const auto to_cell = typename DoFHandler<dim>::active_cell_iterator(
1712 &to_dofh.get_triangulation(), t_cell->level(), t_cell->index(), &to_dofh);
1713 const auto from_cell = typename DoFHandler<dim>::active_cell_iterator(
1714 &from_dofh.get_triangulation(), t_cell->level(), t_cell->index(), &from_dofh);
1715 to_cell->get_dof_indices(to_dofs);
1716 from_cell->get_dof_indices(from_dofs);
1717
1718 std::function<void(decltype(from_cell) &, const int)> add_all_neighbor_dofs = [&](const auto &from_cell,
1719 const int stencil_level) {
1720 for (const auto face_no : from_cell->face_indices()) {
1721 const auto face = from_cell->face(face_no);
1722 if (!face->at_boundary()) {
1723 auto neighbor_cell = from_cell->neighbor(face_no);
1724
1725 if (dim == 1)
1726 while (neighbor_cell->has_children())
1727 neighbor_cell = neighbor_cell->child(face_no == 0 ? 1 : 0);
1728
1729 // add all children
1730 else if (neighbor_cell->has_children()) {
1731 throw std::runtime_error("not yet implemented lol");
1732 }
1733
1734 if (!neighbor_cell->is_active()) continue;
1735
1736 std::vector<types::global_dof_index> tmp(from_dofs_per_cell);
1737 neighbor_cell->get_dof_indices(tmp);
1738
1739 from_dofs.insert(std::end(from_dofs), std::begin(tmp), std::end(tmp));
1740
1741 if (stencil_level < stencil) add_all_neighbor_dofs(neighbor_cell, stencil_level + 1);
1742 }
1743 }
1744 };
1745
1746 add_all_neighbor_dofs(from_cell, 1);
1747
1748 for (const auto i : to_dofs)
1749 for (const auto j : from_dofs)
1750 dsp.add(i, j);
1751 }
1752
1753 if (add_extractor_dofs)
1754 for (uint row = 0; row < dsp.n_rows(); ++row)
1755 for (const auto &col : extractor_dof_indices)
1756 dsp.add(row, col);
1757
1758 sparsity_pattern.copy_from(dsp);
1759 }
1760
1767 void build_inverse(const SparseMatrix<NumberType> &in, SparseMatrix<NumberType> &out) const
1768 {
1769 GrowingVectorMemory<Vector<NumberType>> mem;
1770 SparseDirectUMFPACK inverse;
1771 inverse.initialize(in);
1772 // out is m x n
1773 // we go row-wise, i.e. we keep one n fixed and insert a row
1774 tbb::parallel_for(tbb::blocked_range<int>(0, out.n()), [&](tbb::blocked_range<int> r) {
1775 typename VectorMemory<Vector<NumberType>>::Pointer tmp(mem);
1776 tmp->reinit(out.m());
1777 for (int n = r.begin(); n < r.end(); ++n) {
1778 *tmp = 0;
1779 (*tmp)[n] = 1.;
1780 inverse.solve(*tmp);
1781 for (auto it = out.begin(n); it != out.end(n); ++it)
1782 it->value() = (*tmp)[it->column()];
1783 }
1784 });
1785 }
1786
1787 protected:
1788 constexpr static int nothing = 0;
1789 using Base::EoM;
1790 using Base::EoM_cell;
1791 using Base::extractor_jacobian_u;
1792 using Base::old_EoM_cell;
1793
1794 void readouts(DataOutput<dim, VectorType> &data_out, const VectorType &solution_global,
1795 const VectorType &variables) const
1796 {
1797 auto helper = [&](auto EoMfun, auto outputter) {
1798 auto EoM_cell = this->EoM_cell;
1799 auto EoM = get_EoM_point(
1800 EoM_cell, solution_global, dof_handler, mapping, EoMfun, [&](const auto &p, const auto &) { return p; },
1801 EoM_abs_tol, EoM_max_iter);
1802 auto EoM_unit = mapping.transform_real_to_unit_cell(EoM_cell, EoM);
1803
1804 using t_Iterator = typename Triangulation<dim>::active_cell_iterator;
1805
1806 std::vector<std::shared_ptr<FEValues<dim>>> fe_v;
1807 for (uint k = 0; k < Components::count_fe_subsystems(); ++k) {
1808 fe_v.emplace_back(std::make_shared<FEValues<dim>>(
1809 mapping, discretization.get_fe(k), EoM_unit,
1810 update_values | update_gradients | update_quadrature_points | update_JxW_values | update_hessians));
1811
1812 auto cell = dof_handler_list[k]->begin_active();
1813 cell->copy_from(*t_Iterator(EoM_cell));
1814 fe_v[k]->reinit(cell);
1815 }
1816
1817 std::vector<std::vector<Vector<NumberType>>> solutions;
1818 for (uint k = 0; k < Components::count_fe_subsystems(); ++k) {
1819 solutions.push_back({Vector<NumberType>(Components::count_fe_functions(k))});
1820 if (k == 0)
1821 fe_v[0]->get_function_values(solution_global, solutions[k]);
1822 else
1823 fe_v[k]->get_function_values(sol_vector[k], solutions[k]);
1824 }
1825 std::vector<Vector<NumberType>> solutions_vector;
1826 for (uint k = 0; k < Components::count_fe_subsystems(); ++k)
1827 solutions_vector.push_back(solutions[k][0]);
1828
1829 std::array<NumberType, Components::count_extractors()> __extracted_data{{}};
1830 if constexpr (Components::count_extractors() > 0)
1831 extract(__extracted_data, solution_global, variables, true, false, false);
1832 const auto &extracted_data = __extracted_data;
1833
1834 std::vector<std::vector<Tensor<1, dim, NumberType>>> solution_grad{
1835 std::vector<Tensor<1, dim, NumberType>>(Components::count_fe_functions())};
1836 std::vector<std::vector<Tensor<2, dim, NumberType>>> solution_hess{
1837 std::vector<Tensor<2, dim, NumberType>>(Components::count_fe_functions())};
1838 fe_v[0]->get_function_gradients(solution_global, solution_grad);
1839 fe_v[0]->get_function_hessians(solution_global, solution_hess);
1840
1841 auto solution_tuple = std::tuple_cat(vector_to_tuple<Components::count_fe_subsystems()>(solutions_vector),
1842 std::tie(solution_grad[0], solution_hess[0], extracted_data, variables));
1843
1844 outputter(data_out, EoM, fe_more_conv(solution_tuple));
1845 };
1846 model.readouts_multiple(helper, data_out);
1847 }
1848
1849 void extract(std::array<NumberType, Components::count_extractors()> &data, const VectorType &solution_global,
1850 const VectorType &variables, bool search_EoM, bool set_EoM, bool postprocess) const
1851 {
1852 auto EoM = this->EoM;
1853 auto EoM_cell = this->EoM_cell;
1854 if (search_EoM || EoM_cell == *(dof_handler.active_cell_iterators().end()))
1855 EoM = get_EoM_point(
1856 EoM_cell, solution_global, dof_handler, mapping,
1857 [&](const auto &p, const auto &values) { return model.EoM(p, values); },
1858 [&](const auto &p, const auto &values) { return postprocess ? model.EoM_postprocess(p, values) : p; },
1859 EoM_abs_tol, EoM_max_iter);
1860 if (set_EoM) {
1861 this->EoM = EoM;
1862 this->EoM_cell = EoM_cell;
1863 }
1864 auto EoM_unit = mapping.transform_real_to_unit_cell(EoM_cell, EoM);
1865
1866 rebuild_ldg_vectors(solution_global);
1867
1868 using t_Iterator = typename Triangulation<dim>::active_cell_iterator;
1869
1870 std::vector<std::shared_ptr<FEValues<dim>>> fe_v;
1871 for (uint k = 0; k < Components::count_fe_subsystems(); ++k) {
1872 fe_v.emplace_back(std::make_shared<FEValues<dim>>(
1873 mapping, discretization.get_fe(k), EoM_unit,
1874 update_values | update_gradients | update_quadrature_points | update_JxW_values | update_hessians));
1875
1876 auto cell = dof_handler_list[k]->begin_active();
1877 cell->copy_from(*t_Iterator(EoM_cell));
1878 fe_v[k]->reinit(cell);
1879 }
1880
1881 std::vector<std::vector<Vector<NumberType>>> solutions;
1882 for (uint k = 0; k < Components::count_fe_subsystems(); ++k) {
1883 solutions.push_back({Vector<NumberType>(Components::count_fe_functions(k))});
1884 if (k == 0)
1885 fe_v[0]->get_function_values(solution_global, solutions[k]);
1886 else
1887 fe_v[k]->get_function_values(sol_vector[k], solutions[k]);
1888 }
1889 std::vector<Vector<NumberType>> solutions_vector;
1890 for (uint k = 0; k < Components::count_fe_subsystems(); ++k)
1891 solutions_vector.push_back(solutions[k][0]);
1892
1893 std::vector<std::vector<Tensor<1, dim, NumberType>>> solution_grad{
1894 std::vector<Tensor<1, dim, NumberType>>(Components::count_fe_functions())};
1895 std::vector<std::vector<Tensor<2, dim, NumberType>>> solution_hess{
1896 std::vector<Tensor<2, dim, NumberType>>(Components::count_fe_functions())};
1897 fe_v[0]->get_function_gradients(solution_global, solution_grad);
1898 fe_v[0]->get_function_hessians(solution_global, solution_hess);
1899
1900 auto solution_tuple = std::tuple_cat(vector_to_tuple<Components::count_fe_subsystems()>(solutions_vector),
1901 std::tie(solution_grad[0], solution_hess[0], this->nothing, variables));
1902
1903 model.extract(data, EoM, fe_more_conv(solution_tuple));
1904 }
1905
1906 bool jacobian_extractors(FullMatrix<NumberType> &extractor_jacobian, const VectorType &solution_global,
1907 const VectorType &variables)
1908 {
1909 if (extractor_jacobian_u.m() != Components::count_extractors() ||
1910 extractor_jacobian_u.n() != Components::count_fe_functions())
1911 extractor_jacobian_u =
1912 FullMatrix<NumberType>(Components::count_extractors(), Components::count_fe_functions());
1913
1914 EoM = get_EoM_point(
1915 EoM_cell, solution_global, dof_handler, mapping,
1916 [&](const auto &p, const auto &values) { return model.EoM(p, values); },
1917 [&](const auto &p, const auto &values) { return model.EoM_postprocess(p, values); }, EoM_abs_tol,
1918 EoM_max_iter);
1919 auto EoM_unit = mapping.transform_real_to_unit_cell(EoM_cell, EoM);
1920 bool new_cell = (old_EoM_cell != EoM_cell);
1921 old_EoM_cell = EoM_cell;
1922
1923 using t_Iterator = typename Triangulation<dim>::active_cell_iterator;
1924
1925 std::vector<std::shared_ptr<FEValues<dim>>> fe_v;
1926 for (uint k = 0; k < Components::count_fe_subsystems(); ++k) {
1927 fe_v.emplace_back(std::make_shared<FEValues<dim>>(
1928 mapping, discretization.get_fe(k), EoM_unit,
1929 update_values | update_gradients | update_quadrature_points | update_JxW_values | update_hessians));
1930
1931 auto cell = dof_handler_list[k]->begin_active();
1932 cell->copy_from(*t_Iterator(EoM_cell));
1933 fe_v[k]->reinit(cell);
1934 }
1935
1936 const uint n_dofs = fe_v[0]->get_fe().n_dofs_per_cell();
1937 if (new_cell) {
1938 // spdlog::get("log")->info("FEM: Rebuilding the jacobian sparsity pattern");
1939 extractor_dof_indices.resize(n_dofs);
1940 EoM_cell->get_dof_indices(extractor_dof_indices);
1941 rebuild_jacobian_sparsity();
1942 }
1943
1944 std::vector<std::vector<Vector<NumberType>>> solutions;
1945 for (uint k = 0; k < Components::count_fe_subsystems(); ++k) {
1946 solutions.push_back({Vector<NumberType>(Components::count_fe_functions(k))});
1947 if (k == 0)
1948 fe_v[0]->get_function_values(solution_global, solutions[k]);
1949 else
1950 fe_v[k]->get_function_values(sol_vector[k], solutions[k]);
1951 }
1952 std::vector<Vector<NumberType>> solutions_vector;
1953 for (uint k = 0; k < Components::count_fe_subsystems(); ++k)
1954 solutions_vector.push_back(solutions[k][0]);
1955
1956 std::vector<std::vector<Tensor<1, dim, NumberType>>> solution_grad{
1957 std::vector<Tensor<1, dim, NumberType>>(Components::count_fe_functions())};
1958 std::vector<std::vector<Tensor<2, dim, NumberType>>> solution_hess{
1959 std::vector<Tensor<2, dim, NumberType>>(Components::count_fe_functions())};
1960 fe_v[0]->get_function_gradients(solution_global, solution_grad);
1961 fe_v[0]->get_function_hessians(solution_global, solution_hess);
1962
1963 auto solution_tuple = std::tuple_cat(vector_to_tuple<Components::count_fe_subsystems()>(solutions_vector),
1964 std::tie(solution_grad[0], solution_hess[0], this->nothing, variables));
1965
1966 extractor_jacobian_u = 0;
1967 model.template jacobian_extractors<0>(extractor_jacobian_u, EoM, fe_more_conv(solution_tuple));
1968
1969 if (extractor_jacobian.m() != Components::count_extractors() || extractor_jacobian.n() != n_dofs)
1970 extractor_jacobian = FullMatrix<NumberType>(Components::count_extractors(), n_dofs);
1971
1972 for (uint e = 0; e < Components::count_extractors(); ++e)
1973 for (uint i = 0; i < n_dofs; ++i) {
1974 const auto component_i = fe_v[0]->get_fe().system_to_component_index(i).first;
1975 extractor_jacobian(e, i) =
1976 extractor_jacobian_u(e, component_i) * fe_v[0]->shape_value_component(i, 0, component_i);
1977 }
1978
1979 return new_cell;
1980 }
1981
1982 using Base::timings_variable_jacobian;
1983 using Base::timings_variable_residual;
1984 template <typename... T> static constexpr auto v_tie(T &&...t)
1985 {
1986 return named_tuple<std::tuple<T &...>, StringSet<"variables", "extractors">>(std::tie(t...));
1987 }
1988
1989 template <typename... T> static constexpr auto e_tie(T &&...t)
1990 {
1991 return named_tuple<std::tuple<T &...>,
1992 StringSet<"fe_functions", "fe_derivatives", "fe_hessians", "extractors", "variables">>(
1993 std::tie(t...));
1994 }
1995
1996 virtual void residual_variables(VectorType &residual, const VectorType &variables,
1997 const VectorType &spatial_solution) override
1998 {
1999 Timer timer;
2000 std::array<NumberType, Components::count_extractors()> __extracted_data{{}};
2001 if constexpr (Components::count_extractors() > 0)
2002 extract(__extracted_data, spatial_solution, variables, true, false, false);
2003 const auto &extracted_data = __extracted_data;
2004 model.dt_variables(residual, v_tie(variables, extracted_data));
2005 Kokkos::fence();
2006 timings_variable_residual.push_back(timer.wall_time());
2007 }
2008
2009 virtual void jacobian_variables(FullMatrix<NumberType> &jacobian, const VectorType &variables,
2010 const VectorType &spatial_solution) override
2011 {
2012 Timer timer;
2013 std::array<NumberType, Components::count_extractors()> __extracted_data{{}};
2014 if constexpr (Components::count_extractors() > 0)
2015 extract(__extracted_data, spatial_solution, variables, true, false, false);
2016 const auto &extracted_data = __extracted_data;
2017 model.template jacobian_variables<0>(jacobian, v_tie(variables, extracted_data));
2018 Kokkos::fence();
2019 timings_variable_jacobian.push_back(timer.wall_time());
2020 }
2021 };
2022 } // namespace LDG
2023} // namespace DiFfRG
This is the general assembler interface for any kind of discretization. An assembler is responsible f...
Definition abstract_assembler.hh:39
Class to manage writing to files. FEM functions are written to vtk files and other data is written to...
Definition data_output.hh:20
FEOutput< dim, VectorType > & fe_output()
Returns a reference to the FEOutput object used to write FEM functions to .vtu files and ....
A wrapper around the boost json value class.
Definition json.hh:19
The LDG assembler that can be used for any LDG scheme, with as many levels as one wants.
Definition ldg.hh:397
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 ldg.hh:1849
double average_time_residual_assembly() const
Definition ldg.hh:1293
array< BlockVector< NumberType >, Components::count_fe_subsystems()> sol_vector
Definition ldg.hh:1327
void build_ldg_jacobian(const VectorType &solution_global, BlockSparseMatrix< NumberType > &ldg_jacobian, BlockSparseMatrix< NumberType > &ldg_jacobian_tmp) const
Build the LDG jacobian at level 'to', which takes information from level 'from'.
Definition ldg.hh:1532
const FiniteElement< dim > & fe
Definition ldg.hh:126
SparseMatrix< NumberType > component_mass_matrix_inverse
Definition ldg.hh:1340
BlockSparsityPattern sparsity_pattern_jacobian
Definition ldg.hh:1331
virtual void residual(VectorType &residual, const VectorType &solution_global, NumberType weight, const VectorType &solution_global_dot, NumberType weight_mass, const VectorType &variables=VectorType()) override
Construct the system residual, i.e. Res = grad(flux) - source.
Definition ldg.hh:730
virtual void jacobian_mass(BlockSparseMatrix< NumberType > &jacobian, const VectorType &solution_global, const VectorType &solution_global_dot, NumberType alpha=1., NumberType beta=1.) override
Definition ldg.hh:886
uint num_residuals() const
Definition ldg.hh:1301
array< BlockSparseMatrix< NumberType >, Components::count_fe_subsystems()> j_wg_tmp
Definition ldg.hh:1344
virtual const BlockSparseMatrix< NumberType > & get_mass_matrix() const override
Obtain the mass matrix.
Definition ldg.hh:665
void build_ldg_sparsity(BlockSparsityPattern &sparsity_pattern, const DoFHandler< dim > &to_dofh, const DoFHandler< dim > &from_dofh, const int stencil=1, bool add_extractor_dofs=false) const
Create a sparsity pattern for matrices between the DoFs of two DoFHandlers, with given stencil size.
Definition ldg.hh:1683
double average_time_jacobian_assembly() const
Definition ldg.hh:1303
void readouts(DataOutput< dim, VectorType > &data_out, const VectorType &solution_global, const VectorType &variables) const
Definition ldg.hh:1794
typename Discretization::NumberType NumberType
Definition ldg.hh:403
static constexpr auto v_tie(T &&...t)
Definition ldg.hh:1984
const Mapping< dim > & mapping
Definition ldg.hh:128
std::vector< double > timings_residual
Definition ldg.hh:1347
array< BlockSparsityPattern, Components::count_fe_subsystems()> sparsity_pattern_ug
Definition ldg.hh:1333
array< BlockVector< NumberType >, Components::count_fe_subsystems()> sol_vector_tmp
Definition ldg.hh:1328
static constexpr uint stencil
Definition ldg.hh:408
auto fe_more_conv(std::tuple< T &... > &t) const
Definition ldg.hh:425
virtual void residual_variables(VectorType &residual, const VectorType &variables, const VectorType &spatial_solution) override
Definition ldg.hh:1996
typename Discretization::VectorType VectorType
Definition ldg.hh:404
BlockSparsityPattern sparsity_pattern_mass
Definition ldg.hh:1332
virtual void reinit_vector(VectorType &vec) const override
Definition ldg.hh:463
virtual void jacobian_variables(FullMatrix< NumberType > &jacobian, const VectorType &variables, const VectorType &spatial_solution) override
Definition ldg.hh:2009
typename Discretization::Components Components
Definition ldg.hh:406
static constexpr auto e_tie(T &&...t)
Definition ldg.hh:1989
uint num_jacobians() const
Definition ldg.hh:1311
virtual void attach_data_output(DataOutput< dim, VectorType > &data_out, const VectorType &solution, const VectorType &variables, const VectorType &dt_solution=VectorType(), const VectorType &residual=VectorType()) override
Attach all intermediate (ldg) vectors to the data output.
Definition ldg.hh:471
virtual void mass(VectorType &residual, const VectorType &solution_global, const VectorType &solution_global_dot, NumberType weight) override
Construct the mass.
Definition ldg.hh:674
std::vector< const DoFHandler< dim > * > dof_handler_list
Definition ldg.hh:1325
auto fe_conv(std::tuple< T &... > &t) const
Definition ldg.hh:411
Model & model
Definition ldg.hh:125
QGauss< dim - 1 > quadrature_face
Definition ldg.hh:1321
array< BlockSparsityPattern, Components::count_fe_subsystems()> sparsity_pattern_wg
Definition ldg.hh:1335
void rebuild_ldg_jacobian(const VectorType &sol) const
Definition ldg.hh:1373
static constexpr uint dim
Definition ldg.hh:407
void log(const std::string logger)
Definition ldg.hh:1271
array< bool, Components::count_fe_subsystems()> ldg_matrix_built
Definition ldg.hh:1350
array< BlockSparseMatrix< NumberType >, Components::count_fe_subsystems()> j_ug
Definition ldg.hh:1341
virtual void reinit() override
Reinitialize the assembler. This is necessary if the mesh has changed, e.g. after a mesh refinement.
Definition ldg.hh:496
virtual void rebuild_jacobian_sparsity() override
Definition ldg.hh:572
auto ref_conv(std::tuple< T &... > &t) const
Definition ldg.hh:440
bool jacobian_extractors(FullMatrix< NumberType > &extractor_jacobian, const VectorType &solution_global, const VectorType &variables)
Definition ldg.hh:1906
void build_inverse(const SparseMatrix< NumberType > &in, SparseMatrix< NumberType > &out) const
Build the inverse of matrix in and save the result to out.
Definition ldg.hh:1767
Discretization_ Discretization
Definition ldg.hh:401
array< BlockSparsityPattern, Components::count_fe_subsystems()> sparsity_pattern_gu
Definition ldg.hh:1334
Discretization & discretization
Definition ldg.hh:124
uint batch_size
Definition ldg.hh:131
uint threads
Definition ldg.hh:130
array< BlockSparseMatrix< NumberType >, Components::count_fe_subsystems()> jacobian_tmp
Definition ldg.hh:1337
virtual const BlockSparsityPattern & get_sparsity_pattern_jacobian() const override
Obtain the sparsity pattern of the jacobian matrix.
Definition ldg.hh:661
Assembler(Discretization &discretization, Model &model, const JSONValue &json)
Definition ldg.hh:453
BlockSparseMatrix< NumberType > mass_matrix
Definition ldg.hh:1339
virtual void jacobian(BlockSparseMatrix< NumberType > &jacobian, const VectorType &solution_global, NumberType weight, const VectorType &solution_global_dot, NumberType alpha, NumberType beta, const VectorType &variables=VectorType()) override
Construct the system jacobian, i.e. dRes/du.
Definition ldg.hh:954
array< BlockSparseMatrix< NumberType >, Components::count_fe_subsystems()> j_wg
Definition ldg.hh:1343
std::vector< types::global_dof_index > extractor_dof_indices
Definition ldg.hh:142
uint num_reinits() const
Definition ldg.hh:1291
void rebuild_ldg_vectors(const VectorType &sol) const
Definition ldg.hh:1357
array< bool, Components::count_fe_subsystems()> jacobian_tmp_built
Definition ldg.hh:1351
QGauss< dim > quadrature
Definition ldg.hh:1320
std::vector< double > timings_reinit
Definition ldg.hh:1346
array< Vector< NumberType >, Components::count_fe_subsystems()> sol_vector_vec_tmp
Definition ldg.hh:1329
std::vector< double > timings_jacobian
Definition ldg.hh:1348
Model_ Model
Definition ldg.hh:402
double average_time_reinit() const
Definition ldg.hh:1283
array< BlockSparseMatrix< NumberType >, Components::count_fe_subsystems()> j_gu
Definition ldg.hh:1342
void build_ldg_vector(const VectorType &solution_global, VectorTypeldg &ldg_vector, VectorTypeldg &ldg_vector_tmp) const
Build the LDG vector at level 'to', which takes information from level 'from'.
Definition ldg.hh:1401
virtual void refinement_indicator(Vector< double > &indicator, const VectorType &solution_global) override
Definition ldg.hh:579
const DoFHandler< dim > & dof_handler
Definition ldg.hh:127
NumberType_ NumberType
Definition ldg.hh:34
Components_ Components
Definition ldg.hh:33
static constexpr uint dim
Definition ldg.hh:38
Vector< NumberType > VectorType
Definition ldg.hh:35
Definition ldg.hh:41
FullMatrix< NumberType > extractor_jacobian_du
Definition ldg.hh:140
const double EoM_abs_tol
Definition ldg.hh:135
const FiniteElement< dim > & fe
Definition ldg.hh:126
uint num_variable_jacobians() const
Definition ldg.hh:121
FullMatrix< NumberType > extractor_jacobian_u
Definition ldg.hh:139
FullMatrix< NumberType > extractor_jacobian_ddu
Definition ldg.hh:141
typename Discretization::NumberType NumberType
Definition ldg.hh:45
auto & get_discretization()
Definition ldg.hh:71
double average_time_variable_residual_assembly()
Definition ldg.hh:103
const Mapping< dim > & mapping
Definition ldg.hh:128
static constexpr uint dim
Definition ldg.hh:49
FullMatrix< NumberType > extractor_jacobian
Definition ldg.hh:138
DoFHandler< dim >::cell_iterator old_EoM_cell
Definition ldg.hh:134
Discretization_ Discretization
Definition ldg.hh:43
typename Discretization::VectorType VectorType
Definition ldg.hh:46
virtual void set_time(double t) override
Set the current time. The assembler should usually just forward this to the numerical model.
Definition ldg.hh:99
const auto & get_discretization() const
Definition ldg.hh:70
Model & model
Definition ldg.hh:125
virtual void refinement_indicator(Vector< double > &, const VectorType &)=0
virtual void reinit() override
Reinitialize the assembler. This is necessary if the mesh has changed, e.g. after a mesh refinement.
Definition ldg.hh:73
double average_time_variable_jacobian_assembly()
Definition ldg.hh:113
const uint EoM_max_iter
Definition ldg.hh:136
virtual IndexSet get_differential_indices() const override
Obtain the dofs which contain time derivatives.
Definition ldg.hh:64
std::vector< double > timings_variable_residual
Definition ldg.hh:144
std::vector< double > timings_variable_jacobian
Definition ldg.hh:145
typename Discretization::Components Components
Definition ldg.hh:48
Discretization & discretization
Definition ldg.hh:124
uint batch_size
Definition ldg.hh:131
uint threads
Definition ldg.hh:130
LDGAssemblerBase(Discretization &discretization, Model &model, const JSONValue &json)
Definition ldg.hh:51
Point< dim > EoM
Definition ldg.hh:137
DoFHandler< dim >::cell_iterator EoM_cell
Definition ldg.hh:133
std::vector< types::global_dof_index > extractor_dof_indices
Definition ldg.hh:142
uint num_variable_residuals() const
Definition ldg.hh:111
Model_ Model
Definition ldg.hh:44
virtual void rebuild_jacobian_sparsity()=0
const DoFHandler< dim > & dof_handler
Definition ldg.hh:127
Definition quadrature.hh:56
size_t size() const
A simple NxM-matrix class, which is used for cell-wise Jacobians.
Definition tuples.hh:156
Definition complex_math.hh:10
dealii::Point< dim > get_EoM_point(typename dealii::DoFHandler< dim >::cell_iterator &EoM_cell, const VectorType &sol, const dealii::DoFHandler< dim > &dof_handler, const dealii::Mapping< dim > &mapping, const EoMFUN &get_EoM, const EoMPFUN &EoM_postprocess=[](const auto &p, const auto &values) { return p;}, const double EoM_abs_tol=1e-5, const uint max_iter=100)
Get the EoM point for a given solution and model.
Definition eom.hh:674
auto local_sol_q(const std::array< T, N > &a, uint q_index)
Definition tuples.hh:278
auto jacobian_tuple()
Definition tuples.hh:288
bool KOKKOS_INLINE_FUNCTION is_close(T1 a, T2 b, T3 eps_)
Function to evaluate whether two floats are equal to numerical precision. Tests for both relative and...
Definition math.hh:168
auto vector_to_tuple(const std::vector< T > &v)
Definition tuples.hh:217
constexpr void constexpr_for(F &&f)
A compile-time for loop, which calls the lambda f of signature void(integer) for each index.
Definition utils.hh:31
auto jacobian_2_tuple()
Definition tuples.hh:299
unsigned int uint
Definition utils.hh:24
std::array< uint, 2 > cell_indices
Definition ldg.hh:380
std::array< double, 2 > values
Definition ldg.hh:381
uint cell_index
Definition ldg.hh:385
double value
Definition ldg.hh:384
std::vector< CopyFaceData_I > face_data
Definition ldg.hh:383
FullMatrix< NumberType > cell_jacobian
Definition ldg.hh:299
std::vector< types::global_dof_index > joint_dof_indices_from
Definition ldg.hh:300
std::vector< types::global_dof_index > joint_dof_indices_to
Definition ldg.hh:301
FullMatrix< NumberType > extractor_cell_jacobian
Definition ldg.hh:334
array< FullMatrix< NumberType >, n_fe_subsystems > cell_jacobian
Definition ldg.hh:333
array< vector< types::global_dof_index >, n_fe_subsystems > joint_dof_indices
Definition ldg.hh:335
void reinit(const array< Iterator, n_fe_subsystems > &cell, const uint n_extractors)
Definition ldg.hh:346
CopyDataFace_J & new_face_data(const array< unique_ptr< FEInterfaceValues< dim > >, n_fe_subsystems > &fe_iv, const uint n_extractors)
Definition ldg.hh:363
FullMatrix< NumberType > cell_mass_jacobian
Definition ldg.hh:339
array< FullMatrix< NumberType >, n_fe_subsystems > cell_jacobian
Definition ldg.hh:338
vector< CopyDataFace_J > face_data
Definition ldg.hh:342
FullMatrix< NumberType > extractor_cell_jacobian
Definition ldg.hh:340
array< vector< types::global_dof_index >, n_fe_subsystems > local_dof_indices
Definition ldg.hh:341
uint dofs_per_cell
Definition ldg.hh:344
CopyDataFace_J & new_face_data(const FEInterfaceValues< dim > &fe_iv_from, const FEInterfaceValues< dim > &fe_iv_to)
Definition ldg.hh:320
std::vector< types::global_dof_index > local_dof_indices_to
Definition ldg.hh:306
FullMatrix< NumberType > cell_jacobian
Definition ldg.hh:304
std::vector< CopyDataFace_J > face_data
Definition ldg.hh:307
std::vector< types::global_dof_index > local_dof_indices_from
Definition ldg.hh:305
void reinit(const Iterator &cell_from, const Iterator &cell_to, uint dofs_per_cell_from, uint dofs_per_cell_to)
Definition ldg.hh:310
Vector< NumberType > cell_residual
Definition ldg.hh:268
std::vector< types::global_dof_index > joint_dof_indices
Definition ldg.hh:269
std::vector< CopyDataFace_R > face_data
Definition ldg.hh:275
void reinit(const Iterator &cell, uint dofs_per_cell)
Definition ldg.hh:277
Vector< NumberType > cell_mass
Definition ldg.hh:273
std::vector< types::global_dof_index > local_dof_indices
Definition ldg.hh:274
Vector< NumberType > cell_residual
Definition ldg.hh:272
CopyDataFace_R & new_face_data(const FEInterfaceValues< dim > &fe_iv)
Definition ldg.hh:287
Class to hold data for each assembly thread, i.e. FEValues for cells, interfaces, as well as pre-allo...
Definition ldg.hh:154
array< uint, n_fe_subsystems > n_components
Definition ldg.hh:251
typename Discretization::NumberType NumberType
Definition ldg.hh:156
vector< Vector< NumberType > > solution_dot
Definition ldg.hh:262
static constexpr uint n_fe_subsystems
Definition ldg.hh:157
const auto & new_fe_values(const t_Iterator &t_cell)
Definition ldg.hh:224
static constexpr uint dim
Definition ldg.hh:155
array< Iterator, n_fe_subsystems > cell
Definition ldg.hh:252
array< unique_ptr< FEFaceValues< dim > >, n_fe_subsystems > fe_boundary_values
Definition ldg.hh:257
typename Triangulation< dim >::active_cell_iterator t_Iterator
Definition ldg.hh:159
ScratchData(const ScratchData< Discretization > &scratch_data)
Definition ldg.hh:196
typename DoFHandler< dim >::active_cell_iterator Iterator
Definition ldg.hh:158
array< unique_ptr< FEInterfaceValues< dim > >, n_fe_subsystems > fe_interface_values
Definition ldg.hh:256
array< array< vector< Vector< NumberType > >, n_fe_subsystems >, 2 > solution_interface
Definition ldg.hh:263
ScratchData(const Mapping< dim > &mapping, const vector< const DoFHandler< dim > * > &dofh, const Quadrature< dim > &quadrature, const Quadrature< dim - 1 > &quadrature_face, const UpdateFlags update_flags=update_values|update_gradients|update_quadrature_points|update_JxW_values, const UpdateFlags interface_update_flags=update_values|update_gradients|update_quadrature_points|update_JxW_values|update_normal_vectors)
Definition ldg.hh:161
const auto & new_fe_interface_values(const t_Iterator &t_cell, uint f, uint sf, const t_Iterator &t_ncell, uint nf, unsigned int nsf)
Definition ldg.hh:232
array< Iterator, n_fe_subsystems > ncell
Definition ldg.hh:253
array< std::vector< uint >, n_fe_subsystems > comp
Definition ldg.hh:259
array< unique_ptr< FEValues< dim > >, n_fe_subsystems > fe_values
Definition ldg.hh:255
array< vector< Vector< NumberType > >, n_fe_subsystems > solution
Definition ldg.hh:261
const auto & new_fe_boundary_values(const t_Iterator &t_cell, uint face_no)
Definition ldg.hh:242
Definition tuples.hh:34
A class to store a tuple with elements that can be accessed by name. The names are stored as FixedStr...
Definition tuples.hh:56