openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
SF_fem_utils_emi.h
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // openCARP is an open cardiac electrophysiology simulator.
3 //
4 // Copyright (C) 2020 openCARP project
5 //
6 // This program is licensed under the openCARP Academic Public License (APL)
7 // v1.0: You can use and redistribute it and/or modify it in non-commercial
8 // academic environments under the terms of APL as published by the openCARP
9 // project v1.0, or (at your option) any later version. Commercial use requires
10 // a commercial license (info@opencarp.org).
11 //
12 // This program is distributed without any warranty; see the openCARP APL for
13 // more details.
14 //
15 // You should have received a copy of the openCARP APL along with this program
16 // and can find it online: http://www.opencarp.org/license
17 // ----------------------------------------------------------------------------
18 
29 #if WITH_EMI_MODEL
30 
31 #ifndef _SF_FEM_EMI_H
32 #define _SF_FEM_EMI_H
33 
34 #include "SF_container.h"
35 #include "SF_fem_utils.h"
36 namespace SF {
37 
48 inline double computeTriangleArea(const SF::Point& p1, const SF::Point& p2, const SF::Point& p3)
49 {
50  // Define two edges of the triangle
51  Point edge1 = p2 - p1;
52  Point edge2 = p3 - p1;
53 
54  // Compute the cross product
55  Point crossProduct = cross(edge1,edge2);
56 
57  // Compute the magnitude of the cross product
58  double crossProductMagnitude = mag(crossProduct);
59 
60  return 0.5 * crossProductMagnitude;
61 }
62 
73 inline void compute_barycentric_coordinates_coefficients(const SF::Point& p1, const SF::Point& p2, const SF::Point& P, vector<double>& interpolationCoefficients, double & lengthSquared)
74 {
75  SF::Point d = p2 - p1;
76  lengthSquared = inner_prod(d, d);
77 
78  SF::Point v = P - p1;
79  double lambda2 = inner_prod(v, d) / lengthSquared;
80  double lambda1 = 1.0 - lambda2;
81 
82  interpolationCoefficients[0] = lambda1;
83  interpolationCoefficients[1] = lambda2;
84 }
85 
97 inline void compute_barycentric_coordinates_coefficients(const SF::Point& p1, const SF::Point& p2, const SF::Point& p3, const SF::Point& P, vector<double>& interpolationCoefficients, double & area)
98 {
99 
100  area = computeTriangleArea(p1, p2, p3);
101 
102  // Vectors for the edges of the triangle
103  SF::Point v0 = p2 - p1;
104  SF::Point v1 = p3 - p1;
105  SF::Point v2 = P - p1;
106 
107  // Dot products
108  double d00 = inner_prod(v0,v0);
109  double d01 = inner_prod(v0,v1);
110  double d11 = inner_prod(v1,v1);
111  double d20 = inner_prod(v2,v0);
112  double d21 = inner_prod(v2,v1);
113 
114  // Compute barycentric coordinates
115  double denom = d00 * d11 - d01 * d01;
116  double beta = (d11 * d20 - d01 * d21) / denom;
117  double gamma = (d00 * d21 - d01 * d20) / denom;
118  double alpha = 1.0 - beta - gamma;
119  interpolationCoefficients[0] = alpha; // α
120  interpolationCoefficients[1] = beta; // β
121  interpolationCoefficients[2] = gamma; // ζ
122 }
123 
124 
137 inline void compute_barycentric_coordinates_coefficients(const SF::Point& p1, const SF::Point& p2, const SF::Point& p3, const SF::Point& p4, const SF::Point& P, vector<double>& interpolationCoefficients, double & area)
138 {
139  // Area: compute as sum of two triangles
140  double area1 = computeTriangleArea(p1, p2, p4);
141  double area2 = computeTriangleArea(p2, p3, p4);
142  area = area1 + area2;
143 
144  // Define local axes
145  SF::Point origin = p1;
146  SF::Point u_dir = p2 - p1;
147  SF::Point v_dir = p4 - p1;
148 
149  // Project P into the local (u,v) frame
150  SF::Point d = P - origin;
151  double u = inner_prod(d, u_dir) / inner_prod(u_dir, u_dir);
152  double v = inner_prod(d, v_dir) / inner_prod(v_dir, v_dir);
153 
154  // Clamp u and v to [0,1] to avoid extrapolation (optional)
155  u = SF::clamp(u, 0.0, 1.0);
156  v = SF::clamp(v, 0.0, 1.0);
157 
158  // Bilinear shape functions
159  interpolationCoefficients[0] = (1 - u) * (1 - v); // α
160  interpolationCoefficients[1] = u * (1 - v); // β
161  interpolationCoefficients[2] = u * v; // γ
162  interpolationCoefficients[3] = (1 - u) * v; // ζ
163 }
164 
175 template<class S>
176 inline void compute_integrate_matrix_barycentric(vector<SF::Point> face_coordinates, SF_int nnodes, dmat<SF_real> & ebuff, dmat<SF_real> & ebuff_s, dmat<SF_real> & ebuff_counter, S mass_scale)
177 {
178  //compute the barycentric coordinates: calculateCentroid
179  double x = 0;
180  double y = 0;
181  double z = 0;
182  for (int i = 0; i < nnodes; ++i)
183  {
184  x += face_coordinates[i].x;
185  y += face_coordinates[i].y;
186  z += face_coordinates[i].z;
187  }
188  SF::Point b; b.x = x/nnodes; b.y = y/nnodes; b.z = z/nnodes;
189 
190  vector<double> interpolationCoefficients(nnodes);
191  double area, weighted_area;
192  if(nnodes==2){
193  compute_barycentric_coordinates_coefficients(face_coordinates[0], face_coordinates[1], b, interpolationCoefficients, area);
194  }
195  else if(nnodes==3)
196  {
197  compute_barycentric_coordinates_coefficients(face_coordinates[0], face_coordinates[1], face_coordinates[2], b, interpolationCoefficients, area);
198  }
199  else if(nnodes==4){
200  compute_barycentric_coordinates_coefficients(face_coordinates[0], face_coordinates[1], face_coordinates[2], face_coordinates[3], b, interpolationCoefficients, area);
201  }
202 
203  weighted_area = area/nnodes;
204 
205  if(nnodes==2){
206  ebuff[0][0] = interpolationCoefficients[0];
207  ebuff[0][1] = interpolationCoefficients[1];
208 
209  ebuff_counter[0][0] = interpolationCoefficients[0];
210  ebuff_counter[0][1] = interpolationCoefficients[1];
211 
212  ebuff_s[0][0] = mass_scale*weighted_area;
213  ebuff_s[0][1] = mass_scale*weighted_area;
214  }
215  else if(nnodes==3){
216  ebuff[0][0] = interpolationCoefficients[0];
217  ebuff[0][1] = interpolationCoefficients[1];
218  ebuff[0][2] = interpolationCoefficients[2];
219 
220  ebuff_counter[0][0] = interpolationCoefficients[0];
221  ebuff_counter[0][1] = interpolationCoefficients[1];
222  ebuff_counter[0][2] = interpolationCoefficients[2];
223 
224  ebuff_s[0][0] = mass_scale*weighted_area;
225  ebuff_s[0][1] = mass_scale*weighted_area;
226  ebuff_s[0][2] = mass_scale*weighted_area;
227  }
228  else if(nnodes==4){
229  ebuff[0][0] = interpolationCoefficients[0];
230  ebuff[0][1] = interpolationCoefficients[1];
231  ebuff[0][2] = interpolationCoefficients[2];
232  ebuff[0][3] = interpolationCoefficients[3];
233 
234  ebuff_counter[0][0] = interpolationCoefficients[0];
235  ebuff_counter[0][1] = interpolationCoefficients[1];
236  ebuff_counter[0][2] = interpolationCoefficients[2];
237  ebuff_counter[0][3] = interpolationCoefficients[3];
238 
239  ebuff_s[0][0] = mass_scale*weighted_area;
240  ebuff_s[0][1] = mass_scale*weighted_area;
241  ebuff_s[0][2] = mass_scale*weighted_area;
242  ebuff_s[0][3] = mass_scale*weighted_area;
243  }
244 }
245 
291 template<class tuple_key, class tuple_value, class tri_key, class tri_value, class quad_key, class quad_value, class T, class S>
292 inline void assemble_restrict_operator( abstract_matrix<T,S> & B,
293  abstract_matrix<T,S> & Bi,
294  abstract_matrix<T,S> & BsM,
295  SF::vector<mesh_int_t> elemTag_surface_mesh,
296  hashmap::unordered_map<std::pair<mesh_int_t,mesh_int_t>,
297  std::pair<mesh_int_t,mesh_int_t>> map_vertex_tag_to_dof_petsc,
298  hashmap::unordered_map<tuple_key,
299  std::pair<tuple_value, tuple_value>> line_face,
300  hashmap::unordered_map<tri_key,
301  std::pair<tri_value, tri_value>> tri_face,
302  hashmap::unordered_map<quad_key,
303  std::pair<quad_value, quad_value>> quad_face,
304  const meshdata<mesh_int_t,mesh_real_t> & surface_mesh,
305  const meshdata<mesh_int_t,mesh_real_t> & emi_mesh,
306  S mass_scale)
307 {
308  // we want to make sure that the element integrator fits the matrix
309  T row_dpn = 1; T col_dpn = 1;
310 
311  const vector<SF_int> & petsc_nbr = emi_mesh.get_numbering(NBR_PETSC);// remove it
312  const SF::vector<mesh_int_t> & rnod_emi = emi_mesh.get_numbering(SF::NBR_REF);
315  for(size_t i=0; i<rnod_emi.size(); i++){
316  g2l_emi[rnod_emi[i]] = i;
317  l2g_emi[i] = rnod_emi[i];
318  }
319 
320 
321  const SF::vector<mesh_int_t> & rnod = surface_mesh.get_numbering(SF::NBR_REF);
324 
325  for(size_t i=0; i<rnod.size(); i++){
326  g2l[rnod[i]] = i;
327  l2g[i] = rnod[i];
328  }
329 
330  // allocate row / col index buffers
331  vector<SF_int> row_idx(SF_MAX_ELEM_NODES * row_dpn), row_idx_counter(SF_MAX_ELEM_NODES * row_dpn), col_idx(SF_MAX_ELEM_NODES * col_dpn), col_idx_counter(SF_MAX_ELEM_NODES * col_dpn);
332 
333  const SF::vector<mesh_int_t> & emi_surfmesh_elem = surface_mesh.get_numbering(SF::NBR_ELEM_REF);
334 
335  // allocate elem index buffer
336  dmat<SF_real> ebuff(SF_MAX_ELEM_NODES * row_dpn, SF_MAX_ELEM_NODES * col_dpn);
337  dmat<SF_real> ebuff_s(SF_MAX_ELEM_NODES * row_dpn, SF_MAX_ELEM_NODES * col_dpn);
338  dmat<SF_real> ebuff_counter(SF_MAX_ELEM_NODES * row_dpn, SF_MAX_ELEM_NODES * col_dpn);
339 
340  const T* con_emi = emi_mesh.con.data();
341  const T* con = surface_mesh.con.data();
342 
343  // start with assembly
344  for(size_t eidx=0; eidx < surface_mesh.l_numelem; eidx++)
345  {
346  T tag = surface_mesh.tag[eidx];
347 
348  // get the counter part
349  std::vector<int> elem_nodes;
350  std::vector<int> elem_nodes_old;
351  std::vector<int> petsc_first;
352  std::vector<int> petsc_second;
353 
354  T tag_first = 0;
355  T tag_second = 0;
356 
357  T rank_first = 0;
358  T rank_second = 0;
359 
360  T mem_first = 0;
361  T mem_second = 0;
362 
363  T sign = 1.0;
364 
365  for (int n = surface_mesh.dsp[eidx]; n < surface_mesh.dsp[eidx+1];n++)
366  {
367  T l_idx = surface_mesh.con[n];
368 
369  std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
370  Index_tag_old = std::make_pair(l2g[l_idx],tag);
371  mesh_int_t Index_new = map_vertex_tag_to_dof_petsc[Index_tag_old].first;
372  elem_nodes.push_back(Index_new);
373  elem_nodes_old.push_back(l2g[l_idx]);
374  }
375 
376  std::sort(elem_nodes.begin(),elem_nodes.end());
377  if(elem_nodes.size()==2){
378  tuple_key key;
379 
380  key.v1 = elem_nodes[0];
381  key.v2 = elem_nodes[1];
382  std::pair<tuple_value, tuple_value> value = line_face[key];
383 
384  tag_first = value.first.tag;
385  tag_second = value.second.tag;
386 
387  rank_first = value.first.rank;
388  rank_second = value.second.rank;
389 
390  mem_first = value.first.mem;
391  mem_second = value.second.mem;
392  }
393  else if(elem_nodes.size()==3){
394  tri_key key;
395  key.v1 = elem_nodes[0];
396  key.v2 = elem_nodes[1];
397  key.v3 = elem_nodes[2];
398  std::pair<tri_value, tri_value> value = tri_face[key];
399 
400  tag_first = value.first.tag;
401  tag_second = value.second.tag;
402 
403  rank_first = value.first.rank;
404  rank_second = value.second.rank;
405 
406  mem_first = value.first.mem;
407  mem_second = value.second.mem;
408  }
409  else if(elem_nodes.size()==4){
410  quad_key key;
411  key.v1 = elem_nodes[0];
412  key.v2 = elem_nodes[1];
413  key.v3 = elem_nodes[2];
414  key.v4 = elem_nodes[3];
415  std::pair<quad_value, quad_value> value = quad_face[key];
416 
417  tag_first = value.first.tag;
418  tag_second = value.second.tag;
419 
420  rank_first = value.first.rank;
421  rank_second = value.second.rank;
422 
423  mem_first = value.first.mem;
424  mem_second = value.second.mem;
425  }
426 
427  if(tag != tag_first) std::swap(tag_first, tag_second);
428 
429  std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
430 
431  for (int indx = 0; indx < elem_nodes_old.size(); ++indx)
432  {
433  std::pair <mesh_int_t,mesh_int_t> Index_tag_old_first;
434  Index_tag_old_first = std::make_pair(elem_nodes_old[indx],tag_first);
435 
436  auto it_first = map_vertex_tag_to_dof_petsc.find(Index_tag_old_first);
437  if (it_first == map_vertex_tag_to_dof_petsc.end()) {
438  std::cerr << "ERROR: tag_first=" << tag_first << " not found for vertex=" << elem_nodes_old[indx] << std::endl;
439  throw std::runtime_error("PETSc index not found for first tag");
440  }
441  std::pair <mesh_int_t,mesh_int_t> newIndex_petsc_first = it_first->second;
442  mesh_int_t newIndex_first = newIndex_petsc_first.first;
443  petsc_first.push_back(newIndex_petsc_first.second);
444 
445  std::pair <mesh_int_t,mesh_int_t> Index_tag_old_second;
446  Index_tag_old_second = std::make_pair(elem_nodes_old[indx],tag_second);
447 
448  auto it_second = map_vertex_tag_to_dof_petsc.find(Index_tag_old_second);
449  if (it_second == map_vertex_tag_to_dof_petsc.end()) {
450  std::cerr << "ERROR: tag_second=" << tag_second << " not found for vertex=" << elem_nodes_old[indx] << std::endl;
451  throw std::runtime_error("PETSc index not found for counter-face tag");
452  }
453  std::pair <mesh_int_t,mesh_int_t> newIndex_petsc_second = it_second->second;
454  mesh_int_t newIndex_second = newIndex_petsc_second.first;
455  petsc_second.push_back(newIndex_petsc_second.second);
456 
457  // elemTag_surface_mesh[eidx] == 1 means that the tag belongs to the extracellular region
458  if(mem_first==1 and elemTag_surface_mesh[eidx]==1) // condition for the membrane which the sign is always negative on extracellular side
459  sign = -1.0;
460  else if (mem_first==2 and tag<tag_second) // condition for the gap juntion which the sign is always negative for smaller tag
461  sign = -1.0;
462  }
463 
464  SF_int nnodes = elem_nodes.size();
465  vector<SF::Point> face_coordinates(nnodes);
466  // calculate row/col indices of entries
467  row_idx.resize(nnodes*row_dpn);
468  row_idx_counter.resize(nnodes*row_dpn);
469  col_idx.resize(nnodes*col_dpn);
470  col_idx_counter.resize(nnodes*col_dpn);
471 
472  {
473  for(SF_int i=0; i<nnodes; i++){
474  // all the row sets with element index
475  for(short j=0; j<row_dpn; j++){
476  row_idx.data()[i*row_dpn + j] = emi_surfmesh_elem[eidx]*row_dpn;
477  }
478 
479  for(short j=0; j<col_dpn; j++){
480  col_idx[i*col_dpn + j] = (petsc_first[i])*col_dpn + j;
481  col_idx_counter[i*col_dpn + j] = (petsc_second[i])*col_dpn + j;
482  }
483  }
484  }
485 
486  // assign ebuff and ebuff_s
487  ebuff.assign(nnodes, nnodes, 0.0);
488  ebuff_s.assign(nnodes, nnodes, 0.0);
489 
490  ebuff_counter.assign(nnodes, nnodes, 0.0);
491 
492  for (int n = surface_mesh.dsp[eidx], i = 0; n < surface_mesh.dsp[eidx+1];n++,i++)
493  {
494  T l_idx = surface_mesh.con[n];
495 
496  std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
497  Index_tag_old = std::make_pair(l2g[l_idx],tag);
498  mesh_int_t Index_new = map_vertex_tag_to_dof_petsc[Index_tag_old].first;
499 
500  double x = emi_mesh.xyz[g2l_emi[Index_new]*3+0];
501  double y = emi_mesh.xyz[g2l_emi[Index_new]*3+1];
502  double z = emi_mesh.xyz[g2l_emi[Index_new]*3+2];
503 
504  face_coordinates[i].x = x;
505  face_coordinates[i].y = y;
506  face_coordinates[i].z = z;
507  }
508  compute_integrate_matrix_barycentric(face_coordinates, nnodes, ebuff, ebuff_s, ebuff_counter, mass_scale);
509 
510  // add values into system matrix
511  const bool add = true;
512 
513  // we simply build Bi in the same way as B, but without the sign change, so we end up with Bi*Iij_stim = Ib_stim = Ii + Ij
514  Bi.set_values(row_idx, col_idx, ebuff.data(), add);
515  Bi.set_values(row_idx, col_idx_counter, ebuff_counter.data(), add);
516  // the operator matrix in order to project potential of dofs to the barycenteric of each face as transmembrane (vm = ui - uj)
517  ebuff*=(sign);
518  B.set_values(row_idx, col_idx, ebuff.data(), add);
519  ebuff_counter*=(-sign);
520  B.set_values(row_idx, col_idx_counter, ebuff_counter.data(), add); // counter face
521 
522  // the operator matrix in order to project the current density to the dofs of the face
523  ebuff_s*=(sign);
524  BsM.set_values(col_idx, row_idx, ebuff_s.data(), add);
525  }
526  // finish assembly and progress output
527  B.finish_assembly();
528  Bi.finish_assembly();
529  BsM.finish_assembly();
530 }
531 
548 template<class tuple_key, class tuple_value, class tri_key, class tri_value, class quad_key, class quad_value, class T, class S>
549 inline void assemble_lhs_emi(abstract_matrix<T,S> & mat,
550  abstract_matrix<T,S> & mat_surf,
551  const meshdata<mesh_int_t,mesh_real_t> & emi_mesh,
552  const meshdata<mesh_int_t,mesh_real_t> & surface_mesh,
553  hashmap::unordered_map<std::pair<mesh_int_t,mesh_int_t>, std::pair<mesh_int_t,mesh_int_t>> map_vertex_tag_to_dof_petsc,
554  hashmap::unordered_map<tuple_key,
555  std::pair<tuple_value, tuple_value>> line_face,
556  hashmap::unordered_map<tri_key,
557  std::pair<tri_value, tri_value>> tri_face,
558  hashmap::unordered_map<quad_key,
559  std::pair<quad_value, quad_value>> quad_face,
560  matrix_integrator<mesh_int_t,mesh_real_t> & stiffness_integrator,
561  matrix_integrator<mesh_int_t, mesh_real_t> & mass_integrator,
562  S stiffness_scale,
563  S mass_scale)
564 {
565  // we want to make sure that the element integrator fits the matrix
566  T row_dpn = 1; T col_dpn = 1;
567 
568  // allocate row / col index buffers
569  vector<SF_int> idx(SF_MAX_ELEM_NODES * col_dpn), idx_counter(SF_MAX_ELEM_NODES * col_dpn);
570  vector<T> row_idx(SF_MAX_ELEM_NODES * row_dpn), col_idx(SF_MAX_ELEM_NODES * col_dpn);
571 
572  // allocate elem index buffer
573  dmat<SF_real> ebuff(SF_MAX_ELEM_NODES * col_dpn, SF_MAX_ELEM_NODES * col_dpn);
574  dmat<SF_real> ebuff_mass(SF_MAX_ELEM_NODES * col_dpn, SF_MAX_ELEM_NODES * col_dpn);
575  // Assemble stiffness matrix (K)
576  const vector<mesh_int_t> & stiffness_petsc_nbr = emi_mesh.get_numbering(NBR_PETSC);
577  element_view<mesh_int_t, mesh_real_t> stiffness_view(emi_mesh, NBR_PETSC);
578 
579  // Assembly of global stiffness matrix into lhs matrix
580  for(size_t eidx=0; eidx < emi_mesh.l_numelem; eidx++)
581  {
582  // set element view to current element
583  stiffness_view.set_elem(eidx);
584 
585  // calculate row/col indices of entries
586  mesh_int_t nnodes = stiffness_view.num_nodes();
587 
588  row_idx.resize(nnodes*row_dpn);
589  col_idx.resize(nnodes*col_dpn);
590  canonic_indices<mesh_int_t,SF_int>(stiffness_view.nodes(), stiffness_petsc_nbr.data(), nnodes, row_dpn, row_idx.data());
591  canonic_indices<mesh_int_t,SF_int>(stiffness_view.nodes(), stiffness_petsc_nbr.data(), nnodes, col_dpn, col_idx.data());
592 
593  // call integrator
594  stiffness_integrator(stiffness_view, ebuff);
595  ebuff *= stiffness_scale;
596 
597  // add values into system matrix
598  const bool add = true;
599  mat.set_values(row_idx, col_idx, ebuff.data(), add);
600  }
601 
602  // Assemble the global surface mass matrix (M).
603  // Each local mass matrix computed for a surface element is mapped into the global system
604  // using the corresponding global row and column indices of that element’s nodes.
605 
606  const vector<SF_int> & ref_nbr = surface_mesh.get_numbering(SF::NBR_REF);
607  const vector<SF_int> & petsc_nbr = surface_mesh.get_numbering(SF::NBR_PETSC);
608 
609  const SF::vector<mesh_int_t> & rnod_emi = emi_mesh.get_numbering(SF::NBR_REF);
612  for(size_t i=0; i<rnod_emi.size(); i++){
613  g2l_emi[rnod_emi[i]] = i;
614  l2g_emi[i] = rnod_emi[i];
615  }
616  const SF::vector<mesh_int_t> & rnod = surface_mesh.get_numbering(SF::NBR_REF);
619 
620  for(size_t i=0; i<rnod.size(); i++){
621  g2l[rnod[i]] = i;
622  l2g[i] = rnod[i];
623  }
624 
625  // start with assembly mass matrix into lhs matrix
626  element_view<mesh_int_t, mesh_real_t> view(surface_mesh, NBR_PETSC);
627  for(size_t eidx=0; eidx < surface_mesh.l_numelem; eidx++)
628  {
629  // set element view to current element
630  view.set_elem(eidx);
631  mesh_int_t tag = surface_mesh.tag[eidx]; // selected face
632 
633  // get the counter part
634  std::vector<int> elem_nodes;
635  std::vector<int> elem_nodes_old;
636  std::vector<int> elem_nodes_first;
637  std::vector<int> elem_nodes_second;
638 
639  std::vector<int> petsc_first;
640  std::vector<int> petsc_second;
641 
642  T tag_first = 0;
643  T tag_second = 0;
644 
645  for (int n = surface_mesh.dsp[eidx]; n < surface_mesh.dsp[eidx+1];n++)
646  {
647  T l_idx = surface_mesh.con[n];
648 
649  std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
650  Index_tag_old = std::make_pair(l2g[l_idx],tag);
651  mesh_int_t Index_new = map_vertex_tag_to_dof_petsc[Index_tag_old].first;
652  elem_nodes.push_back(Index_new);
653  elem_nodes_old.push_back(l2g[l_idx]);
654  }
655 
656  std::sort(elem_nodes.begin(),elem_nodes.end());
657  if(elem_nodes.size()==2){
658  tuple_key key;
659 
660  key.v1 = elem_nodes[0];
661  key.v2 = elem_nodes[1];
662  std::pair<tuple_value, tuple_value> value = line_face[key];
663 
664  tag_first = value.first.tag;
665  tag_second = value.second.tag;
666  }
667  else if(elem_nodes.size()==3){
668  tri_key key;
669  key.v1 = elem_nodes[0];
670  key.v2 = elem_nodes[1];
671  key.v3 = elem_nodes[2];
672  std::pair<tri_value, tri_value> value = tri_face[key];
673 
674  tag_first = value.first.tag;
675  tag_second = value.second.tag;
676  }
677  else if(elem_nodes.size()==4){
678  quad_key key;
679  key.v1 = elem_nodes[0];
680  key.v2 = elem_nodes[1];
681  key.v3 = elem_nodes[2];
682  key.v4 = elem_nodes[3];
683  std::pair<quad_value, quad_value> value = quad_face[key];
684 
685  tag_first = value.first.tag;
686  tag_second = value.second.tag;
687  }
688 
689  if(tag != tag_first) std::swap(tag_first, tag_second);
690 
691  std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
692 
693  for (int indx = 0; indx < elem_nodes_old.size(); ++indx)
694  {
695  std::pair <mesh_int_t,mesh_int_t> Index_tag_old_first;
696  Index_tag_old_first = std::make_pair(elem_nodes_old[indx],tag_first);
697  std::pair <mesh_int_t,mesh_int_t> newIndex_petsc_first = map_vertex_tag_to_dof_petsc[Index_tag_old_first];
698  mesh_int_t newIndex_first = newIndex_petsc_first.first;
699  petsc_first.push_back(newIndex_petsc_first.second);
700 
701  std::pair <mesh_int_t,mesh_int_t> Index_tag_old_second;
702  Index_tag_old_second = std::make_pair(elem_nodes_old[indx],tag_second);
703  std::pair <mesh_int_t,mesh_int_t> newIndex_petsc_second = map_vertex_tag_to_dof_petsc[Index_tag_old_second];
704  mesh_int_t newIndex_second = newIndex_petsc_second.first;
705  petsc_second.push_back(newIndex_petsc_second.second);
706  }
707 
708  // calculate row/col indices of entries
709  SF_int nnodes = view.num_nodes();
710  idx.resize(nnodes*row_dpn);
711  idx_counter.resize(nnodes*col_dpn);
712 
713  {
714  for(T i=0; i<nnodes; i++)
715  {
716  for(short j=0; j<row_dpn; j++){
717  idx[i*row_dpn + j] = (petsc_first[i])*row_dpn + j;
718  }
719  for(short j=0; j<col_dpn; j++){
720  idx_counter[i*col_dpn + j] = (petsc_second[i])*col_dpn + j;
721  }
722  }
723  }
724 
725  // add values into system matrix
726  const bool add = true;
727 
728  // call integrator
729  mass_integrator(view, ebuff);
730  ebuff_mass.assign(nnodes, nnodes, 0.0);
731  ebuff_mass = ebuff;
732  // Note: surface mesh by default has both face and counter face;
733  // to avoid the dublication, factor 0.5 is necessary
734  ebuff*=0.5 * mass_scale;
735  ebuff_mass*=0.5;
736 
737  mat.set_values(idx, idx, ebuff.data(), add);
738  mat.set_values(idx_counter, idx_counter, ebuff.data(), add);
739 
740  mat_surf.set_values(idx, idx, ebuff_mass.data(), add);
741  mat_surf.set_values(idx_counter, idx_counter, ebuff_mass.data(), add);
742 
743  ebuff*=(-1.0);
744  ebuff_mass*=(-1.0);
745 
746  mat.set_values(idx, idx_counter, ebuff.data(), add);
747  mat.set_values(idx_counter, idx, ebuff.data(), add);
748 
749  mat_surf.set_values(idx, idx_counter, ebuff_mass.data(), add);
750  mat_surf.set_values(idx_counter, idx, ebuff_mass.data(), add);
751 
752  // Each face contributes the following 2x2 block structure:
753  //
754  // s* [ + M - M ;
755  // - M + M ]
756  //
757  // with s = 0.5 * alpha,
758  // where M is the local face mass matrix.
759 
760  }
761  // finish assembly and progress output
762  mat.finish_assembly();
763  mat_surf.finish_assembly();
764 }
765 
783 template<class tuple_key, class tuple_value, class tri_key, class tri_value, class quad_key, class quad_value, class T, class S>
784 inline void assign_resting_potential_from_ionic_models_on_myocyte(abstract_matrix<T,S> & Vm_emi,
785  abstract_vector<T, S>* vb,
786  SF::vector<mesh_int_t> elemTag_emi_mesh,
787  hashmap::unordered_map<std::pair<T,T>,
788  std::pair<T,T>> map_vertex_tag_to_dof_petsc,
789  hashmap::unordered_map<tuple_key,
790  std::pair<tuple_value, tuple_value>> line_face,
791  hashmap::unordered_map<tri_key,
792  std::pair<tri_value, tri_value>> tri_face,
793  hashmap::unordered_map<quad_key,
794  std::pair<quad_value, quad_value>> quad_face,
795  const meshdata<T,mesh_real_t> & surface_mesh,
796  const meshdata<T,mesh_real_t> & emi_mesh)
797 {
798  // we want to make sure that the element integrator fits the matrix
799  T row_dpn = 1; T col_dpn = 1;
800 
801  const vector<SF_int> & petsc_nbr = emi_mesh.get_numbering(NBR_PETSC);// remove it
802  const SF::vector<T> & rnod_emi = emi_mesh.get_numbering(SF::NBR_REF);
805  for(size_t i=0; i<rnod_emi.size(); i++){
806  g2l_emi[rnod_emi[i]] = i;
807  l2g_emi[i] = rnod_emi[i];
808  }
809 
810  const SF::vector<T> & rnod = surface_mesh.get_numbering(SF::NBR_REF);
811  const vector<SF_int> & petsc_nbr_surface = surface_mesh.get_numbering(NBR_PETSC);// remove it
812 
815 
817 
818  for(size_t i=0; i<rnod.size(); i++){
819  g2l[rnod[i]] = i;
820  l2g[i] = rnod[i];
821  }
822  auto vb_data = vb->const_ptr();
823 
824  // start with assembly
825  for(size_t eidx=0; eidx < surface_mesh.l_numelem; eidx++)
826  {
827  T tag = surface_mesh.tag[eidx];
828 
829  // get the counter part
830  std::vector<int> elem_nodes;
831  bool tag_current;
832  T tag_first = 0;
833  T tag_second = 0;
834  T mem_first = 0;
835  T mem_second = 0;
836 
837  T sign = 1.0;
838 
839  for (int n = surface_mesh.dsp[eidx], i = 0; n < surface_mesh.dsp[eidx+1];n++,i++)
840  {
841  T l_idx = surface_mesh.con[n];
842  std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
843  Index_tag_old = std::make_pair(l2g[l_idx],tag);
844  mesh_int_t Index_new = map_vertex_tag_to_dof_petsc[Index_tag_old].first;
845  elem_nodes.push_back(Index_new);
846  }
847 
848  std::sort(elem_nodes.begin(),elem_nodes.end());
849  if(elem_nodes.size()==2){
850  tuple_key key;
851 
852  key.v1 = elem_nodes[0];
853  key.v2 = elem_nodes[1];
854  std::pair<tuple_value, tuple_value> value = line_face[key];
855 
856  tag_first = value.first.tag;
857  tag_second = value.second.tag;
858 
859  mem_first = value.first.mem;
860  mem_second = value.second.mem;
861  }
862  else if(elem_nodes.size()==3){
863  tri_key key;
864  key.v1 = elem_nodes[0];
865  key.v2 = elem_nodes[1];
866  key.v3 = elem_nodes[2];
867  std::pair<tri_value, tri_value> value = tri_face[key];
868 
869  tag_first = value.first.tag;
870  tag_second = value.second.tag;
871 
872  mem_first = value.first.mem;
873  mem_second = value.second.mem;
874  }
875  else if(elem_nodes.size()==4){
876  quad_key key;
877  key.v1 = elem_nodes[0];
878  key.v2 = elem_nodes[1];
879  key.v3 = elem_nodes[2];
880  key.v4 = elem_nodes[3];
881  std::pair<quad_value, quad_value> value = quad_face[key];
882 
883  tag_first = value.first.tag;
884  tag_second = value.second.tag;
885 
886  mem_first = value.first.mem;
887  mem_second = value.second.mem;
888  }
889 
890  SF_int nnodes = elem_nodes.size();
891  for (int i = 0; i < nnodes; ++i)
892  {
893  if(mem_first==1 || mem_second==1){
894  tag_2_vm[tag_first] = vb_data[eidx];
895  tag_2_vm[tag_second] = vb_data[eidx];
896  }
897  }
898  }
899 
900  vb->const_release_ptr(vb_data);
901 
902  // assign all the tags
903  {
904  // we want to make sure that the element integrator fits the matrix
905  mesh_int_t row_dpn = 1, col_dpn = 1;
906 
907  // allocate row and value
908  vector<T> row_idx(SF_MAX_ELEM_NODES * row_dpn);
909  vector<T> col_idx(SF_MAX_ELEM_NODES * row_dpn);
910  vector<SF_real> val_idx(SF_MAX_ELEM_NODES * row_dpn);
911 
912  // allocate elem index buffer
913  dmat<SF_real> ebuff(SF_MAX_ELEM_NODES * row_dpn, SF_MAX_ELEM_NODES * col_dpn);
914 
915  const vector<mesh_int_t> & petsc_nbr = emi_mesh.get_numbering(NBR_PETSC);
916 
917  // start with assembly
918  element_view<mesh_int_t, mesh_real_t> view(emi_mesh, NBR_PETSC);
919  for(size_t eidx=0; eidx < emi_mesh.l_numelem; eidx++)
920  {
921  // set element view to current element
922  view.set_elem(eidx);
923  T tag = emi_mesh.tag[eidx];
924  // calculate row/col indices of entries
925  mesh_int_t nnodes = view.num_nodes();
926 
927  row_idx.resize(nnodes*row_dpn);val_idx.resize(nnodes*row_dpn);
928  col_idx.resize(nnodes*row_dpn);
929  ebuff.assign(nnodes, nnodes, 0.0);
930  canonic_indices<mesh_int_t,SF_int>(view.nodes(), petsc_nbr.data(), nnodes, row_dpn, row_idx.data());
931  canonic_indices<mesh_int_t,SF_int>(view.nodes(), petsc_nbr.data(), nnodes, col_dpn, col_idx.data());
932 
933  for (int i = 0; i < nnodes; ++i)
934  {
935  ebuff[i][i] = 0;
936  // elemTag_emi_mesh[eidx]==2 means that the tag belongs to the intracellular region
937  if(elemTag_emi_mesh[eidx]==2){
938  ebuff[i][i] = tag_2_vm[tag];
939  }
940  }
941  Vm_emi.set_values(row_idx, col_idx, ebuff.data(), false);
942  }
943  Vm_emi.finish_assembly();
944  }
945 }
946 
947 }
948 
949 #endif
950 #endif
#define sign(x)
Definition: ION_IF.h:57
Basic containers.
int mesh_int_t
Definition: SF_container.h:46
FEM utilities.
#define SF_MAX_ELEM_NODES
max #nodes defining an element
Definition: SF_fem_utils.h:40
std::int32_t SF_int
Use the general std::int32_t as int type.
Definition: SF_globals.h:37
size_t size() const
The current size of the vector.
Definition: SF_vector.h:104
void assign(InputIterator s, InputIterator e)
Assign a memory range.
Definition: SF_vector.h:161
Definition: dense_mat.hpp:34
double mag(const Point &vect)
vector magnitude
Definition: SF_container.h:111
double inner_prod(const Point &a, const Point &b)
Definition: SF_container.h:90
Point cross(const Point &a, const Point &b)
cross product
Definition: SF_container.h:84
V clamp(const V val, const W start, const W end)
Clamp a value into an interval [start, end].
Definition: SF_base.h:63
@ NBR_PETSC
PETSc numbering of nodes.
Definition: SF_container.h:203
@ NBR_ELEM_REF
The element numbering of the reference mesh (the one stored on HD).
Definition: SF_container.h:204
@ NBR_REF
The nodal numbering of the reference mesh (the one stored on HD).
Definition: SF_container.h:201
vec3< POINT_REAL > Point
Definition: vect.h:93
Point and vector struct.
Definition: SF_container.h:65
double y
Definition: SF_container.h:67
double z
Definition: SF_container.h:68
double x
Definition: SF_container.h:66