DUBeat 1.0.1
High-order discontinuous Galerkin methods and applications to cardiac electrophysiology
Loading...
Searching...
No Matches
DUB_FEM_handler.hpp
Go to the documentation of this file.
1/********************************************************************************
2 Copyright (C) 2024 by the DUBeat authors.
3
4 This file is part of DUBeat.
5
6 DUBeat is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 DUBeat is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with DUBeat. If not, see <http://www.gnu.org/licenses/>.
18********************************************************************************/
19
27#ifndef DUBFEMHandler_HPP_
28#define DUBFEMHandler_HPP_
29
30#include <deal.II/base/exceptions.h>
31#include <deal.II/base/quadrature.h>
32
33#include <deal.II/dofs/dof_handler.h>
34
35#include <deal.II/fe/fe.h>
36#include <deal.II/fe/mapping_q1_eulerian.h>
37
38#include <deal.II/lac/trilinos_vector.h>
39
40#include <cmath>
41#include <filesystem>
42#include <vector>
43
44#include "DUBValues.hpp"
45#include "dof_handler_DG.hpp"
46#include "source/geometry/mesh_handler.hpp"
47#include "source/init.hpp"
48#include "volume_handler_DG.hpp"
49
59template <class basis>
60class DUBFEMHandler : public DUBValues<lifex::dim>
61{
62private:
65
68 const unsigned int n_quad_points;
69
70public:
72 DUBFEMHandler<basis>(const unsigned int degree,
73 const DoFHandlerDG<basis> &dof_hand)
74 : DUBValues<lifex::dim>(degree)
75 , dof_handler(dof_hand)
76 , n_quad_points(static_cast<int>(std::pow(degree + 2, lifex::dim)))
77 {}
78
81
84
87
92 lifex::LinAlg::MPI::Vector
93 dubiner_to_fem(const lifex::LinAlg::MPI::Vector &dub_solution) const;
94
99 lifex::LinAlg::MPI::Vector
100 dubiner_to_fem(const lifex::LinAlg::MPI::Vector &dub_solution,
101 const std::string &FEM_mesh_path,
102 const std::string &subsection,
103 const MPI_Comm &mpi_comm_,
104 const unsigned int degree_fem = 1,
105 const double scaling_factor = 1) const;
106
109 lifex::LinAlg::MPI::Vector
110 fem_to_dubiner(const lifex::LinAlg::MPI::Vector &fem_solution) const;
111
113 lifex::LinAlg::MPI::Vector
114 fem_to_dubiner(const lifex::LinAlg::MPI::Vector &fem_solution,
115 const std::string &FEM_mesh_path,
116 const std::string &subsection,
117 const MPI_Comm &mpi_comm_,
118 const unsigned int degree_fem = 1,
119 const double scaling_factor = 1) const;
120
122 lifex::LinAlg::MPI::Vector
124 lifex::LinAlg::MPI::Vector dub_solution,
125 const std::shared_ptr<dealii::Function<lifex::dim>> &u_analytical) const;
126
128 bool
129 is_in_unit_cell(const dealii::Point<lifex::dim> &unit_q,
130 const double tol = 0) const;
131};
132
133template <class basis>
134lifex::LinAlg::MPI::Vector
136 const lifex::LinAlg::MPI::Vector &dub_solution) const
137{
138 lifex::LinAlg::MPI::Vector fem_solution;
139 fem_solution.reinit(dub_solution);
140
141
142 // Due to the current deal.II availabilities, FE spaces can be of order at
143 // most 2, see dof_handler_DG.hpp description. If the Dubiner space is of
144 // higher order, this method generates a FEM vector of order 2, i.e. the
145 // maximum possible. Hence, due to the possibility that DUB space and FEM
146 // space have different orders, we need to specify two different degrees,
147 // dof_handler, dofs_per_cell...
148 const unsigned int degree_fem =
149 (this->poly_degree < 3) ? this->poly_degree : 2;
150
151 const dealii::FE_SimplexDGP<lifex::dim> fe_dg(degree_fem);
152 const std::vector<dealii::Point<lifex::dim>> support_points =
153 fe_dg.get_unit_support_points();
154 const unsigned int dofs_per_cell_fem = this->get_dofs_per_cell(degree_fem);
155
156 // Generation of a new dof_handler for the FE evaluations.
157 dealii::DoFHandler<lifex::dim> dof_handler_fem(
158 dof_handler.get_triangulation());
159 dof_handler_fem.distribute_dofs(fe_dg);
160
161 std::vector<unsigned int> dof_indices(this->dofs_per_cell);
162 std::vector<unsigned int> dof_indices_fem(dofs_per_cell_fem);
163
164
165 // To perform the conversion to FEM, we just need to evaluate the linear
166 // combination of Dubiner functions over the dof points.
167 for (const auto &cell : dof_handler_fem.active_cell_iterators())
168 {
169 dof_indices = dof_handler.get_dof_indices(cell);
170 cell->get_dof_indices(dof_indices_fem);
171
172 for (unsigned int i = 0; i < dofs_per_cell_fem; ++i)
173 {
174 for (unsigned int j = 0; j < this->dofs_per_cell; ++j)
175 {
176 fem_solution[dof_indices_fem[i]] +=
177 dub_solution[dof_indices[j]] *
178 this->shape_value(j, support_points[i]);
179 }
180 }
181 }
182
183 return fem_solution;
184}
185
186
187template <class basis>
188lifex::LinAlg::MPI::Vector
190 const lifex::LinAlg::MPI::Vector &dub_solution,
191 const std::string &FEM_mesh_path,
192 const std::string &subsection,
193 const MPI_Comm &mpi_comm_,
194 const unsigned int degree_fem,
195 const double scaling_factor) const
196{
197 // Creation of the new FEM evaluation mesh.
198 lifex::utils::MeshHandler triangulation_fem(subsection, mpi_comm_);
199 AssertThrow(std::filesystem::exists(FEM_mesh_path),
200 dealii::StandardExceptions::ExcMessage(
201 "This mesh file/directory does not exist."));
202 triangulation_fem.initialize_from_file(FEM_mesh_path, scaling_factor);
203 triangulation_fem.set_element_type(
204 lifex::utils::MeshHandler::ElementType::Tet);
205 triangulation_fem.create_mesh();
206
207 const dealii::FE_SimplexDGP<lifex::dim> fe_dg(degree_fem);
208 const std::vector<dealii::Point<lifex::dim>> support_points =
209 fe_dg.get_unit_support_points();
210 const unsigned int dofs_per_cell_fem = this->get_dofs_per_cell(degree_fem);
211
212 // Generation of the mapping.
213 const std::unique_ptr<dealii::MappingFE<lifex::dim>> mapping(
214 std::make_unique<dealii::MappingFE<lifex::dim>>(fe_dg));
215
216 // Generation of a new dof_handler for the FE evaluations.
217 dealii::DoFHandler<lifex::dim> dof_handler_fem;
218 dof_handler_fem.reinit(triangulation_fem.get());
219 dof_handler_fem.distribute_dofs(fe_dg);
220
221 // Initialization of the FEM evaluation vector.
222 lifex::LinAlg::MPI::Vector fem_solution;
223 dealii::IndexSet owned_dofs = dof_handler_fem.locally_owned_dofs();
224 fem_solution.reinit(owned_dofs, mpi_comm_);
225
226 // Initialization of the dof_indices.
227 std::vector<unsigned int> dof_indices(this->dofs_per_cell);
228 std::vector<unsigned int> dof_indices_fem(dofs_per_cell_fem);
229
230 // Tolerance.
231 const double tol = 1e-10;
232
233 // To perform the conversion to FEM, we just need to evaluate the linear
234 // combination of Dubiner functions over the dof points.
235 for (const auto &cell_fem : dof_handler_fem.active_cell_iterators())
236 {
237 cell_fem->get_dof_indices(dof_indices_fem);
238 for (unsigned int i = 0; i < dofs_per_cell_fem; ++i)
239 {
240 dealii::Point<lifex::dim> real_support_point =
241 mapping->transform_unit_to_real_cell(cell_fem, support_points[i]);
242
243 for (const auto &cell : dof_handler.active_cell_iterators())
244 {
245 // This condition needs to guarantee that neighbor elements do not
246 // both contribute to the FEM evaluation.
247 if (std::abs(fem_solution[dof_indices_fem[i]]) < tol)
248 {
249 dealii::Point<lifex::dim> unit_support_point_dub =
250 mapping->transform_real_to_unit_cell(cell,
251 real_support_point);
252 if (is_in_unit_cell(unit_support_point_dub, tol))
253 {
254 dof_indices = dof_handler.get_dof_indices(cell);
255 for (unsigned int j = 0; j < this->dofs_per_cell; ++j)
256 {
257 fem_solution[dof_indices_fem[i]] +=
258 (dub_solution[dof_indices[j]] *
259 this->shape_value(j, unit_support_point_dub));
260 }
261 }
262 }
263 }
264 }
265 }
266
267 return fem_solution;
268}
269
270template <class basis>
271lifex::LinAlg::MPI::Vector
273 const lifex::LinAlg::MPI::Vector &fem_solution) const
274{
275 const dealii::FE_SimplexDGP<lifex::dim> fe_dg(this->poly_degree);
276 VolumeHandlerDG<lifex::dim> vol_handler(this->poly_degree);
277
278 lifex::LinAlg::MPI::Vector dub_solution;
279 dub_solution.reinit(fem_solution);
280
281 std::vector<unsigned int> dof_indices(this->dofs_per_cell);
282 double eval_on_quad;
283
284
285 // To perform the conversion to Dubiner, we just need to perform a (numerical)
286 // L2 scalar product between the discretized solution and the Dubiner
287 // functions. More precisely, thanks to the L2-orthonormality of the Dubiner
288 // basis, the i-th coefficient w.r.t. the Dubiner basis is the L2 scalar
289 // product between the solution and the i-th Dubiner function.
290 for (const auto &cell : dof_handler.active_cell_iterators())
291 {
292 dof_indices = dof_handler.get_dof_indices(cell);
293 vol_handler.reinit(cell);
294
295 for (unsigned int i = 0; i < this->dofs_per_cell; ++i)
296 {
297 for (unsigned int q = 0; q < n_quad_points; ++q)
298 {
299 eval_on_quad = 0;
300
301 for (unsigned int j = 0; j < this->dofs_per_cell; ++j)
302 {
303 eval_on_quad +=
304 fem_solution[dof_indices[j]] *
305 fe_dg.shape_value(j, vol_handler.quadrature_ref(q));
306 }
307
308 dub_solution[dof_indices[i]] +=
309 eval_on_quad *
310 this->shape_value(i, vol_handler.quadrature_ref(q)) *
311 vol_handler.quadrature_weight(q);
312 }
313 }
314 }
315
316 return dub_solution;
317}
318
319
320template <class basis>
321lifex::LinAlg::MPI::Vector
323 const lifex::LinAlg::MPI::Vector &fem_solution,
324 const std::string &FEM_mesh_path,
325 const std::string &subsection,
326 const MPI_Comm &mpi_comm_,
327 const unsigned int degree_fem,
328 const double scaling_factor) const
329{
330 // Creation of the new FEM evaluation mesh.
331 lifex::utils::MeshHandler triangulation_fem(subsection, mpi_comm_);
332 AssertThrow(std::filesystem::exists(FEM_mesh_path),
333 dealii::StandardExceptions::ExcMessage(
334 "This mesh file/directory does not exist."));
335 triangulation_fem.initialize_from_file(FEM_mesh_path, scaling_factor);
336 triangulation_fem.set_element_type(
337 lifex::utils::MeshHandler::ElementType::Tet);
338 triangulation_fem.create_mesh();
339
340 const dealii::FE_SimplexDGP<lifex::dim> fe_dg(degree_fem);
341 const std::vector<dealii::Point<lifex::dim>> support_points =
342 fe_dg.get_unit_support_points();
343 const unsigned int dofs_per_cell_fem = this->get_dofs_per_cell(degree_fem);
344
345 // Generation of the mapping.
346 const std::unique_ptr<dealii::MappingFE<lifex::dim>> mapping(
347 std::make_unique<dealii::MappingFE<lifex::dim>>(fe_dg));
348
349 // Generation of a new dof_handler for the FE evaluations.
350 dealii::DoFHandler<lifex::dim> dof_handler_fem;
351 dof_handler_fem.reinit(triangulation_fem.get());
352 dof_handler_fem.distribute_dofs(fe_dg);
353
354 // Initialization of the dof_indices.
355 std::vector<unsigned int> dof_indices(this->dofs_per_cell);
356 std::vector<unsigned int> dof_indices_fem(dofs_per_cell_fem);
357
358 // Tolerance.
359 const double tol = 1e-10;
360
361 VolumeHandlerDG<lifex::dim> vol_handler(this->poly_degree);
362
363 lifex::LinAlg::MPI::Vector dub_solution;
364 dealii::IndexSet owned_dofs = dof_handler.locally_owned_dofs();
365 dub_solution.reinit(owned_dofs, mpi_comm_);
366
367 double eval_on_quad;
368
369
370 // To perform the conversion to Dubiner, we just need to perform a (numerical)
371 // L2 scalar product between the discretized solution and the Dubiner
372 // functions. More precisely, thanks to the L2-orthonormality of the Dubiner
373 // basis, the i-th coefficient w.r.t. the Dubiner basis is the L2 scalar
374 // product between the solution and the i-th Dubiner function.
375 for (const auto &cell : dof_handler.active_cell_iterators())
376 {
377 dof_indices = dof_handler.get_dof_indices(cell);
378 vol_handler.reinit(cell);
379
380 for (unsigned int i = 0; i < this->dofs_per_cell; ++i)
381 {
382 for (unsigned int q = 0; q < n_quad_points; ++q)
383 {
384 dealii::Point<lifex::dim> real_q =
385 mapping->transform_unit_to_real_cell(
386 cell, vol_handler.quadrature_ref(q));
387
388 for (const auto &cell_fem :
389 dof_handler_fem.active_cell_iterators())
390 {
391 // The following condition is to ensure that every Dubiner
392 // quadrature point belongs to only one FEM cell (and not the
393 // neighbor too).
394 if (std::abs(eval_on_quad) < tol)
395 {
396 cell_fem->get_dof_indices(dof_indices_fem);
397 dealii::Point<lifex::dim> unit_q =
398 mapping->transform_real_to_unit_cell(cell_fem, real_q);
399 if (is_in_unit_cell(unit_q, tol))
400 {
401 for (unsigned int j = 0; j < dofs_per_cell_fem; ++j)
402 {
403 eval_on_quad += fem_solution[dof_indices_fem[j]] *
404 fe_dg.shape_value(j, unit_q);
405 }
406
407 dub_solution[dof_indices[i]] +=
408 eval_on_quad *
409 this->shape_value(i,
410 vol_handler.quadrature_ref(q)) *
411 vol_handler.quadrature_weight(q);
412 }
413 }
414 }
415 eval_on_quad = 0;
416 }
417 }
418 }
419
420 return dub_solution;
421}
422
423
424template <class basis>
425lifex::LinAlg::MPI::Vector
427 lifex::LinAlg::MPI::Vector dub_solution,
428 const std::shared_ptr<dealii::Function<lifex::dim>> &u_analytical) const
429{
430 VolumeHandlerDG<lifex::dim> vol_handler(this->poly_degree);
431
432 dealii::IndexSet owned_dofs = dof_handler.locally_owned_dofs();
433
434 std::vector<unsigned int> dof_indices(this->dofs_per_cell);
435 double eval_on_quad;
436
437
438 // Here, we apply the same idea as in fem_to_dubiner. The only difference is
439 // that here we can directly evaluate the solution on the quadrature point
440 // since the solution is analytical.
441 for (const auto &cell : dof_handler.active_cell_iterators())
442 {
443 dof_indices = dof_handler.get_dof_indices(cell);
444 vol_handler.reinit(cell);
445
446 for (unsigned int i = 0; i < this->dofs_per_cell; ++i)
447 {
448 dub_solution[dof_indices[i]] = 0;
449 for (unsigned int q = 0; q < n_quad_points; ++q)
450 {
451 eval_on_quad =
452 u_analytical->value(vol_handler.quadrature_real(q));
453
454 dub_solution[dof_indices[i]] +=
455 eval_on_quad *
456 this->shape_value(i, vol_handler.quadrature_ref(q)) *
457 vol_handler.quadrature_weight(q);
458 }
459 }
460 }
461
462 return dub_solution;
463}
464
465
466template <class basis>
467bool
468DUBFEMHandler<basis>::is_in_unit_cell(const dealii::Point<lifex::dim> &unit_q,
469 const double tol) const
470{
471 if (lifex::dim == 2)
472 {
473 return (unit_q[0] + unit_q[1] < 1.0 + tol && unit_q[0] > -tol &&
474 unit_q[1] > -tol);
475 }
476 else if (lifex::dim == 3)
477 {
478 return (unit_q[0] + unit_q[1] + unit_q[2] < 1.0 + tol &&
479 unit_q[0] > -tol && unit_q[1] > -tol && unit_q[2] > -tol);
480 }
481}
482
483#endif /* DUBFEMHandler_HPP_*/
Class used to discretize analytical solutions as linear combinations of Lagrangian or Dubiner basis.
const unsigned int n_quad_points
Number of quadrature points in the volume element.
lifex::LinAlg::MPI::Vector dubiner_to_fem(const lifex::LinAlg::MPI::Vector &dub_solution) const
Conversion of a discretized solution vector from Dubiner coefficients to FEM coefficients.
lifex::LinAlg::MPI::Vector fem_to_dubiner(const lifex::LinAlg::MPI::Vector &fem_solution) const
Conversion of a discretized solution vector from FEM coefficients to Dubiner coefficients.
const DoFHandlerDG< basis > & dof_handler
Dof handler object of the problem.
bool is_in_unit_cell(const dealii::Point< lifex::dim > &unit_q, const double tol=0) const
To check if a point is inside the reference cell, needed in other methods.
lifex::LinAlg::MPI::Vector analytical_to_dubiner(lifex::LinAlg::MPI::Vector dub_solution, const std::shared_ptr< dealii::Function< lifex::dim > > &u_analytical) const
Conversion of an analytical solution to a vector of Dubiner coefficients.
Class representing the Dubiner basis functions definitions.
Definition DUBValues.hpp:50
const double tol
Default tolerance.
Definition DUBValues.hpp:56
Class to work with global and local degrees of freedom and their mapping.
Class for the main operations on a discontinuous Galerkin volume element.
virtual double quadrature_weight(const unsigned int q) const
Return the quadrature weight associated to the -th quadrature point.
void reinit(const typename dealii::DoFHandler< dim >::active_cell_iterator &new_cell)
Reinit objects on the current new_cell.
virtual dealii::Point< dim > quadrature_ref(const unsigned int q) const
Return the -th spatial quadrature point position on the reference element.
virtual dealii::Point< dim > quadrature_real(const unsigned int q) const
Return the -th spatial quadrature point position on the actual element.