openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
electric_integrators.cc
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (c) NumeriCor GmbH
2 // SPDX-License-Identifier: LicenseRef-APL-1.1
3 
12 #include "electric_integrators.h"
13 #include "sf_interface.h"
14 #include "SF_init.h"
15 
16 namespace opencarp {
17 
18 #define ELEC_STIFFNESS 1
19 #define ELEC_MASS 2
20 
21 inline int get_preferred_int_order(SF::elem_t & etype, int mat_type)
22 {
23  switch(mat_type) {
24  default:
25  case ELEC_MASS: return 2;
26 
27  case ELEC_STIFFNESS: {
28  switch(etype) {
29  default:
30  case SF::Quad:
31  case SF::Hexa:
32  case SF::Prism:
33  case SF::Pyramid:
34  return 2;
35 
36  case SF::Line:
37  case SF::Tetra:
38  case SF::Tri:
39  return 1;
40  }
41  }
42  }
43 }
44 
45 void read_el_scale_vec(const char* file, mesh_t mt, SF::vector<double>& el_scale, int& el_scale_dpn)
46 {
47  sf_mesh& mesh = get_mesh(mt);
48  int rank; MPI_Comm_rank(mesh.comm, &rank);
49 
50  int dpn = 1;
51  FILE* fd = NULL;
52 
53  if (rank == 0) {
54  fd = fopen(file, "r");
55  assert(fd != NULL);
56 
57  char buf[1024];
58  fgets(buf, sizeof(buf), fd);
59 
60  // A bare integer (no decimal, no second token) of value 1 or 3 is the header.
61  // Anything else is the first data row; infer dpn from its field count.
62  int dpn_candidate; char rest[4];
63  if (sscanf(buf, "%d %3s", &dpn_candidate, rest) == 1 && (dpn_candidate == 1 || dpn_candidate == 3)) {
64  dpn = dpn_candidate;
65  } else {
66  float v[3];
67  dpn = (sscanf(buf, "%f %f %f", v, v+1, v+2) == 3) ? 3 : 1;
68  fseek(fd, 0, SEEK_SET);
69  }
70  }
71 
72  MPI_Bcast(&dpn, 1, MPI_INT, 0, mesh.comm);
73 
74  sf_vec* escale;
75  SF::init_vector(&escale, mesh, dpn, sf_vec::elemwise);
76  size_t nrd = escale->read_ascii(fd);
77  if (rank == 0) fclose(fd);
78 
79  if (nrd != (size_t)(mesh.g_numelem * dpn)) {
80  log_msg(0, 4, 0, "%s error: %s scale file has wrong size (expected %zu elements * dpn=%d); skipping",
81  __func__, get_mesh_type_name(mt), mesh.g_numelem, dpn);
82  delete escale;
83  return;
84  }
85 
86  if (get_size() > 1) {
87  if (get_permutation(mt, ELEM_PETSC_TO_CANONICAL, dpn) == NULL)
90  sc(*escale, false);
91  }
92 
93  SF_real* p = escale->ptr();
94  el_scale.assign(p, p + escale->lsize());
95  escale->release_ptr(p);
96  delete escale;
97 
98  el_scale_dpn = dpn;
99 }
100 
102  const int* reg,
103  const double vol,
104  const double* eVals,
105  const char* msg_start)
106 {
107  if(msg_start)
108  printf("\n%s", msg_start);
109 
110  if(reg)
111  printf("gregion[%d], ", *reg);
112 
113  printf("element %ld: ", (long int) elem.global_element_index(SF::NBR_ELEM_REF));
114 
115  for(int i=0; i<elem.num_nodes(); i++)
116  printf("%lld ", static_cast<long long>(elem.global_node(i, SF::NBR_REF)));
117 
118  printf("\nVolume: %g", vol);
119 
120  if(eVals) {
121  SF::Point fib = elem.fiber(), she = elem.sheet();
122  printf(", fib: (%g, %g, %g), she: (%g, %g, %g), G: (%g, %g, %g)",
123  fib.x, fib.y, fib.z, she.x, she.y, she.z, eVals[0]*1e3, eVals[1]*1e3, eVals[2]*1e3);
124  }
125 
126  printf("\n\n");
127 }
128 
130  const bool is_bath,
131  double* evals)
132 {
133  if(is_bath == false) {
134  switch(emat.g) {
135  case intra_cond:
136  evals[0] = emat.InVal[0], evals[1] = emat.InVal[1], evals[2] = emat.InVal[2];
137  break;
138 
139  case extra_cond:
140  evals[0] = emat.ExVal[0], evals[1] = emat.ExVal[1], evals[2] = emat.ExVal[2];
141  break;
142 
143  case sum_cond:
144  evals[0] = emat.InVal[0] + emat.ExVal[0],
145  evals[1] = emat.InVal[1] + emat.ExVal[1],
146  evals[2] = emat.InVal[2] + emat.ExVal[2];
147  break;
148 
149  case para_cond:
150  evals[0] = (emat.InVal[0] * emat.ExVal[0]) / (emat.InVal[0] + emat.ExVal[0]),
151  evals[1] = (emat.InVal[1] * emat.ExVal[1]) / (emat.InVal[1] + emat.ExVal[1]),
152  evals[2] = (emat.InVal[2] * emat.ExVal[2]) / (emat.InVal[2] + emat.ExVal[2]);
153  break;
154  }
155  }
156  else
157  evals[0] = emat.BathVal[0], evals[1] = emat.BathVal[1], evals[2] = emat.BathVal[2];
158 }
159 
160 void get_conductivity(const double* evals,
161  const SF::Point *f,
162  const SF::Point *s,
163  SF::dmat<double> & cond)
164 {
165  cond.set_size(3,3);
166 
167  // longitudonal, transversal, and normal direction eigenvalues
168  double gl = evals[0], gt = evals[1], gn = evals[2];
169 
170  if(s == nullptr) {
171  // we compute the non-orthotropic conductivity:
172  // sigma = gl f f^T + gt (I - f f^T)
173  // = gt I + (gl - gt) f f^T
174  SF::outer_prod(*f, *f, gl - gt, cond[0]);
175  cond[0][0] += gt, cond[1][1] += gt, cond[2][2] += gt;
176  }
177  else {
178  // we compute the orthotropic conductivity:
179  // sigma = gl f f^T + gt s s^T + gn n n^T
180  SF::Point n = cross(*f, *s);
181 
182  SF::outer_prod(*f, *f, gl, cond[0], false);
183  SF::outer_prod(*s, *s, gt, cond[0], true);
184  SF::outer_prod( n, n, gn, cond[0], true);
185  }
186 }
187 
188 void print_points(SF::Point* pts, int npts, int eidx)
189 {
190  for(int i=0; i<npts; i++){
191  if(pts[i].x!=pts[i].x or pts[i].y!=pts[i].y or pts[i].z!=pts[i].z){
192  printf(" eidx = %d print_points ( %g %g %g ) \n", eidx, pts[i].x, pts[i].y, pts[i].z);
193  printf("\n");
194  }
195  }
196 }
197 
199 {
200  const size_t eidx = elem.element_index();
201  SF::elem_t type = elem.type();
202  mesh_int_t nnodes = elem.num_nodes();
203  const int int_order = get_preferred_int_order(type, ELEC_STIFFNESS);
204 
205  const int reg = material.regionIDs.size() ? material.regionIDs[eidx] : 0;
206  const RegionSpecs & reg_spec = material.regions[reg];
207 
208  assert(reg_spec.material->material_type == ElecMat);
209  const elecMaterial & mat = *static_cast<elecMaterial*>(reg_spec.material);
210  SF::Point fib = elem.fiber(), she = elem.sheet();
211 
212  bool is_bath = SF::inner_prod(fib, fib) < 0.01; // bath elements have no fiber direction
213  bool has_sheet = elem.has_sheet(); // do we have a sheet direciton
214 
215  double eVals[3];
216  get_conductivity_evals(mat, is_bath, eVals);
217 
218  assert(material.el_scale_dpn == 0 || material.el_scale.size() > 0);
219 
220  if (material.el_scale_dpn == 1) {
221  double s = material.el_scale[eidx];
222  eVals[0] *= s; eVals[1] *= s; eVals[2] *= s;
223  } else if (material.el_scale_dpn == 3) {
224  if (is_bath) {
225  double s = material.el_scale[3*eidx];
226  eVals[0] *= s; eVals[1] *= s; eVals[2] *= s;
227  } else {
228  eVals[0] *= material.el_scale[3*eidx];
229  eVals[1] *= material.el_scale[3*eidx + 1];
230  eVals[2] *= material.el_scale[3*eidx + 2];
231  }
232  }
233 
234  if(is_bath) {
235  fib = {1, 0, 0};
236  she = {0, 1, 0};
237  }
238 
239  SF::get_transformed_pts(elem, lpts, fib);
240 
241 #ifdef DEBUG_INT
242  print_points(lpts, nnodes);
243 #endif
244 
245  get_conductivity(eVals, &fib, has_sheet ? &she : nullptr, cond);
246 
247  int ndof = nnodes;
248  shape.set_size(4, ndof), gshape.set_size(4, ndof);
249  buff.assign(ndof, ndof, 0.0);
250 
251  int nint = 0;
252  elem.integration_points(int_order, ipts, w, nint);
253 
254  double vol = 0.0;
255  bool have_nan = false;
256 
257  // Integration loop
258  for(int iidx=0; iidx < nint; iidx++)
259  {
260  double detj;
261  // get shape function and its spatial derivatives
262  SF::reference_shape(type, ipts[iidx], shape);
263  SF::jacobian_matrix(shape, nnodes, lpts, J);
264  SF::invert_jacobian_matrix(type, J, detj);
265 
266 #ifdef DEBUG_INT
267  SF::dmat<double> Jbuff(3,3);
268  memcpy(Jbuff.data(), J, 9*sizeof(double));
269  std::string j_name = "J_" + std::to_string(iidx);
270  Jbuff.disp(j_name.c_str());
271 #endif
272 
273  SF::shape_deriv(J, shape, ndof, gshape);
274 
275  double dx = w[iidx] * detj;
276  vol += dx;
277 
278  for (int k = 0; k < ndof; k++) {
279  // derivatives of shape function k
280  double skx = gshape[1][k], sky = gshape[2][k], skz = gshape[3][k];
281  double cx = cond[0][0] * skx + cond[0][1] * sky + cond[0][2] * skz;
282  double cy = cond[1][0] * skx + cond[1][1] * sky + cond[1][2] * skz;
283  double cz = cond[2][0] * skx + cond[2][1] * sky + cond[2][2] * skz;
284 
285  for (int l = 0; l < ndof; l++) {
286  // derivatives of shape function l
287  double slx = gshape[1][l], sly = gshape[2][l], slz = gshape[3][l];
288  double val = - (cx*slx + cy * sly + cz * slz) * dx;
289  buff[k][l] += val;
290 
291  if(val != val) have_nan = true;
292  }
293  }
294  }
295 
296 
297 #ifdef DEBUG_INT
298  printf("\n vol: %g \n\n", vol);
299  buff.disp("Element Matrix:");
300 #endif
301 
302  if(have_nan) {
303  print_element_info(elem, &reg, vol, eVals, "NaN detected: ");
304  exit(EXIT_FAILURE);
305  }
306 }
307 
309 {
310  row_dpn = 1;
311  col_dpn = 1;
312 }
313 
314 
316 {
317  SF::elem_t type = elem.type();
318  mesh_int_t nnodes = elem.num_nodes();
319  const int int_order = get_preferred_int_order(type, ELEC_MASS);
320 
321  int ndof = nnodes;
322  shape.set_size(4, ndof);
323  buff.assign(ndof, ndof, 0.0);
324 
325  SF::Point fib = {1, 0, 0}; // this is a dummy fibre to satisfy get_transformed_pts()
326 
327  double vol = 0.0;
328  bool have_nan = false;
329  int nint;
330 
331  elem.integration_points(int_order, ipts, w, nint);
333 
334  // Integration loop
335  for(int iidx=0; iidx < nint; iidx++)
336  {
337  double detj;
338  // get shape function and its spatial derivatives
339  SF::reference_shape(type, ipts[iidx], shape);
340  SF::jacobian_matrix(shape, nnodes, lpts, J);
341  SF::invert_jacobian_matrix(type, J, detj);
342 
343  double dx = w[iidx] * detj;
344  vol += dx;
345 
346  for (int k = 0; k < ndof; k++) {
347  double sk = shape[0][k];
348  for (int l = 0; l < ndof; l++) {
349  double sl = shape[0][l];
350  double val = sk * sl * dx;
351  buff[k][l] += val;
352 
353  if(val != val) have_nan = true;
354  }
355  }
356  }
357 
358 #ifdef DEBUG_INT
359  printf("\n vol: %g \n\n", vol);
360  buff.disp("Mass Matrix:");
361 #endif
362 
363  if(have_nan) {
364  print_element_info(elem, NULL, vol, NULL, "NaN detected: ");
365  exit(EXIT_FAILURE);
366  }
367 }
368 
369 void mass_integrator::dpn(mesh_int_t & row_dpn, mesh_int_t & col_dpn)
370 {
371  row_dpn = 1;
372  col_dpn = 1;
373 }
374 
375 } // namespace opencarp
opencarp::local_index_t mesh_int_t
Definition: SF_container.h:31
opencarp::real_t SF_real
Global scalar type.
Definition: SF_globals.h:18
size_t read_ascii(FILE *fd)
virtual S * ptr()=0
virtual void release_ptr(S *&p)=0
virtual T lsize() const =0
void assign(const dmat< S > &m)
copy a mtrix.
Definition: dense_mat.hpp:120
void set_size(const short irows, const short icols)
set the matrix dimensions
Definition: dense_mat.hpp:58
S * data()
Definition: dense_mat.hpp:249
void disp(const char *name)
Definition: dense_mat.hpp:239
Comfort class. Provides getter functions to access the mesh member variables more comfortably.
Definition: SF_fem_utils.h:689
Point fiber() const
Get element fiber direction.
Definition: SF_fem_utils.h:835
void integration_points(const short order, Point *ip, double *w, int &nint) const
Definition: SF_fem_utils.h:902
elem_t type() const
Getter function for the element type.
Definition: SF_fem_utils.h:756
const T & global_node(short nidx) const
Access the connectivity information.
Definition: SF_fem_utils.h:790
size_t global_element_index() const
Get currently selected element index.
Definition: SF_fem_utils.h:880
size_t element_index() const
Get currently selected element index.
Definition: SF_fem_utils.h:870
bool has_sheet() const
Check if a sheet direction is present.
Definition: SF_fem_utils.h:860
T num_nodes() const
Getter function for the number of nodes.
Definition: SF_fem_utils.h:746
Point sheet() const
Get element sheet direction.
Definition: SF_fem_utils.h:847
size_t g_numelem
global number of elements
Definition: SF_container.h:383
MPI_Comm comm
the parallel mesh is defined on a MPI world
Definition: SF_container.h:389
Container for a PETSc VecScatter.
size_t size() const
The current size of the vector.
Definition: SF_vector.h:89
void assign(InputIterator s, InputIterator e)
Assign a memory range.
Definition: SF_vector.h:146
void operator()(const SF::element_view< mesh_int_t, mesh_real_t > &elem, SF::dmat< double > &buff)
compute the element matrix for a given element.
void dpn(mesh_int_t &row_dpn, mesh_int_t &col_dpn)
return (by reference) the row and column dimensions
void dpn(mesh_int_t &row_dpn, mesh_int_t &col_dpn)
return (by reference) the row and column dimensions
void operator()(const SF::element_view< mesh_int_t, mesh_real_t > &elem, SF::dmat< double > &buff)
compute the element matrix for a given element.
#define ELEC_STIFFNESS
#define ELEC_MASS
void shape_deriv(const double *iJ, const dmat< double > &rshape, const int ndof, dmat< double > &shape)
Compute shape derivatives for an element, based on the shape derivatives of the associated reference ...
Definition: SF_fem_utils.h:659
double inner_prod(const Point &a, const Point &b)
Definition: SF_container.h:75
void outer_prod(const Point &a, const Point &b, const double s, double *buff, const bool add=false)
Definition: SF_container.h:80
void jacobian_matrix(const dmat< double > &rshape, const int npts, const Point *pts, double *J)
Compute Jacobian matrix from the real element to the reference element.
Definition: SF_fem_utils.h:595
void init_vector(SF::abstract_vector< T, S > **vec)
Definition: SF_init.h:110
elem_t
element type enum
Definition: SF_container.h:38
@ Line
Definition: SF_container.h:46
@ Tri
Definition: SF_container.h:45
@ Prism
Definition: SF_container.h:43
@ Pyramid
Definition: SF_container.h:42
@ Tetra
Definition: SF_container.h:39
@ Quad
Definition: SF_container.h:44
@ Hexa
Definition: SF_container.h:40
void get_transformed_pts(const element_view< T, S > &view, Point *loc_pts, Point &trsf_fibre, bool orthogonal=true)
void reference_shape(const elem_t type, const Point ip, dmat< double > &rshape)
Compute shape function and its derivatives on a reference element.
Definition: SF_fem_utils.h:368
@ NBR_ELEM_REF
The element numbering of the reference mesh (the one stored on HD).
Definition: SF_container.h:189
@ NBR_REF
The nodal numbering of the reference mesh (the one stored on HD).
Definition: SF_container.h:186
void invert_jacobian_matrix(const elem_t type, double *J, double &detJ)
Definition: SF_fem_utils.h:618
void print_element_info(const SF::element_view< mesh_int_t, mesh_real_t > &elem, const int *reg, const double vol, const double *eVals, const char *msg_start)
void print_points(SF::Point *pts, int npts, int eidx)
void read_el_scale_vec(const char *file, mesh_t mt, SF::vector< double > &el_scale, int &el_scale_dpn)
sf_mesh & get_mesh(const mesh_t gt)
Get a mesh by specifying the gridID.
Definition: sf_interface.cc:18
@ extra_cond
Definition: fem_types.h:28
@ sum_cond
Definition: fem_types.h:28
@ intra_cond
Definition: fem_types.h:28
@ para_cond
Definition: fem_types.h:28
SF::scattering * get_permutation(const int mesh_id, const int perm_id, const int dpn)
Get the PETSC to canonical permutation scattering for a given mesh and number of dpn.
vec3< V > cross(const vec3< V > &a, const vec3< V > &b)
Definition: vect.h:129
const char * get_mesh_type_name(mesh_t t)
get a char* to the name of a mesh type
Definition: sf_interface.cc:31
bool phys_defined(int physreg)
function to check if certain physics are defined
void get_conductivity_evals(const elecMaterial &emat, const bool is_bath, double *evals)
SF::scattering * register_permutation(const int mesh_id, const int perm_id, const int dpn)
Register a permutation between two orderings for a mesh.
void log_msg(FILE_SPEC out, int level, unsigned char flag, const char *fmt,...)
Definition: basics.cc:57
mesh_t
The enum identifying the different meshes we might want to load.
Definition: sf_interface.h:44
int get_preferred_int_order(SF::elem_t &etype, int mat_type)
int get_size(MPI_Comm comm=PETSC_COMM_WORLD)
Definition: basics.h:283
void get_conductivity(const double *evals, const SF::Point *f, const SF::Point *s, SF::dmat< double > &cond)
@ ElecMat
Definition: fem_types.h:24
Interface to SlimFem.
#define PHYSREG_INTRA_ELEC
Definition: sf_interface.h:69
#define ELEM_PETSC_TO_CANONICAL
Permute algebraic element data from PETSC to canonical ordering.
Definition: sf_interface.h:66
Point and vector struct.
Definition: SF_container.h:50
double y
Definition: SF_container.h:52
double z
Definition: SF_container.h:53
double x
Definition: SF_container.h:51
SF::vector< RegionSpecs > regions
array with region params
Definition: fem_types.h:111
SF::vector< double > el_scale
optionally provided per-element params scale
Definition: fem_types.h:112
SF::vector< int > regionIDs
elemental region IDs (multiple tags are grouped in one region)
Definition: fem_types.h:110
int el_scale_dpn
0=disabled, 1=isotropic scalar, 3=anisotropic (sl, st, sn) per element
Definition: fem_types.h:113
region based variations of arbitrary material parameters
Definition: fem_types.h:78
physMaterial * material
material parameter description
Definition: fem_types.h:83
double ExVal[3]
extracellular conductivity eigenvalues
Definition: fem_types.h:47
cond_t g
rule to build conductivity tensor
Definition: fem_types.h:49
double InVal[3]
intracellular conductivity eigenvalues
Definition: fem_types.h:46
double BathVal[3]
bath conductivity eigenvalues
Definition: fem_types.h:48
physMat_t material_type
ID of physics material.
Definition: fem_types.h:38