DUBeat  1.0.1
High-order discontinuous Galerkin methods and applications to cardiac electrophysiology
monodomain_fhn_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 MONODOMAIN_FHN_DG_HPP_
28 #define MONODOMAIN_FHN_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.hpp"
40 #include "../source/model_DG_t.hpp"
41 #include "../source/volume_handler_DG.hpp"
42 #include "source/core_model.hpp"
43 #include "source/geometry/mesh_handler.hpp"
44 #include "source/init.hpp"
45 #include "source/io/data_writer.hpp"
46 #include "source/numerics/bc_handler.hpp"
47 #include "source/numerics/linear_solver_handler.hpp"
48 #include "source/numerics/preconditioner_handler.hpp"
49 #include "source/numerics/tools.hpp"
50 
51 namespace DUBeat::models
52 {
53  namespace monodomain_fhn_DG
54  {
58  class ExactSolution : public lifex::utils::FunctionDirichlet
59  {
60  public:
63  : lifex::utils::FunctionDirichlet()
64  {}
65 
67  virtual double
68  value(const dealii::Point<lifex::dim> &p,
69  const unsigned int /*component*/ = 0) const override
70  {
71  if (lifex::dim == 2)
72  return std::sin(2 * M_PI * p[0]) * std::sin(2 * M_PI * p[1]) *
73  std::exp(-5 * this->get_time());
74  else
75  return std::sin(2 * M_PI * p[0] + M_PI / 4) *
76  std::sin(2 * M_PI * p[1] + M_PI / 4) *
77  std::sin(2 * M_PI * p[2] + M_PI / 4) *
78  std::exp(-5 * this->get_time());
79  }
80  };
81 
85  class RightHandSide : public lifex::Function<lifex::dim>
86  {
87  private:
89  double ChiM;
90 
92  double Sigma;
93 
95  double Cm;
96 
98  double kappa;
99 
101  double epsilon;
102 
104  double gamma;
105 
107  double a;
108 
109  public:
112  double Sigma,
113  double Cm,
114  double kappa,
115  double epsilon,
116  double gamma,
117  double a)
118  : lifex::Function<lifex::dim>()
119  , ChiM(ChiM)
120  , Sigma(Sigma)
121  , Cm(Cm)
122  , kappa(kappa)
123  , epsilon(epsilon)
124  , gamma(gamma)
125  , a(a)
126  {}
127 
129  virtual double
130  value(const dealii::Point<lifex::dim> &p,
131  const unsigned int /*component*/ = 0) const override
132  {
133  if (lifex::dim == 2)
134  return std::sin(2 * M_PI * p[0]) * std::sin(2 * M_PI * p[1]) *
135  std::exp(-5 * this->get_time()) *
136  (-ChiM * Cm * 5 + Sigma * 8 * pow(M_PI, 2) +
137  ChiM * kappa *
138  (std::sin(2 * M_PI * p[0]) * std::sin(2 * M_PI * p[1]) *
139  std::exp(-5 * this->get_time()) -
140  a) *
141  (std::sin(2 * M_PI * p[0]) * std::sin(2 * M_PI * p[1]) *
142  std::exp(-5 * this->get_time()) -
143  1) +
144  ChiM * (epsilon / (epsilon * gamma - 5)));
145  else
146  return std::sin(2 * M_PI * p[0] + M_PI / 4) *
147  std::sin(2 * M_PI * p[1] + M_PI / 4) *
148  std::sin(2 * M_PI * p[2] + M_PI / 4) *
149  std::exp(-5 * this->get_time()) *
150  (-ChiM * Cm * 5 + Sigma * 12 * pow(M_PI, 2) +
151  ChiM * kappa *
152  (std::sin(2 * M_PI * p[0] + M_PI / 4) *
153  std::sin(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()) -
156  a) *
157  (std::sin(2 * M_PI * p[0] + M_PI / 4) *
158  std::sin(2 * M_PI * p[1] + M_PI / 4) *
159  std::sin(2 * M_PI * p[2] + M_PI / 4) *
160  std::exp(-5 * this->get_time()) -
161  1) +
162  ChiM * (epsilon / (epsilon * gamma - 5)));
163  }
164  };
165 
169  class BCNeumann : public lifex::Function<lifex::dim>
170  {
171  private:
173  double Sigma;
174 
175  public:
177  BCNeumann(double Sigma)
178  : lifex::Function<lifex::dim>()
179  , Sigma(Sigma)
180  {}
181 
183  virtual double
184  value(const dealii::Point<lifex::dim> &p,
185  const unsigned int /*component*/ = 0) const override
186  {
187  if (lifex::dim == 2)
188  return Sigma *
189  (-2 * M_PI * std::sin(2 * M_PI * p[0]) *
190  std::cos(2 * M_PI * p[1]) *
191  std::exp(-5 * this->get_time()) * (std::abs(p[1]) < 1e-10) +
192  2 * M_PI * std::cos(2 * M_PI * p[0]) *
193  std::sin(2 * M_PI * p[1]) *
194  std::exp(-5 * this->get_time()) *
195  (std::abs(p[0] - 1) < 1e-10) +
196  2 * M_PI * std::sin(2 * M_PI * p[0]) *
197  std::cos(2 * M_PI * p[1]) *
198  std::exp(-5 * this->get_time()) *
199  (std::abs(p[1] - 1) < 1e-10) -
200  2 * M_PI * std::cos(2 * M_PI * p[0]) *
201  std::sin(2 * M_PI * p[1]) *
202  std::exp(-5 * this->get_time()) * (std::abs(p[0]) < 1e-10));
203  else
204  return Sigma *
205  (2 * M_PI * std::cos(2 * M_PI * p[0] + M_PI / 4) *
206  std::sin(2 * M_PI * p[1] + M_PI / 4) *
207  std::sin(2 * M_PI * p[2] + M_PI / 4) *
208  std::exp(-5 * this->get_time()) *
209  (std::abs(p[0] - 1) < 1e-10) -
210  2 * M_PI * std::cos(2 * M_PI * p[0] + M_PI / 4) *
211  std::sin(2 * M_PI * p[1] + M_PI / 4) *
212  std::sin(2 * M_PI * p[2] + M_PI / 4) *
213  std::exp(-5 * this->get_time()) * (std::abs(p[0]) < 1e-10) +
214  2 * M_PI * std::sin(2 * M_PI * p[0] + M_PI / 4) *
215  std::cos(2 * M_PI * p[1] + M_PI / 4) *
216  std::sin(2 * M_PI * p[2] + M_PI / 4) *
217  std::exp(-5 * this->get_time()) *
218  (std::abs(p[1] - 1) < 1e-10) -
219  2 * M_PI * std::sin(2 * M_PI * p[0] + M_PI / 4) *
220  std::cos(2 * M_PI * p[1] + M_PI / 4) *
221  std::sin(2 * M_PI * p[2] + M_PI / 4) *
222  std::exp(-5 * this->get_time()) * (std::abs(p[1]) < 1e-10) +
223  2 * M_PI * std::sin(2 * M_PI * p[0] + M_PI / 4) *
224  std::sin(2 * M_PI * p[1] + M_PI / 4) *
225  std::cos(2 * M_PI * p[2] + M_PI / 4) *
226  std::exp(-5 * this->get_time()) *
227  (std::abs(p[2] - 1) < 1e-10) -
228  2 * M_PI * std::sin(2 * M_PI * p[0] + M_PI / 4) *
229  std::sin(2 * M_PI * p[1] + M_PI / 4) *
230  std::cos(2 * M_PI * p[2] + M_PI / 4) *
231  std::exp(-5 * this->get_time()) * (std::abs(p[2]) < 1e-10));
232  }
233  };
234 
238  class GradExactSolution : public lifex::Function<lifex::dim>
239  {
240  public:
243  : lifex::Function<lifex::dim>()
244  {}
245 
247  virtual double
248  value(const dealii::Point<lifex::dim> &p,
249  const unsigned int component = 0) const override
250  {
251  if (lifex::dim == 2)
252  {
253  if (component == 0) // x
254  return 2 * M_PI * std::cos(2 * M_PI * p[0]) *
255  std::sin(2 * M_PI * p[1]) *
256  std::exp(-5 * this->get_time());
257  else // y
258  return 2 * M_PI * std::sin(2 * M_PI * p[0]) *
259  std::cos(2 * M_PI * p[1]) *
260  std::exp(-5 * this->get_time());
261  }
262  else // dim=3
263  {
264  if (component == 0) // x.
265  return 2 * M_PI * std::cos(2 * M_PI * p[0] + M_PI / 4) *
266  std::sin(2 * M_PI * p[1] + M_PI / 4) *
267  std::sin(2 * M_PI * p[2] + M_PI / 4) *
268  std::exp(-5 * this->get_time());
269  if (component == 1) // y.
270  return 2 * M_PI * std::sin(2 * M_PI * p[0] + M_PI / 4) *
271  std::cos(2 * M_PI * p[1] + M_PI / 4) *
272  std::sin(2 * M_PI * p[2] + M_PI / 4) *
273  std::exp(-5 * this->get_time());
274  else // z.
275  return 2 * M_PI * std::sin(2 * M_PI * p[0] + M_PI / 4) *
276  std::sin(2 * M_PI * p[1] + M_PI / 4) *
277  std::cos(2 * M_PI * p[2] + M_PI / 4) *
278  std::exp(-5 * this->get_time());
279  }
280  }
281  };
282 
286  class ExactSolution_w : public lifex::utils::FunctionDirichlet
287  {
288  private:
290  double epsilon;
291 
293  double gamma;
294 
295  public:
297  ExactSolution_w(double epsilon, double gamma)
298  : lifex::utils::FunctionDirichlet()
299  , epsilon(epsilon)
300  , gamma(gamma)
301  {}
302 
304  virtual double
305  value(const dealii::Point<lifex::dim> &p,
306  const unsigned int /*component*/ = 0) const override
307  {
308  if (lifex::dim == 2)
309  return epsilon / (epsilon * gamma - 5) * std::sin(2 * M_PI * p[0]) *
310  std::sin(2 * M_PI * p[1]) * std::exp(-5 * this->get_time());
311  else
312  return epsilon / (epsilon * gamma - 5) * std::sin(2 * M_PI * p[0]) *
313  std::sin(2 * M_PI * p[1]) * std::sin(2 * M_PI * p[2]) *
314  std::exp(-5 * this->get_time());
315  }
316  };
317 
321  class GradExactSolution_w : public lifex::Function<lifex::dim>
322  {
323  private:
325  double epsilon;
326 
328  double gamma;
329 
330  public:
333  : lifex::Function<lifex::dim>()
334  , epsilon(epsilon)
335  , gamma(gamma)
336  {}
337 
339  virtual double
340  value(const dealii::Point<lifex::dim> &p,
341  const unsigned int component = 0) const override
342  {
343  if (lifex::dim == 2)
344  {
345  if (component == 0) // x
346  return 2 * M_PI * epsilon / (epsilon * gamma - 5) *
347  std::cos(2 * M_PI * p[0]) * std::sin(2 * M_PI * p[1]) *
348  std::exp(-5 * this->get_time());
349  else // y
350  return 2 * M_PI * epsilon / (epsilon * gamma - 5) *
351  std::sin(2 * M_PI * p[0]) * std::cos(2 * M_PI * p[1]) *
352  std::exp(-5 * this->get_time());
353  }
354  else // dim=3
355  {
356  if (component == 0) // x
357  return 2 * M_PI * epsilon / (epsilon * gamma - 5) *
358  std::cos(2 * M_PI * p[0]) * std::sin(2 * M_PI * p[1]) *
359  std::sin(2 * M_PI * p[2]) *
360  std::exp(-5 * this->get_time());
361  if (component == 1) // y
362  return 2 * M_PI * epsilon / (epsilon * gamma - 5) *
363  std::sin(2 * M_PI * p[0]) * std::cos(2 * M_PI * p[1]) *
364  std::sin(2 * M_PI * p[2]) *
365  std::exp(-5 * this->get_time());
366  else // z
367  return 2 * M_PI * epsilon / (epsilon * gamma - 5) *
368  std::sin(2 * M_PI * p[0]) * std::sin(2 * M_PI * p[1]) *
369  std::cos(2 * M_PI * p[2]) *
370  std::exp(-5 * this->get_time());
371  }
372  }
373  };
374  } // namespace monodomain_fhn_DG
375 
445  template <class basis>
446  class MonodomainFHNDG : public ModelDG_t<basis>
447  {
448  public:
451  : ModelDG_t<basis>("Monodomain Fitzhugh-Nagumo")
452  {}
453 
454  private:
456  double ChiM;
458  double Sigma;
460  double Cm;
462  double kappa;
464  double epsilon;
466  double gamma;
468  double a;
470  lifex::LinAlg::MPI::Vector solution_owned_w;
472  lifex::LinAlg::MPI::Vector solution_w;
474  lifex::LinAlg::MPI::Vector solution_ex_owned_w;
476  lifex::LinAlg::MPI::Vector solution_ex_w;
478  std::shared_ptr<lifex::utils::FunctionDirichlet> w_ex;
480  std::shared_ptr<dealii::Function<lifex::dim>> grad_w_ex;
482  lifex::utils::BDFHandler<lifex::LinAlg::MPI::Vector> bdf_handler_w;
484  lifex::LinAlg::MPI::Vector solution_bdf_w;
486  lifex::LinAlg::MPI::Vector solution_ext_w;
487 
489  lifex::LinAlg::MPI::SparseMatrix matrix_t0;
490 
492  virtual void
493  declare_parameters(lifex::ParamHandler &params) const override;
494 
496  virtual void
497  parse_parameters(lifex::ParamHandler &params) override;
498 
500  void
501  run() override;
502 
504  void
505  update_time() override;
506 
508  void
509  time_initialization() override;
510 
512  void
513  assemble_system() override;
514  };
515 
516  template <class basis>
517  void
518  MonodomainFHNDG<basis>::declare_parameters(lifex::ParamHandler &params) const
519  {
520  // Default parameters.
521  this->linear_solver.declare_parameters(params);
522  this->preconditioner.declare_parameters(params);
523 
524  // Extra parameters.
525  params.enter_subsection("Mesh and space discretization");
526  {
527  params.declare_entry(
528  "Number of refinements",
529  "2",
530  dealii::Patterns::Integer(0),
531  "Number of global mesh refinement steps applied to initial grid.");
532  params.declare_entry("FE space degree",
533  "1",
534  dealii::Patterns::Integer(1),
535  "Degree of the FE space.");
536  }
537  params.leave_subsection();
538 
539  params.enter_subsection("Discontinuous Galerkin");
540  {
541  params.declare_entry(
542  "Penalty coefficient",
543  "1",
544  dealii::Patterns::Double(-1, 1),
545  "Penalty coefficient in the Discontinuous Galerkin formulation.");
546  params.declare_entry(
547  "Stability coefficient",
548  "10",
549  dealii::Patterns::Double(0),
550  "Stabilization term in the Discontinuous Galerkin formulation.");
551  }
552  params.leave_subsection();
553 
554  params.enter_subsection("Time solver");
555  {
556  params.declare_entry("Initial time",
557  "0",
558  dealii::Patterns::Double(0),
559  "Initial time.");
560  params.declare_entry("Final time",
561  "0.001",
562  dealii::Patterns::Double(0),
563  "Final time.");
564  params.declare_entry("Time step",
565  "0.0001",
566  dealii::Patterns::Double(0),
567  "Time step.");
568  params.declare_entry("BDF order",
569  "1",
570  dealii::Patterns::Integer(1, 3),
571  "BDF order: 1, 2, 3.");
572  }
573  params.leave_subsection();
574 
575  params.enter_subsection("Parameters of the model");
576  {
577  params.declare_entry("ChiM",
578  "1e5",
579  dealii::Patterns::Double(0),
580  "Surface-to-volume ratio of cells parameter.");
581  params.declare_entry("Cm",
582  "1e-2",
583  dealii::Patterns::Double(0),
584  "Membrane capacity.");
585  params.declare_entry("Sigma",
586  "0.12",
587  dealii::Patterns::Double(0),
588  "Diffusion parameter in the principal directions.");
589  params.declare_entry(
590  "kappa",
591  "19.5",
592  dealii::Patterns::Double(0),
593  "Factor for the nonlinear reaction in Fitzhugh Nagumo model.");
594  params.declare_entry("epsilon",
595  "1.2",
596  dealii::Patterns::Double(0),
597  "Parameter for Fitzhugh Nagumo model.");
598  params.declare_entry("gamma",
599  "0.1",
600  dealii::Patterns::Double(0),
601  "Parameter for Fitzhugh Nagumo model.");
602  params.declare_entry("a",
603  "13e-3",
604  dealii::Patterns::Double(0),
605  "Parameter for Fitzhugh Nagumo model.");
606  }
607  params.leave_subsection();
608  }
609 
610  template <class basis>
611  void
612  MonodomainFHNDG<basis>::parse_parameters(lifex::ParamHandler &params)
613  {
614  // Parse input file.
615  params.parse();
616  // Read input parameters.
617  this->linear_solver.parse_parameters(params);
618  this->preconditioner.parse_parameters(params);
619 
620  // Extra parameters.
621  params.enter_subsection("Mesh and space discretization");
622  this->prm_n_refinements = params.get_integer("Number of refinements");
623  this->prm_fe_degree = params.get_integer("FE space degree");
624  params.leave_subsection();
625 
626  params.enter_subsection("Discontinuous Galerkin");
627  this->prm_penalty_coeff = params.get_double("Penalty coefficient");
628  AssertThrow(
629  this->prm_penalty_coeff == 1. || this->prm_penalty_coeff == 0. ||
630  this->prm_penalty_coeff == -1.,
631  dealii::StandardExceptions::ExcMessage(
632  "Penalty coefficient must be 1 (SIP method) or 0 (IIP method) "
633  "or -1 (NIP method)."));
634 
635  this->prm_stability_coeff = params.get_double("Stability coefficient");
636  params.leave_subsection();
637 
638  params.enter_subsection("Time solver");
639  this->prm_time_init = params.get_double("Initial time");
640  this->prm_time_final = params.get_double("Final time");
641  AssertThrow(this->prm_time_final > this->prm_time_init,
642  dealii::StandardExceptions::ExcMessage(
643  "Final time must be greater than initial time."));
644 
645  this->prm_time_step = params.get_double("Time step");
646  this->prm_bdf_order = params.get_integer("BDF order");
647  params.leave_subsection();
648 
649  params.enter_subsection("Parameters of the model");
650  ChiM = params.get_double("ChiM");
651  Cm = params.get_double("Cm");
652  Sigma = params.get_double("Sigma");
653  kappa = params.get_double("kappa");
654  epsilon = params.get_double("epsilon");
655  gamma = params.get_double("gamma");
656  a = params.get_double("a");
657  params.leave_subsection();
658  }
659 
660  template <class basis>
661  void
663  {
664  this->create_mesh();
665  this->setup_system();
666 
667  this->initialize_solution(this->solution_owned, this->solution);
668  this->initialize_solution(this->solution_ex_owned, this->solution_ex);
669  this->initialize_solution(this->solution_owned_w, this->solution_w);
670  this->initialize_solution(this->solution_ex_owned_w, this->solution_ex_w);
671 
672  time_initialization();
673 
674  while (this->time < this->prm_time_final)
675  {
676  this->time += this->prm_time_step;
677  ++this->timestep_number;
678 
679  pcout << "Time step " << std::setw(6) << this->timestep_number
680  << " at t = " << std::setw(8) << std::fixed
681  << std::setprecision(6) << this->time << std::endl;
682 
683  this->update_time();
684  this->solution_ext = this->bdf_handler.get_sol_extrapolation();
685 
686  this->assemble_system();
687 
688  // Initial guess.
689  this->solution_owned = this->solution_ext;
690  this->solve_system();
691 
692  this->intermediate_error_print(this->solution_owned,
693  this->solution_ex_owned,
694  this->u_ex,
695  "u");
696  this->intermediate_error_print(this->solution_owned_w,
697  this->solution_ex_owned_w,
698  this->w_ex,
699  "w");
700  }
701 
702 
703  this->compute_errors(this->solution_owned,
704  this->solution_ex_owned,
705  this->u_ex,
706  this->grad_u_ex,
707  "u");
708  this->compute_errors(this->solution_owned_w,
709  this->solution_ex_owned_w,
710  this->w_ex,
711  this->grad_w_ex,
712  "w");
713 
714  // Generation of the graphical output.
715  this->solution_ex = this->solution_ex_owned;
716  this->conversion_to_fem(this->solution_ex);
717  this->solution = this->solution_owned;
718  this->conversion_to_fem(this->solution);
719  this->output_results();
720  }
721 
722  template <class basis>
723  void
725  {
726  this->u_ex->set_time(this->time);
727  this->f_ex->set_time(this->time);
728  this->g_n->set_time(this->time);
729  this->grad_u_ex->set_time(this->time);
730 
731  w_ex->set_time(this->time);
732  grad_w_ex->set_time(this->time);
733 
734  this->bdf_handler.time_advance(this->solution_owned, true);
735  this->solution_bdf = this->bdf_handler.get_sol_bdf();
736  bdf_handler_w.time_advance(solution_owned_w, true);
737  solution_bdf_w = this->bdf_handler_w.get_sol_bdf();
738 
739  // Update solution_ex_owned from the updated u_ex.
740  this->discretize_analytical_solution(this->u_ex, this->solution_ex_owned);
741  this->discretize_analytical_solution(this->w_ex, this->solution_ex_owned_w);
742  }
743 
744  template <class basis>
745  void
747  {
748  this->u_ex = std::make_shared<monodomain_fhn_DG::ExactSolution>();
749  this->grad_u_ex = std::make_shared<monodomain_fhn_DG::GradExactSolution>();
750  this->f_ex = std::make_shared<monodomain_fhn_DG::RightHandSide>(
751  ChiM, Sigma, Cm, kappa, epsilon, gamma, a);
752  this->g_n = std::make_shared<monodomain_fhn_DG::BCNeumann>(Sigma);
753  w_ex = std::make_shared<monodomain_fhn_DG::ExactSolution_w>(epsilon, gamma);
754  grad_w_ex =
755  std::make_shared<monodomain_fhn_DG::GradExactSolution_w>(epsilon, gamma);
756 
757  this->u_ex->set_time(this->prm_time_init);
758  this->discretize_analytical_solution(this->u_ex, this->solution_ex_owned);
759  this->solution_ex = this->solution_ex_owned;
760  this->solution = this->solution_owned = this->solution_ex_owned;
761 
762  w_ex->set_time(this->prm_time_init);
763  this->discretize_analytical_solution(this->w_ex, this->solution_ex_owned_w);
764  solution_ex_w = solution_ex_owned_w;
765  solution_w = solution_owned_w = solution_ex_owned_w;
766 
767  const std::vector<lifex::LinAlg::MPI::Vector> sol_init(
768  this->prm_bdf_order, this->solution_owned);
769 
770  this->bdf_handler.initialize(this->prm_bdf_order, sol_init);
771 
772  const std::vector<lifex::LinAlg::MPI::Vector> sol_init_w(
773  this->prm_bdf_order, solution_owned_w);
774 
775  bdf_handler_w.initialize(this->prm_bdf_order, sol_init_w);
776  }
777 
778  template <class basis>
779  void
781  {
782  const double &alpha_bdf = this->bdf_handler.get_alpha();
783 
784  solution_owned_w *= -gamma;
785  solution_owned_w.add(1, this->solution_owned);
786  solution_owned_w *= epsilon;
787  solution_owned_w.add(1 / this->prm_time_step, this->solution_bdf_w);
788  solution_owned_w *= this->prm_time_step / alpha_bdf;
789 
790  solution_w = solution_owned_w;
791 
792  this->matrix = 0;
793  this->rhs = 0;
794 
795  // The method is needed to define how the system matrix and rhs term are
796  // defined for the monodomain problem with Fithugh-Nagumo ionic model. The
797  // full matrix is composed by different sub-matrices that are called with
798  // simple capital letters. We refer here to the DG_Assemble methods for
799  // their definition.
800 
801  // See DG_Assemble::local_non_linear_fitzhugh().
802  dealii::FullMatrix<double> C(this->dofs_per_cell, this->dofs_per_cell);
803  dealii::Vector<double> cell_rhs(this->dofs_per_cell);
804  dealii::Vector<double> cell_rhs_edge(this->dofs_per_cell);
805  dealii::Vector<double> u0_rhs(this->dofs_per_cell);
806  dealii::Vector<double> w0_rhs(this->dofs_per_cell);
807  std::vector<lifex::types::global_dof_index> dof_indices(
808  this->dofs_per_cell);
809 
810  dealii::IndexSet owned_dofs = this->dof_handler.locally_owned_dofs();
811 
812 
813  // This part of the LHS matrix is assembled only once and not at each time
814  // step.
815  if (this->timestep_number == 1)
816  {
817  this->matrix_t0.reinit(this->matrix);
818 
819  // See DG_Assemble::local_V().
820  dealii::FullMatrix<double> V(this->dofs_per_cell, this->dofs_per_cell);
821  // See DG_Assemble::local_M().
822  dealii::FullMatrix<double> M(this->dofs_per_cell, this->dofs_per_cell);
823  // See DG_Assemble::local_SC().
824  dealii::FullMatrix<double> SC(this->dofs_per_cell, this->dofs_per_cell);
825  // See DG_Assemble::local_IC().
826  dealii::FullMatrix<double> IC(this->dofs_per_cell, this->dofs_per_cell);
827  // Transpose of IC.
828  dealii::FullMatrix<double> IC_t(this->dofs_per_cell,
829  this->dofs_per_cell);
830  // See DG_Assemble::local_IN().
831  dealii::FullMatrix<double> IN(this->dofs_per_cell, this->dofs_per_cell);
832  // Transpose of IN.
833  dealii::FullMatrix<double> IN_t(this->dofs_per_cell,
834  this->dofs_per_cell);
835  // See DG_Assemble::local_SN().
836  dealii::FullMatrix<double> SN(this->dofs_per_cell, this->dofs_per_cell);
837 
838  for (const auto &cell : this->dof_handler.active_cell_iterators())
839  {
840  if (cell->is_locally_owned())
841  {
842  this->assemble->reinit(cell);
843  dof_indices = this->dof_handler.get_dof_indices(cell);
844 
845  V = this->assemble->local_V();
846  V *= Sigma;
847  M = this->assemble->local_M();
848  M /= this->prm_time_step;
849  M *= alpha_bdf;
850  M *= ChiM;
851  M *= Cm;
852 
853  this->matrix_t0.add(dof_indices, V);
854  this->matrix_t0.add(dof_indices, M);
855 
856  for (const auto &edge : cell->face_indices())
857  {
858  this->assemble->reinit(cell, edge);
859  std::vector<lifex::types::global_dof_index>
860  dof_indices_neigh(this->dofs_per_cell);
861 
862  if (!cell->at_boundary(edge))
863  {
864  SC =
865  this->assemble->local_SC(this->prm_stability_coeff);
866  SC *= Sigma;
867  std::tie(IC, IC_t) =
868  this->assemble->local_IC(this->prm_penalty_coeff);
869  IC *= Sigma;
870  IC_t *= Sigma;
871  this->matrix_t0.add(dof_indices, SC);
872  this->matrix_t0.add(dof_indices, IC);
873  this->matrix_t0.add(dof_indices, IC_t);
874 
875  const auto neighcell = cell->neighbor(edge);
876  dof_indices_neigh =
877  this->dof_handler.get_dof_indices(neighcell);
878 
879  std::tie(IN, IN_t) =
880  this->assemble->local_IN(this->prm_penalty_coeff);
881  IN *= Sigma;
882  IN_t *= Sigma;
883  SN =
884  this->assemble->local_SN(this->prm_stability_coeff);
885  SN *= Sigma;
886 
887  this->matrix_t0.add(dof_indices, dof_indices_neigh, IN);
888  this->matrix_t0.add(dof_indices_neigh,
889  dof_indices,
890  IN_t);
891  this->matrix_t0.add(dof_indices, dof_indices_neigh, SN);
892  }
893  }
894  }
895  }
896  this->matrix_t0.compress(lifex::VectorOperation::add);
897  }
898 
899  // The LHS matrix is the sum of matrix_t0 plus the components that depend on
900  // time.
901  this->matrix.copy_from(this->matrix_t0);
902 
903  for (const auto &cell : this->dof_handler.active_cell_iterators())
904  {
905  if (cell->is_locally_owned())
906  {
907  this->assemble->reinit(cell);
908  dof_indices = this->dof_handler.get_dof_indices(cell);
909 
910  C = this->assemble->local_non_linear_fitzhugh(this->solution_owned,
911  a,
912  dof_indices);
913  C *= kappa * ChiM;
914 
915  cell_rhs = this->assemble->local_rhs(this->f_ex);
916 
917  u0_rhs =
918  this->assemble->local_u0_M_rhs(this->solution_bdf, dof_indices);
919  u0_rhs *= Cm * ChiM / this->prm_time_step;
920 
921  w0_rhs =
922  this->assemble->local_w0_M_rhs(solution_owned_w, dof_indices);
923  w0_rhs *= -ChiM;
924 
925  if (cell->at_boundary())
926  {
927  for (const auto &edge : cell->face_indices())
928  {
929  if (cell->at_boundary(edge))
930  {
931  this->assemble->reinit(cell, edge);
932  cell_rhs_edge =
933  this->assemble->local_rhs_edge_neumann(this->g_n);
934  this->rhs.add(dof_indices, cell_rhs_edge);
935  }
936  }
937  }
938 
939  this->matrix.add(dof_indices, C);
940  this->rhs.add(dof_indices, cell_rhs);
941  this->rhs.add(dof_indices, u0_rhs);
942  this->rhs.add(dof_indices, w0_rhs);
943  }
944  }
945 
946  this->matrix.compress(lifex::VectorOperation::add);
947  this->rhs.compress(lifex::VectorOperation::add);
948  }
949 } // namespace DUBeat::models
950 
951 #endif /* MONODOMAIN_FHN_DG_HPP_*/
Class to solve the monodomain equation with Fitzhugh-Nagumo ionic model for the cardiac electrophysio...
lifex::LinAlg::MPI::Vector solution_bdf_w
BDF solution, with ghost entries.
lifex::LinAlg::MPI::Vector solution_ex_owned_w
Solution exact gating variable, without ghost entries.
double kappa
Factor for the nonlinear reaction in Fitzhugh Nagumo model.
std::shared_ptr< dealii::Function< lifex::dim > > grad_w_ex
dealii::Pointer to exact gradient solution Function gating variable
std::shared_ptr< lifex::utils::FunctionDirichlet > w_ex
dealii::Pointer to exact solution function gating variable.
virtual void declare_parameters(lifex::ParamHandler &params) const override
Override for declaration of additional parameters.
lifex::LinAlg::MPI::Vector solution_ex_w
Solution exact gating variable, without ghost entries.
void run() override
Override for the simulation run.
lifex::LinAlg::MPI::Vector solution_owned_w
Solution gating variable, without ghost entries.
void time_initialization() override
Override to initialize both u and w.
lifex::utils::BDFHandler< lifex::LinAlg::MPI::Vector > bdf_handler_w
BDF time advancing handler.
void update_time() override
Override to update time for both u and w.
lifex::LinAlg::MPI::Vector solution_w
Solution gating variable, with ghost entries.
lifex::LinAlg::MPI::Vector solution_ext_w
BDF extrapolated solution, with ghost entries.
double Sigma
Diffusion scalar parameter.
void assemble_system() override
Assembly of the Monodomain system.
double ChiM
Monodomain equation parameter.
virtual void parse_parameters(lifex::ParamHandler &params) override
Override to parse additional parameters.
lifex::LinAlg::MPI::SparseMatrix matrix_t0
Component of the system matrix that does not depend on time.
Neumann boundary condition of the trans-membrane potential.
virtual double value(const dealii::Point< lifex::dim > &p, const unsigned int=0) const override
Evaluate the Neumann boundary condition function in a point.
double Sigma
Diffusion scalar parameter.
Exact solution of the gating variable.
ExactSolution_w(double epsilon, double gamma)
Constructor.
virtual double value(const dealii::Point< lifex::dim > &p, const unsigned int=0) const override
Evaluate the exact solution in a point.
Exact solution of the trans-membrane potential.
virtual double value(const dealii::Point< lifex::dim > &p, const unsigned int=0) const override
Evaluate the exact solution in a point.
GradExactSolution_w(double epsilon, double gamma)
Constructor.
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.
Gradient of the trans-membrane potential.
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.
RightHandSide(double ChiM, double Sigma, double Cm, double kappa, double epsilon, double gamma, double a)
Constructor.
double kappa
Factor for the nonlinear reaction in Fitzhugh Nagumo model.
double ChiM
Parameter monodomain equation.
virtual double value(const dealii::Point< lifex::dim > &p, const unsigned int=0) const override
Evaluate the source term in a point.
Class representing the resolution of time-dependent problems using discontinuous Galerkin methods.
Definition: model_DG_t.hpp:62