Line |
Branch |
Exec |
Source |
1 |
|
|
// ______ ______ _ _ _____ ______ |
2 |
|
|
// | ____| ____| | (_)/ ____| | ____| |
3 |
|
|
// | |__ | |__ | | _| (___ ___| |__ |
4 |
|
|
// | __| | __| | | | |\___ \ / __| __| |
5 |
|
|
// | | | |____| |____| |____) | (__| |____ |
6 |
|
|
// |_| |______|______|_|_____/ \___|______| |
7 |
|
|
// Finite Elements for Life Sciences and Engineering |
8 |
|
|
// |
9 |
|
|
// License: LGL2.1 License |
10 |
|
|
// FELiScE default license: LICENSE in root folder |
11 |
|
|
// |
12 |
|
|
// Main authors: E. Schenone C. Corrado |
13 |
|
|
// |
14 |
|
|
|
15 |
|
|
// System includes |
16 |
|
|
|
17 |
|
|
// External includes |
18 |
|
|
|
19 |
|
|
// Project includes |
20 |
|
|
#include "Model/bidomainDecoupledModel.hpp" |
21 |
|
|
|
22 |
|
|
namespace felisce { |
23 |
|
✗ |
BidomainDecoupledModel::BidomainDecoupledModel(): |
24 |
|
|
BidomainModel(), |
25 |
|
✗ |
m_linearProblemBidomainTransMemb(nullptr), |
26 |
|
✗ |
m_linearProblemBidomainExtraCell(nullptr) { |
27 |
|
✗ |
m_name = "BidomainDecoupled"; |
28 |
|
|
} |
29 |
|
|
|
30 |
|
✗ |
BidomainDecoupledModel::~BidomainDecoupledModel() { |
31 |
|
✗ |
m_linearProblemBidomainTransMemb = nullptr; |
32 |
|
✗ |
m_linearProblemBidomainExtraCell = nullptr; |
33 |
|
✗ |
m_Ue_0.destroy(); |
34 |
|
✗ |
m_extrapolatePotTransMemb.destroy(); |
35 |
|
✗ |
if(!FelisceParam::instance().monodomain) |
36 |
|
✗ |
m_extrapolatePotExtraCell.destroy(); |
37 |
|
|
} |
38 |
|
|
|
39 |
|
|
//! Define m_ionic and m_bdfEdp (used in linearProblem). |
40 |
|
✗ |
void BidomainDecoupledModel::initializeDerivedModel() { |
41 |
|
✗ |
BidomainModel::initializeDerivedModel(); |
42 |
|
|
//Define order for bdf used to calculate m_extrapolateExtraCell. |
43 |
|
✗ |
if(!FelisceParam::instance().monodomain) |
44 |
|
✗ |
m_bdfExtraCell.defineOrder(FelisceParam::instance().orderBdfEdp); |
45 |
|
|
} |
46 |
|
|
|
47 |
|
✗ |
void BidomainDecoupledModel::initializeDerivedLinearProblem() { |
48 |
|
✗ |
m_linearProblemBidomainTransMemb = dynamic_cast<LinearProblemBidomainTransMemb*>(m_linearProblem[0]); |
49 |
|
✗ |
m_linearProblemBidomainExtraCell = dynamic_cast<LinearProblemBidomainExtraCell*>(m_linearProblem[1]); |
50 |
|
|
|
51 |
|
✗ |
initializeAppCurrent(); |
52 |
|
|
} |
53 |
|
|
|
54 |
|
✗ |
void BidomainDecoupledModel::initializeAppCurrent() { |
55 |
|
✗ |
m_iApp = new AppCurrent(); |
56 |
|
✗ |
m_iApp->initialize(m_fstransient); |
57 |
|
|
} |
58 |
|
|
|
59 |
|
|
|
60 |
|
✗ |
void BidomainDecoupledModel::evalIapp() { |
61 |
|
✗ |
m_iAppValue.clear(); |
62 |
|
✗ |
if ( (FelisceParam::instance().typeOfAppliedCurrent == "zygote") || (FelisceParam::instance().typeOfAppliedCurrent == "heart") || (FelisceParam::instance().typeOfAppliedCurrent == "ellipseheart") ) { |
63 |
|
✗ |
m_linearProblemBidomainTransMemb->evalFunctionOnDof(*m_iApp,m_fstransient->time,m_iAppValue,m_linearProblemBidomainTransMemb->EndocardiumDistance()); |
64 |
|
|
} else { |
65 |
|
✗ |
m_linearProblemBidomainTransMemb->evalFunctionOnDof(*m_iApp,m_fstransient->time,m_iAppValue); |
66 |
|
|
} |
67 |
|
|
} |
68 |
|
|
|
69 |
|
✗ |
void BidomainDecoupledModel::finalizeRHSDP(int iProblem) { |
70 |
|
✗ |
if (iProblem == 0) { |
71 |
|
|
|
72 |
|
✗ |
int size = m_linearProblem[iProblem]->numDofLocalPerUnknown(potTransMemb); |
73 |
|
✗ |
double valueForPetsc[size]; |
74 |
|
✗ |
felInt idLocalValue[size]; |
75 |
|
✗ |
felInt idGlobalValue[size]; |
76 |
|
|
|
77 |
|
✗ |
double coefAm = FelisceParam::instance().Am; |
78 |
|
✗ |
double coefCm = FelisceParam::instance().Cm; |
79 |
|
|
|
80 |
|
|
// m_massRHS = Am * Cm * m_bdfEdp.RHS |
81 |
|
✗ |
m_linearProblemBidomainTransMemb->massRHS().axpy(coefAm*coefCm, m_bdfEdp.vector()); |
82 |
|
|
|
83 |
|
✗ |
for (felInt i = 0; i < size; i++) { |
84 |
|
✗ |
idLocalValue[i] = i; |
85 |
|
|
} |
86 |
|
✗ |
ISLocalToGlobalMappingApply(m_linearProblem[iProblem]->mappingDofLocalToDofGlobal(potTransMemb),size,&idLocalValue[0],&idGlobalValue[0]); |
87 |
|
|
|
88 |
|
✗ |
for (felInt i = 0; i < size; i++) { |
89 |
|
✗ |
valueForPetsc[i] = coefAm*m_iAppValue[idGlobalValue[i]]; |
90 |
|
|
} |
91 |
|
|
// m_massRHS = m_massRHS + Am * Iapp |
92 |
|
✗ |
m_linearProblemBidomainTransMemb->massRHS().setValues(size,&idGlobalValue[0],&valueForPetsc[0],ADD_VALUES); |
93 |
|
✗ |
m_linearProblemBidomainTransMemb->massRHS().assembly(); |
94 |
|
|
|
95 |
|
|
// m_massRHS = m_massRHS + Am * Iion |
96 |
|
✗ |
m_linearProblemBidomainTransMemb->massRHS().axpy(coefAm,m_ionic->ion()); |
97 |
|
|
|
98 |
|
✗ |
if(!FelisceParam::instance().monodomain) { |
99 |
|
|
// _KsigmaiRHS = m_extrapolatePotExtraCell |
100 |
|
✗ |
m_linearProblemBidomainTransMemb->ksigmaiRHS().axpy(1.,m_extrapolatePotExtraCell); |
101 |
|
|
} |
102 |
|
|
|
103 |
|
✗ |
} else if (iProblem == 1) { |
104 |
|
✗ |
m_linearProblemBidomainExtraCell->vector().axpy(1.0,m_linearProblem[0]->solution()); |
105 |
|
|
} |
106 |
|
|
|
107 |
|
|
} |
108 |
|
|
|
109 |
|
✗ |
void BidomainDecoupledModel::preAssemblingMatrixRHS(std::size_t iProblem) { |
110 |
|
|
|
111 |
|
|
// Initialize useful vectors of initial solution and initial extrapolate with a std::vector with same structure of _RHS for each linearProblem. |
112 |
|
|
|
113 |
|
✗ |
if (iProblem == 0) { // First problem solves first equation on potTransMemb |
114 |
|
|
// Give _U_0 and m_W_0 values: |
115 |
|
|
// initialize PotTransMemb with value Vmin |
116 |
|
|
// and W_0 with 1/(vMax-vMin)^2. |
117 |
|
|
|
118 |
|
|
// 1) copy structure in _U_0 and in m_extrapolatePotTransMemb. |
119 |
|
✗ |
m_U_0.duplicateFrom(m_linearProblem[iProblem]->vector()); |
120 |
|
✗ |
m_U_0.set(FelisceParam::instance().vMin); |
121 |
|
|
|
122 |
|
|
// 2) copy structure in m_W_0 (initialize solution at time 0 of EDO in schaf solver) - only for first equation. |
123 |
|
✗ |
m_W_0.resize(1); |
124 |
|
✗ |
m_W_0[0].duplicateFrom(m_linearProblem[iProblem]->vector()); |
125 |
|
✗ |
double valueByDofW_0 = 1./((FelisceParam::instance().vMax - FelisceParam::instance().vMin)*(FelisceParam::instance().vMax - FelisceParam::instance().vMin)); |
126 |
|
✗ |
m_W_0[0].set(valueByDofW_0); |
127 |
|
|
|
128 |
|
|
// 3) copy structure in m_extrapolatePotTransMemb (contains extrapolate values of linearProblem[0] solution). |
129 |
|
✗ |
m_extrapolatePotTransMemb.duplicateFrom(m_linearProblem[iProblem]->vector()); |
130 |
|
✗ |
m_extrapolatePotTransMemb.zeroEntries(); |
131 |
|
|
|
132 |
|
|
// 3) copy structure in m_massRHS and _KsigmaiRHS. |
133 |
|
✗ |
m_linearProblemBidomainTransMemb->massRHS().duplicateFrom(m_linearProblem[iProblem]->vector()); |
134 |
|
✗ |
m_linearProblemBidomainTransMemb->massRHS().set(0.); |
135 |
|
✗ |
m_linearProblemBidomainTransMemb->ksigmaiRHS().duplicateFrom(m_linearProblem[iProblem]->vector()); |
136 |
|
✗ |
m_linearProblemBidomainTransMemb->ksigmaiRHS().set(0.); |
137 |
|
|
|
138 |
|
|
//Initialize solution for solver. |
139 |
|
✗ |
m_linearProblem[iProblem]->solution().copyFrom(m_U_0); |
140 |
|
|
|
141 |
|
|
// Read fibers and distance mapp (for Zygote geometry). |
142 |
|
✗ |
m_linearProblem[iProblem]->readData(*io()); |
143 |
|
|
|
144 |
|
|
//Initialize heterogeneous parameters for schaf solver (useful only for first problem). |
145 |
|
✗ |
if (FelisceParam::instance().typeOfIonicModel == "schaf") { |
146 |
|
✗ |
initializeSchafParameter(); |
147 |
|
|
} |
148 |
|
|
|
149 |
|
✗ |
} else if (iProblem == 1) { |
150 |
|
|
// 1) copy structure in _Ue_0. |
151 |
|
✗ |
m_Ue_0.duplicateFrom(m_linearProblem[iProblem]->vector()); |
152 |
|
✗ |
m_Ue_0.set(0.0); |
153 |
|
|
|
154 |
|
|
// 2) copy structure in m_extrapolatePotExtraCell (contains extrapolate values of linearProblem[1] solution). |
155 |
|
✗ |
if(!FelisceParam::instance().monodomain) { |
156 |
|
✗ |
m_extrapolatePotExtraCell.duplicateFrom(m_linearProblem[iProblem]->vector()); |
157 |
|
✗ |
m_extrapolatePotExtraCell.set(0.); |
158 |
|
|
} |
159 |
|
|
|
160 |
|
|
//Initialize solution for solver. |
161 |
|
✗ |
m_linearProblem[iProblem]->solution().copyFrom(m_Ue_0); |
162 |
|
|
|
163 |
|
|
// Read fibers and distance mapp (for Zygote geometry). |
164 |
|
✗ |
m_linearProblem[iProblem]->readData(*io()); |
165 |
|
|
} |
166 |
|
|
|
167 |
|
|
//Debug print. |
168 |
|
|
// /todo fix value of verbose |
169 |
|
✗ |
if (m_verbose > 2 && iProblem == 0) { |
170 |
|
✗ |
PetscPrintf(MpiInfo::petscComm(),"\n\t\t _U_0 (==m_sol (linearProblem[0]) at time == 0):\n"); |
171 |
|
✗ |
m_U_0.view(); |
172 |
|
✗ |
PetscPrintf(MpiInfo::petscComm(),"\n\t\t m_W_0 (==m_solEDO (ionicSolver) at time == 0):\n"); |
173 |
|
✗ |
m_W_0[0].view(); |
174 |
|
✗ |
} else if (m_verbose > 2 && iProblem == 1) { |
175 |
|
✗ |
PetscPrintf(MpiInfo::petscComm(),"\n\t\t m_Ue_0 (==m_sol (linearProblem[1]) at time == 0):\n"); |
176 |
|
✗ |
m_Ue_0.view(); |
177 |
|
|
} |
178 |
|
|
} |
179 |
|
|
|
180 |
|
✗ |
void BidomainDecoupledModel::postAssemblingMatrixRHS(std::size_t iProblem) { |
181 |
|
|
// Evaluate applied current std::vector. |
182 |
|
✗ |
if (iProblem == 0) { |
183 |
|
✗ |
evalIapp(); |
184 |
|
|
} |
185 |
|
|
|
186 |
|
|
// Calculate RHS std::vector (to be multiplied by mass matrix). |
187 |
|
✗ |
finalizeRHSDP(iProblem); |
188 |
|
|
// Calculate _RHS : |
189 |
|
|
// if model == 'Bidomain' => _RHS = mass * RHS. |
190 |
|
|
// if model == 'BidomainDecoupled' : |
191 |
|
|
// iProblem==0 => _RHS=mass*RHS+Ksigmai*u_e |
192 |
|
|
// iProblem==1 => _RHS=Ksigmai*V_m |
193 |
|
✗ |
m_linearProblem[iProblem]->addMatrixRHS(); |
194 |
|
|
|
195 |
|
|
//RHS of Luenb. filter |
196 |
|
✗ |
if (FelisceParam::instance().stateFilter) { |
197 |
|
✗ |
if (iProblem == 0) { |
198 |
|
✗ |
double coefAm = FelisceParam::instance().Am; |
199 |
|
✗ |
m_luenbFlter.zeroEntries(); |
200 |
|
✗ |
m_luenbFlter.copyFrom(m_linearProblem[iProblem]->matrix(1),SAME_NONZERO_PATTERN); |
201 |
|
✗ |
m_luenbFlter.assembly(MAT_FINAL_ASSEMBLY); |
202 |
|
|
// copy diagonal stabiliz. matrix in luenberger matrix |
203 |
|
✗ |
m_luenbFlter.diagonalScale(PetscVector::null(),m_ionic->stabTerm()); |
204 |
|
✗ |
m_luenbFlter.scale(coefAm); |
205 |
|
✗ |
m_linearProblem[iProblem]->matrix(0).axpy(1,m_luenbFlter,SAME_NONZERO_PATTERN); |
206 |
|
|
} |
207 |
|
|
} |
208 |
|
|
} |
209 |
|
|
|
210 |
|
✗ |
void BidomainDecoupledModel::initializeSchafParameter() { |
211 |
|
|
// Initialize TauClose. |
212 |
|
✗ |
if (FelisceParam::instance().hasHeteroTauClose) { |
213 |
|
✗ |
m_schaf->fctTauClose().initialize(m_fstransient); |
214 |
|
✗ |
if ( (FelisceParam::instance().typeOfAppliedCurrent == "zygote") || (FelisceParam::instance().typeOfAppliedCurrent == "heart") || (FelisceParam::instance().typeOfAppliedCurrent == "ellipseheart") ) { |
215 |
|
✗ |
m_linearProblemBidomainTransMemb->evalFunctionOnDof(m_schaf->fctTauClose(), m_schaf->tauClose(),m_linearProblemBidomainTransMemb->EndocardiumDistance()); |
216 |
|
|
} |
217 |
|
|
else { |
218 |
|
✗ |
m_linearProblemBidomainTransMemb->evalFunctionOnDof(m_schaf->fctTauClose(), m_schaf->tauClose()); |
219 |
|
|
} |
220 |
|
|
} |
221 |
|
|
// Initialize TauOut : heterogeneous in case of infarct. |
222 |
|
✗ |
if (FelisceParam::instance().hasInfarct) { |
223 |
|
✗ |
m_schaf->fctTauOut().initialize(m_fstransient); |
224 |
|
✗ |
m_linearProblemBidomainTransMemb->evalFunctionOnDof(m_schaf->fctTauOut(), m_schaf->tauOut()); |
225 |
|
|
} |
226 |
|
|
} |
227 |
|
|
|
228 |
|
|
// Pay attention on the call of this : |
229 |
|
|
// call BidomainModel::writeSolution() function instead of (non-virtual) Model::writeSolution() function |
230 |
|
✗ |
void BidomainDecoupledModel::writeSolution() |
231 |
|
|
{ |
232 |
|
✗ |
Model::writeSolution(); |
233 |
|
|
} |
234 |
|
|
|
235 |
|
✗ |
void BidomainDecoupledModel::forward() { |
236 |
|
|
//Write solution with ensight. |
237 |
|
✗ |
BidomainDecoupledModel::writeSolution(); |
238 |
|
|
//Advance time step. |
239 |
|
✗ |
updateTime(FlagMatrixRHS::only_rhs); |
240 |
|
✗ |
printNewTimeIterationBanner(); |
241 |
|
|
|
242 |
|
✗ |
if ( m_fstransient->iteration == 1) { |
243 |
|
|
//Initialization of bdf solvers. |
244 |
|
✗ |
m_bdfEdp.initialize(m_linearProblem[0]->solution()); |
245 |
|
✗ |
if(!FelisceParam::instance().monodomain) |
246 |
|
✗ |
m_bdfExtraCell.initialize(m_linearProblem[1]->solution()); |
247 |
|
✗ |
m_linearProblemBidomainTransMemb->initializeTimeScheme(&m_bdfEdp); |
248 |
|
|
//m_extrapolate = initial solution. |
249 |
|
✗ |
m_bdfEdp.extrapolate(m_extrapolatePotTransMemb); |
250 |
|
✗ |
if(!FelisceParam::instance().monodomain) { |
251 |
|
✗ |
m_bdfExtraCell.extrapolate(m_extrapolatePotExtraCell); |
252 |
|
|
} |
253 |
|
|
//Initialization of ionic solver. |
254 |
|
✗ |
m_ionic->defineSizeAndMappingOfIonicProblem(m_linearProblem[0]->numDofLocalPerUnknown(potTransMemb), m_linearProblem[0]->mappingDofLocalToDofGlobal(potTransMemb), m_linearProblemBidomain[0]->ao()); |
255 |
|
✗ |
m_ionic->initializeExtrap(m_extrapolatePotTransMemb); |
256 |
|
✗ |
m_ionic->initialize(m_W_0[0]); |
257 |
|
✗ |
m_buildIonic = true; |
258 |
|
|
} else { |
259 |
|
✗ |
m_bdfEdp.update(m_linearProblem[0]->solution()); |
260 |
|
✗ |
if(!FelisceParam::instance().monodomain) |
261 |
|
✗ |
m_bdfExtraCell.update(m_linearProblem[1]->solution()); |
262 |
|
✗ |
m_bdfEdp.extrapolate(m_extrapolatePotTransMemb); |
263 |
|
✗ |
if(!FelisceParam::instance().monodomain) { |
264 |
|
✗ |
m_bdfExtraCell.extrapolate(m_extrapolatePotExtraCell); |
265 |
|
|
} |
266 |
|
✗ |
m_ionic->update(m_extrapolatePotTransMemb); |
267 |
|
|
} |
268 |
|
|
|
269 |
|
|
//Solve Mitchell and Schaeffer model and calculate Iion. |
270 |
|
✗ |
m_ionic->computeRHS(); |
271 |
|
✗ |
m_ionic->solveEDO(); |
272 |
|
✗ |
m_ionic->computeIon(); |
273 |
|
✗ |
m_ionic->updateBdf(); |
274 |
|
|
|
275 |
|
✗ |
m_bdfEdp.computeRHSTime(m_fstransient->timeStep); |
276 |
|
|
|
277 |
|
|
// Assembling of matrices at first iteration (because of needs some coefficients not defined at iteration 0). |
278 |
|
✗ |
if ( m_fstransient->iteration == 1 ) { |
279 |
|
✗ |
if ( FelisceParam::instance().hasCoupledAtriaVent ) |
280 |
|
✗ |
m_linearProblemBidomainTransMemb->assembleMatrixRHSCurrentAndCurvilinearElement(MpiInfo::rankProc()); |
281 |
|
|
else |
282 |
|
✗ |
m_linearProblemBidomainTransMemb->assembleMatrixRHS(MpiInfo::rankProc()); |
283 |
|
|
} |
284 |
|
|
|
285 |
|
|
//Specific operations before solve the system : calculate _RHS std::vector used in solve(). |
286 |
|
✗ |
postAssemblingMatrixRHS(0); |
287 |
|
|
|
288 |
|
|
//Solve the system _Matrix[0]*_V_m=_RHS. |
289 |
|
✗ |
m_linearProblem[0]->solve(MpiInfo::rankProc(), MpiInfo::numProc()); |
290 |
|
|
|
291 |
|
|
// Assemble Matrix of linearProblem. |
292 |
|
✗ |
if ( m_fstransient->iteration == 1 ) { |
293 |
|
✗ |
if ( FelisceParam::instance().hasCoupledAtriaVent ) |
294 |
|
✗ |
m_linearProblemBidomainExtraCell->assembleMatrixRHSCurrentAndCurvilinearElement(MpiInfo::rankProc()); |
295 |
|
|
else |
296 |
|
✗ |
m_linearProblemBidomainExtraCell->assembleMatrixRHS(MpiInfo::rankProc()); |
297 |
|
|
} |
298 |
|
|
|
299 |
|
|
//Specific operations before solve the system : calculate _RHS std::vector used in solve(). |
300 |
|
✗ |
postAssemblingMatrixRHS(1); |
301 |
|
|
|
302 |
|
|
//Solve the system _Matrix[0]*m_u_e=_RHS. |
303 |
|
✗ |
m_linearProblem[1]->solve(MpiInfo::rankProc(), MpiInfo::numProc()); |
304 |
|
|
|
305 |
|
✗ |
if ( FelisceParam::instance().hasCoupledAtriaVent == false ) |
306 |
|
✗ |
m_linearProblem[1]->removeAverageFromSolution(potExtraCell, MpiInfo::rankProc()); |
307 |
|
|
|
308 |
|
|
} |
309 |
|
|
|
310 |
|
|
} |
311 |
|
|
|