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