DUBeat  1.0.1
High-order discontinuous Galerkin methods and applications to cardiac electrophysiology
heat_dg.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 HEAT_DG_HPP_
28 #define HEAT_DG_HPP_
29 
30 #include <math.h>
31 
32 #include <memory>
33 #include <vector>
34 
35 #include "../source/DUBValues.hpp"
36 #include "../source/DUB_FEM_handler.hpp"
37 #include "../source/assemble_DG.hpp"
38 #include "../source/face_handler_DG.hpp"
39 #include "../source/model_DG_t.hpp"
40 #include "../source/volume_handler_DG.hpp"
41 #include "source/core_model.hpp"
42 #include "source/geometry/mesh_handler.hpp"
43 #include "source/init.hpp"
44 #include "source/io/data_writer.hpp"
45 #include "source/numerics/bc_handler.hpp"
46 #include "source/numerics/linear_solver_handler.hpp"
47 #include "source/numerics/preconditioner_handler.hpp"
48 #include "source/numerics/tools.hpp"
49 
50 namespace DUBeat::models
51 {
52  namespace heat_DG
53  {
57  class ExactSolution : public lifex::utils::FunctionDirichlet
58  {
59  public:
62  : lifex::utils::FunctionDirichlet()
63  {}
64 
66  virtual double
67  value(const dealii::Point<lifex::dim> &p,
68  const unsigned int /*component*/ = 0) const override
69  {
70  if (lifex::dim == 2)
71  return std::sin(2 * M_PI * p[0]) * std::sin(2 * M_PI * p[1]) *
72  std::exp(-5 * this->get_time());
73  else
74  return std::sin(2 * M_PI * p[0] + M_PI / 4) *
75  std::sin(2 * M_PI * p[1] + M_PI / 4) *
76  std::sin(2 * M_PI * p[2] + M_PI / 4) *
77  std::exp(-5 * this->get_time());
78  }
79  };
80 
84  class RightHandSide : public lifex::Function<lifex::dim>
85  {
86  public:
89  : lifex::Function<lifex::dim>()
90  {}
91 
93  virtual double
94  value(const dealii::Point<lifex::dim> &p,
95  const unsigned int /*component*/ = 0) const override
96  {
97  if (lifex::dim == 2)
98  return std::sin(2 * M_PI * p[0]) * std::sin(2 * M_PI * p[1]) *
99  std::exp(-5 * this->get_time()) * (8 * M_PI * M_PI - 5);
100  else
101  return std::sin(2 * M_PI * p[0] + M_PI / 4) *
102  std::sin(2 * M_PI * p[1] + M_PI / 4) *
103  std::sin(2 * M_PI * p[2] + M_PI / 4) *
104  std::exp(-5 * this->get_time()) * (12 * M_PI * M_PI - 5);
105  }
106  };
107 
111  class BCNeumann : public lifex::Function<lifex::dim>
112  {
113  public:
116  : lifex::Function<lifex::dim>()
117  {}
118 
120  virtual double
121  value(const dealii::Point<lifex::dim> &p,
122  const unsigned int /*component*/ = 0) const override
123  {
124  if (lifex::dim == 2)
125  return -2 * M_PI * std::sin(2 * M_PI * p[0]) *
126  std::cos(2 * M_PI * p[1]) * std::exp(-5 * this->get_time()) *
127  (std::abs(p[1]) < 1e-10) +
128  2 * M_PI * std::cos(2 * M_PI * p[0]) *
129  std::sin(2 * M_PI * p[1]) * std::exp(-5 * this->get_time()) *
130  (std::abs(p[0] - 1) < 1e-10) +
131  2 * M_PI * std::sin(2 * M_PI * p[0]) *
132  std::cos(2 * M_PI * p[1]) * std::exp(-5 * this->get_time()) *
133  (std::abs(p[1] - 1) < 1e-10) -
134  2 * M_PI * std::cos(2 * M_PI * p[0]) *
135  std::sin(2 * M_PI * p[1]) * std::exp(-5 * this->get_time()) *
136  (std::abs(p[0]) < 1e-10);
137  else
138  return 2 * M_PI * std::cos(2 * M_PI * p[0] + M_PI / 4) *
139  std::sin(2 * M_PI * p[1] + M_PI / 4) *
140  std::sin(2 * M_PI * p[2] + M_PI / 4) *
141  std::exp(-5 * this->get_time()) *
142  (std::abs(p[0] - 1) < 1e-10) -
143  2 * M_PI * std::cos(2 * M_PI * p[0] + M_PI / 4) *
144  std::sin(2 * M_PI * p[1] + M_PI / 4) *
145  std::sin(2 * M_PI * p[2] + M_PI / 4) *
146  std::exp(-5 * this->get_time()) * (std::abs(p[0]) < 1e-10) +
147  2 * M_PI * std::sin(2 * M_PI * p[0] + M_PI / 4) *
148  std::cos(2 * M_PI * p[1] + M_PI / 4) *
149  std::sin(2 * M_PI * p[2] + M_PI / 4) *
150  std::exp(-5 * this->get_time()) *
151  (std::abs(p[1] - 1) < 1e-10) -
152  2 * M_PI * std::sin(2 * M_PI * p[0] + M_PI / 4) *
153  std::cos(2 * M_PI * p[1] + M_PI / 4) *
154  std::sin(2 * M_PI * p[2] + M_PI / 4) *
155  std::exp(-5 * this->get_time()) * (std::abs(p[1]) < 1e-10) +
156  2 * M_PI * std::sin(2 * M_PI * p[0] + M_PI / 4) *
157  std::sin(2 * M_PI * p[1] + M_PI / 4) *
158  std::cos(2 * M_PI * p[2] + M_PI / 4) *
159  std::exp(-5 * this->get_time()) *
160  (std::abs(p[2] - 1) < 1e-10) -
161  2 * M_PI * std::sin(2 * M_PI * p[0] + M_PI / 4) *
162  std::sin(2 * M_PI * p[1] + M_PI / 4) *
163  std::cos(2 * M_PI * p[2] + M_PI / 4) *
164  std::exp(-5 * this->get_time()) * (std::abs(p[2]) < 1e-10);
165  }
166  };
167 
171  class GradExactSolution : public lifex::Function<lifex::dim>
172  {
173  public:
176  : lifex::Function<lifex::dim>()
177  {}
178 
180  virtual double
181  value(const dealii::Point<lifex::dim> &p,
182  const unsigned int component = 0) const override
183  {
184  if (lifex::dim == 2)
185  {
186  if (component == 0) // x
187  return 2 * M_PI * std::cos(2 * M_PI * p[0]) *
188  std::sin(2 * M_PI * p[1]) *
189  std::exp(-5 * this->get_time());
190  else // y
191  return 2 * M_PI * std::sin(2 * M_PI * p[0]) *
192  std::cos(2 * M_PI * p[1]) *
193  std::exp(-5 * this->get_time());
194  }
195  else // dim=3
196  {
197  if (component == 0) // x
198  return 2 * M_PI * std::cos(2 * M_PI * p[0] + M_PI / 4) *
199  std::sin(2 * M_PI * p[1] + M_PI / 4) *
200  std::sin(2 * M_PI * p[2] + M_PI / 4) *
201  std::exp(-5 * this->get_time());
202  if (component == 1) // y
203  return 2 * M_PI * std::sin(2 * M_PI * p[0] + M_PI / 4) *
204  std::cos(2 * M_PI * p[1] + M_PI / 4) *
205  std::sin(2 * M_PI * p[2] + M_PI / 4) *
206  std::exp(-5 * this->get_time());
207  else // z
208  return 2 * M_PI * std::sin(2 * M_PI * p[0] + M_PI / 4) *
209  std::sin(2 * M_PI * p[1] + M_PI / 4) *
210  std::cos(2 * M_PI * p[2] + M_PI / 4) *
211  std::exp(-5 * this->get_time());
212  }
213  }
214  };
215  } // namespace heat_DG
216 
258  template <class basis>
259  class HeatDG : public ModelDG_t<basis>
260  {
261  public:
263  HeatDG<basis>()
264  : ModelDG_t<basis>("Heat")
265  {
266  this->u_ex = std::make_shared<heat_DG::ExactSolution>();
267  this->grad_u_ex = std::make_shared<heat_DG::GradExactSolution>();
268  this->f_ex = std::make_shared<heat_DG::RightHandSide>();
269  this->g_n = std::make_shared<heat_DG::BCNeumann>();
270  }
271 
272  private:
273  void
274  assemble_system() override;
275  };
276 
278  template <class basis>
279  void
281  {
282  this->matrix = 0;
283  this->rhs = 0;
284 
285  // The method is needed to define how the system matrix and rhs term are
286  // defined for the heat problem with Neumann boundary conditions. The full
287  // matrix is composed by different sub-matrices that are called with simple
288  // capital letters. We refer here to the DG_Assemble methods for their
289  // definition.
290 
291  // See DG_Assemble::local_V().
292  dealii::FullMatrix<double> V(this->dofs_per_cell, this->dofs_per_cell);
293  // See DG_Assemble::local_M().
294  dealii::FullMatrix<double> M(this->dofs_per_cell, this->dofs_per_cell);
295  // See DG_Assemble::local_SC().
296  dealii::FullMatrix<double> SC(this->dofs_per_cell, this->dofs_per_cell);
297  // See DG_Assemble::local_IC().
298  dealii::FullMatrix<double> IC(this->dofs_per_cell, this->dofs_per_cell);
299  // Transpose of IC.
300  dealii::FullMatrix<double> IC_t(this->dofs_per_cell, this->dofs_per_cell);
301  // See DG_Assemble::local_IN().
302  dealii::FullMatrix<double> IN(this->dofs_per_cell, this->dofs_per_cell);
303  // Transpose of IN.
304  dealii::FullMatrix<double> IN_t(this->dofs_per_cell, this->dofs_per_cell);
305  // See DG_Assemble::local_SN().
306  dealii::FullMatrix<double> SN(this->dofs_per_cell, this->dofs_per_cell);
307 
308  // See DG_Assemble::local_rhs().
309  dealii::Vector<double> cell_rhs(this->dofs_per_cell);
310  // See DG_Assemble::local_rhs_edge_neumann().
311  dealii::Vector<double> cell_rhs_edge(this->dofs_per_cell);
312  // See DG_Assemble::local_u0_M_rhs().
313  dealii::Vector<double> u0_rhs(this->dofs_per_cell);
314 
315  std::vector<lifex::types::global_dof_index> dof_indices(
316  this->dofs_per_cell);
317  dealii::IndexSet owned_dofs = this->dof_handler.locally_owned_dofs();
318  const double &alpha_bdf = this->bdf_handler.get_alpha();
319 
320  for (const auto &cell : this->dof_handler.active_cell_iterators())
321  {
322  if (cell->is_locally_owned())
323  {
324  this->assemble->reinit(cell);
325  dof_indices = this->dof_handler.get_dof_indices(cell);
326 
327  V = this->assemble->local_V();
328  M = this->assemble->local_M();
329  M /= this->prm_time_step;
330  M *= alpha_bdf;
331 
332  cell_rhs = this->assemble->local_rhs(this->f_ex);
333  u0_rhs =
334  this->assemble->local_u0_M_rhs(this->solution_bdf, dof_indices);
335  u0_rhs /= this->prm_time_step;
336 
337  this->matrix.add(dof_indices, V);
338  this->matrix.add(dof_indices, M);
339  this->rhs.add(dof_indices, cell_rhs);
340  this->rhs.add(dof_indices, u0_rhs);
341 
342  for (const auto &edge : cell->face_indices())
343  {
344  this->assemble->reinit(cell, edge);
345  std::vector<lifex::types::global_dof_index> dof_indices_neigh(
346  this->dofs_per_cell);
347 
348  if (!cell->at_boundary(edge))
349  {
350  SC = this->assemble->local_SC(this->prm_stability_coeff);
351  std::tie(IC, IC_t) =
352  this->assemble->local_IC(this->prm_penalty_coeff);
353  this->matrix.add(dof_indices, SC);
354  this->matrix.add(dof_indices, IC);
355  this->matrix.add(dof_indices, IC_t);
356 
357  const auto neighcell = cell->neighbor(edge);
358  dof_indices_neigh =
359  this->dof_handler.get_dof_indices(neighcell);
360 
361  std::tie(IN, IN_t) =
362  this->assemble->local_IN(this->prm_penalty_coeff);
363  SN = this->assemble->local_SN(this->prm_stability_coeff);
364 
365  this->matrix.add(dof_indices, dof_indices_neigh, IN);
366  this->matrix.add(dof_indices_neigh, dof_indices, IN_t);
367  this->matrix.add(dof_indices, dof_indices_neigh, SN);
368  }
369  else
370  {
371  cell_rhs_edge =
372  this->assemble->local_rhs_edge_neumann(this->g_n);
373  this->rhs.add(dof_indices, cell_rhs_edge);
374  }
375  }
376  }
377  }
378 
379  this->matrix.compress(dealii::VectorOperation::add);
380  this->rhs.compress(dealii::VectorOperation::add);
381  }
382 } // namespace DUBeat::models
383 
384 #endif /* HEAT_DG_HPP_*/
Class to solve the heat equation using the Discontinuous Galerkin method.
Definition: heat_dg.hpp:260
void assemble_system() override
Assembly of the linear system.
Definition: heat_dg.hpp:280
Neumann boundary condition.
Definition: heat_dg.hpp:112
virtual double value(const dealii::Point< lifex::dim > &p, const unsigned int=0) const override
Evaluate the Neumann boundary condition function in a point.
Definition: heat_dg.hpp:121
Exact solution of the problem.
Definition: heat_dg.hpp:58
virtual double value(const dealii::Point< lifex::dim > &p, const unsigned int=0) const override
Evaluate the exact solution in a point.
Definition: heat_dg.hpp:67
Gradient of the exact solution.
Definition: heat_dg.hpp:172
virtual double value(const dealii::Point< lifex::dim > &p, const unsigned int component=0) const override
Evaluate the gradient of the exact solution in a point.
Definition: heat_dg.hpp:181
virtual double value(const dealii::Point< lifex::dim > &p, const unsigned int=0) const override
Evaluate the source term in a point.
Definition: heat_dg.hpp:94
Class representing the resolution of time-dependent problems using discontinuous Galerkin methods.
Definition: model_DG_t.hpp:62
std::shared_ptr< dealii::Function< lifex::dim > > g_n
Neumann boundary conditions.
Definition: model_DG.hpp:257
std::shared_ptr< dealii::Function< lifex::dim > > f_ex
Known forcing term.
Definition: model_DG.hpp:255
std::shared_ptr< dealii::Function< lifex::dim > > grad_u_ex
Pointer to exact gradient solution Function.
Definition: model_DG.hpp:253
std::shared_ptr< lifex::utils::FunctionDirichlet > u_ex
Pointer to exact solution function.
Definition: model_DG.hpp:251