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

DiFfRG: /home/runner/work/DiFfRG_current/DiFfRG_current/DiFfRG/include/DiFfRG/discretization/FEM/assembler/ddg.hh Source File
DiFfRG
Discretization Framework for functional Renormalization Group flows
ddg.hh
Go to the documentation of this file.
1#pragma once
2
3// standard library
4#include <sstream>
5
6// DiFfRG
12
13namespace DiFfRG
14{
15 namespace dDG
16 {
17 using namespace dealii;
18 using std::array;
19
20 template <typename... T> auto fe_tie(T &&...t)
21 {
22 return named_tuple<std::tuple<T &...>, StringSet<"fe_functions", "fe_derivatives", "fe_hessians", "extractors",
23 "variables", "cell_width">>(std::tie(t...));
24 }
25
26 template <typename... T> auto i_tie(T &&...t)
27 {
28 return named_tuple<std::tuple<T &...>, StringSet<"fe_functions", "fe_derivatives", "fe_hessians">>(
29 std::tie(t...));
30 }
31
32 namespace internal
33 {
40 template <typename Discretization> struct ScratchData {
41 static constexpr uint dim = Discretization::dim;
42 using NumberType = typename Discretization::NumberType;
43
44 ScratchData(const Mapping<dim> &mapping, const FiniteElement<dim> &fe,
45 const dealii::Quadrature<dim> &quadrature, const dealii::Quadrature<dim - 1> &quadrature_face,
46 const UpdateFlags update_flags = update_values | update_gradients | update_quadrature_points |
47 update_JxW_values | update_hessians,
48 const UpdateFlags interface_update_flags = update_values | update_gradients |
49 update_quadrature_points | update_JxW_values |
50 update_normal_vectors | update_hessians)
51 : n_components(fe.n_components()), fe_values(mapping, fe, quadrature, update_flags),
52 fe_interface_values(mapping, fe, quadrature_face, interface_update_flags)
53 {
54 solution.resize(quadrature.size(), Vector<NumberType>(n_components));
55 solution_grad.resize(quadrature.size(), std::vector<Tensor<1, dim, NumberType>>(n_components));
56 solution_hess.resize(quadrature.size(), std::vector<Tensor<2, dim, NumberType>>(n_components));
57 solution_dot.resize(quadrature.size(), Vector<NumberType>(n_components));
58 solution_interface[0].resize(quadrature_face.size(), Vector<NumberType>(n_components));
59 solution_interface[1].resize(quadrature_face.size(), Vector<NumberType>(n_components));
60 solution_grad_interface[0].resize(quadrature_face.size(),
61 std::vector<Tensor<1, dim, NumberType>>(n_components));
62 solution_grad_interface[1].resize(quadrature_face.size(),
63 std::vector<Tensor<1, dim, NumberType>>(n_components));
64 solution_hess_interface[0].resize(quadrature_face.size(),
65 std::vector<Tensor<2, dim, NumberType>>(n_components));
66 solution_hess_interface[1].resize(quadrature_face.size(),
67 std::vector<Tensor<2, dim, NumberType>>(n_components));
68 comp.resize(fe.n_dofs_per_cell());
69 cached_shape_values.resize(fe.n_dofs_per_cell());
70 cached_shape_grads.resize(fe.n_dofs_per_cell());
71 cached_shape_hessians.resize(fe.n_dofs_per_cell());
72 }
73
75 : n_components(scratch_data.fe_values.get_fe().n_components()),
76 fe_values(scratch_data.fe_values.get_mapping(), scratch_data.fe_values.get_fe(),
77 scratch_data.fe_values.get_quadrature(), scratch_data.fe_values.get_update_flags()),
78 fe_interface_values(scratch_data.fe_interface_values.get_mapping(),
79 scratch_data.fe_interface_values.get_fe(),
80 scratch_data.fe_interface_values.get_quadrature(),
81 scratch_data.fe_interface_values.get_update_flags())
82 {
83 const uint q_size = scratch_data.fe_values.get_quadrature().size();
84 const uint q_face_size = scratch_data.fe_interface_values.get_quadrature().size();
85
86 solution.resize(q_size, Vector<NumberType>(n_components));
87 solution_grad.resize(q_size, std::vector<Tensor<1, dim, NumberType>>(n_components));
88 solution_hess.resize(q_size, std::vector<Tensor<2, dim, NumberType>>(n_components));
89 solution_dot.resize(q_size, Vector<NumberType>(n_components));
90 solution_interface[0].resize(q_face_size, Vector<NumberType>(n_components));
91 solution_interface[1].resize(q_face_size, Vector<NumberType>(n_components));
92 solution_grad_interface[0].resize(q_face_size, std::vector<Tensor<1, dim, NumberType>>(n_components));
93 solution_grad_interface[1].resize(q_face_size, std::vector<Tensor<1, dim, NumberType>>(n_components));
94 solution_hess_interface[0].resize(q_face_size, std::vector<Tensor<2, dim, NumberType>>(n_components));
95 solution_hess_interface[1].resize(q_face_size, std::vector<Tensor<2, dim, NumberType>>(n_components));
96 comp.resize(scratch_data.fe_values.get_fe().n_dofs_per_cell());
97 cached_shape_values.resize(scratch_data.fe_values.get_fe().n_dofs_per_cell());
98 cached_shape_grads.resize(scratch_data.fe_values.get_fe().n_dofs_per_cell());
99 cached_shape_hessians.resize(scratch_data.fe_values.get_fe().n_dofs_per_cell());
100 }
101
103
104 FEValues<dim> fe_values;
105 FEInterfaceValues<dim> fe_interface_values;
106
107 std::vector<Vector<NumberType>> solution;
108 std::vector<std::vector<Tensor<1, dim, NumberType>>> solution_grad;
109 std::vector<std::vector<Tensor<2, dim, NumberType>>> solution_hess;
110 std::vector<Vector<NumberType>> solution_dot;
111 array<std::vector<Vector<NumberType>>, 2> solution_interface;
112 array<std::vector<std::vector<Tensor<1, dim, NumberType>>>, 2> solution_grad_interface;
113 array<std::vector<std::vector<Tensor<2, dim, NumberType>>>, 2> solution_hess_interface;
114
115 std::vector<uint> comp;
116 std::vector<double> cached_shape_values;
117 std::vector<Tensor<1, dim>> cached_shape_grads;
118 std::vector<Tensor<2, dim>> cached_shape_hessians;
119 };
120
121 template <typename NumberType> struct CopyData_R {
123 Vector<NumberType> cell_residual;
124 std::vector<types::global_dof_index> joint_dof_indices;
125
126 template <int dim> void reinit(const FEInterfaceValues<dim> &fe_iv)
127 {
128 cell_residual.reinit(fe_iv.n_current_interface_dofs());
129 joint_dof_indices = fe_iv.get_interface_dof_indices();
130 }
131 };
132
133 Vector<NumberType> cell_residual;
134 std::vector<types::global_dof_index> local_dof_indices;
135 std::vector<CopyDataFace_R> face_data;
136
137 template <class Iterator> void reinit(const Iterator &cell, uint dofs_per_cell)
138 {
139 cell_residual.reinit(dofs_per_cell);
140 local_dof_indices.resize(dofs_per_cell);
141 cell->get_dof_indices(local_dof_indices);
142 face_data.clear();
143 face_data.reserve(6);
144 }
145 };
146
147 template <typename NumberType> struct CopyData_J {
149 FullMatrix<NumberType> cell_jacobian;
150 FullMatrix<NumberType> extractor_cell_jacobian;
151 std::vector<types::global_dof_index> joint_dof_indices;
152
153 template <int dim> void reinit(const FEInterfaceValues<dim> &fe_iv, uint n_extractors)
154 {
155 uint dofs_per_cell = fe_iv.n_current_interface_dofs();
156 cell_jacobian.reinit(dofs_per_cell, dofs_per_cell);
157 if (n_extractors > 0) extractor_cell_jacobian.reinit(dofs_per_cell, n_extractors);
158 joint_dof_indices = fe_iv.get_interface_dof_indices();
159 }
160 };
161
162 FullMatrix<NumberType> cell_jacobian;
163 FullMatrix<NumberType> extractor_cell_jacobian;
164 std::vector<types::global_dof_index> local_dof_indices;
165 std::vector<CopyDataFace_J> face_data;
166
167 template <class Iterator> void reinit(const Iterator &cell, uint dofs_per_cell, uint n_extractors)
168 {
169 cell_jacobian.reinit(dofs_per_cell, dofs_per_cell);
170 if (n_extractors > 0) extractor_cell_jacobian.reinit(dofs_per_cell, n_extractors);
171 local_dof_indices.resize(dofs_per_cell);
172 cell->get_dof_indices(local_dof_indices);
173 face_data.clear();
174 face_data.reserve(6);
175 }
176 };
177
178 template <typename NumberType> struct CopyData_I {
180 std::array<uint, 2> cell_indices;
181 std::array<double, 2> values;
182 };
183 std::vector<CopyFaceData_I> face_data;
184 double value = 0.;
186 };
187 } // namespace internal
188
194 template <typename Discretization_,
196 class Assembler : public FEMAssembler<Discretization_, Model_>
197 {
199
200 public:
201 using Discretization = Discretization_;
202 using Model = Model_;
203 using NumberType = typename Discretization::NumberType;
204 using VectorType = typename Discretization::VectorType;
205 using SparseMatrixType = typename Discretization::SparseMatrixType;
206
207 using Components = typename Discretization::Components;
208 static constexpr uint dim = Discretization::dim;
211 quadrature(fe.degree + 1 + config.get_uint("/discretization/overintegration", 0)),
212 quadrature_face(fe.degree + 1 + config.get_uint("/discretization/overintegration", 0))
213 {
214 static_assert(Components::count_fe_subsystems() == 1, "A dDG model cannot have multiple submodels!");
215 reinit();
216 }
217
218 virtual void reinit_vector(VectorType &vec) const override
219 {
220 reinit_la_vector(vec, discretization.get_locally_owned_dofs(), discretization.get_communicator());
221 }
222 virtual void reinit_matrix(SparseMatrixType &matrix) const override
223 {
224 reinit_la_matrix(matrix, get_sparsity_pattern_jacobian(), discretization.get_locally_owned_dofs(),
225 discretization.get_communicator());
226 }
227
228 virtual MPI_Comm get_communicator() const override { return discretization.get_communicator(); }
229 virtual void reinit_solution_view(SolutionView<VectorType> &view) const override
230 {
231 view.reinit(discretization.get_locally_owned_dofs(), discretization.get_locally_relevant_dofs(),
232 discretization.get_communicator());
233 }
234
235 virtual void reinit() override
236 {
237 Timer timer;
238
239 Base::reinit();
240
241 // Mass sparsity pattern
242 {
243 DynamicSparsityPattern dsp(discretization.get_locally_relevant_dofs());
244 DoFTools::make_sparsity_pattern(dof_handler, dsp, discretization.get_constraints(),
245 /*keep_constrained_dofs = */ true);
247 discretization.get_locally_relevant_dofs(),
248 discretization.get_communicator());
250 discretization.get_communicator());
251 MatrixCreator::create_mass_matrix(dof_handler, quadrature, mass_matrix, (Function<dim, NumberType> *)nullptr,
252 discretization.get_constraints());
253 }
254 // Jacobian sparsity pattern
255 {
256 DynamicSparsityPattern dsp(discretization.get_locally_relevant_dofs());
257 DoFTools::make_flux_sparsity_pattern(dof_handler, dsp, discretization.get_constraints(),
258 /*keep_constrained_dofs = */ true);
260 dsp, sparsity_pattern_jacobian, discretization.get_locally_owned_dofs(),
261 discretization.get_locally_relevant_dofs(), discretization.get_communicator());
262 }
263
264 timings_reinit.push_back(timer.wall_time());
265 }
266
267 virtual void rebuild_jacobian_sparsity() override
268 {
269 // Jacobian sparsity pattern
270 DynamicSparsityPattern dsp(discretization.get_locally_relevant_dofs());
271 DoFTools::make_flux_sparsity_pattern(dof_handler, dsp, discretization.get_constraints(),
272 /*keep_constrained_dofs = */ true);
273 for (const auto &row : discretization.get_locally_relevant_dofs())
274 for (const auto &col : extractor_dof_indices)
275 dsp.add(row, col);
277 discretization.get_locally_relevant_dofs(),
278 discretization.get_communicator());
279 }
280
285 virtual const SparseMatrixType &get_mass_matrix() const override { return mass_matrix; }
286
293 virtual void refinement_indicator(Vector<double> &indicator, const VectorType &solution_global) override
294 {
295 using Iterator = typename DoFHandler<dim>::active_cell_iterator;
297 using CopyData = internal::CopyData_I<NumberType>;
298
299 const auto cell_worker = [&](const Iterator &t_cell, Scratch &scratch_data, CopyData &copy_data) {
300 scratch_data.fe_values.reinit(t_cell);
301 const auto &fe_v = scratch_data.fe_values;
302 copy_data.cell_index = t_cell->active_cell_index();
303 copy_data.value = 0;
304
305 const auto &JxW = fe_v.get_JxW_values();
306 const auto &q_points = fe_v.get_quadrature_points();
307 const auto &q_indices = fe_v.quadrature_point_indices();
308
309 auto &solution = scratch_data.solution;
310 auto &solution_grad = scratch_data.solution_grad;
311 auto &solution_hess = scratch_data.solution_hess;
312 fe_v.get_function_values(solution_global, solution);
313 fe_v.get_function_gradients(solution_global, solution_grad);
314 fe_v.get_function_hessians(solution_global, solution_hess);
315
316 double local_indicator = 0.;
317 for (const auto &q_index : q_indices) {
318 const auto &x_q = q_points[q_index];
319 model.cell_indicator(local_indicator, x_q,
320 i_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index]));
321 copy_data.value += JxW[q_index] * local_indicator;
322 }
323 };
324 const auto face_worker = [&](const Iterator &t_cell, const uint &f, const uint &sf, const Iterator &t_ncell,
325 const uint &nf, const unsigned int &nsf, Scratch &scratch_data,
326 CopyData &copy_data) {
327 scratch_data.fe_interface_values.reinit(t_cell, f, sf, t_ncell, nf, nsf);
328 const auto &fe_iv = scratch_data.fe_interface_values;
329 const auto &fe_iv_s = scratch_data.fe_interface_values.get_fe_face_values(0);
330 const auto &fe_iv_n = scratch_data.fe_interface_values.get_fe_face_values(1);
331
332 auto &copy_data_face = copy_data.face_data.emplace_back();
333 copy_data_face.cell_indices[0] = t_cell->active_cell_index();
334 copy_data_face.cell_indices[1] = t_ncell->active_cell_index();
335 copy_data_face.values[0] = 0;
336 copy_data_face.values[1] = 0;
337
338 const auto &JxW = fe_iv.get_JxW_values();
339 const auto &q_points = fe_iv.get_quadrature_points();
340 const auto &q_indices = fe_iv.quadrature_point_indices();
341 const std::vector<Tensor<1, dim>> &normals = fe_iv.get_normal_vectors();
342
343 auto &solution_s = scratch_data.solution_interface[0];
344 auto &solution_n = scratch_data.solution_interface[1];
345 auto &solution_grad_s = scratch_data.solution_grad_interface[0];
346 auto &solution_grad_n = scratch_data.solution_grad_interface[1];
347 auto &solution_hess_s = scratch_data.solution_hess_interface[0];
348 auto &solution_hess_n = scratch_data.solution_hess_interface[1];
349 fe_iv_s.get_function_values(solution_global, solution_s);
350 fe_iv_n.get_function_values(solution_global, solution_n);
351 fe_iv_s.get_function_gradients(solution_global, solution_grad_s);
352 fe_iv_n.get_function_gradients(solution_global, solution_grad_n);
353 fe_iv_s.get_function_hessians(solution_global, solution_hess_s);
354 fe_iv_n.get_function_hessians(solution_global, solution_hess_n);
355
356 array<double, 2> local_indicator{};
357 for (const auto &q_index : q_indices) {
358 const auto &x_q = q_points[q_index];
359 model.face_indicator(local_indicator, normals[q_index], x_q,
360 i_tie(solution_s[q_index], solution_grad_s[q_index], solution_hess_s[q_index]),
361 i_tie(solution_n[q_index], solution_grad_n[q_index], solution_hess_n[q_index]));
362
363 copy_data_face.values[0] += JxW[q_index] * local_indicator[0] * (1. + t_cell->at_boundary());
364 copy_data_face.values[1] += JxW[q_index] * local_indicator[1] * (1. + t_ncell->at_boundary());
365 }
366 };
367 const auto copier = [&](const CopyData &c) {
368 for (auto &cdf : c.face_data)
369 for (uint j = 0; j < 2; ++j)
370 indicator[cdf.cell_indices[j]] += cdf.values[j];
371 indicator[c.cell_index] += c.value;
372 };
373
374 Scratch scratch_data(mapping, fe, quadrature, quadrature_face);
375 CopyData copy_data;
376 MeshWorker::AssembleFlags flags = MeshWorker::assemble_own_cells |
377 MeshWorker::assemble_own_interior_faces_once |
378 MeshWorker::assemble_ghost_faces_once;
379
380 // map() is collective and each rank visits only its own cells; see NoMapsHere.
381 const NoMapsHere no_maps_during_assembly;
382 const auto schedule = schedule_for(assembly_cost::local_fe);
383 MeshWorker::mesh_loop(locally_owned_cells(dof_handler), cell_worker, copier, scratch_data, copy_data, flags,
384 nullptr, face_worker, schedule.queue_length, schedule.chunk_size);
385 }
386
387 virtual void mass(VectorType &mass, const VectorType &solution_global, const VectorType &solution_global_dot,
388 NumberType weight) override
389 {
390 using Iterator = typename DoFHandler<dim>::active_cell_iterator;
392 using CopyData = internal::CopyData_R<NumberType>;
393 const auto &constraints = discretization.get_constraints();
394
395 const auto cell_worker = [&](const Iterator &cell, Scratch &scratch_data, CopyData &copy_data) {
396 scratch_data.fe_values.reinit(cell);
397 const auto &fe_v = scratch_data.fe_values;
398 const uint n_dofs = fe_v.get_fe().n_dofs_per_cell();
399
400 copy_data.reinit(cell, n_dofs);
401 const auto &JxW = fe_v.get_JxW_values();
402 const auto &q_points = fe_v.get_quadrature_points();
403 const auto &q_indices = fe_v.quadrature_point_indices();
404
405 auto &solution = scratch_data.solution;
406 auto &solution_dot = scratch_data.solution_dot;
407 fe_v.get_function_values(solution_global, solution);
408 fe_v.get_function_values(solution_global_dot, solution_dot);
409
410 auto &comp = scratch_data.comp;
411 for (uint i = 0; i < n_dofs; ++i)
412 comp[i] = fe.system_to_component_index(i).first;
413
414 array<NumberType, Components::count_fe_functions()> mass{};
415 for (const auto &q_index : q_indices) {
416 const auto &x_q = q_points[q_index];
417 model.mass(mass, x_q, solution[q_index], solution_dot[q_index]);
418
419 for (uint i = 0; i < n_dofs; ++i) {
420 const auto component_i = comp[i];
421 copy_data.cell_residual(i) += weight * JxW[q_index] *
422 fe_v.shape_value_component(i, q_index, component_i) *
423 mass[component_i]; // +phi_i(x_q) * mass(x_q, u_q)
424 }
425 }
426 };
427 const auto copier = [&](const CopyData &c) {
428 constraints.distribute_local_to_global(c.cell_residual, c.local_dof_indices, mass);
429 };
430
431 Scratch scratch_data(mapping, fe, quadrature, quadrature_face);
432 CopyData copy_data;
433 MeshWorker::AssembleFlags flags = MeshWorker::assemble_own_cells;
434
435 // map() is collective and each rank visits only its own cells; see NoMapsHere.
436 const NoMapsHere no_maps_during_assembly;
437 const auto schedule = schedule_for(assembly_cost::local_fe);
438 MeshWorker::mesh_loop(locally_owned_cells(dof_handler), cell_worker, copier, scratch_data, copy_data, flags,
439 nullptr, nullptr, schedule.queue_length, schedule.chunk_size);
440 // Resolve contributions this rank made to rows it does not own. A partition-boundary
441 // face is assembled by exactly one of its two neighbours (mesh_loop hands it to the
442 // smaller subdomain id), and that rank writes BOTH sides -- so the other side's rows
443 // arrive here. A no-op for the serial types.
444 mass.compress(dealii::VectorOperation::add);
445 }
446
447 virtual void residual(VectorType &residual, const VectorType &solution_global, NumberType weight,
448 const VectorType &solution_global_dot, NumberType weight_mass,
449 const VectorType &variables = VectorType()) override
450 {
451 using Iterator = typename DoFHandler<dim>::active_cell_iterator;
453 using CopyData = internal::CopyData_R<NumberType>;
454 const auto &constraints = discretization.get_constraints();
455
456 // Find the EoM and extract whatever data is needed for the model.
457 std::array<NumberType, Components::count_extractors()> __extracted_data{{}};
458 if constexpr (Components::count_extractors() > 0)
459 this->extract(__extracted_data, solution_global, variables, true, false, true);
460 const auto &extracted_data = __extracted_data;
461
462 const auto cell_worker = [&](const Iterator &cell, Scratch &scratch_data, CopyData &copy_data) {
463 const double cell_width = DiFfRG::internal::cell_width(cell);
464 scratch_data.fe_values.reinit(cell);
465 const auto &fe_v = scratch_data.fe_values;
466 const uint n_dofs = fe_v.get_fe().n_dofs_per_cell();
467
468 copy_data.reinit(cell, n_dofs);
469 const auto &JxW = fe_v.get_JxW_values();
470 const auto &q_points = fe_v.get_quadrature_points();
471 const auto &q_indices = fe_v.quadrature_point_indices();
472
473 auto &solution = scratch_data.solution;
474 auto &solution_grad = scratch_data.solution_grad;
475 auto &solution_hess = scratch_data.solution_hess;
476 auto &solution_dot = scratch_data.solution_dot;
477 fe_v.get_function_values(solution_global, solution);
478 fe_v.get_function_gradients(solution_global, solution_grad);
479 fe_v.get_function_hessians(solution_global, solution_hess);
480 fe_v.get_function_values(solution_global_dot, solution_dot);
481
482 auto &comp = scratch_data.comp;
483 for (uint i = 0; i < n_dofs; ++i)
484 comp[i] = fe.system_to_component_index(i).first;
485
486 array<Tensor<1, dim, NumberType>, Components::count_fe_functions()> flux{};
487 array<NumberType, Components::count_fe_functions()> source{};
488 array<NumberType, Components::count_fe_functions()> mass{};
489 for (const auto &q_index : q_indices) {
490 const auto &x_q = q_points[q_index];
491 model.flux(flux, x_q,
492 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data,
493 variables, cell_width));
494 model.source(source, x_q,
495 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index], extracted_data,
496 variables, cell_width));
497 model.mass(mass, x_q, solution[q_index], solution_dot[q_index]);
498
499 for (uint i = 0; i < n_dofs; ++i) {
500 const auto component_i = comp[i];
501 copy_data.cell_residual(i) += JxW[q_index] * weight * // dx *
502 (-scalar_product(fe_v.shape_grad_component(i, q_index, component_i),
503 flux[component_i]) // -dphi_i(x_q) * flux(x_q, u_q)
504 + fe_v.shape_value_component(i, q_index, component_i) *
505 source[component_i]); // -phi_i(x_q) * source(x_q, u_q)
506 copy_data.cell_residual(i) += weight_mass * JxW[q_index] *
507 fe_v.shape_value_component(i, q_index, component_i) *
508 mass[component_i]; // +phi_i(x_q) * mass(x_q, u_q)
509 }
510 }
511 };
512 const auto boundary_worker = [&](const Iterator &cell, const uint &face_no, Scratch &scratch_data,
513 CopyData &copy_data) {
514 const double cell_width = DiFfRG::internal::cell_width(cell);
515 scratch_data.fe_interface_values.reinit(cell, face_no);
516 const auto &fe_fv = scratch_data.fe_interface_values.get_fe_face_values(0);
517 const uint n_dofs = fe_fv.get_fe().n_dofs_per_cell();
518
519 const auto &JxW = fe_fv.get_JxW_values();
520 const auto &q_points = fe_fv.get_quadrature_points();
521 const auto &q_indices = fe_fv.quadrature_point_indices();
522 const std::vector<Tensor<1, dim>> &normals = fe_fv.get_normal_vectors();
523
524 auto &solution = scratch_data.solution_interface[0];
525 auto &solution_grad = scratch_data.solution_grad_interface[0];
526 auto &solution_hess = scratch_data.solution_hess_interface[0];
527 fe_fv.get_function_values(solution_global, solution);
528 fe_fv.get_function_gradients(solution_global, solution_grad);
529 fe_fv.get_function_hessians(solution_global, solution_hess);
530
531 auto &comp = scratch_data.comp;
532 for (uint i = 0; i < n_dofs; ++i)
533 comp[i] = fe.system_to_component_index(i).first;
534
535 array<Tensor<1, dim, NumberType>, Components::count_fe_functions()> numflux{};
536 for (const auto &q_index : q_indices) {
537 const auto &x_q = q_points[q_index];
538 model.boundary_numflux(numflux, normals[q_index], x_q,
539 fe_tie(solution[q_index], solution_grad[q_index], solution_hess[q_index],
540 extracted_data, variables, cell_width));
541
542 for (uint i = 0; i < n_dofs; ++i) {
543 const auto component_i = comp[i];
544 copy_data.cell_residual(i) +=
545 weight * JxW[q_index] * // dx
546 (fe_fv.shape_value_component(i, q_index, component_i) *
547 scalar_product(numflux[component_i], normals[q_index])); // phi_i(x_q) * numflux(x_q, u_q) * n(x_q)
548 }
549 }
550 };
551 const auto face_worker = [&](const Iterator &cell, const uint &f, const unsigned int &sf, const Iterator &ncell,
552 const unsigned int &nf, const unsigned int &nsf, Scratch &scratch_data,
553 CopyData &copy_data) {
554 const double cell_width = DiFfRG::internal::cell_width(cell);
555 const double ncell_width = DiFfRG::internal::cell_width(ncell);
556 scratch_data.fe_interface_values.reinit(cell, f, sf, ncell, nf, nsf);
557 const auto &fe_iv = scratch_data.fe_interface_values;
558 const auto &fe_iv_s = scratch_data.fe_interface_values.get_fe_face_values(0);
559 const auto &fe_iv_n = scratch_data.fe_interface_values.get_fe_face_values(1);
560 const uint n_dofs = fe_iv.n_current_interface_dofs();
561
562 copy_data.face_data.emplace_back();
563 auto &copy_data_face = copy_data.face_data.back();
564 copy_data_face.reinit(fe_iv);
565
566 const auto &JxW = fe_iv.get_JxW_values();
567 const auto &q_points = fe_iv.get_quadrature_points();
568 const auto &q_indices = fe_iv.quadrature_point_indices();
569 const std::vector<Tensor<1, dim>> &normals = fe_iv.get_normal_vectors();
570
571 auto &solution_s = scratch_data.solution_interface[0];
572 auto &solution_n = scratch_data.solution_interface[1];
573 auto &solution_grad_s = scratch_data.solution_grad_interface[0];
574 auto &solution_grad_n = scratch_data.solution_grad_interface[1];
575 auto &solution_hess_s = scratch_data.solution_hess_interface[0];
576 auto &solution_hess_n = scratch_data.solution_hess_interface[1];
577 fe_iv_s.get_function_values(solution_global, solution_s);
578 fe_iv_n.get_function_values(solution_global, solution_n);
579 fe_iv_s.get_function_gradients(solution_global, solution_grad_s);
580 fe_iv_n.get_function_gradients(solution_global, solution_grad_n);
581 fe_iv_s.get_function_hessians(solution_global, solution_hess_s);
582 fe_iv_n.get_function_hessians(solution_global, solution_hess_n);
583
584 auto &comp = scratch_data.comp;
585 comp.resize(n_dofs);
586 for (uint i = 0; i < n_dofs; ++i) {
587 const auto &cd_i = fe_iv.interface_dof_to_dof_indices(i);
588 comp[i] = cd_i[0] == numbers::invalid_unsigned_int ? fe.system_to_component_index(cd_i[1]).first
589 : fe.system_to_component_index(cd_i[0]).first;
590 }
591
592 array<Tensor<1, dim, NumberType>, Components::count_fe_functions()> numflux{};
593 for (const auto &q_index : q_indices) {
594 const auto &x_q = q_points[q_index];
595 model.numflux(numflux, normals[q_index], x_q,
596 fe_tie(solution_s[q_index], solution_grad_s[q_index], solution_hess_s[q_index],
597 extracted_data, variables, cell_width),
598 fe_tie(solution_n[q_index], solution_grad_n[q_index], solution_hess_n[q_index],
599 extracted_data, variables, ncell_width));
600
601 for (uint i = 0; i < n_dofs; ++i) {
602 const auto component_i = comp[i];
603 copy_data_face.cell_residual(i) +=
604 weight * JxW[q_index] * // dx
605 (fe_iv.jump_in_shape_values(i, q_index, component_i) *
606 scalar_product(numflux[component_i],
607 normals[q_index])); // [[phi_i(x_q)]] * numflux(x_q, u_q) * n(x_q)
608 }
609 }
610 };
611 const auto copier = [&](const CopyData &c) {
612 constraints.distribute_local_to_global(c.cell_residual, c.local_dof_indices, residual);
613 for (auto &cdf : c.face_data)
614 constraints.distribute_local_to_global(cdf.cell_residual, cdf.joint_dof_indices, residual);
615 };
616
617 Scratch scratch_data(mapping, fe, quadrature, quadrature_face);
618 CopyData copy_data;
619 MeshWorker::AssembleFlags flags = MeshWorker::assemble_own_cells | MeshWorker::assemble_boundary_faces |
620 MeshWorker::assemble_own_interior_faces_once |
621 MeshWorker::assemble_ghost_faces_once;
622
623 Timer timer;
624 // map() is collective and each rank visits only its own cells; see NoMapsHere.
625 const NoMapsHere no_maps_during_assembly;
626 const auto schedule = schedule_for(assembly_cost::momentum_integral);
627 MeshWorker::mesh_loop(locally_owned_cells(dof_handler), cell_worker, copier, scratch_data, copy_data, flags,
628 boundary_worker, face_worker, schedule.queue_length, schedule.chunk_size);
629 // Resolve contributions this rank made to rows it does not own. A partition-boundary
630 // face is assembled by exactly one of its two neighbours (mesh_loop hands it to the
631 // smaller subdomain id), and that rank writes BOTH sides -- so the other side's rows
632 // arrive here. A no-op for the serial types.
633 residual.compress(dealii::VectorOperation::add);
634 timings_residual.push_back(timer.wall_time());
635 }
636
637 virtual void jacobian_mass(SparseMatrixType &jacobian, const VectorType &solution_global,
638 const VectorType &solution_global_dot, NumberType alpha, NumberType beta) override
639 {
640 using Iterator = typename DoFHandler<dim>::active_cell_iterator;
642 using CopyData = internal::CopyData_J<NumberType>;
643 const auto &constraints = discretization.get_constraints();
644
645 const auto cell_worker = [&](const Iterator &cell, Scratch &scratch_data, CopyData &copy_data) {
646 scratch_data.fe_values.reinit(cell);
647 const auto &fe_v = scratch_data.fe_values;
648 const uint n_dofs = fe_v.get_fe().n_dofs_per_cell();
649
650 copy_data.reinit(cell, n_dofs, Components::count_extractors());
651 const auto &JxW = fe_v.get_JxW_values();
652 const auto &q_points = fe_v.get_quadrature_points();
653 const auto &q_indices = fe_v.quadrature_point_indices();
654 auto &solution = scratch_data.solution;
655 auto &solution_dot = scratch_data.solution_dot;
656
657 SimpleMatrix<NumberType, Components::count_fe_functions()> j_mass;
658 SimpleMatrix<NumberType, Components::count_fe_functions()> j_mass_dot;
659
660 fe_v.get_function_values(solution_global, solution);
661 fe_v.get_function_values(solution_global_dot, solution_dot);
662
663 auto &comp = scratch_data.comp;
664 for (uint i = 0; i < n_dofs; ++i)
665 comp[i] = fe.system_to_component_index(i).first;
666
667 for (const auto &q_index : q_indices) {
668 const auto &x_q = q_points[q_index];
669 model.template jacobian_mass<0>(j_mass, x_q, solution[q_index], solution_dot[q_index]);
670 model.template jacobian_mass<1>(j_mass_dot, x_q, solution[q_index], solution_dot[q_index]);
671
672 for (uint i = 0; i < n_dofs; ++i) {
673 const auto component_i = comp[i];
674 for (uint j = 0; j < n_dofs; ++j) {
675 const auto component_j = comp[j];
676 copy_data.cell_jacobian(i, j) +=
677 JxW[q_index] * fe_v.shape_value_component(j, q_index, component_j) *
678 fe_v.shape_value_component(i, q_index, component_i) *
679 (alpha * j_mass_dot(component_i, component_j) + beta * j_mass(component_i, component_j));
680 }
681 }
682 }
683 };
684 const auto copier = [&](const CopyData &c) {
685 constraints.distribute_local_to_global(c.cell_jacobian, c.local_dof_indices, jacobian);
686 };
687
688 Scratch scratch_data(mapping, fe, quadrature, quadrature_face);
689 CopyData copy_data;
690 MeshWorker::AssembleFlags flags = MeshWorker::assemble_own_cells;
691
692 Timer timer;
693 // map() is collective and each rank visits only its own cells; see NoMapsHere.
694 const NoMapsHere no_maps_during_assembly;
695 const auto schedule = schedule_for(assembly_cost::local_fe);
696 MeshWorker::mesh_loop(locally_owned_cells(dof_handler), cell_worker, copier, scratch_data, copy_data, flags,
697 nullptr, nullptr, schedule.queue_length, schedule.chunk_size);
698 // Resolve contributions this rank made to rows it does not own. A partition-boundary
699 // face is assembled by exactly one of its two neighbours (mesh_loop hands it to the
700 // smaller subdomain id), and that rank writes BOTH sides -- so the other side's rows
701 // arrive here. A no-op for the serial types.
702 jacobian.compress(dealii::VectorOperation::add);
703 timings_jacobian.push_back(timer.wall_time());
704 }
705
706 virtual void jacobian(SparseMatrixType &jacobian, const VectorType &solution_global, NumberType weight,
707 const VectorType &solution_global_dot, NumberType alpha, NumberType beta,
708 const VectorType &variables = VectorType()) override
709 {
710 using Iterator = typename DoFHandler<dim>::active_cell_iterator;
712 using CopyData = internal::CopyData_J<NumberType>;
713 const auto &constraints = discretization.get_constraints();
714
715 // Find the EoM and extract whatever data is needed for the model.
716 std::array<NumberType, Components::count_extractors()> __extracted_data{{}};
717 if constexpr (Components::count_extractors() > 0) {
718 this->extract(__extracted_data, solution_global, variables, true, true, true);
719 if (this->jacobian_extractors(this->extractor_jacobian, solution_global, variables))
721 discretization.get_communicator());
722 }
723 const auto &extracted_data = __extracted_data;
724
725 const auto cell_worker = [&](const Iterator &cell, Scratch &scratch_data, CopyData &copy_data) {
726 const double cell_width = DiFfRG::internal::cell_width(cell);
727 scratch_data.fe_values.reinit(cell);
728 const auto &fe_v = scratch_data.fe_values;
729 const uint n_dofs = fe_v.get_fe().n_dofs_per_cell();
730
731 copy_data.reinit(cell, n_dofs, Components::count_extractors());
732 const auto &JxW = fe_v.get_JxW_values();
733 const auto &q_points = fe_v.get_quadrature_points();
734 const auto &q_indices = fe_v.quadrature_point_indices();
735
736 auto &solution = scratch_data.solution;
737 auto &solution_dot = scratch_data.solution_dot;
738 auto &solution_grad = scratch_data.solution_grad;
739 auto &solution_hess = scratch_data.solution_hess;
740
741 fe_v.get_function_values(solution_global, solution);
742 fe_v.get_function_values(solution_global_dot, solution_dot);
743 fe_v.get_function_gradients(solution_global, solution_grad);
744 fe_v.get_function_hessians(solution_global, solution_hess);
745
746 auto &comp = scratch_data.comp;
747 for (uint i = 0; i < n_dofs; ++i)
748 comp[i] = fe.system_to_component_index(i).first;
749
750 auto &cached_shape_values = scratch_data.cached_shape_values;
751 auto &cached_shape_grads = scratch_data.cached_shape_grads;
752 auto &cached_shape_hessians = scratch_data.cached_shape_hessians;
753
754 SimpleMatrix<Tensor<1, dim>, Components::count_fe_functions()> j_flux;
755 SimpleMatrix<Tensor<1, dim, Tensor<1, dim, NumberType>>, Components::count_fe_functions()> j_grad_flux;
756 SimpleMatrix<Tensor<1, dim, Tensor<2, dim, NumberType>>, Components::count_fe_functions()> j_hess_flux;
757 SimpleMatrix<Tensor<1, dim, NumberType>, Components::count_fe_functions(), Components::count_extractors()>
758 j_extr_flux;
759 SimpleMatrix<NumberType, Components::count_fe_functions()> j_source;
760 SimpleMatrix<Tensor<1, dim, NumberType>, Components::count_fe_functions()> j_grad_source;
761 SimpleMatrix<Tensor<2, dim, NumberType>, Components::count_fe_functions()> j_hess_source;
762 SimpleMatrix<NumberType, Components::count_fe_functions(), Components::count_extractors()> j_extr_source;
763 SimpleMatrix<NumberType, Components::count_fe_functions()> j_mass;
764 SimpleMatrix<NumberType, Components::count_fe_functions()> j_mass_dot;
765
766 for (const auto &q_index : q_indices) {
767 const auto &x_q = q_points[q_index];
768 model.template jacobian_flux_source<0, 0>(j_flux, j_source, x_q,
769 fe_tie(solution[q_index], solution_grad[q_index],
770 solution_hess[q_index], extracted_data, variables,
771 cell_width));
772 model.template jacobian_flux_source_grad<1>(j_grad_flux, j_grad_source, x_q,
773 fe_tie(solution[q_index], solution_grad[q_index],
774 solution_hess[q_index], extracted_data, variables,
775 cell_width));
776 model.template jacobian_flux_source_hess<2>(j_hess_flux, j_hess_source, x_q,
777 fe_tie(solution[q_index], solution_grad[q_index],
778 solution_hess[q_index], extracted_data, variables,
779 cell_width));
780 if constexpr (Components::count_extractors() > 0) {
781 model.template jacobian_flux_source_extr<3>(j_extr_flux, j_extr_source, x_q,
782 fe_tie(solution[q_index], solution_grad[q_index],
783 solution_hess[q_index], extracted_data, variables,
784 cell_width));
785 }
786 model.template jacobian_mass<0>(j_mass, x_q, solution[q_index], solution_dot[q_index]);
787 model.template jacobian_mass<1>(j_mass_dot, x_q, solution[q_index], solution_dot[q_index]);
788
789 // Cache shape values, gradients, and hessians for all DoFs at this quadrature point
790 for (uint k = 0; k < n_dofs; ++k) {
791 cached_shape_values[k] = fe_v.shape_value_component(k, q_index, comp[k]);
792 cached_shape_grads[k] = fe_v.shape_grad_component(k, q_index, comp[k]);
793 cached_shape_hessians[k] = fe_v.shape_hessian_component(k, q_index, comp[k]);
794 }
795
796 for (uint i = 0; i < n_dofs; ++i) {
797 const auto component_i = comp[i];
798 const auto &shape_value_i = cached_shape_values[i];
799 const auto &shape_grad_i = cached_shape_grads[i];
800 for (uint j = 0; j < n_dofs; ++j) {
801 const auto component_j = comp[j];
802 const auto &shape_value_j = cached_shape_values[j];
803 const auto &shape_grad_j = cached_shape_grads[j];
804 const auto &shape_hessian_j = cached_shape_hessians[j];
805 // consolidated contribution: scalar + gradient + hessian + mass
806 copy_data.cell_jacobian(i, j) +=
807 weight * JxW[q_index] *
808 (shape_value_j * // dx * phi_j * (
809 (-scalar_product(shape_grad_i, j_flux(component_i, component_j)) // -dphi_i * jflux
810 + shape_value_i * j_source(component_i, component_j)) // -phi_i * jsource)
811 + scalar_product(
812 shape_grad_j, // gradient contribution
813 -scalar_product(shape_grad_i, j_grad_flux(component_i, component_j)) // -dphi_i * jflux
814 + shape_value_i * j_grad_source(component_i, component_j)) // -phi_i * jsource
815 + scalar_product(shape_hessian_j, // hessian contribution
816 -scalar_product(shape_grad_i, j_hess_flux(component_i, component_j)) +
817 shape_value_i * j_hess_source(component_i, component_j))) +
818 JxW[q_index] * shape_value_j * shape_value_i * // mass contribution
819 (alpha * j_mass_dot(component_i, component_j) + beta * j_mass(component_i, component_j));
820 }
821 // extractor contribution
822 if constexpr (Components::count_extractors() > 0)
823 for (uint e = 0; e < Components::count_extractors(); ++e)
824 copy_data.extractor_cell_jacobian(i, e) +=
825 weight * JxW[q_index] * // dx * phi_j * (
826 (-scalar_product(shape_grad_i, j_extr_flux(component_i, e)) // -dphi_i * jflux
827 + shape_value_i * j_extr_source(component_i, e)); // -phi_i * jsource)
828 }
829 }
830 };
831 const auto boundary_worker = [&](const Iterator &cell, const uint &face_no, Scratch &scratch_data,
832 CopyData &copy_data) {
833 const double cell_width = DiFfRG::internal::cell_width(cell);
834 scratch_data.fe_interface_values.reinit(cell, face_no);
835 const auto &fe_fv = scratch_data.fe_interface_values.get_fe_face_values(0);
836 const uint n_dofs = fe_fv.get_fe().n_dofs_per_cell();
837
838 const auto &JxW = fe_fv.get_JxW_values();
839 const auto &q_points = fe_fv.get_quadrature_points();
840 const auto &q_indices = fe_fv.quadrature_point_indices();
841 const std::vector<Tensor<1, dim>> &normals = fe_fv.get_normal_vectors();
842
843 auto &solution = scratch_data.solution_interface[0];
844 auto &solution_grad = scratch_data.solution_grad_interface[0];
845 auto &solution_hess = scratch_data.solution_hess_interface[0];
846 fe_fv.get_function_values(solution_global, solution);
847 fe_fv.get_function_gradients(solution_global, solution_grad);
848 fe_fv.get_function_hessians(solution_global, solution_hess);
849
850 auto &comp = scratch_data.comp;
851 for (uint i = 0; i < n_dofs; ++i)
852 comp[i] = fe.system_to_component_index(i).first;
853
854 auto &cached_shape_values = scratch_data.cached_shape_values;
855 auto &cached_shape_grads = scratch_data.cached_shape_grads;
856 auto &cached_shape_hessians = scratch_data.cached_shape_hessians;
857
858 SimpleMatrix<Tensor<1, dim, NumberType>, Components::count_fe_functions()> j_boundary_numflux;
859 SimpleMatrix<Tensor<1, dim, Tensor<1, dim, NumberType>>, Components::count_fe_functions()>
860 j_grad_boundary_numflux;
861 SimpleMatrix<Tensor<1, dim, Tensor<2, dim, NumberType>>, Components::count_fe_functions()>
862 j_hess_boundary_numflux;
863 SimpleMatrix<Tensor<1, dim, NumberType>, Components::count_fe_functions(), Components::count_extractors()>
864 j_extr_boundary_numflux;
865 for (const auto &q_index : q_indices) {
866 const auto &x_q = q_points[q_index];
867 model.template jacobian_boundary_numflux<0, 0>(j_boundary_numflux, normals[q_index], x_q,
868 fe_tie(solution[q_index], solution_grad[q_index],
869 solution_hess[q_index], extracted_data, variables,
870 cell_width));
871 model.template jacobian_boundary_numflux_grad<1>(j_grad_boundary_numflux, normals[q_index], x_q,
872 fe_tie(solution[q_index], solution_grad[q_index],
873 solution_hess[q_index], extracted_data, variables,
874 cell_width));
875 model.template jacobian_boundary_numflux_hess<2>(j_hess_boundary_numflux, normals[q_index], x_q,
876 fe_tie(solution[q_index], solution_grad[q_index],
877 solution_hess[q_index], extracted_data, variables,
878 cell_width));
879 if constexpr (Components::count_extractors() > 0) {
880 model.template jacobian_boundary_numflux_extr<3>(j_extr_boundary_numflux, normals[q_index], x_q,
881 fe_tie(solution[q_index], solution_grad[q_index],
882 solution_hess[q_index], extracted_data, variables,
883 cell_width));
884 }
885
886 // Cache shape values, gradients, and hessians for all DoFs at this quadrature point
887 for (uint k = 0; k < n_dofs; ++k) {
888 cached_shape_values[k] = fe_fv.shape_value_component(k, q_index, comp[k]);
889 cached_shape_grads[k] = fe_fv.shape_grad_component(k, q_index, comp[k]);
890 cached_shape_hessians[k] = fe_fv.shape_hessian_component(k, q_index, comp[k]);
891 }
892
893 for (uint i = 0; i < n_dofs; ++i) {
894 const auto component_i = comp[i];
895 const auto &shape_value_i = cached_shape_values[i];
896 for (uint j = 0; j < n_dofs; ++j) {
897 const auto component_j = comp[j];
898 const auto &shape_value_j = cached_shape_values[j];
899 const auto &shape_grad_j = cached_shape_grads[j];
900 const auto &shape_hessian_j = cached_shape_hessians[j];
901 // consolidated contribution: scalar + gradient + hessian
902 copy_data.cell_jacobian(i, j) +=
903 weight * JxW[q_index] *
904 (shape_value_j * // dx * phi_j(x_q)
905 (shape_value_i * scalar_product(j_boundary_numflux(component_i, component_j),
906 normals[q_index])) // phi_i(x_q) * j_numflux(x_q, u_q) * n(x_q)
907 + scalar_product(shape_grad_j, // gradient contribution
908 shape_value_i * scalar_product(j_grad_boundary_numflux(component_i, component_j),
909 normals[q_index])) +
910 scalar_product(shape_hessian_j, // hessian contribution
911 shape_value_i * scalar_product(j_hess_boundary_numflux(component_i, component_j),
912 normals[q_index])));
913 }
914 // extractor contribution
915 if constexpr (Components::count_extractors() > 0)
916 for (uint e = 0; e < Components::count_extractors(); ++e)
917 copy_data.extractor_cell_jacobian(i, e) +=
918 weight * JxW[q_index] * // dx * phi_j(x_q)
919 (shape_value_i * scalar_product(j_extr_boundary_numflux(component_i, e),
920 normals[q_index])); // phi_i(x_q) * j_numflux(x_q, u_q) * n(x_q)
921 }
922 }
923 };
924 const auto face_worker = [&](const Iterator &cell, const uint &f, const uint &sf, const Iterator &ncell,
925 const uint &nf, const uint &nsf, Scratch &scratch_data, CopyData &copy_data) {
926 const double cell_width = DiFfRG::internal::cell_width(cell);
927 const double ncell_width = DiFfRG::internal::cell_width(ncell);
928 scratch_data.fe_interface_values.reinit(cell, f, sf, ncell, nf, nsf);
929 const auto &fe_iv = scratch_data.fe_interface_values;
930 const auto &fe_iv_s = scratch_data.fe_interface_values.get_fe_face_values(0);
931 const auto &fe_iv_n = scratch_data.fe_interface_values.get_fe_face_values(1);
932 const uint n_dofs = fe_iv.n_current_interface_dofs();
933
934 copy_data.face_data.emplace_back();
935 auto &copy_data_face = copy_data.face_data.back();
936 copy_data_face.reinit(fe_iv, Components::count_extractors());
937
938 const auto &JxW = fe_iv.get_JxW_values();
939 const auto &q_points = fe_iv.get_quadrature_points();
940 const auto &q_indices = fe_iv.quadrature_point_indices();
941 const std::vector<Tensor<1, dim>> &normals = fe_iv.get_normal_vectors();
942
943 auto &solution_s = scratch_data.solution_interface[0];
944 auto &solution_n = scratch_data.solution_interface[1];
945 auto &solution_grad_s = scratch_data.solution_grad_interface[0];
946 auto &solution_grad_n = scratch_data.solution_grad_interface[1];
947 auto &solution_hess_s = scratch_data.solution_hess_interface[0];
948 auto &solution_hess_n = scratch_data.solution_hess_interface[1];
949 fe_iv_s.get_function_values(solution_global, solution_s);
950 fe_iv_n.get_function_values(solution_global, solution_n);
951 fe_iv_s.get_function_gradients(solution_global, solution_grad_s);
952 fe_iv_n.get_function_gradients(solution_global, solution_grad_n);
953 fe_iv_s.get_function_hessians(solution_global, solution_hess_s);
954 fe_iv_n.get_function_hessians(solution_global, solution_hess_n);
955
956 // Pre-compute interface DoF component indices and face numbers
957 auto &comp = scratch_data.comp;
958 comp.resize(n_dofs);
959 std::vector<uint> face_no_arr(n_dofs);
960 std::vector<uint> local_dof_arr(n_dofs);
961 for (uint i = 0; i < n_dofs; ++i) {
962 const auto &cd_i = fe_iv.interface_dof_to_dof_indices(i);
963 face_no_arr[i] = cd_i[0] == numbers::invalid_unsigned_int ? 1 : 0;
964 local_dof_arr[i] = cd_i[face_no_arr[i]];
965 comp[i] = fe.system_to_component_index(local_dof_arr[i]).first;
966 }
967
968 auto &cached_shape_values = scratch_data.cached_shape_values;
969 auto &cached_shape_grads = scratch_data.cached_shape_grads;
970 auto &cached_shape_hessians = scratch_data.cached_shape_hessians;
971 cached_shape_values.resize(n_dofs);
972 cached_shape_grads.resize(n_dofs);
973 cached_shape_hessians.resize(n_dofs);
974
975 array<SimpleMatrix<Tensor<1, dim, NumberType>, Components::count_fe_functions()>, 2> j_numflux;
976 array<SimpleMatrix<Tensor<1, dim, Tensor<1, dim, NumberType>>, Components::count_fe_functions()>, 2>
977 j_grad_numflux;
978 array<SimpleMatrix<Tensor<1, dim, Tensor<2, dim, NumberType>>, Components::count_fe_functions()>, 2>
979 j_hess_numflux;
980 array<SimpleMatrix<Tensor<1, dim, NumberType>, Components::count_fe_functions(),
981 Components::count_extractors()>,
982 2>
983 j_extr_numflux;
984 for (const auto &q_index : q_indices) {
985 const auto &x_q = q_points[q_index];
986 model.template jacobian_numflux<0, 0>(
987 j_numflux, normals[q_index], x_q,
988 fe_tie(solution_s[q_index], solution_grad_s[q_index], solution_hess_s[q_index], extracted_data,
989 variables, cell_width),
990 fe_tie(solution_n[q_index], solution_grad_n[q_index], solution_hess_n[q_index], extracted_data,
991 variables, ncell_width));
992 model.template jacobian_numflux_grad<1>(
993 j_grad_numflux, normals[q_index], x_q,
994 fe_tie(solution_s[q_index], solution_grad_s[q_index], solution_hess_s[q_index], extracted_data,
995 variables, cell_width),
996 fe_tie(solution_n[q_index], solution_grad_n[q_index], solution_hess_n[q_index], extracted_data,
997 variables, ncell_width));
998 model.template jacobian_numflux_hess<2>(
999 j_hess_numflux, normals[q_index], x_q,
1000 fe_tie(solution_s[q_index], solution_grad_s[q_index], solution_hess_s[q_index], extracted_data,
1001 variables, cell_width),
1002 fe_tie(solution_n[q_index], solution_grad_n[q_index], solution_hess_n[q_index], extracted_data,
1003 variables, ncell_width));
1004 if constexpr (Components::count_extractors() > 0) {
1005 model.template jacobian_numflux_extr<3>(
1006 j_extr_numflux, normals[q_index], x_q,
1007 fe_tie(solution_s[q_index], solution_grad_s[q_index], solution_hess_s[q_index], extracted_data,
1008 variables, cell_width),
1009 fe_tie(solution_n[q_index], solution_grad_n[q_index], solution_hess_n[q_index], extracted_data,
1010 variables, ncell_width));
1011 }
1012
1013 // Cache shape values, gradients, and hessians for all interface DoFs at this quadrature point
1014 for (uint k = 0; k < n_dofs; ++k) {
1015 cached_shape_values[k] =
1016 fe_iv.get_fe_face_values(face_no_arr[k]).shape_value_component(local_dof_arr[k], q_index, comp[k]);
1017 cached_shape_grads[k] =
1018 fe_iv.get_fe_face_values(face_no_arr[k]).shape_grad_component(local_dof_arr[k], q_index, comp[k]);
1019 cached_shape_hessians[k] =
1020 fe_iv.get_fe_face_values(face_no_arr[k]).shape_hessian_component(local_dof_arr[k], q_index, comp[k]);
1021 }
1022
1023 for (uint i = 0; i < n_dofs; ++i) {
1024 const auto component_i = comp[i];
1025 const auto face_no_i = face_no_arr[i];
1026 const auto jump_i = fe_iv.jump_in_shape_values(i, q_index, component_i);
1027 for (uint j = 0; j < n_dofs; ++j) {
1028 const auto component_j = comp[j];
1029 const auto face_no_j = face_no_arr[j];
1030 const auto &shape_value_j = cached_shape_values[j];
1031 const auto &shape_grad_j = cached_shape_grads[j];
1032 const auto &shape_hessian_j = cached_shape_hessians[j];
1033 // consolidated contribution: scalar + gradient + hessian
1034 copy_data_face.cell_jacobian(i, j) +=
1035 weight * JxW[q_index] *
1036 (shape_value_j * // dx * phi_j(x_q)
1037 (jump_i * scalar_product(j_numflux[face_no_j](component_i, component_j),
1038 normals[q_index])) // [[phi_i(x_q)]] * j_numflux(x_q, u_q)
1039 + scalar_product(shape_grad_j, // gradient contribution
1040 jump_i * scalar_product(j_grad_numflux[face_no_j](component_i, component_j),
1041 normals[q_index])) +
1042 scalar_product(shape_hessian_j, // hessian contribution
1043 jump_i * scalar_product(j_hess_numflux[face_no_j](component_i, component_j),
1044 normals[q_index])));
1045 }
1046 // extractor contribution
1047 if constexpr (Components::count_extractors() > 0)
1048 for (uint e = 0; e < Components::count_extractors(); ++e)
1049 copy_data_face.extractor_cell_jacobian(i, e) +=
1050 weight * JxW[q_index] * // dx * phi_j(x_q)
1051 (jump_i * scalar_product(j_extr_numflux[face_no_i](component_i, e),
1052 normals[q_index])); // [[phi_i(x_q)]] * j_numflux(x_q, u_q)
1053 }
1054 }
1055 };
1056 const auto copier = [&](const CopyData &c) {
1057 constraints.distribute_local_to_global(c.cell_jacobian, c.local_dof_indices, jacobian);
1058 for (auto &cdf : c.face_data) {
1059 constraints.distribute_local_to_global(cdf.cell_jacobian, cdf.joint_dof_indices, jacobian);
1060 if constexpr (Components::count_extractors() > 0) {
1061 FullMatrix<NumberType> extractor_dependence(cdf.joint_dof_indices.size(), extractor_dof_indices.size());
1062 cdf.extractor_cell_jacobian.mmult(extractor_dependence, this->extractor_jacobian);
1063 constraints.distribute_local_to_global(extractor_dependence, cdf.joint_dof_indices, extractor_dof_indices,
1064 jacobian);
1065 }
1066 }
1067 if constexpr (Components::count_extractors() > 0) {
1068 FullMatrix<NumberType> extractor_dependence(c.local_dof_indices.size(), extractor_dof_indices.size());
1069 c.extractor_cell_jacobian.mmult(extractor_dependence, this->extractor_jacobian);
1070 constraints.distribute_local_to_global(extractor_dependence, c.local_dof_indices, extractor_dof_indices,
1071 jacobian);
1072 }
1073 };
1074
1075 Scratch scratch_data(mapping, fe, quadrature, quadrature_face);
1076 CopyData copy_data;
1077 MeshWorker::AssembleFlags flags = MeshWorker::assemble_own_cells | MeshWorker::assemble_boundary_faces |
1078 MeshWorker::assemble_own_interior_faces_once |
1079 MeshWorker::assemble_ghost_faces_once;
1080
1081 Timer timer;
1082 // map() is collective and each rank visits only its own cells; see NoMapsHere.
1083 const NoMapsHere no_maps_during_assembly;
1084 const auto schedule = schedule_for(assembly_cost::momentum_integral);
1085 MeshWorker::mesh_loop(locally_owned_cells(dof_handler), cell_worker, copier, scratch_data, copy_data, flags,
1086 boundary_worker, face_worker, schedule.queue_length, schedule.chunk_size);
1087 // Resolve contributions this rank made to rows it does not own. A partition-boundary
1088 // face is assembled by exactly one of its two neighbours (mesh_loop hands it to the
1089 // smaller subdomain id), and that rank writes BOTH sides -- so the other side's rows
1090 // arrive here. A no-op for the serial types.
1091 jacobian.compress(dealii::VectorOperation::add);
1092 timings_jacobian.push_back(timer.wall_time());
1093 }
1094 SummaryEvent summary() const override
1095 {
1096 SummaryEvent result{.component = "dDG"};
1097 result.timing("reinit", average_time_reinit() * 1000, num_reinits())
1098 .timing("residual", average_time_residual_assembly() * 1000, num_residuals())
1100 return result;
1101 }
1102
1103 double average_time_reinit() const
1104 {
1105 double t = 0.;
1106 double n = timings_reinit.size();
1107 for (const auto &t_ : timings_reinit)
1108 t += t_ / n;
1109 return t;
1110 }
1111 uint num_reinits() const { return timings_reinit.size(); }
1112
1114 {
1115 double t = 0.;
1116 double n = timings_residual.size();
1117 for (const auto &t_ : timings_residual)
1118 t += t_ / n;
1119 return t;
1120 }
1121 uint num_residuals() const { return timings_residual.size(); }
1122
1124 {
1125 double t = 0.;
1126 double n = timings_jacobian.size();
1127 for (const auto &t_ : timings_jacobian)
1128 t += t_ / n;
1129 return t;
1130 }
1131 uint num_jacobians() const { return timings_jacobian.size(); }
1132
1133 protected:
1135 using Base::dof_handler;
1136 using Base::fe;
1137 using Base::mapping;
1138 using Base::model;
1139
1140 QGauss<dim> quadrature;
1142 using Base::schedule_for;
1143
1147
1148 std::vector<double> timings_reinit;
1149 std::vector<double> timings_residual;
1150 std::vector<double> timings_jacobian;
1151
1153 };
1154 } // namespace dDG
1155} // namespace DiFfRG
A hierarchical configuration tree, readable from JSON and TOML files.
Definition config_tree.hh:32
The basic assembler that can be used for any standard CG scheme with flux and source.
Definition common.hh:42
Model & model
Definition common.hh:405
const Mapping< dim > & mapping
Definition common.hh:409
FullMatrix< NumberType > extractor_jacobian
Definition common.hh:459
const FiniteElement< dim > & fe
Definition common.hh:407
void extract(std::array< NumberType, Components::count_extractors()> &data, const VectorType &solution_global, const VectorType &variables, bool search_EoM, bool set_EoM, bool postprocess) const
Definition common.hh:274
bool jacobian_extractors(FullMatrix< NumberType > &extractor_jacobian, const VectorType &solution_global, const VectorType &variables)
Definition common.hh:302
virtual void reinit() override
Reinitialize the assembler. This is necessary if the mesh has changed, e.g. after a mesh refinement.
Definition common.hh:116
const DoFHandler< dim > & dof_handler
Definition common.hh:408
Discretization & discretization
Definition common.hh:404
std::vector< types::global_dof_index > extractor_dof_indices
Definition common.hh:463
AssemblySchedule schedule_for(const double cost_ns) const
The mesh_loop schedule for a loop whose cell worker costs cost_ns nanoseconds.
Definition common.hh:418
Decides, without any user input, which rank computes which part of each map().
Definition map_scheduler.hh:195
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 basic assembler that can be used for any standard DG scheme with flux and source.
Definition ddg.hh:197
Model & model
Definition common.hh:405
uint num_residuals() const
Definition ddg.hh:1121
virtual void reinit() override
Reinitialize the assembler. This is necessary if the mesh has changed, e.g. after a mesh refinement.
Definition ddg.hh:235
const Mapping< dim > & mapping
Definition common.hh:409
Discretization_ Discretization
Definition ddg.hh:201
virtual void refinement_indicator(Vector< double > &indicator, const VectorType &solution_global) override
refinement indicator for adaptivity. Calls the model's cell_indicator and face_indicator functions.
Definition ddg.hh:293
double average_time_reinit() const
Definition ddg.hh:1103
QGauss< dim > quadrature
Definition ddg.hh:1140
uint num_reinits() const
Definition ddg.hh:1111
const FiniteElement< dim > & fe
Definition common.hh:407
Model_ Model
Definition ddg.hh:202
get_type::SparsityPattern< SparseMatrixType > sparsity_pattern_mass
Definition ddg.hh:1144
QGauss< dim - 1 > quadrature_face
Definition ddg.hh:1141
std::vector< double > timings_jacobian
Definition ddg.hh:1150
double average_time_residual_assembly() const
Definition ddg.hh:1113
typename Discretization::SparseMatrixType SparseMatrixType
Definition ddg.hh:205
virtual const get_type::SparsityPattern< SparseMatrixType > & get_sparsity_pattern_jacobian() const override
Obtain the sparsity pattern of the jacobian matrix.
Definition ddg.hh:281
uint num_jacobians() const
Definition ddg.hh:1131
typename Discretization::NumberType NumberType
Definition ddg.hh:203
virtual void reinit_matrix(SparseMatrixType &matrix) const override
Definition ddg.hh:222
SummaryEvent summary() const override
Definition ddg.hh:1094
Assembler(Discretization &discretization, Model &model, const ConfigTree &config)
Definition ddg.hh:209
static constexpr uint dim
Definition ddg.hh:208
std::vector< double > timings_residual
Definition ddg.hh:1149
virtual const SparseMatrixType & get_mass_matrix() const override
Obtain the mass matrix.
Definition ddg.hh:285
const DoFHandler< dim > & dof_handler
Definition common.hh:408
double average_time_jacobian_assembly() const
Definition ddg.hh:1123
Discretization & discretization
Definition common.hh:404
SparseMatrixType mass_matrix
Definition ddg.hh:1146
virtual void jacobian_mass(SparseMatrixType &jacobian, const VectorType &solution_global, const VectorType &solution_global_dot, NumberType alpha, NumberType beta) override
Definition ddg.hh:637
typename Discretization::VectorType VectorType
Definition ddg.hh:204
virtual void mass(VectorType &mass, const VectorType &solution_global, const VectorType &solution_global_dot, NumberType weight) override
Definition ddg.hh:387
virtual void rebuild_jacobian_sparsity() override
Definition ddg.hh:267
std::vector< types::global_dof_index > extractor_dof_indices
Definition common.hh:463
virtual MPI_Comm get_communicator() const override
The communicator this assembler's linear algebra lives on.
Definition ddg.hh:228
virtual void jacobian(SparseMatrixType &jacobian, const VectorType &solution_global, NumberType weight, const VectorType &solution_global_dot, NumberType alpha, NumberType beta, const VectorType &variables=VectorType()) override
Definition ddg.hh:706
std::vector< double > timings_reinit
Definition ddg.hh:1148
AssemblySchedule schedule_for(const double cost_ns) const
The mesh_loop schedule for a loop whose cell worker costs cost_ns nanoseconds.
Definition common.hh:418
typename Discretization::Components Components
Definition ddg.hh:207
virtual void residual(VectorType &residual, const VectorType &solution_global, NumberType weight, const VectorType &solution_global_dot, NumberType weight_mass, const VectorType &variables=VectorType()) override
Definition ddg.hh:447
get_type::SparsityPattern< SparseMatrixType > sparsity_pattern_jacobian
Definition ddg.hh:1145
virtual void reinit_vector(VectorType &vec) const override
Definition ddg.hh:218
virtual void reinit_solution_view(SolutionView< VectorType > &view) const override
Definition ddg.hh:229
All compile-time knowledge about which linear algebra types DiFfRG uses.
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
auto i_tie(T &&...t)
Definition ddg.hh:26
auto fe_tie(T &&...t)
Definition ddg.hh:20
typename internal::_SparsityPattern< SparseMatrixType >::value SparsityPattern
Definition linear_algebra.hh:155
double cell_width(const CellIterator &cell)
The smallest face-normal width over all faces of cell.
Definition cell_geometry.hh:42
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
@ config
/discretization/threads.
void reinit_la_matrix(SparseMatrixType &matrix, const get_type::SparsityPattern< SparseMatrixType > &pattern, const dealii::IndexSet &locally_owned, MPI_Comm comm)
Size a matrix from a finalized sparsity pattern.
Definition la_policy.hh:92
void reinit_la_vector(VectorType &vec, const dealii::IndexSet &locally_owned, MPI_Comm comm)
Size a vector to the rank's share of the rows.
Definition la_policy.hh:27
unsigned int uint
Definition utils.hh:24
void finalize_la_sparsity(dealii::DynamicSparsityPattern &dsp, get_type::SparsityPattern< SparseMatrixType > &pattern, const dealii::IndexSet &locally_owned, const dealii::IndexSet &locally_relevant, MPI_Comm comm)
Turn a freshly built DynamicSparsityPattern into the pattern type the matrix wants.
Definition la_policy.hh:50
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
std::array< uint, 2 > cell_indices
Definition ddg.hh:180
std::array< double, 2 > values
Definition ddg.hh:181
std::vector< CopyFaceData_I > face_data
Definition ddg.hh:183
uint cell_index
Definition ddg.hh:185
double value
Definition ddg.hh:184
FullMatrix< NumberType > extractor_cell_jacobian
Definition ddg.hh:150
std::vector< types::global_dof_index > joint_dof_indices
Definition ddg.hh:151
FullMatrix< NumberType > cell_jacobian
Definition ddg.hh:149
void reinit(const FEInterfaceValues< dim > &fe_iv, uint n_extractors)
Definition ddg.hh:153
void reinit(const Iterator &cell, uint dofs_per_cell, uint n_extractors)
Definition ddg.hh:167
FullMatrix< NumberType > extractor_cell_jacobian
Definition ddg.hh:163
std::vector< CopyDataFace_J > face_data
Definition ddg.hh:165
FullMatrix< NumberType > cell_jacobian
Definition ddg.hh:162
std::vector< types::global_dof_index > local_dof_indices
Definition ddg.hh:164
std::vector< types::global_dof_index > joint_dof_indices
Definition ddg.hh:124
void reinit(const FEInterfaceValues< dim > &fe_iv)
Definition ddg.hh:126
Vector< NumberType > cell_residual
Definition ddg.hh:123
Vector< NumberType > cell_residual
Definition ddg.hh:133
std::vector< CopyDataFace_R > face_data
Definition ddg.hh:135
void reinit(const Iterator &cell, uint dofs_per_cell)
Definition ddg.hh:137
std::vector< types::global_dof_index > local_dof_indices
Definition ddg.hh:134
Class to hold data for each assembly thread, i.e. FEValues for cells, interfaces, as well as pre-allo...
Definition ddg.hh:40
std::vector< Vector< NumberType > > solution_dot
Definition ddg.hh:110
std::vector< Tensor< 1, dim > > cached_shape_grads
Definition ddg.hh:117
std::vector< double > cached_shape_values
Definition ddg.hh:116
std::vector< Vector< NumberType > > solution
Definition ddg.hh:107
std::vector< uint > comp
Definition ddg.hh:115
typename Discretization::NumberType NumberType
Definition ddg.hh:42
std::vector< std::vector< Tensor< 1, dim, NumberType > > > solution_grad
Definition ddg.hh:108
const uint n_components
Definition ddg.hh:102
array< std::vector< std::vector< Tensor< 1, dim, NumberType > > >, 2 > solution_grad_interface
Definition ddg.hh:112
ScratchData(const ScratchData< Discretization > &scratch_data)
Definition ddg.hh:74
array< std::vector< Vector< NumberType > >, 2 > solution_interface
Definition ddg.hh:111
FEValues< dim > fe_values
Definition ddg.hh:104
ScratchData(const Mapping< dim > &mapping, const FiniteElement< dim > &fe, 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|update_hessians, const UpdateFlags interface_update_flags=update_values|update_gradients|update_quadrature_points|update_JxW_values|update_normal_vectors|update_hessians)
Definition ddg.hh:44
std::vector< Tensor< 2, dim > > cached_shape_hessians
Definition ddg.hh:118
FEInterfaceValues< dim > fe_interface_values
Definition ddg.hh:105
static constexpr uint dim
Definition ddg.hh:41
std::vector< std::vector< Tensor< 2, dim, NumberType > > > solution_hess
Definition ddg.hh:109
array< std::vector< std::vector< Tensor< 2, dim, NumberType > > >, 2 > solution_hess_interface
Definition ddg.hh:113
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