/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
Discretization Framework for functional Renormalization Group flows
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
34
35namespace DiFfRG
36{
37 namespace LDG
38 {
39 using namespace dealii;
40 using std::array, std::vector, std::unique_ptr;
41
42 template <typename Discretization_, typename Model_>
43 class LDGAssemblerBase : public AbstractAssembler<typename Discretization_::VectorType,
44 typename Discretization_::SparseMatrixType, Discretization_::dim>
45 {
46 public:
47 using Discretization = Discretization_;
48 using Model = Model_;
52
54 static constexpr uint dim = Discretization::dim;
57 fe(discretization.get_fe()), dof_handler(discretization.get_dof_handler()),
59 EoM_cell(*(dof_handler.active_cell_iterators().end())),
60 old_EoM_cell(*(dof_handler.active_cell_iterators().end())),
61 old_extractor_cell(*(dof_handler.active_cell_iterators().end())),
62 EoM_config(DiFfRG::internal::resolve_eom_config(dof_handler, Config::EoMConfig(config)))
63 {
64 // reinit() refreshes this, but a derived assembler is not obliged to call it before its
65 // first mesh_loop, and an unset schedule would be a zero queue length.
67 }
68
69 virtual IndexSet get_differential_indices() const override
70 {
71 ComponentMask component_mask(model.template differential_components<dim>());
72 return DoFTools::extract_dofs(dof_handler, component_mask);
73 }
74
75 const auto &get_discretization() const { return discretization; }
77
78 virtual void reinit() override
79 {
81 const AffineConstraintContext<Components, dim> context(metadata);
82
83 auto &constraints = discretization.get_constraints();
84 constraints.clear();
85 DoFTools::make_hanging_node_constraints(dof_handler, constraints);
87 constraints.close();
88
90 }
91
92 virtual void rebuild_jacobian_sparsity() = 0;
93
94 virtual void set_time(double t) override { model.set_time(t); }
95
96 virtual void refinement_indicator(Vector<double> & /*indicator*/, const VectorType & /*solution*/) = 0;
97
99 {
100 double t = 0.;
101 double n = timings_variable_residual.size();
102 for (const auto &t_ : timings_variable_residual)
103 t += t_ / n;
104 return t;
105 }
107
109 {
110 double t = 0.;
111 double n = timings_variable_jacobian.size();
112 for (const auto &t_ : timings_variable_jacobian)
113 t += t_ / n;
114 return t;
115 }
117
118 protected:
122 const FiniteElement<dim> &fe;
123 const DoFHandler<dim> &dof_handler;
124 const Mapping<dim> &mapping;
125
127 AssemblySchedule schedule_for(const double cost_ns) const
128 {
130 }
131
134 {
136 const bool unchanged = n_owned == n_owned_cells;
137 n_owned_cells = n_owned;
138 if (unchanged) return;
139
140 const uint threads = DiFfRG::n_threads();
141 const auto cheap = schedule_for(assembly_cost::local_fe);
142 const auto integral = schedule_for(assembly_cost::momentum_integral);
143 report_port.info("FEM: Assembling {} cells on {} threads -- {}x{} workers/cells for a cheap cell loop, "
144 "{}x{} for an integral one.",
145 n_owned_cells, threads, cheap.queue_length, cheap.chunk_size, integral.queue_length,
146 integral.chunk_size);
147 }
148
150 auto extractor_raw_potential(const VectorType &solution_global) const
151 {
152 if constexpr (Model::extract_uses_potential)
154 solution_global, dof_handler, mapping,
155 [&](const auto &p, const auto &values) { return model.raw_potential_gradient(p, values); }, EoM_config,
157 else
158 return UnusedPotential{};
159 }
160
162 std::pair<Point<dim>, typename DoFHandler<dim>::cell_iterator>
163 resolve_extractor_point(const Point<dim> &EoM_point, const typename DoFHandler<dim>::cell_iterator &EoM_cell_,
164 [[maybe_unused]] const VectorType &solution_global) const
165 {
167 const auto sample = make_solution_sample(solution_global, dof_handler, mapping);
168 const auto point = model.template extractor_point<dim, NumberType>(EoM_point, sample);
169 if (point == EoM_point) return {EoM_point, EoM_cell_};
170 return {point, GridTools::find_active_cell_around_point(dof_handler, point)};
171 } else
172 return {EoM_point, EoM_cell_};
173 }
174
178
179 mutable typename DoFHandler<dim>::cell_iterator EoM_cell;
180 typename DoFHandler<dim>::cell_iterator old_EoM_cell;
182 typename DoFHandler<dim>::cell_iterator old_extractor_cell;
184 mutable Point<dim> EoM;
185 mutable std::optional<Point<dim>> EoM_minimum_guess;
188 FullMatrix<NumberType> extractor_jacobian;
189 FullMatrix<NumberType> extractor_jacobian_u;
190 FullMatrix<NumberType> extractor_jacobian_du;
191 FullMatrix<NumberType> extractor_jacobian_ddu;
192 std::vector<types::global_dof_index> extractor_dof_indices;
193
194 std::vector<double> timings_variable_residual;
195 std::vector<double> timings_variable_jacobian;
196 };
197
198 namespace internal
199 {
204 template <typename Discretization> struct ScratchData {
205 static constexpr uint dim = Discretization::dim;
207 static constexpr uint n_fe_subsystems = Discretization::Components::count_fe_subsystems();
208 using Iterator = typename DoFHandler<dim>::active_cell_iterator;
209 using t_Iterator = typename Triangulation<dim>::active_cell_iterator;
210
211 ScratchData(const Mapping<dim> &mapping, const vector<const DoFHandler<dim> *> &dofh,
212 const dealii::Quadrature<dim> &quadrature, const dealii::Quadrature<dim - 1> &quadrature_face,
213 const UpdateFlags update_flags = update_values | update_gradients | update_quadrature_points |
214 update_JxW_values,
215 const UpdateFlags interface_update_flags = update_values | update_gradients |
216 update_quadrature_points | update_JxW_values |
217 update_normal_vectors)
218 {
219 AssertThrow(dofh.size() >= n_fe_subsystems,
220 StandardExceptions::ExcDimensionMismatch(dofh.size(), n_fe_subsystems));
221
222 for (uint i = 0; i < n_fe_subsystems; ++i) {
223 const auto &fe = dofh[i]->get_fe();
224 fe_values[i] = std::make_unique<FEValues<dim>>(mapping, fe, quadrature, update_flags);
226 std::make_unique<FEInterfaceValues<dim>>(mapping, fe, quadrature_face, interface_update_flags);
228 std::make_unique<FEFaceValues<dim>>(mapping, fe, quadrature_face, interface_update_flags);
229
230 n_components[i] = fe.n_components();
231 solution[i].resize(quadrature.size(), Vector<NumberType>(n_components[i]));
232 solution_interface[0][i].resize(quadrature_face.size(), Vector<NumberType>(n_components[i]));
233 solution_interface[1][i].resize(quadrature_face.size(), Vector<NumberType>(n_components[i]));
234
235 const uint n_dofs_per_cell = fe.n_dofs_per_cell();
236 comp[i].resize(n_dofs_per_cell);
237 for (uint d = 0; d < n_dofs_per_cell; ++d)
238 comp[i][d] = fe.system_to_component_index(d).first;
239
240 cell[i] = dofh[i]->begin_active();
241 ncell[i] = dofh[i]->begin_active();
242 }
243 solution_dot.resize(quadrature.size(), Vector<NumberType>(n_components[0]));
244 }
245
247 {
248 for (uint i = 0; i < n_fe_subsystems; ++i) {
249 const auto &old_fe = scratch_data.fe_values[i];
250 const auto &old_fe_i = scratch_data.fe_interface_values[i];
251 const auto &old_fe_b = scratch_data.fe_boundary_values[i];
252
253 fe_values[i] = unique_ptr<FEValues<dim>>(new FEValues<dim>(
254 old_fe->get_mapping(), old_fe->get_fe(), old_fe->get_quadrature(), old_fe->get_update_flags()));
255 fe_interface_values[i] = unique_ptr<FEInterfaceValues<dim>>(new FEInterfaceValues<dim>(
256 old_fe_i->get_mapping(), old_fe_i->get_fe(), old_fe_i->get_quadrature(), old_fe_i->get_update_flags()));
257 fe_boundary_values[i] = unique_ptr<FEFaceValues<dim>>(new FEFaceValues<dim>(
258 old_fe_b->get_mapping(), old_fe_b->get_fe(), old_fe_b->get_quadrature(), old_fe_b->get_update_flags()));
259
260 n_components[i] = scratch_data.n_components[i];
261 comp[i] = scratch_data.comp[i];
262 solution[i].resize(scratch_data.solution[i].size(), Vector<NumberType>(n_components[i]));
263 solution_interface[0][i].resize(scratch_data.solution_interface[0][i].size(),
264 Vector<NumberType>(n_components[i]));
265 solution_interface[1][i].resize(scratch_data.solution_interface[1][i].size(),
266 Vector<NumberType>(n_components[i]));
267
268 cell[i] = scratch_data.cell[i];
269 ncell[i] = scratch_data.ncell[i];
270 }
271 solution_dot.resize(scratch_data.solution_dot.size(), Vector<NumberType>(n_components[0]));
272 }
273
274 const auto &new_fe_values(const t_Iterator &t_cell)
275 {
276 for (uint i = 0; i < n_fe_subsystems; ++i) {
277 cell[i]->copy_from(*t_cell);
278 fe_values[i]->reinit(cell[i]);
279 }
280 return fe_values;
281 }
282 const auto &new_fe_interface_values(const t_Iterator &t_cell, uint f, uint sf, const t_Iterator &t_ncell,
283 uint nf, unsigned int nsf)
284 {
285 for (uint i = 0; i < n_fe_subsystems; ++i) {
286 cell[i]->copy_from(*t_cell);
287 ncell[i]->copy_from(*t_ncell);
288 fe_interface_values[i]->reinit(cell[i], f, sf, ncell[i], nf, nsf);
289 }
290 return fe_interface_values;
291 }
292 const auto &new_fe_boundary_values(const t_Iterator &t_cell, uint face_no)
293 {
294 for (uint i = 0; i < n_fe_subsystems; ++i) {
295 cell[i]->copy_from(*t_cell);
296 fe_boundary_values[i]->reinit(cell[i], face_no);
297 }
298 return fe_boundary_values;
299 }
300
301 array<uint, n_fe_subsystems> n_components;
302 array<Iterator, n_fe_subsystems> cell;
303 array<Iterator, n_fe_subsystems> ncell;
304
305 array<unique_ptr<FEValues<dim>>, n_fe_subsystems> fe_values;
306 array<unique_ptr<FEInterfaceValues<dim>>, n_fe_subsystems> fe_interface_values;
307 array<unique_ptr<FEFaceValues<dim>>, n_fe_subsystems> fe_boundary_values;
308
309 array<std::vector<uint>, n_fe_subsystems> comp;
310
311 array<vector<Vector<NumberType>>, n_fe_subsystems> solution;
312 vector<Vector<NumberType>> solution_dot;
313 array<array<vector<Vector<NumberType>>, n_fe_subsystems>, 2> solution_interface;
314 };
315
316 template <typename NumberType> struct CopyData_R {
318 Vector<NumberType> cell_residual;
319 std::vector<types::global_dof_index> joint_dof_indices;
320 };
321
322 Vector<NumberType> cell_residual;
323 Vector<NumberType> cell_mass;
324 std::vector<types::global_dof_index> local_dof_indices;
325 std::vector<CopyDataFace_R> face_data;
326
327 template <class Iterator> void reinit(const Iterator &cell, uint dofs_per_cell)
328 {
329 cell_residual.reinit(dofs_per_cell);
330 cell_mass.reinit(dofs_per_cell);
331 local_dof_indices.resize(dofs_per_cell);
332 cell->get_dof_indices(local_dof_indices);
333 face_data.clear();
334 face_data.reserve(6);
335 }
336
337 template <int dim> CopyDataFace_R &new_face_data(const FEInterfaceValues<dim> &fe_iv)
338 {
339 face_data.emplace_back();
340 auto &copy_data_face = face_data.back();
341 copy_data_face.cell_residual.reinit(fe_iv.n_current_interface_dofs());
342 copy_data_face.joint_dof_indices = fe_iv.get_interface_dof_indices();
343 return copy_data_face;
344 }
345 };
346
347 template <typename NumberType> struct CopyData_J {
349 FullMatrix<NumberType> cell_jacobian;
350 std::vector<types::global_dof_index> joint_dof_indices_from;
351 std::vector<types::global_dof_index> joint_dof_indices_to;
352 };
353
354 FullMatrix<NumberType> cell_jacobian;
355 std::vector<types::global_dof_index> local_dof_indices_from;
356 std::vector<types::global_dof_index> local_dof_indices_to;
357 std::vector<CopyDataFace_J> face_data;
358
359 template <class Iterator>
360 void reinit(const Iterator &cell_from, const Iterator &cell_to, uint dofs_per_cell_from, uint dofs_per_cell_to)
361 {
362 cell_jacobian.reinit(dofs_per_cell_to, dofs_per_cell_from);
363 local_dof_indices_from.resize(dofs_per_cell_from);
364 local_dof_indices_to.resize(dofs_per_cell_to);
365 cell_from->get_dof_indices(local_dof_indices_from);
366 cell_to->get_dof_indices(local_dof_indices_to);
367 }
368
369 template <int dim>
370 CopyDataFace_J &new_face_data(const FEInterfaceValues<dim> &fe_iv_from, const FEInterfaceValues<dim> &fe_iv_to)
371 {
372 auto &copy_data_face = face_data.emplace_back();
373 copy_data_face.cell_jacobian.reinit(fe_iv_to.n_current_interface_dofs(),
374 fe_iv_from.n_current_interface_dofs());
375 copy_data_face.joint_dof_indices_from = fe_iv_from.get_interface_dof_indices();
376 copy_data_face.joint_dof_indices_to = fe_iv_to.get_interface_dof_indices();
377 return copy_data_face;
378 }
379 };
380
381 template <typename NumberType, uint n_fe_subsystems> struct CopyData_J_full {
383 array<FullMatrix<NumberType>, n_fe_subsystems> cell_jacobian;
384 FullMatrix<NumberType> extractor_cell_jacobian;
385 array<vector<types::global_dof_index>, n_fe_subsystems> joint_dof_indices;
386 };
387
388 array<FullMatrix<NumberType>, n_fe_subsystems> cell_jacobian;
389 FullMatrix<NumberType> cell_mass_jacobian;
390 FullMatrix<NumberType> extractor_cell_jacobian;
391 array<vector<types::global_dof_index>, n_fe_subsystems> local_dof_indices;
392 vector<CopyDataFace_J> face_data;
393
395
396 template <class Iterator> void reinit(const array<Iterator, n_fe_subsystems> &cell, const uint n_extractors)
397 {
398 const uint n_dofs = cell[0]->get_fe().n_dofs_per_cell();
399 dofs_per_cell = n_dofs;
400 for (uint i = 0; i < n_fe_subsystems; ++i) {
401 const uint from_n_dofs = cell[i]->get_fe().n_dofs_per_cell();
402 if (i == 0) cell_mass_jacobian.reinit(n_dofs, from_n_dofs);
403 cell_jacobian[i].reinit(n_dofs, from_n_dofs);
404 local_dof_indices[i].resize(from_n_dofs);
405 cell[i]->get_dof_indices(local_dof_indices[i]);
406 }
407 if (n_extractors > 0) extractor_cell_jacobian.reinit(dofs_per_cell, n_extractors);
408 face_data.clear();
409 face_data.reserve(6);
410 }
411
412 template <int dim>
413 CopyDataFace_J &new_face_data(const array<unique_ptr<FEInterfaceValues<dim>>, n_fe_subsystems> &fe_iv,
414 const uint n_extractors)
415 {
416 auto &copy_data_face = face_data.emplace_back();
417 for (uint i = 0; i < n_fe_subsystems; ++i) {
418 copy_data_face.cell_jacobian[i].reinit(fe_iv[0]->n_current_interface_dofs(),
419 fe_iv[i]->n_current_interface_dofs());
420 copy_data_face.joint_dof_indices[i] = fe_iv[i]->get_interface_dof_indices();
421 }
422 if (n_extractors > 0)
423 copy_data_face.extractor_cell_jacobian.reinit(fe_iv[0]->n_current_interface_dofs(), n_extractors);
424 return copy_data_face;
425 }
426 };
427
428 template <typename NumberType> struct CopyData_I {
430 std::array<uint, 2> cell_indices;
431 std::array<double, 2> values;
432 };
433 std::vector<CopyFaceData_I> face_data;
434 double value = 0.;
436 };
437 } // namespace internal
438
445 template <typename Discretization_,
447 class Assembler : public LDGAssemblerBase<Discretization_, Model_>
448 {
450
451 public:
452 using Discretization = Discretization_;
453 using Model = Model_;
457
459 static constexpr uint dim = Discretization::dim;
460 static constexpr uint stencil = Components::count_fe_subsystems();
461
462 private:
463 template <typename... T> auto fe_conv(std::tuple<T &...> &t) const
464 {
465 if constexpr (stencil == 2)
466 return named_tuple<std::tuple<T &...>,
468 else if constexpr (stencil == 3)
469 return named_tuple<std::tuple<T &...>,
471 else if constexpr (stencil == 4)
472 return named_tuple<std::tuple<T &...>, StringSet<"fe_functions", "LDG1", "LDG2", "LDG3", "extractors",
473 "variables", "cell_width">>(t);
474 else
475 throw std::runtime_error("Only <= 3 LDG subsystems are supported.");
476 }
477
478 template <typename... T> auto fe_more_conv(std::tuple<T &...> &t) const
479 {
480 if constexpr (stencil == 2)
481 return named_tuple<std::tuple<T &...>,
482 StringSet<"fe_functions", "LDG1", "fe_derivatives", "fe_hessians", "extractors",
483 "variables", "potential", "potential_gradient", "potential_hessian">>(t);
484 else if constexpr (stencil == 3)
485 return named_tuple<std::tuple<T &...>,
486 StringSet<"fe_functions", "LDG1", "LDG2", "fe_derivatives", "fe_hessians", "extractors",
487 "variables", "potential", "potential_gradient", "potential_hessian">>(t);
488 else if constexpr (stencil == 4)
489 return named_tuple<std::tuple<T &...>, StringSet<"fe_functions", "LDG1", "LDG2", "LDG3", "fe_derivatives",
490 "fe_hessians", "extractors", "variables", "potential",
491 "potential_gradient", "potential_hessian">>(t);
492 else
493 throw std::runtime_error("Only <= 3 LDG subsystems are supported.");
494 }
495
496 template <typename... T> auto ref_conv(std::tuple<T &...> &t) const
497 {
498 if constexpr (stencil == 2)
499 return named_tuple<std::tuple<T &...>, StringSet<"fe_functions", "LDG1">>(t);
500 else if constexpr (stencil == 3)
501 return named_tuple<std::tuple<T &...>, StringSet<"fe_functions", "LDG1", "LDG2">>(t);
502 else if constexpr (stencil == 4)
504 else
505 throw std::runtime_error("Only <= 3 LDG subsystems are supported.");
506 }
507
508 public:
511 quadrature(fe.degree + 1 + config.get_uint("/discretization/overintegration", 0)),
512 quadrature_face(fe.degree + 1 + config.get_uint("/discretization/overintegration", 0)),
513 dof_handler_list(discretization.get_dof_handler_list())
514 {
515 static_assert(Components::count_fe_subsystems() > 1, "LDG must have a submodel with index 1.");
516 reinit();
517 }
518
519 virtual void reinit_vector(VectorType &vec) const override { vec.reinit(dof_handler.n_dofs()); }
520 // LDG stays on the serial policy (see LDG::Discretization for why), so this is the plain
521 // pattern-based reinit rather than a policy call.
522 virtual void reinit_matrix(SparseMatrixType &matrix) const override
523 {
524 matrix.reinit(get_sparsity_pattern_jacobian());
525 }
526 virtual MPI_Comm get_communicator() const override { return discretization.get_communicator(); }
527 virtual void reinit_solution_view(SolutionView<VectorType> &view) const override
528 {
529 view.reinit(discretization.get_locally_owned_dofs(), discretization.get_locally_relevant_dofs(),
530 discretization.get_communicator());
531 }
532
539 virtual void attach_data_output(OutputFrame<dim, VectorType> &data_out, const VectorType &solution,
540 const VectorType &variables, const VectorType &dt_solution = VectorType(),
541 const VectorType &residual = VectorType()) override
542 {
543 rebuild_ldg_vectors(solution);
544 readouts(data_out, solution, variables);
545
546 const auto fe_function_names = Components::FEFunction_Descriptor::get_names_vector();
547 std::vector<std::string> fe_function_names_residual;
548 for (const auto &name : fe_function_names)
549 fe_function_names_residual.push_back(name + "_residual");
550 std::vector<std::string> fe_function_names_dot;
551 for (const auto &name : fe_function_names)
552 fe_function_names_dot.push_back(name + "_dot");
553
554 auto fe_out = data_out.fields();
555 fe_out.attach(*dof_handler_list[0], solution, fe_function_names);
556 if (dt_solution.size() > 0) fe_out.attach(dof_handler, dt_solution, fe_function_names_dot);
557 if (residual.size() > 0) fe_out.attach(dof_handler, residual, fe_function_names_residual);
558 for (uint k = 1; k < Components::count_fe_subsystems(); ++k) {
560 fe_out.attach(*dof_handler_list[k], sol_vector_vec_tmp[k], "LDG" + std::to_string(k));
561 }
562 }
563
564 virtual void reinit() override
565 {
566 const auto init_mass = [&](uint i) {
567 // build the sparsity of the mass matrix and mass matrix of all ldg levels
568 auto dofs_per_component = DoFTools::count_dofs_per_fe_component(*(dof_handler_list[i]));
569
570 if (i == 0) {
571 auto n_fe = dofs_per_component.size();
572 for (uint j = 1; j < n_fe; ++j)
573 if (dofs_per_component[j] != dofs_per_component[0])
574 throw std::runtime_error("For LDG the FE basis of all systems must be equal!");
575
576 BlockDynamicSparsityPattern dsp(n_fe, n_fe);
577 for (uint i = 0; i < n_fe; ++i)
578 for (uint j = 0; j < n_fe; ++j)
579 dsp.block(i, j).reinit(dofs_per_component[0], dofs_per_component[0]);
580 dsp.collect_sizes();
581 DoFTools::make_sparsity_pattern(*(dof_handler_list[0]), dsp, discretization.get_constraints(0), true);
582 sparsity_pattern_mass.copy_from(dsp);
583
584 // i do not understand why this is needed.
587
588 MatrixCreator::create_mass_matrix(*(dof_handler_list[0]), quadrature, mass_matrix,
589 (Function<dim, NumberType> *)nullptr, discretization.get_constraints(0));
591 } else {
592 sol_vector[i].reinit(dofs_per_component);
593 sol_vector_tmp[i].reinit(dofs_per_component);
594 ldg_matrix_built[i] = false;
595 jacobian_tmp_built[i] = false;
596 }
597 };
598
599 const auto init_jacobian = [&](uint i) {
600 // build the jacobian and subjacobians
601 if (i == 0) {
603 true);
604 for (uint k = 1; k < Components::count_fe_subsystems(); ++k)
606 } else {
608 j_ug[i].reinit(sparsity_pattern_ug[i]);
609 }
610 };
611
612 auto init_ldg = [&](uint i) {
613 // build the subjacobian sparsity patterns of all matrices that contribute to the jacobian = uu + ug*gu
615 j_gu[i].reinit(sparsity_pattern_gu[i]);
616
617 // these are the "in-between" dependencies of the ldg levels
619 j_wg[i].reinit(sparsity_pattern_wg[i]);
620 j_wg_tmp[i].reinit(sparsity_pattern_wg[i]);
621 };
622
623 Timer timer;
624
625 Base::reinit();
626
627 vector<std::thread> init_threads;
628 for (uint i = 0; i < Components::count_fe_subsystems(); ++i)
629 init_threads.emplace_back(std::thread(init_mass, i));
630 for (uint i = 0; i < Components::count_fe_subsystems(); ++i)
631 init_threads.emplace_back(std::thread(init_jacobian, i));
632 for (uint i = 1; i < Components::count_fe_subsystems(); ++i)
633 init_threads.emplace_back(std::thread(init_ldg, i));
634 for (auto &t : init_threads)
635 t.join();
636
637 timings_reinit.push_back(timer.wall_time());
638 }
639
640 virtual void rebuild_jacobian_sparsity() override
641 {
643 for (uint k = 1; k < Components::count_fe_subsystems(); ++k)
645 }
646
647 virtual void refinement_indicator(Vector<double> &indicator, const VectorType &solution_global) override
648 {
649 using Iterator = typename Triangulation<dim>::active_cell_iterator;
651 using CopyData = internal::CopyData_I<NumberType>;
652
653 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
654 const auto &fe_v = scratch_data.new_fe_values(t_cell);
655 copy_data.cell_index = t_cell->active_cell_index();
656 copy_data.value = 0;
657
658 const auto &JxW = fe_v[0]->get_JxW_values();
659 const auto &q_points = fe_v[0]->get_quadrature_points();
660 const auto &q_indices = fe_v[0]->quadrature_point_indices();
661
662 auto &solution = scratch_data.solution;
663 fe_v[0]->get_function_values(solution_global, solution[0]);
664 for (uint i = 1; i < Components::count_fe_subsystems(); ++i)
665 fe_v[i]->get_function_values(sol_vector[i], solution[i]);
666
667 double local_indicator = 0.;
668 for (const auto &q_index : q_indices) {
669 const auto &x_q = q_points[q_index];
670 auto sol_q = local_sol_q(solution, q_index);
671 model.cell_indicator(local_indicator, x_q, ref_conv(sol_q));
672
673 copy_data.value += JxW[q_index] * local_indicator;
674 }
675 };
676 const auto face_worker = [&](const Iterator &t_cell, const uint &f, const uint &sf, const Iterator &t_ncell,
677 const uint &nf, const unsigned int &nsf, Scratch &scratch_data,
678 CopyData &copy_data) {
679 const auto &fe_iv = scratch_data.new_fe_interface_values(t_cell, f, sf, t_ncell, nf, nsf);
680
681 auto &copy_data_face = copy_data.face_data.emplace_back();
682 copy_data_face.cell_indices[0] = t_cell->active_cell_index();
683 copy_data_face.cell_indices[1] = t_ncell->active_cell_index();
684 copy_data_face.values[0] = 0;
685 copy_data_face.values[1] = 0;
686
687 const auto &JxW = fe_iv[0]->get_JxW_values();
688 const auto &q_points = fe_iv[0]->get_quadrature_points();
689 const auto &q_indices = fe_iv[0]->quadrature_point_indices();
690 const std::vector<Tensor<1, dim>> &normals = fe_iv[0]->get_normal_vectors();
691 array<double, 2> local_indicator{};
692
693 auto &solution = scratch_data.solution_interface;
694 fe_iv[0]->get_fe_face_values(0).get_function_values(solution_global, solution[0][0]);
695 fe_iv[0]->get_fe_face_values(1).get_function_values(solution_global, solution[1][0]);
696 for (uint i = 1; i < Components::count_fe_subsystems(); ++i) {
697 fe_iv[i]->get_fe_face_values(0).get_function_values(sol_vector[i], solution[0][i]);
698 fe_iv[i]->get_fe_face_values(1).get_function_values(sol_vector[i], solution[1][i]);
699 }
700
701 for (const auto &q_index : q_indices) {
702 const auto &x_q = q_points[q_index];
703 auto sol_q_s = local_sol_q(solution[0], q_index);
704 auto sol_q_n = local_sol_q(solution[1], q_index);
705 model.face_indicator(local_indicator, normals[q_index], x_q, ref_conv(sol_q_s), ref_conv(sol_q_n));
706
707 copy_data_face.values[0] += JxW[q_index] * local_indicator[0] * (1. + t_cell->at_boundary());
708 copy_data_face.values[1] += JxW[q_index] * local_indicator[1] * (1. + t_ncell->at_boundary());
709 }
710 };
711 const auto copier = [&](const CopyData &c) {
712 for (auto &cdf : c.face_data)
713 for (uint j = 0; j < 2; ++j)
714 indicator[cdf.cell_indices[j]] += cdf.values[j];
715 indicator[c.cell_index] += c.value;
716 };
717
718 const UpdateFlags update_flags = update_values | update_quadrature_points | update_JxW_values;
719 Scratch scratch_data(mapping, dof_handler_list, quadrature, quadrature_face, update_flags);
720 CopyData copy_data;
721 MeshWorker::AssembleFlags assemble_flags =
722 MeshWorker::assemble_own_cells | MeshWorker::assemble_own_interior_faces_once;
723
724 rebuild_ldg_vectors(solution_global);
725 const auto schedule = schedule_for(assembly_cost::local_fe);
726 MeshWorker::mesh_loop(locally_owned_cells(dof_handler), cell_worker, copier, scratch_data, copy_data,
727 assemble_flags, nullptr, face_worker, schedule.queue_length, schedule.chunk_size);
728 }
729
730 virtual const BlockSparsityPattern &get_sparsity_pattern_jacobian() const override
731 {
733 }
734 virtual const BlockSparseMatrix<NumberType> &get_mass_matrix() const override { return mass_matrix; }
735
743 virtual void mass(VectorType &residual, const VectorType &solution_global, const VectorType &solution_global_dot,
744 NumberType weight) override
745 {
746 using Iterator = typename Triangulation<dim>::active_cell_iterator;
748 using CopyData = internal::CopyData_R<NumberType>;
749 const auto &constraints = discretization.get_constraints();
750
751 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
752 const auto &fe_v = scratch_data.new_fe_values(t_cell);
753 const uint n_dofs = fe_v[0]->get_fe().n_dofs_per_cell();
754 copy_data.reinit(scratch_data.cell[0], n_dofs);
755
756 const auto &JxW = fe_v[0]->get_JxW_values();
757 const auto &q_points = fe_v[0]->get_quadrature_points();
758 const auto &q_indices = fe_v[0]->quadrature_point_indices();
759
760 auto &solution = scratch_data.solution;
761 auto &solution_dot = scratch_data.solution_dot;
762 fe_v[0]->get_function_values(solution_global, solution[0]);
763 fe_v[0]->get_function_values(solution_global_dot, solution_dot);
764
765 const auto &comp_0 = scratch_data.comp[0];
766 array<NumberType, Components::count_fe_functions(0)> mass{};
767 for (const auto &q_index : q_indices) {
768 const auto &x_q = q_points[q_index];
769 model.mass(mass, x_q, solution[0][q_index], solution_dot[q_index]);
770
771 for (uint i = 0; i < n_dofs; ++i) {
772 const auto component_i = comp_0[i];
773 copy_data.cell_residual(i) += weight * JxW[q_index] * // dx
774 fe_v[0]->shape_value_component(i, q_index, component_i) *
775 mass[component_i]; // phi_i(x_q) * mass(x_q, u_q)
776 }
777 }
778 };
779 const auto copier = [&](const CopyData &c) {
780 constraints.distribute_local_to_global(c.cell_residual, c.local_dof_indices, residual);
781 };
782
783 const UpdateFlags update_flags = update_values | update_quadrature_points | update_JxW_values;
784 const MeshWorker::AssembleFlags assemble_flags = MeshWorker::assemble_own_cells;
785 Scratch scratch_data(mapping, dof_handler_list, quadrature, quadrature_face, update_flags);
786 CopyData copy_data;
787
788 const auto schedule = schedule_for(assembly_cost::local_fe);
789 MeshWorker::mesh_loop(locally_owned_cells(dof_handler), cell_worker, copier, scratch_data, copy_data,
790 assemble_flags, nullptr, nullptr, schedule.queue_length, schedule.chunk_size);
791 }
792
800 virtual void residual(VectorType &residual, const VectorType &solution_global, NumberType weight,
801 const VectorType &solution_global_dot, NumberType weight_mass,
802 const VectorType &variables = VectorType()) override
803 {
804 using Iterator = typename Triangulation<dim>::active_cell_iterator;
806 using CopyData = internal::CopyData_R<NumberType>;
807 const auto &constraints = discretization.get_constraints();
808
809 // Find the EoM and extract whatever data is needed for the model.
810 std::array<NumberType, Components::count_extractors()> __extracted_data{{}};
811 if constexpr (Components::count_extractors() > 0)
812 this->extract(__extracted_data, solution_global, variables, true, false, true);
813 const auto &extracted_data = __extracted_data;
814
815 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
816 const double cell_width = DiFfRG::internal::cell_width(t_cell);
817 const auto &fe_v = scratch_data.new_fe_values(t_cell);
818 const uint n_dofs = fe_v[0]->get_fe().n_dofs_per_cell();
819 copy_data.reinit(scratch_data.cell[0], n_dofs);
820
821 const auto &JxW = fe_v[0]->get_JxW_values();
822 const auto &q_points = fe_v[0]->get_quadrature_points();
823 const auto &q_indices = fe_v[0]->quadrature_point_indices();
824
825 auto &solution = scratch_data.solution;
826 auto &solution_dot = scratch_data.solution_dot;
827 fe_v[0]->get_function_values(solution_global, solution[0]);
828 fe_v[0]->get_function_values(solution_global_dot, solution_dot);
829 for (uint i = 1; i < Components::count_fe_subsystems(); ++i)
830 fe_v[i]->get_function_values(sol_vector[i], solution[i]);
831
832 const auto &comp_0 = scratch_data.comp[0];
833 array<NumberType, Components::count_fe_functions(0)> mass{};
834 array<Tensor<1, dim, NumberType>, Components::count_fe_functions(0)> flux{};
835 array<NumberType, Components::count_fe_functions(0)> source{};
836 for (const auto &q_index : q_indices) {
837 const auto &x_q = q_points[q_index];
838 auto sol_q =
839 std::tuple_cat(local_sol_q(solution, q_index), std::tie(extracted_data, variables, cell_width));
840 model.mass(mass, x_q, solution[0][q_index], solution_dot[q_index]);
841 model.flux(flux, x_q, fe_conv(sol_q));
842 model.source(source, x_q, fe_conv(sol_q));
843
844 for (uint i = 0; i < n_dofs; ++i) {
845 const auto component_i = comp_0[i];
846 copy_data.cell_residual(i) += weight * JxW[q_index] * // dx
847 (-scalar_product(fe_v[0]->shape_grad_component(i, q_index, component_i),
848 flux[component_i]) // -dphi_i(x_q) * flux(x_q, u_q)
849 + fe_v[0]->shape_value_component(i, q_index, component_i) *
850 (source[component_i])); // -phi_i(x_q) * source(x_q, u_q)
851 copy_data.cell_mass(i) += weight_mass * JxW[q_index] * // dx
852 fe_v[0]->shape_value_component(i, q_index, component_i) *
853 mass[component_i]; // phi_i(x_q) * mass(x_q, u_q)
854 }
855 }
856 };
857 const auto boundary_worker = [&](const Iterator &t_cell, const uint &face_no, Scratch &scratch_data,
858 CopyData &copy_data) {
859 const double cell_width = DiFfRG::internal::cell_width(t_cell);
860 const auto &fe_fv = scratch_data.new_fe_boundary_values(t_cell, face_no);
861 const uint n_dofs = fe_fv[0]->get_fe().n_dofs_per_cell();
862
863 const auto &JxW = fe_fv[0]->get_JxW_values();
864 const auto &q_points = fe_fv[0]->get_quadrature_points();
865 const auto &q_indices = fe_fv[0]->quadrature_point_indices();
866 const auto &normals = fe_fv[0]->get_normal_vectors();
867
868 auto &solution = scratch_data.solution;
869 fe_fv[0]->get_function_values(solution_global, solution[0]);
870 for (uint i = 1; i < Components::count_fe_subsystems(); ++i)
871 fe_fv[i]->get_function_values(sol_vector[i], solution[i]);
872
873 const auto &comp_0 = scratch_data.comp[0];
874 array<Tensor<1, dim, NumberType>, Components::count_fe_functions(0)> numflux{};
875 for (const auto &q_index : q_indices) {
876 const auto &x_q = q_points[q_index];
877 auto sol_q =
878 std::tuple_cat(local_sol_q(solution, q_index), std::tie(extracted_data, variables, cell_width));
879 model.boundary_numflux(numflux, normals[q_index], x_q, fe_conv(sol_q));
880
881 for (uint i = 0; i < n_dofs; ++i) {
882 const auto component_i = comp_0[i];
883 copy_data.cell_residual(i) +=
884 weight * JxW[q_index] * // weight * dx
885 (fe_fv[0]->shape_value_component(i, q_index, component_i) *
886 scalar_product(numflux[component_i], normals[q_index])); // phi_i(x_q) * numflux(x_q, u_q) * n(x_q)
887 }
888 }
889 };
890 const auto face_worker = [&](const Iterator &t_cell, const uint &f, const uint &sf, const Iterator &t_ncell,
891 const uint &nf, const unsigned int &nsf, Scratch &scratch_data,
892 CopyData &copy_data) {
893 const double cell_width = DiFfRG::internal::cell_width(t_cell);
894 const double ncell_width = DiFfRG::internal::cell_width(t_ncell);
895 const auto &fe_iv = scratch_data.new_fe_interface_values(t_cell, f, sf, t_ncell, nf, nsf);
896 const uint n_dofs = fe_iv[0]->n_current_interface_dofs();
897 auto &copy_data_face = copy_data.new_face_data(*(fe_iv[0]));
898
899 const auto &JxW = fe_iv[0]->get_JxW_values();
900 const auto &q_points = fe_iv[0]->get_quadrature_points();
901 const auto &q_indices = fe_iv[0]->quadrature_point_indices();
902 const auto &normals = fe_iv[0]->get_normal_vectors();
903
904 auto &solution = scratch_data.solution_interface;
905 fe_iv[0]->get_fe_face_values(0).get_function_values(solution_global, solution[0][0]);
906 fe_iv[0]->get_fe_face_values(1).get_function_values(solution_global, solution[1][0]);
907 for (uint i = 1; i < Components::count_fe_subsystems(); ++i) {
908 fe_iv[i]->get_fe_face_values(0).get_function_values(sol_vector[i], solution[0][i]);
909 fe_iv[i]->get_fe_face_values(1).get_function_values(sol_vector[i], solution[1][i]);
910 }
911
912 // Pre-compute component indices for interface DoFs
913 std::vector<uint> iface_comp_0(n_dofs);
914 for (uint i = 0; i < n_dofs; ++i) {
915 const auto &cd_i = fe_iv[0]->interface_dof_to_dof_indices(i);
916 iface_comp_0[i] = cd_i[0] == numbers::invalid_unsigned_int
917 ? fe_iv[0]->get_fe().system_to_component_index(cd_i[1]).first
918 : fe_iv[0]->get_fe().system_to_component_index(cd_i[0]).first;
919 }
920
921 array<Tensor<1, dim, NumberType>, Components::count_fe_functions(0)> numflux{};
922 for (const auto &q_index : q_indices) {
923 const auto &x_q = q_points[q_index];
924 auto sol_q_s =
925 std::tuple_cat(local_sol_q(solution[0], q_index), std::tie(extracted_data, variables, cell_width));
926 auto sol_q_n =
927 std::tuple_cat(local_sol_q(solution[1], q_index), std::tie(extracted_data, variables, ncell_width));
928 model.numflux(numflux, normals[q_index], x_q, fe_conv(sol_q_s), fe_conv(sol_q_n));
929
930 for (uint i = 0; i < n_dofs; ++i) {
931 const auto component_i = iface_comp_0[i];
932 copy_data_face.cell_residual(i) +=
933 weight * JxW[q_index] * // weight * dx
934 (fe_iv[0]->jump_in_shape_values(i, q_index, component_i) *
935 scalar_product(numflux[component_i],
936 normals[q_index])); // [[phi_i(x_q)]] * numflux(x_q, u_q) * n(x_q)
937 }
938 }
939 };
940 const auto copier = [&](const CopyData &c) {
941 constraints.distribute_local_to_global(c.cell_residual, c.local_dof_indices, residual);
942 constraints.distribute_local_to_global(c.cell_mass, c.local_dof_indices, residual);
943 for (auto &cdf : c.face_data)
944 constraints.distribute_local_to_global(cdf.cell_residual, cdf.joint_dof_indices, residual);
945 };
946
947 const UpdateFlags update_flags =
948 update_values | update_gradients | update_quadrature_points | update_JxW_values;
949 const MeshWorker::AssembleFlags assemble_flags = MeshWorker::assemble_own_cells |
950 MeshWorker::assemble_boundary_faces |
951 MeshWorker::assemble_own_interior_faces_once;
952 Scratch scratch_data(mapping, dof_handler_list, quadrature, quadrature_face, update_flags);
953 CopyData copy_data;
954
955 Timer timer;
956
957 rebuild_ldg_vectors(solution_global);
958 const auto schedule = schedule_for(assembly_cost::momentum_integral);
959 MeshWorker::mesh_loop(locally_owned_cells(dof_handler), cell_worker, copier, scratch_data, copy_data,
960 assemble_flags, boundary_worker, face_worker, schedule.queue_length, schedule.chunk_size);
961
962 timings_residual.push_back(timer.wall_time());
963 }
964
965 virtual void jacobian_mass(BlockSparseMatrix<NumberType> &jacobian, const VectorType &solution_global,
966 const VectorType &solution_global_dot, NumberType alpha = 1.,
967 NumberType beta = 1.) override
968 {
969 using Iterator = typename Triangulation<dim>::active_cell_iterator;
971 using CopyData = internal::CopyData_J_full<NumberType, Components::count_fe_subsystems()>;
972 const auto &constraints = discretization.get_constraints();
973
974 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
975 const auto &fe_v = scratch_data.new_fe_values(t_cell);
976 const uint to_n_dofs = fe_v[0]->get_fe().n_dofs_per_cell();
977 copy_data.reinit(scratch_data.cell, Components::count_extractors());
978
979 const auto &JxW = fe_v[0]->get_JxW_values();
980 const auto &q_points = fe_v[0]->get_quadrature_points();
981 const auto &q_indices = fe_v[0]->quadrature_point_indices();
982
983 auto &solution = scratch_data.solution;
984 auto &solution_dot = scratch_data.solution_dot;
985
986 fe_v[0]->get_function_values(solution_global, solution[0]);
987 fe_v[0]->get_function_values(solution_global_dot, solution_dot);
988
989 const auto &comp_0 = scratch_data.comp[0];
990 SimpleMatrix<NumberType, Components::count_fe_functions(0)> j_mass;
991 SimpleMatrix<NumberType, Components::count_fe_functions(0)> j_mass_dot;
992
993 const uint from_n_dofs = fe_v[0]->get_fe().n_dofs_per_cell();
994 for (const auto &q_index : q_indices) {
995 const auto &x_q = q_points[q_index];
996
997 model.template jacobian_mass<0>(j_mass, x_q, solution[0][q_index], solution_dot[q_index]);
998 model.template jacobian_mass<1>(j_mass_dot, x_q, solution[0][q_index], solution_dot[q_index]);
999
1000 for (uint i = 0; i < to_n_dofs; ++i) {
1001 const auto component_i = comp_0[i];
1002 for (uint j = 0; j < from_n_dofs; ++j) {
1003 const auto component_j = comp_0[j];
1004 copy_data.cell_jacobian[0](i, j) +=
1005 JxW[q_index] * fe_v[0]->shape_value_component(j, q_index, component_j) * // weight * dx * phi_j(x_q)
1006 fe_v[0]->shape_value_component(i, q_index, component_i) *
1007 (alpha * j_mass_dot(component_i, component_j) +
1008 beta * j_mass(component_i, component_j)); // -phi_i(x_q) * jsource(x_q, u_q)
1009 }
1010 }
1011 }
1012 };
1013 const auto copier = [&](const CopyData &c) {
1014 constraints.distribute_local_to_global(c.cell_jacobian[0], c.local_dof_indices[0], jacobian);
1015 };
1016
1017 const UpdateFlags update_flags = update_values | update_quadrature_points | update_JxW_values;
1018 const MeshWorker::AssembleFlags assemble_flags = MeshWorker::assemble_own_cells;
1019 Scratch scratch_data(mapping, dof_handler_list, quadrature, quadrature_face, update_flags);
1020 CopyData copy_data;
1021
1022 const auto schedule = schedule_for(assembly_cost::local_fe);
1023 MeshWorker::mesh_loop(locally_owned_cells(dof_handler), cell_worker, copier, scratch_data, copy_data,
1024 assemble_flags, nullptr, nullptr, schedule.queue_length, schedule.chunk_size);
1025 }
1026
1034 virtual void jacobian(BlockSparseMatrix<NumberType> &jacobian, const VectorType &solution_global,
1035 NumberType weight, const VectorType &solution_global_dot, NumberType alpha, NumberType beta,
1036 const VectorType &variables = VectorType()) override
1037 {
1038 if (is_close(weight, 0.))
1039 throw std::runtime_error("Please call jacobian_mass instead of jacobian for weight == 0).");
1040 using Iterator = typename Triangulation<dim>::active_cell_iterator;
1042 using CopyData = internal::CopyData_J_full<NumberType, Components::count_fe_subsystems()>;
1043 const auto &constraints = discretization.get_constraints();
1044
1045 // Find the EoM and extract whatever data is needed for the model.
1046 std::array<NumberType, Components::count_extractors()> __extracted_data{{}};
1047 if constexpr (Components::count_extractors() > 0) {
1048 this->extract(__extracted_data, solution_global, variables, true, true, true);
1049 if (this->jacobian_extractors(this->extractor_jacobian, solution_global, variables))
1051 }
1052 const auto &extracted_data = __extracted_data;
1053
1054 bool exception = false;
1055
1056 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
1057 const double cell_width = DiFfRG::internal::cell_width(t_cell);
1058 const auto &fe_v = scratch_data.new_fe_values(t_cell);
1059 const uint to_n_dofs = fe_v[0]->get_fe().n_dofs_per_cell();
1060 copy_data.reinit(scratch_data.cell, Components::count_extractors());
1061
1062 const auto &JxW = fe_v[0]->get_JxW_values();
1063 const auto &q_points = fe_v[0]->get_quadrature_points();
1064 const auto &q_indices = fe_v[0]->quadrature_point_indices();
1065
1066 auto &solution = scratch_data.solution;
1067 auto &solution_dot = scratch_data.solution_dot;
1068
1069 fe_v[0]->get_function_values(solution_global, solution[0]);
1070 fe_v[0]->get_function_values(solution_global_dot, solution_dot);
1071 for (uint i = 1; i < Components::count_fe_subsystems(); ++i)
1072 fe_v[i]->get_function_values(sol_vector[i], solution[i]);
1073
1074 SimpleMatrix<NumberType, Components::count_fe_functions(0)> j_mass;
1075 SimpleMatrix<NumberType, Components::count_fe_functions(0)> j_mass_dot;
1077 auto j_source = jacobian_tuple<NumberType, Model>();
1078 SimpleMatrix<Tensor<1, dim, NumberType>, Components::count_fe_functions(), Components::count_extractors()>
1079 j_extr_flux;
1080 SimpleMatrix<NumberType, Components::count_fe_functions(), Components::count_extractors()> j_extr_source;
1082 if (jacobian_tmp_built[k] && model.get_components().jacobians_constant(0, k)) return;
1083
1084 const uint from_n_dofs = fe_v[k]->get_fe().n_dofs_per_cell();
1085 for (const auto &q_index : q_indices) {
1086 const auto &x_q = q_points[q_index];
1087 auto sol_q =
1088 std::tuple_cat(local_sol_q(solution, q_index), std::tie(extracted_data, variables, cell_width));
1089
1090 if constexpr (k == 0) {
1091 this->model.template jacobian_mass<0>(j_mass, x_q, solution[0][q_index], solution_dot[q_index]);
1092 this->model.template jacobian_mass<1>(j_mass_dot, x_q, solution[0][q_index], solution_dot[q_index]);
1093 if constexpr (Components::count_extractors() > 0) {
1094 this->model.template jacobian_flux_source_extr<stencil>(j_extr_flux, j_extr_source, x_q,
1095 fe_conv(sol_q));
1096 }
1097 }
1098 model.template jacobian_flux_source<k, 0>(std::get<k>(j_flux), std::get<k>(j_source), x_q,
1099 fe_conv(sol_q));
1100
1101 if (!std::get<k>(j_flux).is_finite() || !std::get<k>(j_source).is_finite()) exception = true;
1102
1103 const auto &comp_0 = scratch_data.comp[0];
1104 const auto &comp_k = scratch_data.comp[k];
1105 for (uint i = 0; i < to_n_dofs; ++i) {
1106 const auto component_i = comp_0[i];
1107 for (uint j = 0; j < from_n_dofs; ++j) {
1108 const auto component_j = comp_k[j];
1109
1110 copy_data.cell_jacobian[k](i, j) +=
1111 JxW[q_index] *
1112 fe_v[k]->shape_value_component(j, q_index, component_j) * // weight * dx * phi_j(x_q)
1113 (-scalar_product(fe_v[0]->shape_grad_component(i, q_index, component_i),
1114 std::get<k>(j_flux)(component_i, component_j)) // -dphi_i(x_q) * jflux(x_q, u_q)
1115 + fe_v[0]->shape_value_component(i, q_index, component_i) *
1116 std::get<k>(j_source)(component_i, component_j)); // -phi_i(x_q) * jsource(x_q, u_q)
1117 if constexpr (k == 0) {
1118 copy_data.cell_mass_jacobian(i, j) +=
1119 JxW[q_index] *
1120 fe_v[0]->shape_value_component(j, q_index, component_j) * // weight * dx * phi_j(x_q)
1121 fe_v[0]->shape_value_component(i, q_index, component_i) *
1122 (alpha / weight * j_mass_dot(component_i, component_j) +
1123 beta / weight * j_mass(component_i, component_j)); // -phi_i(x_q) * jsource(x_q, u_q)
1124 }
1125 }
1126
1127 // extractor contribution
1128 if constexpr (k == 0)
1129 if constexpr (Components::count_extractors() > 0)
1130 for (uint e = 0; e < Components::count_extractors(); ++e)
1131 copy_data.extractor_cell_jacobian(i, e) +=
1132 weight * JxW[q_index] * // dx * phi_j * (
1133 (-scalar_product(fe_v[0]->shape_grad_component(i, q_index, component_i),
1134 j_extr_flux(component_i, e)) // -dphi_i * jflux
1135 + fe_v[0]->shape_value_component(i, q_index, component_i) *
1136 j_extr_source(component_i, e)); // -phi_i * jsource)
1137 }
1138 }
1139 });
1140 };
1141 const auto boundary_worker = [&](const Iterator &t_cell, const uint &face_no, Scratch &scratch_data,
1142 CopyData &copy_data) {
1143 const double cell_width = DiFfRG::internal::cell_width(t_cell);
1144 const auto &fe_fv = scratch_data.new_fe_boundary_values(t_cell, face_no);
1145 const uint to_n_dofs = fe_fv[0]->get_fe().n_dofs_per_cell();
1146
1147 const auto &JxW = fe_fv[0]->get_JxW_values();
1148 const auto &q_points = fe_fv[0]->get_quadrature_points();
1149 const auto &q_indices = fe_fv[0]->quadrature_point_indices();
1150 const auto &normals = fe_fv[0]->get_normal_vectors();
1151 auto &solution = scratch_data.solution;
1152
1153 fe_fv[0]->get_function_values(solution_global, solution[0]);
1154 for (uint i = 1; i < Components::count_fe_subsystems(); ++i)
1155 fe_fv[i]->get_function_values(sol_vector[i], solution[i]);
1156
1157 auto j_boundary_numflux = jacobian_tuple<Tensor<1, dim>, Model>();
1159 if (jacobian_tmp_built[k] && model.get_components().jacobians_constant(0, k)) return;
1160 for (const auto &q_index : q_indices) {
1161 const auto &x_q = q_points[q_index];
1162 auto sol_q =
1163 std::tuple_cat(local_sol_q(solution, q_index), std::tie(extracted_data, variables, cell_width));
1164
1165 const uint from_n_dofs = fe_fv[k]->get_fe().n_dofs_per_cell();
1166
1167 model.template jacobian_boundary_numflux<k, 0>(std::get<k>(j_boundary_numflux), normals[q_index], x_q,
1168 fe_conv(sol_q));
1169
1170 if (!std::get<k>(j_boundary_numflux).is_finite()) exception = true;
1171
1172 const auto &comp_0 = scratch_data.comp[0];
1173 const auto &comp_k = scratch_data.comp[k];
1174 for (uint i = 0; i < to_n_dofs; ++i) {
1175 const auto component_i = comp_0[i];
1176 for (uint j = 0; j < from_n_dofs; ++j) {
1177 const auto component_j = comp_k[j];
1178
1179 copy_data.cell_jacobian[k](i, j) +=
1180 JxW[q_index] *
1181 fe_fv[k]->shape_value_component(j, q_index, component_j) * // weight * dx * phi_j(x_q)
1182 (fe_fv[0]->shape_value_component(i, q_index, component_i) *
1183 scalar_product(std::get<k>(j_boundary_numflux)(component_i, component_j),
1184 normals[q_index])); // phi_i(x_q) * j_numflux(x_q, u_q) * n(x_q)
1185 }
1186 }
1187 }
1188 });
1189 };
1190 const auto face_worker = [&](const Iterator &t_cell, const uint &f, const uint &sf, const Iterator &t_ncell,
1191 const uint &nf, const unsigned int &nsf, Scratch &scratch_data,
1192 CopyData &copy_data) {
1193 const double cell_width = DiFfRG::internal::cell_width(t_cell);
1194 const double ncell_width = DiFfRG::internal::cell_width(t_ncell);
1195 const auto &fe_iv = scratch_data.new_fe_interface_values(t_cell, f, sf, t_ncell, nf, nsf);
1196 const uint to_n_dofs = fe_iv[0]->n_current_interface_dofs();
1197 auto &copy_data_face = copy_data.new_face_data(fe_iv, Components::count_extractors());
1198
1199 const auto &JxW = fe_iv[0]->get_JxW_values();
1200 const auto &q_points = fe_iv[0]->get_quadrature_points();
1201 const auto &q_indices = fe_iv[0]->quadrature_point_indices();
1202 const auto &normals = fe_iv[0]->get_normal_vectors();
1203
1204 auto &solution = scratch_data.solution_interface;
1205 fe_iv[0]->get_fe_face_values(0).get_function_values(solution_global, solution[0][0]);
1206 fe_iv[0]->get_fe_face_values(1).get_function_values(solution_global, solution[1][0]);
1207 for (uint i = 1; i < Components::count_fe_subsystems(); ++i) {
1208 fe_iv[i]->get_fe_face_values(0).get_function_values(sol_vector[i], solution[0][i]);
1209 fe_iv[i]->get_fe_face_values(1).get_function_values(sol_vector[i], solution[1][i]);
1210 }
1211
1212 auto j_numflux = jacobian_2_tuple<Tensor<1, dim>, Model>();
1214 if (jacobian_tmp_built[k] && model.get_components().jacobians_constant(0, k)) return;
1215
1216 const uint from_n_dofs = fe_iv[k]->n_current_interface_dofs();
1217
1218 // Pre-compute interface DoF component indices and face numbers
1219 std::vector<uint> iface_comp_0(to_n_dofs);
1220 std::vector<uint> iface_face_0(to_n_dofs);
1221 for (uint i = 0; i < to_n_dofs; ++i) {
1222 const auto &cd_i = fe_iv[0]->interface_dof_to_dof_indices(i);
1223 iface_face_0[i] = cd_i[0] == numbers::invalid_unsigned_int ? 1 : 0;
1224 iface_comp_0[i] = fe_iv[0]->get_fe().system_to_component_index(cd_i[iface_face_0[i]]).first;
1225 }
1226 std::vector<uint> iface_comp_k(from_n_dofs);
1227 std::vector<uint> iface_face_k(from_n_dofs);
1228 std::vector<uint> iface_dof_k(from_n_dofs);
1229 for (uint j = 0; j < from_n_dofs; ++j) {
1230 const auto &cd_j = fe_iv[k]->interface_dof_to_dof_indices(j);
1231 iface_face_k[j] = cd_j[0] == numbers::invalid_unsigned_int ? 1 : 0;
1232 iface_dof_k[j] = cd_j[iface_face_k[j]];
1233 iface_comp_k[j] = fe_iv[k]->get_fe().system_to_component_index(iface_dof_k[j]).first;
1234 }
1235
1236 for (const auto &q_index : q_indices) {
1237 const auto &x_q = q_points[q_index];
1238 auto sol_q_s =
1239 std::tuple_cat(local_sol_q(solution[0], q_index), std::tie(extracted_data, variables, cell_width));
1240 auto sol_q_n =
1241 std::tuple_cat(local_sol_q(solution[1], q_index), std::tie(extracted_data, variables, ncell_width));
1242
1243 model.template jacobian_numflux<k, 0>(std::get<k>(j_numflux), normals[q_index], x_q, fe_conv(sol_q_s),
1244 fe_conv(sol_q_n));
1245
1246 if (!std::get<k>(j_numflux)[0].is_finite() || !std::get<k>(j_numflux)[1].is_finite()) exception = true;
1247
1248 for (uint i = 0; i < to_n_dofs; ++i) {
1249 const auto component_i = iface_comp_0[i];
1250 for (uint j = 0; j < from_n_dofs; ++j) {
1251 const auto component_j = iface_comp_k[j];
1252 const uint face_no_j = iface_face_k[j];
1253
1254 copy_data_face.cell_jacobian[k](i, j) +=
1255 JxW[q_index] *
1256 fe_iv[k]->get_fe_face_values(face_no_j).shape_value_component(
1257 iface_dof_k[j], q_index, component_j) * // weight * dx * phi_j(x_q)
1258 (fe_iv[0]->jump_in_shape_values(i, q_index, component_i) *
1259 scalar_product(std::get<k>(j_numflux)[face_no_j](component_i, component_j),
1260 normals[q_index])); // [[phi_i(x_q)]] * j_numflux(x_q, u_q)
1261 }
1262 }
1263 }
1264 });
1265 };
1266 const auto copier = [&](const CopyData &c) {
1267 try {
1268 constraints.distribute_local_to_global(c.cell_jacobian[0], c.local_dof_indices[0], jacobian);
1269 constraints.distribute_local_to_global(c.cell_mass_jacobian, c.local_dof_indices[0], jacobian);
1270 for (auto &cdf : c.face_data) {
1271 constraints.distribute_local_to_global(cdf.cell_jacobian[0], cdf.joint_dof_indices[0], jacobian);
1272 if constexpr (Components::count_extractors() > 0) {
1273 FullMatrix<NumberType> extractor_dependence(cdf.joint_dof_indices[0].size(),
1274 extractor_dof_indices.size());
1275 cdf.extractor_cell_jacobian.mmult(extractor_dependence, this->extractor_jacobian);
1276 constraints.distribute_local_to_global(extractor_dependence, cdf.joint_dof_indices[0],
1278 }
1279 }
1280 if constexpr (Components::count_extractors() > 0) {
1281 FullMatrix<NumberType> extractor_dependence(c.local_dof_indices[0].size(), extractor_dof_indices.size());
1282 c.extractor_cell_jacobian.mmult(extractor_dependence, this->extractor_jacobian);
1283 constraints.distribute_local_to_global(extractor_dependence, c.local_dof_indices[0],
1285 }
1286
1287 // LDG things
1288
1289 for (uint i = 1; i < Components::count_fe_subsystems(); ++i) {
1290 if (jacobian_tmp_built[i] && model.get_components().jacobians_constant(0, i)) continue;
1291 j_ug[i].add(c.local_dof_indices[0], c.local_dof_indices[i], c.cell_jacobian[i]);
1292 }
1293 for (auto &cdf : c.face_data) {
1294 for (uint i = 1; i < Components::count_fe_subsystems(); ++i) {
1295 if (jacobian_tmp_built[i] && model.get_components().jacobians_constant(0, i)) continue;
1296 j_ug[i].add(cdf.joint_dof_indices[0], cdf.joint_dof_indices[i], cdf.cell_jacobian[i]);
1297 }
1298 }
1299 } catch (...) {
1300 exception = true;
1301 }
1302 };
1303
1304 const UpdateFlags update_flags =
1305 update_values | update_gradients | update_quadrature_points | update_JxW_values;
1306 const MeshWorker::AssembleFlags assemble_flags = MeshWorker::assemble_own_cells |
1307 MeshWorker::assemble_boundary_faces |
1308 MeshWorker::assemble_own_interior_faces_once;
1309 Scratch scratch_data(mapping, dof_handler_list, quadrature, quadrature_face, update_flags);
1310 CopyData copy_data;
1311
1312 Timer timer;
1313
1315 if (!ldg_matrix_built[k] || !model.get_components().jacobians_constant(k, k - 1))
1316 rebuild_ldg_jacobian<k>(solution_global);
1317 if (!jacobian_tmp_built[k] || !model.get_components().jacobians_constant(0, k)) j_ug[k] = 0;
1318 });
1319 rebuild_ldg_vectors(solution_global);
1320
1321 const auto schedule = schedule_for(assembly_cost::momentum_integral);
1322 MeshWorker::mesh_loop(locally_owned_cells(dof_handler), cell_worker, copier, scratch_data, copy_data,
1323 assemble_flags, boundary_worker, face_worker, schedule.queue_length, schedule.chunk_size);
1324
1325 if (exception) throw std::runtime_error("Infinity encountered in jacobian construction");
1326
1327 tbb::parallel_for(
1328 tbb::blocked_range<uint>(1, Components::count_fe_subsystems()), [&](tbb::blocked_range<uint> rk) {
1329 for (uint k = rk.begin(); k < rk.end(); ++k) {
1330 if (!jacobian_tmp_built[k] || !model.get_components().jacobians_constant(0, k)) {
1331 jacobian_tmp[k] = 0;
1332 tbb::parallel_for(
1333 tbb::blocked_range<uint>(0, Components::count_fe_functions(0)), [&](tbb::blocked_range<uint> r) {
1334 for (uint q = r.begin(); q < r.end(); ++q)
1335 for (const auto &c : model.get_components().ldg_couplings(k, 0))
1336 j_ug[k].block(q, c[0]).mmult(jacobian_tmp[k].block(q, c[1]), j_gu[k].block(c[0], c[1]),
1337 Vector<NumberType>(), false);
1338 });
1339 jacobian_tmp_built[k] = true;
1340 }
1341 }
1342 });
1343
1344 tbb::parallel_for(
1345 tbb::blocked_range<uint>(0, Components::count_fe_functions(0)), [&](tbb::blocked_range<uint> rc1) {
1346 tbb::parallel_for(tbb::blocked_range<uint>(0, Components::count_fe_functions(0)),
1347 [&](tbb::blocked_range<uint> rc2) {
1348 for (uint c1 = rc1.begin(); c1 < rc1.end(); ++c1)
1349 for (uint c2 = rc2.begin(); c2 < rc2.end(); ++c2) {
1350 for (uint k = 1; k < Components::count_fe_subsystems(); ++k)
1351 jacobian.block(c1, c2).add(NumberType(1.), jacobian_tmp[k].block(c1, c2));
1352 jacobian.block(c1, c2) *= weight;
1353 }
1354 });
1355 });
1356
1357 timings_jacobian.push_back(timer.wall_time());
1358 }
1359 SummaryEvent summary() const override
1360 {
1361 SummaryEvent result{.component = "LDG"};
1362 result.timing("reinit", average_time_reinit() * 1000, num_reinits())
1363 .timing("residual", average_time_residual_assembly() * 1000, num_residuals())
1365 return result;
1366 }
1367
1368 double average_time_reinit() const
1369 {
1370 double t = 0.;
1371 double n = timings_reinit.size();
1372 for (const auto &t_ : timings_reinit)
1373 t += t_ / n;
1374 return t;
1375 }
1376 uint num_reinits() const { return timings_reinit.size(); }
1377
1379 {
1380 double t = 0.;
1381 double n = timings_residual.size();
1382 for (const auto &t_ : timings_residual)
1383 t += t_ / n;
1384 return t;
1385 }
1386 uint num_residuals() const { return timings_residual.size(); }
1387
1389 {
1390 double t = 0.;
1391 double n = timings_jacobian.size();
1392 for (const auto &t_ : timings_jacobian)
1393 t += t_ / n;
1394 return t;
1395 }
1396 uint num_jacobians() const { return timings_jacobian.size(); }
1397
1398 protected:
1400 using Base::dof_handler;
1401 using Base::fe;
1402 using Base::mapping;
1403 using Base::model;
1404
1405 QGauss<dim> quadrature;
1407 using Base::schedule_for;
1408
1409 std::vector<const DoFHandler<dim> *> dof_handler_list;
1410
1411 mutable array<BlockVector<NumberType>, Components::count_fe_subsystems()> sol_vector;
1412 mutable array<BlockVector<NumberType>, Components::count_fe_subsystems()> sol_vector_tmp;
1413 mutable array<Vector<NumberType>, Components::count_fe_subsystems()> sol_vector_vec_tmp;
1414
1415 BlockSparsityPattern sparsity_pattern_jacobian;
1416 BlockSparsityPattern sparsity_pattern_mass;
1417 array<BlockSparsityPattern, Components::count_fe_subsystems()> sparsity_pattern_ug;
1418 array<BlockSparsityPattern, Components::count_fe_subsystems()> sparsity_pattern_gu;
1419 array<BlockSparsityPattern, Components::count_fe_subsystems()> sparsity_pattern_wg;
1420
1421 array<BlockSparseMatrix<NumberType>, Components::count_fe_subsystems()> jacobian_tmp;
1422
1423 BlockSparseMatrix<NumberType> mass_matrix;
1424 SparseMatrix<NumberType> component_mass_matrix_inverse;
1425 array<BlockSparseMatrix<NumberType>, Components::count_fe_subsystems()> j_ug;
1426 mutable array<BlockSparseMatrix<NumberType>, Components::count_fe_subsystems()> j_gu;
1427 mutable array<BlockSparseMatrix<NumberType>, Components::count_fe_subsystems()> j_wg;
1428 mutable array<BlockSparseMatrix<NumberType>, Components::count_fe_subsystems()> j_wg_tmp;
1429
1430 std::vector<double> timings_reinit;
1431 std::vector<double> timings_residual;
1432 std::vector<double> timings_jacobian;
1433
1434 mutable array<bool, Components::count_fe_subsystems()> ldg_matrix_built;
1435 mutable array<bool, Components::count_fe_subsystems()> jacobian_tmp_built;
1436
1437 using Base::EoM_config;
1439
1440 void rebuild_ldg_vectors(const VectorType &sol) const
1441 {
1443 if (!model.get_components().jacobians_constant(k, k - 1)) {
1444 if (k == 1)
1445 build_ldg_vector<k - 1, k>(sol, sol_vector[k], sol_vector_tmp[k]);
1446 else
1447 build_ldg_vector<k - 1, k>(sol_vector[k - 1], sol_vector[k], sol_vector_tmp[k]);
1448 } else {
1450
1451 j_gu[k].vmult(sol_vector[k], sol);
1452 }
1453 });
1454 }
1455
1456 template <int k> void rebuild_ldg_jacobian(const VectorType &sol) const
1457 {
1458 static_assert(k > 0);
1459 ldg_matrix_built[k] = true;
1460 if constexpr (k == 1)
1462 else {
1463 if (!ldg_matrix_built[k - 1]) rebuild_ldg_jacobian<k - 1>(sol);
1464 build_ldg_jacobian<k - 1, k>(sol_vector[k - 1], j_wg[k], j_wg_tmp[k]);
1465 for (const auto &c : model.get_components().ldg_couplings(k, 0))
1466 for (const auto &b : model.get_components().ldg_couplings(k, k - 1))
1467 if (b[0] == c[0])
1468 j_wg[k]
1469 .block(b[0], b[1])
1470 .mmult(j_gu[k].block(c[0], c[1]), j_gu[k - 1].block(b[1], c[1]), Vector<NumberType>(), false);
1471 }
1472 }
1473
1483 template <int from, int to, typename VectorType, typename VectorTypeldg>
1484 void build_ldg_vector(const VectorType &solution_global, VectorTypeldg &ldg_vector,
1485 VectorTypeldg &ldg_vector_tmp) const
1486 {
1487 static_assert(to - from == 1, "can only build LDG from last level!");
1488 using Iterator = typename Triangulation<dim>::active_cell_iterator;
1490 using CopyData = internal::CopyData_R<NumberType>;
1491 const auto &constraints = discretization.get_constraints(to);
1492
1493 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
1494 const auto &fe_v = scratch_data.new_fe_values(t_cell);
1495 const uint to_n_dofs = fe_v[to]->get_fe().n_dofs_per_cell();
1496 copy_data.reinit(scratch_data.cell[to], to_n_dofs);
1497
1498 const auto &JxW = fe_v[to]->get_JxW_values();
1499 const auto &q_points = fe_v[to]->get_quadrature_points();
1500 const auto &q_indices = fe_v[to]->quadrature_point_indices();
1501 auto &solution = scratch_data.solution;
1502
1503 fe_v[from]->get_function_values(solution_global, solution[from]);
1504
1505 array<Tensor<1, dim, NumberType>, Components::count_fe_functions(to)> flux{};
1506 array<NumberType, Components::count_fe_functions(to)> source{};
1507 for (const auto &q_index : q_indices) {
1508 const auto &x_q = q_points[q_index];
1509 model.template ldg_flux<to>(flux, x_q, solution[from][q_index]);
1510 model.template ldg_source<to>(source, x_q, solution[from][q_index]);
1511
1512 for (uint i = 0; i < to_n_dofs; ++i) {
1513 const auto component_i = fe_v[to]->get_fe().system_to_component_index(i).first;
1514 copy_data.cell_residual(i) += JxW[q_index] * // dx
1515 (-scalar_product(fe_v[to]->shape_grad_component(i, q_index, component_i),
1516 flux[component_i]) // -dphi_i(x_q) * flux(x_q, u_q)
1517 + fe_v[to]->shape_value_component(i, q_index, component_i) *
1518 source[component_i]); // -phi_i(x_q) * source(x_q, u_q)
1519 }
1520 }
1521 };
1522 const auto boundary_worker = [&](const Iterator &t_cell, const uint &face_no, Scratch &scratch_data,
1523 CopyData &copy_data) {
1524 const auto &fe_fv = scratch_data.new_fe_boundary_values(t_cell, face_no);
1525 const uint to_n_dofs = fe_fv[to]->get_fe().n_dofs_per_cell();
1526
1527 const auto &JxW = fe_fv[to]->get_JxW_values();
1528 const auto &q_points = fe_fv[to]->get_quadrature_points();
1529 const auto &q_indices = fe_fv[to]->quadrature_point_indices();
1530 const std::vector<Tensor<1, dim>> &normals = fe_fv[from]->get_normal_vectors();
1531 auto &solution = scratch_data.solution;
1532
1533 fe_fv[from]->get_function_values(solution_global, solution[from]);
1534
1535 array<Tensor<1, dim, NumberType>, Components::count_fe_functions(to)> numflux{};
1536 for (const auto &q_index : q_indices) {
1537 const auto &x_q = q_points[q_index];
1538 model.template ldg_boundary_numflux<to>(numflux, normals[q_index], x_q, solution[from][q_index]);
1539
1540 for (uint i = 0; i < to_n_dofs; ++i) {
1541 const auto component_i = fe_fv[to]->get_fe().system_to_component_index(i).first;
1542 copy_data.cell_residual(i) +=
1543 JxW[q_index] * // weight * dx
1544 (fe_fv[to]->shape_value_component(i, q_index, component_i) *
1545 scalar_product(numflux[component_i], normals[q_index])); // phi_i(x_q) * numflux(x_q, u_q) * n(x_q)
1546 }
1547 }
1548 };
1549 const auto face_worker = [&](const Iterator &t_cell, const uint &f, const uint &sf, const Iterator &t_ncell,
1550 const uint &nf, const unsigned int &nsf, Scratch &scratch_data,
1551 CopyData &copy_data) {
1552 const auto &fe_iv = scratch_data.new_fe_interface_values(t_cell, f, sf, t_ncell, nf, nsf);
1553 const uint to_n_dofs = fe_iv[to]->n_current_interface_dofs();
1554 auto &copy_data_face = copy_data.new_face_data(*(fe_iv[to]));
1555
1556 const auto &JxW = fe_iv[to]->get_JxW_values();
1557 const auto &q_points = fe_iv[to]->get_quadrature_points();
1558 const auto &q_indices = fe_iv[to]->quadrature_point_indices();
1559 // normals are facing outwards!
1560 const std::vector<Tensor<1, dim>> &normals = fe_iv[to]->get_normal_vectors();
1561 auto &solution = scratch_data.solution_interface;
1562
1563 fe_iv[from]->get_fe_face_values(0).get_function_values(solution_global, solution[0][from]);
1564 fe_iv[from]->get_fe_face_values(1).get_function_values(solution_global, solution[1][from]);
1565
1566 array<Tensor<1, dim, NumberType>, Components::count_fe_functions(to)> numflux{};
1567 for (const auto &q_index : q_indices) {
1568 const auto &x_q = q_points[q_index];
1569 model.template ldg_numflux<to>(numflux, normals[q_index], x_q, solution[0][from][q_index],
1570 solution[1][from][q_index]);
1571
1572 for (uint i = 0; i < to_n_dofs; ++i) {
1573 const auto &cd_i = fe_iv[to]->interface_dof_to_dof_indices(i);
1574 const auto component_i = cd_i[0] == numbers::invalid_unsigned_int
1575 ? fe_iv[to]->get_fe().system_to_component_index(cd_i[1]).first
1576 : fe_iv[to]->get_fe().system_to_component_index(cd_i[0]).first;
1577 copy_data_face.cell_residual(i) +=
1578 JxW[q_index] * // weight * dx
1579 (fe_iv[to]->jump_in_shape_values(i, q_index, component_i) *
1580 scalar_product(numflux[component_i],
1581 normals[q_index])); // [[phi_i(x_q)]] * numflux(x_q, u_q) * n(x_q)
1582 }
1583 }
1584 };
1585 const auto copier = [&](const CopyData &c) {
1586 constraints.distribute_local_to_global(c.cell_residual, c.local_dof_indices, ldg_vector_tmp);
1587 for (auto &cdf : c.face_data)
1588 constraints.distribute_local_to_global(cdf.cell_residual, cdf.joint_dof_indices, ldg_vector_tmp);
1589 };
1590
1591 const MeshWorker::AssembleFlags assemble_flags = MeshWorker::assemble_own_cells |
1592 MeshWorker::assemble_boundary_faces |
1593 MeshWorker::assemble_own_interior_faces_once;
1594 Scratch scratch_data(mapping, dof_handler_list, quadrature, quadrature_face);
1595 CopyData copy_data;
1596
1597 ldg_vector_tmp = 0;
1598 const auto schedule = schedule_for(assembly_cost::algebraic);
1599 MeshWorker::mesh_loop(locally_owned_cells(dof_handler), cell_worker, copier, scratch_data, copy_data,
1600 assemble_flags, boundary_worker, face_worker, schedule.queue_length, schedule.chunk_size);
1601
1602 for (uint i = 0; i < Components::count_fe_functions(to); ++i)
1603 component_mass_matrix_inverse.vmult(ldg_vector.block(i), ldg_vector_tmp.block(i));
1604 }
1605
1615 template <int from, int to, typename VectorType>
1616 void build_ldg_jacobian(const VectorType &solution_global, BlockSparseMatrix<NumberType> &ldg_jacobian,
1617 BlockSparseMatrix<NumberType> &ldg_jacobian_tmp) const
1618 {
1619 static_assert(to - from == 1, "can only build LDG from last level!");
1620 using Iterator = typename Triangulation<dim>::active_cell_iterator;
1622 using CopyData = internal::CopyData_J<NumberType>;
1623
1624 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
1625 const auto &fe_v = scratch_data.new_fe_values(t_cell);
1626 const uint to_n_dofs = fe_v[to]->get_fe().n_dofs_per_cell();
1627 const uint from_n_dofs = fe_v[from]->get_fe().n_dofs_per_cell();
1628 copy_data.reinit(scratch_data.cell[from], scratch_data.cell[to], from_n_dofs, to_n_dofs);
1629
1630 const auto &JxW = fe_v[to]->get_JxW_values();
1631 const auto &q_points = fe_v[to]->get_quadrature_points();
1632 const auto &q_indices = fe_v[to]->quadrature_point_indices();
1633 auto &solution = scratch_data.solution;
1634
1635 fe_v[from]->get_function_values(solution_global, solution[from]);
1636
1637 SimpleMatrix<Tensor<1, dim>, Components::count_fe_functions(to), Components::count_fe_functions(from)> j_flux;
1638 SimpleMatrix<NumberType, Components::count_fe_functions(to), Components::count_fe_functions(from)> j_source;
1639 for (const auto &q_index : q_indices) {
1640 const auto &x_q = q_points[q_index];
1641 model.template jacobian_flux_source<from, to>(j_flux, j_source, x_q, solution[from][q_index]);
1642
1643 for (uint i = 0; i < to_n_dofs; ++i) {
1644 const auto component_i = fe_v[to]->get_fe().system_to_component_index(i).first;
1645 for (uint j = 0; j < from_n_dofs; ++j) {
1646 const auto component_j = fe_v[from]->get_fe().system_to_component_index(j).first;
1647 copy_data.cell_jacobian(i, j) +=
1648 JxW[q_index] *
1649 fe_v[from]->shape_value_component(j, q_index, component_j) * // weight * dx * phi_j(x_q)
1650 (-scalar_product(fe_v[to]->shape_grad_component(i, q_index, component_i),
1651 j_flux(component_i, component_j)) // -dphi_i(x_q) * jflux(x_q, u_q)
1652 + fe_v[to]->shape_value_component(i, q_index, component_i) *
1653 j_source(component_i, component_j)); // -phi_i(x_q) * jsource(x_q, u_q)
1654 }
1655 }
1656 }
1657 };
1658 const auto boundary_worker = [&](const Iterator &t_cell, const uint &face_no, Scratch &scratch_data,
1659 CopyData &copy_data) {
1660 const auto &fe_fv = scratch_data.new_fe_boundary_values(t_cell, face_no);
1661 const uint to_n_dofs = fe_fv[to]->get_fe().n_dofs_per_cell();
1662 const uint from_n_dofs = fe_fv[from]->get_fe().n_dofs_per_cell();
1663
1664 const auto &JxW = fe_fv[to]->get_JxW_values();
1665 const auto &q_points = fe_fv[to]->get_quadrature_points();
1666 const auto &q_indices = fe_fv[to]->quadrature_point_indices();
1667 const std::vector<Tensor<1, dim>> &normals = fe_fv[to]->get_normal_vectors();
1668 auto &solution = scratch_data.solution;
1669
1670 fe_fv[from]->get_function_values(solution_global, solution[from]);
1671
1672 SimpleMatrix<Tensor<1, dim>, Components::count_fe_functions(to), Components::count_fe_functions(from)>
1673 j_boundary_numflux;
1674 for (const auto &q_index : q_indices) {
1675 const auto &x_q = q_points[q_index];
1676 model.template jacobian_boundary_numflux<from, to>(j_boundary_numflux, normals[q_index], x_q,
1677 solution[from][q_index]);
1678
1679 for (uint i = 0; i < to_n_dofs; ++i) {
1680 const auto component_i = fe_fv[to]->get_fe().system_to_component_index(i).first;
1681 for (uint j = 0; j < from_n_dofs; ++j) {
1682 const auto component_j = fe_fv[from]->get_fe().system_to_component_index(j).first;
1683 copy_data.cell_jacobian(i, j) +=
1684 JxW[q_index] *
1685 fe_fv[from]->shape_value_component(j, q_index, component_j) * // weight * dx * phi_j(x_q)
1686 (fe_fv[to]->shape_value_component(i, q_index, component_i) *
1687 scalar_product(j_boundary_numflux(component_i, component_j),
1688 normals[q_index])); // phi_i(x_q) * j_numflux(x_q, u_q) * n(x_q)
1689 }
1690 }
1691 }
1692 };
1693 const auto face_worker = [&](const Iterator &t_cell, const uint &f, const uint &sf, const Iterator &t_ncell,
1694 const uint &nf, const unsigned int &nsf, Scratch &scratch_data,
1695 CopyData &copy_data) {
1696 const auto &fe_iv = scratch_data.new_fe_interface_values(t_cell, f, sf, t_ncell, nf, nsf);
1697 const uint to_n_dofs = fe_iv[to]->n_current_interface_dofs();
1698 const uint from_n_dofs = fe_iv[from]->n_current_interface_dofs();
1699 auto &copy_data_face = copy_data.new_face_data(*(fe_iv[from]), *(fe_iv[to]));
1700
1701 const auto &JxW = fe_iv[to]->get_JxW_values();
1702 const auto &q_points = fe_iv[to]->get_quadrature_points();
1703 const auto &q_indices = fe_iv[to]->quadrature_point_indices();
1704 const std::vector<Tensor<1, dim>> &normals = fe_iv[to]->get_normal_vectors();
1705 auto &solution = scratch_data.solution_interface;
1706
1707 fe_iv[from]->get_fe_face_values(0).get_function_values(solution_global, solution[0][from]);
1708 fe_iv[from]->get_fe_face_values(1).get_function_values(solution_global, solution[1][from]);
1709
1710 array<SimpleMatrix<Tensor<1, dim>, Components::count_fe_functions(to), Components::count_fe_functions(from)>,
1711 2>
1712 j_numflux;
1713 for (const auto &q_index : q_indices) {
1714 const auto &x_q = q_points[q_index];
1715 model.template jacobian_numflux<from, to>(j_numflux, normals[q_index], x_q, solution[0][from][q_index],
1716 solution[1][from][q_index]);
1717
1718 for (uint i = 0; i < to_n_dofs; ++i) {
1719 const auto &cd_i = fe_iv[to]->interface_dof_to_dof_indices(i);
1720 const uint face_no_i = cd_i[0] == numbers::invalid_unsigned_int ? 1 : 0;
1721 const auto &component_i = fe_iv[to]->get_fe().system_to_component_index(cd_i[face_no_i]).first;
1722 for (uint j = 0; j < from_n_dofs; ++j) {
1723 const auto &cd_j = fe_iv[from]->interface_dof_to_dof_indices(j);
1724 const uint face_no_j = cd_j[0] == numbers::invalid_unsigned_int ? 1 : 0;
1725 const auto &component_j = fe_iv[from]->get_fe().system_to_component_index(cd_j[face_no_j]).first;
1726
1727 copy_data_face.cell_jacobian(i, j) +=
1728 JxW[q_index] *
1729 fe_iv[from]->get_fe_face_values(face_no_j).shape_value_component(
1730 cd_j[face_no_j], q_index, component_j) * // weight * dx * phi_j(x_q)
1731 (fe_iv[to]->jump_in_shape_values(i, q_index, component_i) *
1732 scalar_product(j_numflux[face_no_j](component_i, component_j),
1733 normals[q_index])); // [[phi_i(x_q)]] * j_numflux(x_q, u_q)
1734 }
1735 }
1736 }
1737 };
1738 const auto copier = [&](const CopyData &c) {
1739 ldg_jacobian_tmp.add(c.local_dof_indices_to, c.local_dof_indices_from, c.cell_jacobian);
1740 for (auto &cdf : c.face_data)
1741 ldg_jacobian_tmp.add(cdf.joint_dof_indices_to, cdf.joint_dof_indices_from, cdf.cell_jacobian);
1742 };
1743
1744 const MeshWorker::AssembleFlags assemble_flags = MeshWorker::assemble_own_cells |
1745 MeshWorker::assemble_boundary_faces |
1746 MeshWorker::assemble_own_interior_faces_once;
1747 Scratch scratch_data(mapping, dof_handler_list, quadrature, quadrature_face);
1748 CopyData copy_data;
1749
1750 ldg_jacobian_tmp = 0;
1751 ldg_jacobian = 0;
1752 const auto schedule = schedule_for(assembly_cost::algebraic);
1753 MeshWorker::mesh_loop(locally_owned_cells(dof_handler), cell_worker, copier, scratch_data, copy_data,
1754 assemble_flags, boundary_worker, face_worker, schedule.queue_length, schedule.chunk_size);
1755 for (const auto &c : model.get_components().ldg_couplings(to, from))
1756 component_mass_matrix_inverse.mmult(ldg_jacobian.block(c[0], c[1]), ldg_jacobian_tmp.block(c[0], c[1]),
1757 Vector<NumberType>(), false);
1758 }
1759
1768 void build_ldg_sparsity(BlockSparsityPattern &sparsity_pattern, const DoFHandler<dim> &to_dofh,
1769 const DoFHandler<dim> &from_dofh, const int stencil = 1,
1770 bool add_extractor_dofs = false) const
1771 {
1772 const auto &triangulation = discretization.get_triangulation();
1773 auto to_dofs_per_component = DoFTools::count_dofs_per_fe_component(to_dofh);
1774 auto from_dofs_per_component = DoFTools::count_dofs_per_fe_component(from_dofh);
1775 auto to_n_fe = to_dofs_per_component.size();
1776 auto from_n_fe = from_dofs_per_component.size();
1777 for (uint j = 1; j < from_dofs_per_component.size(); ++j)
1778 if (from_dofs_per_component[j] != from_dofs_per_component[0])
1779 throw std::runtime_error("For LDG the FE basis of all systems must be equal!");
1780 for (uint j = 1; j < to_dofs_per_component.size(); ++j)
1781 if (to_dofs_per_component[j] != to_dofs_per_component[0])
1782 throw std::runtime_error("For LDG the FE basis of all systems must be equal!");
1783
1784 BlockDynamicSparsityPattern dsp(to_n_fe, from_n_fe);
1785 for (uint i = 0; i < to_n_fe; ++i)
1786 for (uint j = 0; j < from_n_fe; ++j)
1787 dsp.block(i, j).reinit(to_dofs_per_component[i], from_dofs_per_component[j]);
1788 dsp.collect_sizes();
1789
1790 const auto to_dofs_per_cell = to_dofh.get_fe().dofs_per_cell;
1791 const auto from_dofs_per_cell = from_dofh.get_fe().dofs_per_cell;
1792
1793 for (const auto &t_cell : triangulation.active_cell_iterators()) {
1794 std::vector<types::global_dof_index> to_dofs(to_dofs_per_cell);
1795 std::vector<types::global_dof_index> from_dofs(from_dofs_per_cell);
1796 const auto to_cell = typename DoFHandler<dim>::active_cell_iterator(
1797 &to_dofh.get_triangulation(), t_cell->level(), t_cell->index(), &to_dofh);
1798 const auto from_cell = typename DoFHandler<dim>::active_cell_iterator(
1799 &from_dofh.get_triangulation(), t_cell->level(), t_cell->index(), &from_dofh);
1800 to_cell->get_dof_indices(to_dofs);
1801 from_cell->get_dof_indices(from_dofs);
1802
1803 std::function<void(decltype(from_cell) &, const int)> add_all_neighbor_dofs = [&](const auto &from_cell,
1804 const int stencil_level) {
1805 for (const auto face_no : from_cell->face_indices()) {
1806 const auto face = from_cell->face(face_no);
1807 if (!face->at_boundary()) {
1808 auto neighbor_cell = from_cell->neighbor(face_no);
1809
1810 if (dim == 1)
1811 while (neighbor_cell->has_children())
1812 neighbor_cell = neighbor_cell->child(face_no == 0 ? 1 : 0);
1813
1814 // add all children
1815 else if (neighbor_cell->has_children()) {
1816 throw std::runtime_error("not yet implemented lol");
1817 }
1818
1819 if (!neighbor_cell->is_active()) continue;
1820
1821 std::vector<types::global_dof_index> tmp(from_dofs_per_cell);
1822 neighbor_cell->get_dof_indices(tmp);
1823
1824 from_dofs.insert(std::end(from_dofs), std::begin(tmp), std::end(tmp));
1825
1826 if (stencil_level < stencil) add_all_neighbor_dofs(neighbor_cell, stencil_level + 1);
1827 }
1828 }
1829 };
1830
1831 add_all_neighbor_dofs(from_cell, 1);
1832
1833 for (const auto i : to_dofs)
1834 for (const auto j : from_dofs)
1835 dsp.add(i, j);
1836 }
1837
1838 if (add_extractor_dofs)
1839 for (uint row = 0; row < dsp.n_rows(); ++row)
1840 for (const auto &col : extractor_dof_indices)
1841 dsp.add(row, col);
1842
1843 sparsity_pattern.copy_from(dsp);
1844 }
1845
1852 void build_inverse(const SparseMatrix<NumberType> &in, SparseMatrix<NumberType> &out) const
1853 {
1854 GrowingVectorMemory<Vector<NumberType>> mem;
1855 SparseDirectUMFPACK inverse;
1856 inverse.initialize(in);
1857 // out is m x n
1858 // we go row-wise, i.e. we keep one n fixed and insert a row
1859 tbb::parallel_for(tbb::blocked_range<int>(0, out.n()), [&](tbb::blocked_range<int> r) {
1860 typename VectorMemory<Vector<NumberType>>::Pointer tmp(mem);
1861 tmp->reinit(out.m());
1862 for (int n = r.begin(); n < r.end(); ++n) {
1863 *tmp = 0;
1864 (*tmp)[n] = 1.;
1865 inverse.solve(*tmp);
1866 for (auto it = out.begin(n); it != out.end(n); ++it)
1867 it->value() = (*tmp)[it->column()];
1868 }
1869 });
1870 }
1871
1872 protected:
1873 constexpr static int nothing = 0;
1874 using Base::EoM;
1875 using Base::EoM_cell;
1876 using Base::EoM_minimum_guess;
1877 using Base::extractor_jacobian_u;
1878 using Base::old_EoM_cell;
1879 using Base::old_extractor_cell;
1880
1883 template <typename PotentialEvaluation = RawPotentialEvaluation<dim, NumberType>> struct PointEvaluation {
1884 std::vector<Vector<NumberType>> solutions;
1885 std::vector<std::vector<Tensor<1, dim, NumberType>>> gradients{
1886 std::vector<Tensor<1, dim, NumberType>>(Components::count_fe_functions())};
1887 std::vector<std::vector<Tensor<2, dim, NumberType>>> hessians{
1888 std::vector<Tensor<2, dim, NumberType>>(Components::count_fe_functions())};
1889 PotentialEvaluation potential;
1891 std::shared_ptr<FEValues<dim>> fe_values;
1892 };
1893
1901 template <typename RawPotential>
1902 auto evaluate_at(const Point<dim> &x, const typename DoFHandler<dim>::cell_iterator &x_cell,
1903 const VectorType &solution_global, const RawPotential &raw_potential) const
1904 {
1905 using t_Iterator = typename Triangulation<dim>::active_cell_iterator;
1906 const auto x_unit = mapping.transform_real_to_unit_cell(x_cell, x);
1907
1908 std::vector<std::shared_ptr<FEValues<dim>>> fe_v;
1909 for (uint k = 0; k < Components::count_fe_subsystems(); ++k) {
1910 fe_v.emplace_back(std::make_shared<FEValues<dim>>(
1911 mapping, discretization.get_fe(k), x_unit,
1912 update_values | update_gradients | update_quadrature_points | update_JxW_values | update_hessians));
1913 auto cell = dof_handler_list[k]->begin_active();
1914 cell->copy_from(*t_Iterator(x_cell));
1915 fe_v[k]->reinit(cell);
1916 }
1917
1918 PointEvaluation<decltype(evaluate_raw_potential(raw_potential, mapping, x))> evaluation;
1919 for (uint k = 0; k < Components::count_fe_subsystems(); ++k) {
1920 std::vector<Vector<NumberType>> values{Vector<NumberType>(Components::count_fe_functions(k))};
1921 if (k == 0)
1922 fe_v[0]->get_function_values(solution_global, values);
1923 else
1924 fe_v[k]->get_function_values(sol_vector[k], values);
1925 evaluation.solutions.push_back(values[0]);
1926 }
1927 fe_v[0]->get_function_gradients(solution_global, evaluation.gradients);
1928 fe_v[0]->get_function_hessians(solution_global, evaluation.hessians);
1929 evaluation.potential = evaluate_raw_potential(raw_potential, mapping, x);
1930 evaluation.fe_values = fe_v[0];
1931 return evaluation;
1932 }
1933
1934 void readouts(OutputFrame<dim, VectorType> &data_out, const VectorType &solution_global,
1935 const VectorType &variables) const
1936 {
1937 auto raw_potential = reconstruct_raw_potential(
1938 solution_global, dof_handler, mapping,
1939 [&](const auto &p, const auto &values) { return model.raw_potential_gradient(p, values); }, EoM_config,
1940 &this->potential_cache);
1941 auto helper = [&](auto &&...args) {
1942 if constexpr (sizeof...(args) == 3) {
1943 auto &&[id, EoMfun, outputter] = std::forward_as_tuple(std::forward<decltype(args)>(args)...);
1944 data_out.register_readout(id);
1945 auto EoM_cell = this->EoM_cell;
1946 auto EoM_result = get_EoM_point_with_potential(
1947 EoM_cell, solution_global, this->dof_handler, this->mapping, EoMfun,
1948 [&](const auto &p, const auto &) { return p; }, this->EoM_config, this->EoM_minimum_guess,
1949 &this->potential_cache);
1950 if (EoM_result.potential) this->EoM_minimum_guess = EoM_result.potential->minimum;
1951 const auto EoM = EoM_result.point;
1952
1953 // GCC 16 does not find dependent-base members from inside this variadic lambda; keep `this->`.
1954 const auto readout_solution = this->evaluate_at(EoM, EoM_cell, solution_global, raw_potential);
1955 const auto &potential = readout_solution.potential;
1956
1957 // The readout is always at this readout's EoM. The extractors may not be: a model that
1958 // defines extractor_point reads them elsewhere, and dt_variables must see the same
1959 // values here as it does during assembly.
1960 std::array<NumberType, Components::count_extractors()> __extracted_data{{}};
1961 if constexpr (Components::count_extractors() > 0) {
1962 const auto [x, cell] = this->resolve_extractor_point(EoM, EoM_cell, solution_global);
1963 const auto evaluation = this->evaluate_at(x, cell, solution_global, raw_potential);
1964 auto extractor_tuple = std::tuple_cat(
1965 vector_to_tuple<Components::count_fe_subsystems()>(evaluation.solutions),
1966 std::tie(evaluation.gradients[0], evaluation.hessians[0], this->nothing, variables,
1967 evaluation.potential.value, evaluation.potential.gradient, evaluation.potential.mass_hessian));
1968 this->model.extract(__extracted_data, x, fe_more_conv(extractor_tuple));
1969 }
1970 const auto &extracted_data = __extracted_data;
1971
1972 auto solution_tuple =
1973 std::tuple_cat(vector_to_tuple<Components::count_fe_subsystems()>(readout_solution.solutions),
1974 std::tie(readout_solution.gradients[0], readout_solution.hessians[0], extracted_data,
1975 variables, potential.value, potential.gradient, potential.mass_hessian));
1976
1977 outputter(data_out, EoM, fe_more_conv(solution_tuple));
1978 data_out.attach_eom_potential(std::move(EoM_result));
1979 } else {
1981 }
1982 };
1983 model.readouts_multiple(helper, data_out);
1984 data_out.attach_raw_potential(std::move(raw_potential));
1985 }
1986
1987 void extract(std::array<NumberType, Components::count_extractors()> &data, const VectorType &solution_global,
1988 const VectorType &variables, bool search_EoM, bool set_EoM, bool postprocess) const
1989 {
1990 auto EoM = this->EoM;
1991 auto EoM_cell = this->EoM_cell;
1992 if (search_EoM || EoM_cell == *(dof_handler.active_cell_iterators().end())) {
1993 auto EoM_result = get_EoM_point_with_potential(
1994 EoM_cell, solution_global, dof_handler, mapping,
1995 [&](const auto &p, const auto &values) { return model.EoM(p, values); },
1996 [&](const auto &p, const auto &values) { return postprocess ? model.EoM_postprocess(p, values) : p; },
1997 EoM_config, EoM_minimum_guess, &this->potential_cache);
1998 EoM = EoM_result.point;
1999 if (EoM_result.potential) EoM_minimum_guess = EoM_result.potential->minimum;
2000 }
2001 if (set_EoM) {
2002 this->EoM = EoM;
2003 this->EoM_cell = EoM_cell;
2004 }
2005 rebuild_ldg_vectors(solution_global);
2006
2007 const auto raw_potential = this->extractor_raw_potential(solution_global);
2008
2009 const auto [x, cell] = this->resolve_extractor_point(EoM, EoM_cell, solution_global);
2010 const auto evaluation = evaluate_at(x, cell, solution_global, raw_potential);
2011
2012 auto solution_tuple = std::tuple_cat(vector_to_tuple<Components::count_fe_subsystems()>(evaluation.solutions),
2013 std::tie(evaluation.gradients[0], evaluation.hessians[0], this->nothing,
2014 variables, evaluation.potential.value,
2015 evaluation.potential.gradient, evaluation.potential.mass_hessian));
2016
2017 model.extract(data, x, fe_more_conv(solution_tuple));
2018 }
2019
2020 bool jacobian_extractors(FullMatrix<NumberType> &extractor_jacobian, const VectorType &solution_global,
2021 const VectorType &variables)
2022 {
2023 if (extractor_jacobian_u.m() != Components::count_extractors() ||
2024 extractor_jacobian_u.n() != Components::count_fe_functions())
2025 extractor_jacobian_u =
2026 FullMatrix<NumberType>(Components::count_extractors(), Components::count_fe_functions());
2027
2028 auto EoM_result = get_EoM_point_with_potential(
2029 EoM_cell, solution_global, dof_handler, mapping,
2030 [&](const auto &p, const auto &values) { return model.EoM(p, values); },
2031 [&](const auto &p, const auto &values) { return model.EoM_postprocess(p, values); }, EoM_config,
2032 EoM_minimum_guess, &this->potential_cache);
2033 EoM = EoM_result.point;
2034 if (EoM_result.potential) EoM_minimum_guess = EoM_result.potential->minimum;
2035 const auto raw_potential = this->extractor_raw_potential(solution_global);
2036
2037 // The extractor jacobian couples to the dofs of the cell the extractors are actually
2038 // evaluated in, which is the extractor point's cell, not the EoM's.
2039 const auto [x, cell] = this->resolve_extractor_point(EoM, EoM_cell, solution_global);
2040 bool new_cell = (old_extractor_cell != cell);
2041 old_EoM_cell = EoM_cell;
2042 old_extractor_cell = cell;
2043
2044 const auto evaluation = evaluate_at(x, cell, solution_global, raw_potential);
2045 const auto &fe_v = *evaluation.fe_values;
2046 const uint n_dofs = fe_v.get_fe().n_dofs_per_cell();
2047 if (new_cell) {
2048 extractor_dof_indices.resize(n_dofs);
2049 cell->get_dof_indices(extractor_dof_indices);
2050 rebuild_jacobian_sparsity();
2051 }
2052
2053 auto solution_tuple = std::tuple_cat(vector_to_tuple<Components::count_fe_subsystems()>(evaluation.solutions),
2054 std::tie(evaluation.gradients[0], evaluation.hessians[0], this->nothing,
2055 variables, evaluation.potential.value,
2056 evaluation.potential.gradient, evaluation.potential.mass_hessian));
2057
2058 extractor_jacobian_u = 0;
2059 model.template jacobian_extractors<0>(extractor_jacobian_u, x, fe_more_conv(solution_tuple));
2060
2061 if (extractor_jacobian.m() != Components::count_extractors() || extractor_jacobian.n() != n_dofs)
2062 extractor_jacobian = FullMatrix<NumberType>(Components::count_extractors(), n_dofs);
2063
2064 for (uint e = 0; e < Components::count_extractors(); ++e)
2065 for (uint i = 0; i < n_dofs; ++i) {
2066 const auto component_i = fe_v.get_fe().system_to_component_index(i).first;
2067 extractor_jacobian(e, i) =
2068 extractor_jacobian_u(e, component_i) * fe_v.shape_value_component(i, 0, component_i);
2069 }
2070
2071 return new_cell;
2072 }
2073
2074 using Base::timings_variable_jacobian;
2075 using Base::timings_variable_residual;
2076 template <typename... T> static constexpr auto v_tie(T &&...t)
2077 {
2078 return named_tuple<std::tuple<T &...>, StringSet<"variables", "extractors">>(std::tie(t...));
2079 }
2080
2081 template <typename... T> static constexpr auto e_tie(T &&...t)
2082 {
2083 return named_tuple<std::tuple<T &...>,
2084 StringSet<"fe_functions", "fe_derivatives", "fe_hessians", "extractors", "variables">>(
2085 std::tie(t...));
2086 }
2087
2088 virtual void residual_variables(VectorType &residual, const VectorType &variables,
2089 const VectorType &spatial_solution) override
2090 {
2091 Timer timer;
2092 std::array<NumberType, Components::count_extractors()> __extracted_data{{}};
2093 if constexpr (Components::count_extractors() > 0)
2094 extract(__extracted_data, spatial_solution, variables, true, false, false);
2095 const auto &extracted_data = __extracted_data;
2096 model.dt_variables(residual, v_tie(variables, extracted_data));
2097 Kokkos::fence();
2098 timings_variable_residual.push_back(timer.wall_time());
2099 }
2100
2101 virtual void jacobian_variables(FullMatrix<NumberType> &jacobian, const VectorType &variables,
2102 const VectorType &spatial_solution) override
2103 {
2104 Timer timer;
2105 std::array<NumberType, Components::count_extractors()> __extracted_data{{}};
2106 if constexpr (Components::count_extractors() > 0)
2107 extract(__extracted_data, spatial_solution, variables, true, false, false);
2108 const auto &extracted_data = __extracted_data;
2109 model.template jacobian_variables<0>(jacobian, v_tie(variables, extracted_data));
2110 Kokkos::fence();
2111 timings_variable_jacobian.push_back(timer.wall_time());
2112 }
2113 };
2114 } // namespace LDG
2115} // namespace DiFfRG
This is the general assembler interface for any kind of discretization. An assembler is responsible f...
Definition abstract_assembler.hh:54
Definition affine_constraint_metadata.hh:24
A hierarchical configuration tree, readable from JSON and TOML files.
Definition config_tree.hh:32
The LDG assembler that can be used for any LDG scheme, with as many levels as one wants.
Definition ldg.hh:448
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:1987
double average_time_residual_assembly() const
Definition ldg.hh:1378
SummaryEvent summary() const override
Definition ldg.hh:1359
array< BlockVector< NumberType >, Components::count_fe_subsystems()> sol_vector
Definition ldg.hh:1411
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:1616
const FiniteElement< dim > & fe
Definition ldg.hh:122
SparseMatrix< NumberType > component_mass_matrix_inverse
Definition ldg.hh:1424
BlockSparsityPattern sparsity_pattern_jacobian
Definition ldg.hh:1415
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:800
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:965
uint num_residuals() const
Definition ldg.hh:1386
array< BlockSparseMatrix< NumberType >, Components::count_fe_subsystems()> j_wg_tmp
Definition ldg.hh:1428
virtual const BlockSparseMatrix< NumberType > & get_mass_matrix() const override
Obtain the mass matrix.
Definition ldg.hh:734
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:1768
double average_time_jacobian_assembly() const
Definition ldg.hh:1388
virtual void attach_data_output(OutputFrame< 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:539
typename Discretization::NumberType NumberType
Definition ldg.hh:454
static constexpr auto v_tie(T &&...t)
Definition ldg.hh:2076
const Mapping< dim > & mapping
Definition ldg.hh:124
std::vector< double > timings_residual
Definition ldg.hh:1431
array< BlockSparsityPattern, Components::count_fe_subsystems()> sparsity_pattern_ug
Definition ldg.hh:1417
array< BlockVector< NumberType >, Components::count_fe_subsystems()> sol_vector_tmp
Definition ldg.hh:1412
static constexpr uint stencil
Definition ldg.hh:460
virtual void reinit_solution_view(SolutionView< VectorType > &view) const override
Definition ldg.hh:527
auto fe_more_conv(std::tuple< T &... > &t) const
Definition ldg.hh:478
virtual void residual_variables(VectorType &residual, const VectorType &variables, const VectorType &spatial_solution) override
Definition ldg.hh:2088
typename Discretization::SparseMatrixType SparseMatrixType
Definition ldg.hh:456
typename Discretization::VectorType VectorType
Definition ldg.hh:455
BlockSparsityPattern sparsity_pattern_mass
Definition ldg.hh:1416
Assembler(Discretization &discretization, Model &model, const ConfigTree &config)
Definition ldg.hh:509
virtual void reinit_vector(VectorType &vec) const override
Definition ldg.hh:519
virtual void jacobian_variables(FullMatrix< NumberType > &jacobian, const VectorType &variables, const VectorType &spatial_solution) override
Definition ldg.hh:2101
typename Discretization::Components Components
Definition ldg.hh:458
static constexpr auto e_tie(T &&...t)
Definition ldg.hh:2081
uint num_jacobians() const
Definition ldg.hh:1396
virtual void mass(VectorType &residual, const VectorType &solution_global, const VectorType &solution_global_dot, NumberType weight) override
Construct the mass.
Definition ldg.hh:743
std::vector< const DoFHandler< dim > * > dof_handler_list
Definition ldg.hh:1409
AssemblySchedule schedule_for(const double cost_ns) const
Definition ldg.hh:127
auto fe_conv(std::tuple< T &... > &t) const
Definition ldg.hh:463
Model & model
Definition ldg.hh:120
QGauss< dim - 1 > quadrature_face
Definition ldg.hh:1406
array< BlockSparsityPattern, Components::count_fe_subsystems()> sparsity_pattern_wg
Definition ldg.hh:1419
void rebuild_ldg_jacobian(const VectorType &sol) const
Definition ldg.hh:1456
static constexpr uint dim
Definition ldg.hh:459
virtual void reinit_matrix(SparseMatrixType &matrix) const override
Definition ldg.hh:522
array< bool, Components::count_fe_subsystems()> ldg_matrix_built
Definition ldg.hh:1434
array< BlockSparseMatrix< NumberType >, Components::count_fe_subsystems()> j_ug
Definition ldg.hh:1425
virtual void reinit() override
Reinitialize the assembler. This is necessary if the mesh has changed, e.g. after a mesh refinement.
Definition ldg.hh:564
virtual void rebuild_jacobian_sparsity() override
Definition ldg.hh:640
auto ref_conv(std::tuple< T &... > &t) const
Definition ldg.hh:496
bool jacobian_extractors(FullMatrix< NumberType > &extractor_jacobian, const VectorType &solution_global, const VectorType &variables)
Definition ldg.hh:2020
virtual MPI_Comm get_communicator() const override
The communicator this assembler's linear algebra lives on.
Definition ldg.hh:526
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:1852
auto evaluate_at(const Point< dim > &x, const typename DoFHandler< dim >::cell_iterator &x_cell, const VectorType &solution_global, const RawPotential &raw_potential) const
Evaluate every LDG subsystem at x, which lies in x_cell.
Definition ldg.hh:1902
Discretization_ Discretization
Definition ldg.hh:452
array< BlockSparsityPattern, Components::count_fe_subsystems()> sparsity_pattern_gu
Definition ldg.hh:1418
Discretization & discretization
Definition ldg.hh:119
array< BlockSparseMatrix< NumberType >, Components::count_fe_subsystems()> jacobian_tmp
Definition ldg.hh:1421
virtual const BlockSparsityPattern & get_sparsity_pattern_jacobian() const override
Obtain the sparsity pattern of the jacobian matrix.
Definition ldg.hh:730
BlockSparseMatrix< NumberType > mass_matrix
Definition ldg.hh:1423
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:1034
array< BlockSparseMatrix< NumberType >, Components::count_fe_subsystems()> j_wg
Definition ldg.hh:1427
std::vector< types::global_dof_index > extractor_dof_indices
Definition ldg.hh:192
uint num_reinits() const
Definition ldg.hh:1376
void rebuild_ldg_vectors(const VectorType &sol) const
Definition ldg.hh:1440
array< bool, Components::count_fe_subsystems()> jacobian_tmp_built
Definition ldg.hh:1435
QGauss< dim > quadrature
Definition ldg.hh:1405
std::vector< double > timings_reinit
Definition ldg.hh:1430
array< Vector< NumberType >, Components::count_fe_subsystems()> sol_vector_vec_tmp
Definition ldg.hh:1413
std::vector< double > timings_jacobian
Definition ldg.hh:1432
Model_ Model
Definition ldg.hh:453
double average_time_reinit() const
Definition ldg.hh:1368
array< BlockSparseMatrix< NumberType >, Components::count_fe_subsystems()> j_gu
Definition ldg.hh:1426
void readouts(OutputFrame< dim, VectorType > &data_out, const VectorType &solution_global, const VectorType &variables) const
Definition ldg.hh:1934
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:1484
virtual void refinement_indicator(Vector< double > &indicator, const VectorType &solution_global) override
Definition ldg.hh:647
const DoFHandler< dim > & dof_handler
Definition ldg.hh:123
static constexpr uint dim
Definition ldg.hh:44
Vector< NumberType > VectorType
Definition ldg.hh:41
typename DiFfRG::internal::components_of< ModelOrComponents_ >::type Components
Definition ldg.hh:39
BlockSparseMatrix< NumberType > SparseMatrixType
Definition ldg.hh:42
NumberType_ NumberType
Definition ldg.hh:40
Definition ldg.hh:45
void update_assembly_schedules()
Definition ldg.hh:133
DoFHandler< dim >::cell_iterator old_extractor_cell
Definition ldg.hh:182
LDGAssemblerBase(Discretization &discretization, Model &model, const ConfigTree &config)
Definition ldg.hh:55
auto extractor_raw_potential(const VectorType &solution_global) const
Definition ldg.hh:150
FullMatrix< NumberType > extractor_jacobian_du
Definition ldg.hh:190
const FiniteElement< dim > & fe
Definition ldg.hh:122
uint num_variable_jacobians() const
Definition ldg.hh:116
FullMatrix< NumberType > extractor_jacobian_u
Definition ldg.hh:189
std::optional< Point< dim > > EoM_minimum_guess
Definition ldg.hh:185
FullMatrix< NumberType > extractor_jacobian_ddu
Definition ldg.hh:191
typename Discretization::NumberType NumberType
Definition ldg.hh:49
auto & get_discretization()
Definition ldg.hh:76
double average_time_variable_residual_assembly()
Definition ldg.hh:98
const Mapping< dim > & mapping
Definition ldg.hh:124
const Config::EoMConfig EoM_config
Definition ldg.hh:183
static constexpr uint dim
Definition ldg.hh:54
FullMatrix< NumberType > extractor_jacobian
Definition ldg.hh:188
DoFHandler< dim >::cell_iterator old_EoM_cell
Definition ldg.hh:180
Discretization_ Discretization
Definition ldg.hh:47
uint n_owned_cells
Definition ldg.hh:176
typename Discretization::SparseMatrixType SparseMatrixType
Definition ldg.hh:51
DiFfRG::internal::PotentialSystemCache< dim, NumberType > potential_cache
Definition ldg.hh:187
AssemblySchedule schedule_for(const double cost_ns) const
Definition ldg.hh:127
typename Discretization::VectorType VectorType
Definition ldg.hh:50
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:94
const auto & get_discretization() const
Definition ldg.hh:75
Model & model
Definition ldg.hh:120
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:78
double average_time_variable_jacobian_assembly()
Definition ldg.hh:108
virtual IndexSet get_differential_indices() const override
Obtain the dofs which contain time derivatives.
Definition ldg.hh:69
std::vector< double > timings_variable_residual
Definition ldg.hh:194
std::pair< Point< dim >, typename DoFHandler< dim >::cell_iterator > resolve_extractor_point(const Point< dim > &EoM_point, const typename DoFHandler< dim >::cell_iterator &EoM_cell_, const VectorType &solution_global) const
Definition ldg.hh:163
std::vector< double > timings_variable_jacobian
Definition ldg.hh:195
typename Discretization::Components Components
Definition ldg.hh:53
Discretization & discretization
Definition ldg.hh:119
Point< dim > EoM
Definition ldg.hh:184
DoFHandler< dim >::cell_iterator EoM_cell
Definition ldg.hh:179
std::vector< types::global_dof_index > extractor_dof_indices
Definition ldg.hh:192
uint num_variable_residuals() const
Definition ldg.hh:106
Model_ Model
Definition ldg.hh:48
const AssemblyScheduleOverrides schedule_overrides
Definition ldg.hh:177
virtual void rebuild_jacobian_sparsity()=0
ReportPort report_port
Definition ldg.hh:121
const DoFHandler< dim > & dof_handler
Definition ldg.hh:123
void attach(const DoFHandler< dim > &dof_handler, const VectorType &solution, const std::string &name)
Definition output_session.hh:47
Definition output_session.hh:42
void attach_raw_potential(ReconstructedRawPotential< dim, typename VectorType::value_type > potential)
Definition output_session.hh:141
FieldCollector fields()
Definition output_session.hh:92
void register_readout(const std::string &id)
Definition output_session.hh:154
void attach_eom_potential(EoMResult< dim, typename VectorType::value_type > result)
Definition output_session.hh:136
Definition run_reporter.hh:96
void info(spdlog::format_string_t< Args... > format, Args &&...args) const
Definition run_reporter.hh:103
A simple NxM-matrix class, which is used for cell-wise Jacobians.
Definition tuples.hh:170
A read-only, fully-replicated view of the solution.
Definition solution_view.hh:48
void reinit(const dealii::IndexSet &, const dealii::IndexSet &, MPI_Comm)
Establish the layout. No-op for the serial policy.
Definition solution_view.hh:58
The mesh-dependent half of solve_potential, retained across calls.
Definition eom.hh:315
Whether Model chooses its own point at which the extractors are evaluated.
Definition solution_sample.hh:137
constexpr double algebraic
Algebraic couplings without momentum quadrature, e.g. LDG's level-to-level transfers.
Definition assembly_schedule.hh:50
constexpr double local_fe
Local FE work only: mass matrices, refinement indicators. ~0.2-0.3 us/cell for CG p=3.
Definition assembly_schedule.hh:48
constexpr double momentum_integral
Definition assembly_schedule.hh:53
AffineConstraintMetadata< dim > build_affine_constraint_metadata(const Discretization &discretization)
Definition affine_constraint_metadata.hh:99
double cell_width(const CellIterator &cell)
The smallest face-normal width over all faces of cell.
Definition cell_geometry.hh:42
void apply_model_affine_constraints(Model &model, Constraints &constraints, const Context &context)
Definition affine_constraint_metadata.hh:79
constexpr void validate_readout_helper_arity()
Definition abstract_assembler.hh:17
Definition complex_math.hh:10
auto locally_owned_cells(const dealii::DoFHandler< dim > &dof_handler)
The cells this rank assembles.
Definition assembly_schedule.hh:154
ReconstructedRawPotential< dim, typename VectorType::value_type > reconstruct_raw_potential(const VectorType &sol, const dealii::DoFHandler< dim > &dof_handler, const dealii::Mapping< dim > &mapping, const GradientFUN &get_gradient, const Config::EoMConfig &config, internal::PotentialSystemCache< dim, typename VectorType::value_type > *cache=nullptr)
Reconstruct a scalar raw potential without locating its minimum.
Definition eom.hh:1411
AssemblySchedule make_assembly_schedule(const uint n_local_cells, const uint thread_budget, const double cost_ns, const AssemblyScheduleOverrides &overrides={})
Derive one mesh_loop schedule from the cost of a cell.
Definition assembly_schedule.hh:114
uint n_locally_owned_cells(const Discretization &discretization)
How many cells locally_owned_cells() yields.
Definition assembly_schedule.hh:167
auto local_sol_q(const std::array< T, N > &a, uint q_index)
Definition tuples.hh:292
auto jacobian_tuple()
Definition tuples.hh:302
@ config
/discretization/threads.
unsigned int n_threads()
The CPU thread budget this process resolved.
UnusedPotentialEvaluation evaluate_raw_potential(const UnusedPotential &, const dealii::Mapping< dim > &, const dealii::Point< dim > &)
Definition eom.hh:149
auto vector_to_tuple(const std::vector< T > &v)
Definition tuples.hh:231
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:313
unsigned int uint
Definition utils.hh:24
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:177
EoMResult< dim, typename VectorType::value_type > get_EoM_point_with_potential(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 Config::EoMConfig &config, const std::optional< dealii::Point< dim > > &initial_guess=std::nullopt, internal::PotentialSystemCache< dim, typename VectorType::value_type > *cache=nullptr)
Reconstruct a potential whose gradient approximates the model EoM vector field and return a sampled a...
Definition eom.hh:1486
SolutionSample< dim, NumberType > make_solution_sample(const dealii::DoFHandler< dim > &dof_handler, const dealii::Mapping< dim > &mapping, const uint n_components, const FillFUN &fill)
Build a SolutionSample, taking values and gradients from a callback.
Definition solution_sample.hh:154
Overrides, if really wanted by the user: /discretization/{mesh_workers,batch_size}...
Definition assembly_schedule.hh:82
The two trailing arguments of dealii::MeshWorker::mesh_loop.
Definition assembly_schedule.hh:30
Definition eom_config.hh:10
PotentialEvaluation potential
Definition ldg.hh:1889
std::vector< Vector< NumberType > > solutions
Definition ldg.hh:1884
std::shared_ptr< FEValues< dim > > fe_values
Subsystem 0's FEValues at the point, kept for the shape values the extractor jacobian needs.
Definition ldg.hh:1891
std::array< uint, 2 > cell_indices
Definition ldg.hh:430
std::array< double, 2 > values
Definition ldg.hh:431
uint cell_index
Definition ldg.hh:435
double value
Definition ldg.hh:434
std::vector< CopyFaceData_I > face_data
Definition ldg.hh:433
FullMatrix< NumberType > cell_jacobian
Definition ldg.hh:349
std::vector< types::global_dof_index > joint_dof_indices_from
Definition ldg.hh:350
std::vector< types::global_dof_index > joint_dof_indices_to
Definition ldg.hh:351
FullMatrix< NumberType > extractor_cell_jacobian
Definition ldg.hh:384
array< FullMatrix< NumberType >, n_fe_subsystems > cell_jacobian
Definition ldg.hh:383
array< vector< types::global_dof_index >, n_fe_subsystems > joint_dof_indices
Definition ldg.hh:385
void reinit(const array< Iterator, n_fe_subsystems > &cell, const uint n_extractors)
Definition ldg.hh:396
CopyDataFace_J & new_face_data(const array< unique_ptr< FEInterfaceValues< dim > >, n_fe_subsystems > &fe_iv, const uint n_extractors)
Definition ldg.hh:413
FullMatrix< NumberType > cell_mass_jacobian
Definition ldg.hh:389
array< FullMatrix< NumberType >, n_fe_subsystems > cell_jacobian
Definition ldg.hh:388
vector< CopyDataFace_J > face_data
Definition ldg.hh:392
FullMatrix< NumberType > extractor_cell_jacobian
Definition ldg.hh:390
array< vector< types::global_dof_index >, n_fe_subsystems > local_dof_indices
Definition ldg.hh:391
uint dofs_per_cell
Definition ldg.hh:394
CopyDataFace_J & new_face_data(const FEInterfaceValues< dim > &fe_iv_from, const FEInterfaceValues< dim > &fe_iv_to)
Definition ldg.hh:370
std::vector< types::global_dof_index > local_dof_indices_to
Definition ldg.hh:356
FullMatrix< NumberType > cell_jacobian
Definition ldg.hh:354
std::vector< CopyDataFace_J > face_data
Definition ldg.hh:357
std::vector< types::global_dof_index > local_dof_indices_from
Definition ldg.hh:355
void reinit(const Iterator &cell_from, const Iterator &cell_to, uint dofs_per_cell_from, uint dofs_per_cell_to)
Definition ldg.hh:360
Vector< NumberType > cell_residual
Definition ldg.hh:318
std::vector< types::global_dof_index > joint_dof_indices
Definition ldg.hh:319
std::vector< CopyDataFace_R > face_data
Definition ldg.hh:325
void reinit(const Iterator &cell, uint dofs_per_cell)
Definition ldg.hh:327
Vector< NumberType > cell_mass
Definition ldg.hh:323
std::vector< types::global_dof_index > local_dof_indices
Definition ldg.hh:324
Vector< NumberType > cell_residual
Definition ldg.hh:322
CopyDataFace_R & new_face_data(const FEInterfaceValues< dim > &fe_iv)
Definition ldg.hh:337
Class to hold data for each assembly thread, i.e. FEValues for cells, interfaces, as well as pre-allo...
Definition ldg.hh:204
array< uint, n_fe_subsystems > n_components
Definition ldg.hh:301
typename Discretization::NumberType NumberType
Definition ldg.hh:206
vector< Vector< NumberType > > solution_dot
Definition ldg.hh:312
static constexpr uint n_fe_subsystems
Definition ldg.hh:207
const auto & new_fe_values(const t_Iterator &t_cell)
Definition ldg.hh:274
static constexpr uint dim
Definition ldg.hh:205
array< Iterator, n_fe_subsystems > cell
Definition ldg.hh:302
array< unique_ptr< FEFaceValues< dim > >, n_fe_subsystems > fe_boundary_values
Definition ldg.hh:307
typename Triangulation< dim >::active_cell_iterator t_Iterator
Definition ldg.hh:209
ScratchData(const ScratchData< Discretization > &scratch_data)
Definition ldg.hh:246
typename DoFHandler< dim >::active_cell_iterator Iterator
Definition ldg.hh:208
array< unique_ptr< FEInterfaceValues< dim > >, n_fe_subsystems > fe_interface_values
Definition ldg.hh:306
array< array< vector< Vector< NumberType > >, n_fe_subsystems >, 2 > solution_interface
Definition ldg.hh:313
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:282
array< Iterator, n_fe_subsystems > ncell
Definition ldg.hh:303
array< std::vector< uint >, n_fe_subsystems > comp
Definition ldg.hh:309
array< unique_ptr< FEValues< dim > >, n_fe_subsystems > fe_values
Definition ldg.hh:305
array< vector< Vector< NumberType > >, n_fe_subsystems > solution
Definition ldg.hh:311
ScratchData(const Mapping< dim > &mapping, const vector< const DoFHandler< dim > * > &dofh, const dealii::Quadrature< dim > &quadrature, const dealii::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:211
const auto & new_fe_boundary_values(const t_Iterator &t_cell, uint face_no)
Definition ldg.hh:292
Definition tuples.hh:34
Definition run_reporter.hh:73
SummaryEvent & timing(const std::string_view name, const double average_ms, const std::size_t calls)
Definition run_reporter.hh:78
Stand-in for a raw potential that a model has declared it does not read.
Definition eom.hh:140
std::conditional_t< CarriesModel< D >, typename D::Model, D > type
Definition types.hh:63
A class to store a tuple with elements that can be accessed by name. The names are stored as FixedStr...
Definition tuples.hh:56