CEPS  24.01
Cardiac ElectroPhysiology Simulator
FETimedSolver.cpp
Go to the documentation of this file.
1 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2  This file is part of CEPS.
3 
4  CEPS is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  CEPS is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with CEPS (see file LICENSE at root of project).
16  If not, see <https://www.gnu.org/licenses/>.
17 
18 
19  Copyright 2019-2024 Inria, Universite de Bordeaux
20 
21  Authors, in alphabetical order:
22 
23  Pierre-Elliott BECUE, Florian CARO, Yves COUDIERE(*), Andjela DAVIDOVIC,
24  Charlie DOUANLA-LONTSI, Marc FUENTES, Mehdi JUHOOR, Michael LEGUEBE(*),
25  Pauline MIGERDITICHAN, Valentin PANNETIER(*), Nejib ZEMZEMI.
26  * : currently active authors
27 
28 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
32 
35 
36 // #include "ode/solver/RungeKutta.hpp"
37 
39  AbstractTimedPdeSolver(problem),
40  m_fe(ceps::runtimeCast<FiniteElements*>(problem->getSpatialDiscretization())),
41  m_temp1(m_fe->newDofHaloVector()),
42  m_temp2(m_fe->newDofHaloVector())
43 {}
44 
46 {
47 }
48 
49 void
51 {
52  CepsReal time = m_timeStepper->getTime();
55  CepsUInt nSteps = std::min(iter + 1, m_nbMultiSteps);
56 
57  // Assemble what is needed
59  // And fill source terms vectors
61 
62  if (m_type=="SBDF" or m_type=="FBE")
63  assembleAndSolveSBDF(nSteps);
64  else if (m_type=="CN")
65  assembleAndSolveCN(nSteps);
66  // else if (m_type == "RK")
67  // updateAndSolveRK();
68 
69  // Swap pointers for current arrays
70  this->swapMultiStepPointers();
71 
72  // Cheat for convergence tests: if there is an analytic solution,
73  // we use the exact solution for first steps of multisteps methods
77 
78  return;
79 }
80 
81 void
83 {
84  CepsReal time = m_timeStepper->getTime();
87  const CepsReal* ak = c_akSBDF[nSteps-1];
88  CepsReal adt = dt*c_alphaSBDF[nSteps];
89 
90  CepsBool recomputeLhs = false;
91  recomputeLhs = recomputeLhs or iter+1 <= m_nbMultiSteps;
92  recomputeLhs = recomputeLhs or m_opAsb->isChangingBetweenTimes(time,time+dt);
93  recomputeLhs = recomputeLhs or m_bcAsb->isChangingBetweenTimes(time,time+dt);
94 
95  // Build LHS = M + adt * OP
96  if (recomputeLhs)
97  {
98  m_lhs->finalize();
99  m_dtMat->copy(*m_lhs);
100  m_lhs->add(*m_opMat,adt,false);
101  m_lhs->add(*m_bcMat,adt,false);
102 
103  if (not ceps::equals(m_scaleSystem,1.e0))
104  *m_lhs *= m_scaleSystem;
105  }
106 
107  // =============================================================================================
108  // TODO
109  // We must to rewrite this comment for systems of equations with time derivatives only on certain
110  // lines (eg bidomain) and remove not needed F^{n-k} for static equations. Complicate ?
111  // =============================================================================================
112 
113  // Build RHS = M*(sum_k a_k*(U^n-k + dt*(k+1)*F^n-k)), 0<k<nSteps with M = derivative
114  // matrix
115  m_rhs->zero();
116 
117  // 1. Solutions at previous steps
118  m_temp1->zero();
119  for (CepsUInt k=0UL; k<nSteps; k++)
120  m_temp1->addScaled(*m_uNm[k], ak[k]);
121 
122  m_temp2->mult(*m_dtMat, *m_temp1);
123  m_rhs->add(*m_temp2);
124 
125  // 2. Regular source terms
126  if (m_hasRegSrc)
127  {
128  // Sources on equations with time derivative
129  m_temp1->zero();
130  for (CepsUInt k=0UL; k<nSteps; k++)
131  m_temp1->addScaled(*m_regSourcesNm[k], ak[k]*dt*(k+1));
132 
133  m_temp1->pointWiseScale(*m_timeDerEqsVec);
134  m_temp2->mult(*m_fe->getMassMatrix(),*m_temp1);
135  m_rhs->add(*m_temp2);
136 
137  // Sources on "static" equations
138  m_temp1->zero();
139  m_temp1->addScaled(*m_regSourcesNm[0], adt);
140  m_temp1->pointWiseScale(*m_noTimeDerEqsVec);
141  m_temp2->mult(*m_fe->getMassMatrix(),*m_temp1);
142  m_rhs->add(*m_temp2);
143  }
144 
145  // 3. "Laplace" source terms
146  if (m_hasLapSrc)
147  {
148  // Sources on equations with time derivative
149  m_temp1->zero();
150  for (CepsUInt k=0UL; k<nSteps; k++)
151  m_temp1->addScaled(*m_lapSourcesNm[k], ak[k]*dt*(k+1));
152 
153  m_temp1->pointWiseScale(*m_timeDerEqsVec);
154  m_temp2->mult(*m_lapSrcMat,*m_temp1);
155  m_rhs->add(*m_temp2);
156 
157  // Sources on "static" equations
158  m_temp1->zero();
159  m_temp1->addScaled(*m_lapSourcesNm[0],adt);
160  m_temp1->pointWiseScale(*m_noTimeDerEqsVec);
161  m_temp2->mult(*m_lapSrcMat,*m_temp1);
162  m_rhs->add(*m_temp2);
163  }
164 
165  // 4. The neuman and robin boundary conditions
166  m_rhs->addScaled(*m_bcVec,adt);
167 
168  // Then apply Dirichlet BCs (put 1 on diagonal in the matrix, and the corresponding value into the
169  // vector)
170  for (auto bc : *(m_problem->getBoundaryConditionManager()->getDirichletBCs()))
171  bc.second->applyAsDirichlet(m_lhs, m_rhs, time + dt);
172 
173  if (not ceps::equals(m_scaleSystem, 1.e0))
174  *m_rhs *= m_scaleSystem;
175 
176  m_rhs->finalize();
177  m_lhs->finalize();
178 
179  // Solve
181 
182  #ifdef CEPS_DEBUG_ENABLED
183  m_uNp1->checkNanOrInf("in solution vector ");
184  #endif
185 
186  return;
187 }
188 
189 void
191 {
192  CepsReal time = m_timeStepper->getTime();
195  CepsReal adt = dt * c_alphaCN[nSteps];
196 
197  CepsBool recomputeLhs = false;
198  recomputeLhs = recomputeLhs or iter + 1 <= m_nbMultiSteps;
199  recomputeLhs = recomputeLhs or m_opAsb->isChangingBetweenTimes(time, time + dt);
200  recomputeLhs = recomputeLhs or m_bcAsb->isChangingBetweenTimes(time, time + dt);
201 
202  // Build LHS = M + adt * OP
203  if (recomputeLhs)
204  {
205  m_lhs->finalize();
206  m_dtMat->copy(*m_lhs);
207  m_lhs->add(*m_opMat, adt, false);
208  m_lhs->add(*m_bcMat, adt, false);
209 
210  if (not ceps::equals(m_scaleSystem, 1.e0))
211  *m_lhs *= m_scaleSystem;
212  }
213 
214  // =============================================================================================
215  // TODO
216  // We must to rewrite this comment for systems of equations with time derivatives only on certain
217  // lines (eg bidomain) and remove not needed F^{n-k} for static equations. Complicate ?
218  // =============================================================================================
219 
220  // Build RHS = -0.5*dt*K*V_n + M*(V_n + 1.5*dt*F_n - 0.5* dt * F_{n-1})
221  m_rhs->zero();
222 
223  // 1. Solutions at previous steps
224  m_temp1->zero();
225  m_temp1->add(*m_uNm[0]);
226  m_temp2->mult(*m_dtMat, *m_temp1);
227  m_rhs->add(*m_temp2);
228 
229  // 2. Regular source terms
230  if (m_hasRegSrc)
231  {
232  // Sources on equations with time derivative
233  m_temp1->zero();
234  m_temp1->addScaled(*m_regSourcesNm[0], 1.5 * dt);
235  m_temp1->addScaled(*m_regSourcesNm[1], -0.5 * dt);
236 
237  m_temp1->pointWiseScale(*m_timeDerEqsVec);
238  m_temp2->mult(*m_fe->getMassMatrix(), *m_temp1);
239  m_rhs->add(*m_temp2);
240 
241  // Sources on "static" equations
242  m_temp1->zero();
243  m_temp1->addScaled(*m_regSourcesNm[0], adt);
244  m_temp1->pointWiseScale(*m_noTimeDerEqsVec);
245  m_temp2->mult(*m_fe->getMassMatrix(), *m_temp1);
246  m_rhs->add(*m_temp2);
247  }
248 
249  // 3. "Laplace" source terms
250  if (m_hasLapSrc)
251  {
252  // Sources on equations with time derivative
253  m_temp1->zero();
254  m_temp1->addScaled(*m_lapSourcesNm[0], 1.5 * dt);
255  m_temp1->addScaled(*m_lapSourcesNm[1], -0.5 * dt);
256 
257  m_temp1->pointWiseScale(*m_timeDerEqsVec);
258  m_temp2->mult(*m_lapSrcMat, *m_temp1);
259  m_rhs->add(*m_temp2);
260 
261  // Sources on "static" equations
262  m_temp1->zero();
263  m_temp1->addScaled(*m_lapSourcesNm[0], adt);
264  m_temp1->pointWiseScale(*m_noTimeDerEqsVec);
265  m_temp2->mult(*m_lapSrcMat, *m_temp1);
266  m_rhs->add(*m_temp2);
267  }
268 
269  // 4. The neuman and robin boundary conditions
270  m_rhs->addScaled(*m_bcVec, adt);
271 
272  // 5. half Op term
273  m_temp1->zero();
274  m_temp1->addScaled(*m_uNm[0], -0.5 * dt);
275  m_temp2->mult(*m_opMat, *m_temp1);
276  m_rhs->add(*m_temp2);
277 
278  // Then apply Dirichlet BCs (put 1 on diagonal in matrix, and the corresponding value into the
279  // vector)
280  for (auto bc : *(m_problem->getBoundaryConditionManager()->getDirichletBCs()))
281  bc.second->applyAsDirichlet(m_lhs, m_rhs, time + dt, m_scaleSystem);
282 
283  if (not ceps::equals(m_scaleSystem, 1.e0))
284  *m_rhs *= m_scaleSystem;
285 
286  m_rhs->finalize();
287  m_lhs->finalize();
288 
289  // Solve
291 
292  #ifdef CEPS_DEBUG_ENABLED
293  m_uNp1->checkNanOrInf("in solution vector ");
294  #endif
295 
296  return;
297 }
298 
299 /*void
300 FETimedSolver::setupLinearSystemRK()
301 {
302  // LHS matrix is the mass matrix + boundary conditions in this case
303 
304  // Copy mass matrix from finite elements
305  ceps::destroyObject(m_lhs.get());
306  m_lhs = DMatPtr(ceps::getNew<DistributedMatrix>(*(m_fe->getMassMatrix()),true));
307  m_linearSystem->setLhsMatrix(m_lhs);
308 
309  // Neumann and Robin boundary conditions
310  FEDivKGradBCAssembler basb(m_fe);
311  basb.setMatrix(m_lhs)
312  basb.assemble();
313 
314  // Then apply Dirichlet BCs, for the matrix only
315  m_lhs->finalize();
316  for (auto bc : m_problem->getBoundaryConditionsManager()->getDirichletBCs())
317  bc->applyAsDirichlet(m_lhs,nullptr);
318 
319  m_lhs->finalize();
320  return;
321 }*/
322 
323 /* void
324 // FETimedSolver::buildRHSVectorRK ()
325 // {
326 // // RHS = -SV^n_k + M(V^n+dt*sum_j b_jkF(time^n_k))
327 // m_rhs->mult (*m_lhs, *m_regSourcesNm[0]);
328 // m_temp->zero ();
329 // m_temp->mult (*m_stiffMat, *m_VnM[0]);
330 // m_rhs->addScaled (*m_temp, -1.);
331 // }*/
bool CepsBool
Booleans.
Definition: CepsTypes.hpp:124
std::make_unsigned_t< CepsInt > CepsUInt
Unsigned version on CepsInt.
Definition: CepsTypes.hpp:109
float CepsReal
Need single precision floating point.
Definition: CepsTypes.hpp:100
virtual CepsBool isChangingBetweenTimes(CepsReal t1, CepsReal t2) const
Tells if this assembler changes between the two times.
void evaluateFunctionOnDofs(ceps::Function< CepsReal(CepsStandardArgs)> *func, DHVecPtr v, CepsReal t=0.) const
Fills vector v with values of function func at owned and halo dofs and time t.
ScalarFunction * getAnalyticSolution() const
Pointer on analytic or refScalarFunction solution.
CepsBool hasAnalyticSolution() const
Tells if there is an analytic or reference solution.
BoundaryConditionManager * getBoundaryConditionManager() const
Get boundary condition manager.
DMatPtr m_lhs
Left hand side of the system.
DVecPtr m_rhs
Right hand side of the system.
DMatPtr m_bcMat
Matrix of boundary conditions.
CepsBool m_hasRegSrc
Flag telling if there are regular source terms.
AbstractDiscretization * m_discretization
Link to PDE discretization.
AbstractAssembler * m_bcAsb
Assembler for Robin and Neumann BCs.
LinearSystem * m_linearSystem
Linear system.
DMatPtr m_opMat
Matrix of operator.
DVecPtr m_bcVec
Vector of boundary conditions.
AbstractAssembler * m_opAsb
Assembler for the operator matrix.
CepsBool m_hasLapSrc
Flag telling if there are Delta f source terms.
AbstractPdeProblem * m_problem
Link to PDE to solve.
DMatPtr m_lapSrcMat
Matrix used for laplace source terms.
Astract Problem which does depend on time.
Base class for solving PDE with time dependance.
CepsBool m_cheatConvergence
If true, use exact solution for first steps of multi steps methods.
TimeStepper * m_timeStepper
Monitors time evolution.
virtual void swapMultiStepPointers()
Swaps vectors of array pointers for next step (for multistep methods)
CepsVector< DHVecPtr > m_regSourcesNm
Sources at previous steps (if problem has source terms)
CepsString m_type
Numerical scheme descriptor.
DMatPtr m_dtMat
Matrix of time derivative of the system.
CepsVector< DHVecPtr > m_uNm
Solution vectors at different time steps. Several vectors are needed for multi-step methods....
CepsUInt m_nbMultiSteps
Maximum number of steps needed to compute next solution (eg CN:2)
DVecPtr m_timeDerEqsVec
Vector of 1 or 0, depending on whether the dof equation has a time derivative or not.
CepsVector< DHVecPtr > m_lapSourcesNm
Sources at previous steps (if problem has laplace source terms)
static constexpr CepsReal c_alphaCN[3]
LHS coeffcients of the CN scheme.
CepsReal m_scaleSystem
A factor for both matrix and vector (typically 1/dt)
void updateAssemblers() override
Update assemblers and recompute everything is needed.
static constexpr CepsReal c_alphaSBDF[5]
LHS coeffcients of the SBDF schemes.
DVecPtr m_noTimeDerEqsVec
The opposite version of m_timeDerEqsVec, such as m_timeDerEqsVec + m_noTimeDerEqsVec = vec(1)
DHVecPtr m_uNp1
Vectors of unknowns at time t^n+1.
virtual void fillSourceTermsVector(CepsReal tn)
Adds contributions from all source terms into m_regSourcesNm[0].
static constexpr CepsReal c_akSBDF[4][4]
RHS coeffcients of the SBDF schemes.
Manager * getDirichletBCs() const
Get the manager for Dirichlet conditions.
DHVecPtr m_temp2
Temporary vector for assembly of source terms.
void assembleAndSolveSBDF(CepsUInt nSteps)
Updates matrix and RHS for the SBDF numerical scheme.
FETimedSolver()=delete
Deleted constructor.
void assembleAndSolve() override
Main routine used during solving, assemble the system depending on the scheme and solve the linear sy...
DHVecPtr m_temp1
Temporary vector for assembly of source terms.
void assembleAndSolveCN(CepsUInt nSteps)
Updates matrix and RHS for the CN numerical scheme.
~FETimedSolver() override
Destructor.
FiniteElements * m_fe
Geometry and reference FE.
Holds all finite elements corresponding to each geometrical element.
DMatPtr getMassMatrix() const
Pointer on mass matrix.
virtual void solve(DVecPtr solution)
Solve current linear system. Calls the solver's method.
Definition: PETScKSP.cpp:54
CepsReal getTimeStep() const
Time step.
Definition: TimeStepper.cpp:95
virtual CepsReal getTime()
current simulation time
CepsUInt getNbTakenTimeSteps() const
Number of time steps performed until now.
A namespace for all utility methods.
_Derived runtimeCast(_Base x)
Perform a runtime cast between base type and derived type if we can.
Definition: CepsMemory.hpp:94
const _Type & min(const CepsVector< _Type, _Alloc > &vec)
Returns the minimum of the vector, const version.
Definition: CepsVector.hpp:448
CepsBool equals(CepsReal a, CepsReal b, CepsReal error=1.0)
CepsReal equality up to machine precision.
Definition: Precision.hpp:54