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 
29 namespace opencarp {
30 
31 #define ELEC_STIFFNESS 1
32 #define ELEC_MASS 2
33 
34 inline int get_preferred_int_order(SF::elem_t & etype, int mat_type)
35 {
36  switch(mat_type) {
37  default:
38  case ELEC_MASS: return 2;
39 
40  case ELEC_STIFFNESS: {
41  switch(etype) {
42  default:
43  case SF::Quad:
44  case SF::Hexa:
45  case SF::Prism:
46  case SF::Pyramid:
47  return 2;
48 
49  case SF::Line:
50  case SF::Tetra:
51  case SF::Tri:
52  return 1;
53  }
54  }
55  }
56 }
57 
59  const int* reg,
60  const double vol,
61  const double* eVals,
62  const char* msg_start)
63 {
64  if(msg_start)
65  printf("\n%s", msg_start);
66 
67  if(reg)
68  printf("gregion[%d], ", *reg);
69 
70  printf("element %ld: ", (long int) elem.global_element_index(SF::NBR_ELEM_REF));
71 
72  for(int i=0; i<elem.num_nodes(); i++)
73  printf("%d ", elem.global_node(i, SF::NBR_REF));
74 
75  printf("\nVolume: %g", vol);
76 
77  if(eVals) {
78  SF::Point fib = elem.fiber(), she = elem.sheet();
79  printf(", fib: (%g, %g, %g), she: (%g, %g, %g), G: (%g, %g, %g)",
80  fib.x, fib.y, fib.z, she.x, she.y, she.z, eVals[0]*1e3, eVals[1]*1e3, eVals[2]*1e3);
81  }
82 
83  printf("\n\n");
84 }
85 
87  const bool is_bath,
88  double* evals)
89 {
90  if(is_bath == false) {
91  switch(emat.g) {
92  case intra_cond:
93  evals[0] = emat.InVal[0], evals[1] = emat.InVal[1], evals[2] = emat.InVal[2];
94  break;
95 
96  case extra_cond:
97  evals[0] = emat.ExVal[0], evals[1] = emat.ExVal[1], evals[2] = emat.ExVal[2];
98  break;
99 
100  case sum_cond:
101  evals[0] = emat.InVal[0] + emat.ExVal[0],
102  evals[1] = emat.InVal[1] + emat.ExVal[1],
103  evals[2] = emat.InVal[2] + emat.ExVal[2];
104  break;
105 
106  case para_cond:
107  evals[0] = (emat.InVal[0] * emat.ExVal[0]) / (emat.InVal[0] + emat.ExVal[0]),
108  evals[1] = (emat.InVal[1] * emat.ExVal[1]) / (emat.InVal[1] + emat.ExVal[1]),
109  evals[2] = (emat.InVal[2] * emat.ExVal[2]) / (emat.InVal[2] + emat.ExVal[2]);
110  break;
111  }
112  }
113  else
114  evals[0] = emat.BathVal[0], evals[1] = emat.BathVal[1], evals[2] = emat.BathVal[2];
115 }
116 
117 void get_conductivity(const double* evals,
118  const SF::Point *f,
119  const SF::Point *s,
120  SF::dmat<double> & cond)
121 {
122  cond.set_size(3,3);
123 
124  // longitudonal, transversal, and normal direction eigenvalues
125  double gl = evals[0], gt = evals[1], gn = evals[2];
126 
127  if(s == nullptr) {
128  // we compute the non-orthotropic conductivity:
129  // sigma = gl f f^T + gt (I - f f^T)
130  // = gt I + (gl - gt) f f^T
131  SF::outer_prod(*f, *f, gl - gt, cond[0]);
132  cond[0][0] += gt, cond[1][1] += gt, cond[2][2] += gt;
133  }
134  else {
135  // we compute the orthotropic conductivity:
136  // sigma = gl f f^T + gt s s^T + gn n n^T
137  SF::Point n = cross(*f, *s);
138 
139  SF::outer_prod(*f, *f, gl, cond[0], false);
140  SF::outer_prod(*s, *s, gt, cond[0], true);
141  SF::outer_prod( n, n, gn, cond[0], true);
142  }
143 }
144 
145 void print_points(SF::Point* pts, int npts)
146 {
147  for(int i=0; i<npts; i++)
148  printf(" ( %g %g %g ) \n", pts[i].x, pts[i].y, pts[i].z);
149 
150  printf("\n");
151 }
152 
153 //#define DEBUG_INT
154 
156 {
157  const size_t eidx = elem.element_index();
158  SF::elem_t type = elem.type();
159  mesh_int_t nnodes = elem.num_nodes();
160  const int int_order = get_preferred_int_order(type, ELEC_STIFFNESS);
161 
162  const int reg = material.regionIDs.size() ? material.regionIDs[eidx] : 0;
163  const RegionSpecs & reg_spec = material.regions[reg];
164 
165  assert(reg_spec.material->material_type == ElecMat);
166  const elecMaterial & mat = *static_cast<elecMaterial*>(reg_spec.material);
167  SF::Point fib = elem.fiber(), she = elem.sheet();
168 
169  bool is_bath = SF::inner_prod(fib, fib) < 0.01; // bath elements have no fiber direction
170  bool has_sheet = elem.has_sheet(); // do we have a sheet direciton
171 
172  double eVals[3];
173  get_conductivity_evals(mat, is_bath, eVals);
174 
175  // if we have a region scale
176  if(material.el_scale.size()) {
177  eVals[0] *= material.el_scale[eidx];
178  eVals[1] *= material.el_scale[eidx];
179  eVals[2] *= material.el_scale[eidx];
180  }
181 
182  if(is_bath) {
183  fib = {1, 0, 0};
184  she = {0, 1, 0};
185  }
186 
187  SF::get_transformed_pts(elem, lpts, fib);
188 
189 #ifdef DEBUG_INT
190  print_points(lpts, nnodes);
191 #endif
192 
193  get_conductivity(eVals, &fib, has_sheet ? &she : nullptr, cond);
194 
195  int ndof = nnodes;
196  shape.set_size(4, ndof), gshape.set_size(4, ndof);
197  buff.assign(ndof, ndof, 0.0);
198 
199  int nint = 0;
200  elem.integration_points(int_order, ipts, w, nint);
201 
202  double vol = 0.0;
203  bool have_nan = false;
204 
205  // Integration loop
206  for(int iidx=0; iidx < nint; iidx++)
207  {
208  double detj;
209  // get shape function and its spatial derivatives
210  SF::reference_shape(type, ipts[iidx], shape);
211  SF::jacobian_matrix(shape, nnodes, lpts, J);
212  SF::invert_jacobian_matrix(type, J, detj);
213 
214 #ifdef DEBUG_INT
215  SF::dmat<double> Jbuff(3,3);
216  memcpy(Jbuff.data(), J, 9*sizeof(double));
217  std::string j_name = "J_" + std::to_string(iidx);
218  Jbuff.disp(j_name.c_str());
219 #endif
220 
221  SF::shape_deriv(J, shape, ndof, gshape);
222 
223  double dx = w[iidx] * detj;
224  vol += dx;
225 
226  for (int k = 0; k < ndof; k++) {
227  // derivatives of shape function k
228  double skx = gshape[1][k], sky = gshape[2][k], skz = gshape[3][k];
229  double cx = cond[0][0] * skx + cond[0][1] * sky + cond[0][2] * skz;
230  double cy = cond[1][0] * skx + cond[1][1] * sky + cond[1][2] * skz;
231  double cz = cond[2][0] * skx + cond[2][1] * sky + cond[2][2] * skz;
232 
233  for (int l = 0; l < ndof; l++) {
234  // derivatives of shape function l
235  double slx = gshape[1][l], sly = gshape[2][l], slz = gshape[3][l];
236  double val = - (cx*slx + cy * sly + cz * slz) * dx;
237  buff[k][l] += val;
238 
239  if(val != val) have_nan = true;
240  }
241  }
242  }
243 
244 #ifdef DEBUG_INT
245  printf("\n vol: %g \n\n", vol);
246  buff.disp("Element Matrix:");
247 #endif
248 
249  if(have_nan) {
250  print_element_info(elem, &reg, vol, eVals, "NaN detected: ");
251  exit(EXIT_FAILURE);
252  }
253 }
254 
256 {
257  row_dpn = 1;
258  col_dpn = 1;
259 }
260 
261 
263 {
264  SF::elem_t type = elem.type();
265  mesh_int_t nnodes = elem.num_nodes();
266  const int int_order = get_preferred_int_order(type, ELEC_MASS);
267 
268  int ndof = nnodes;
269  shape.set_size(4, ndof);
270  buff.assign(ndof, ndof, 0.0);
271 
272  SF::Point fib = {1, 0, 0}; // this is a dummy fibre to satisfy get_transformed_pts()
273 
274  double vol = 0.0;
275  bool have_nan = false;
276  int nint;
277  elem.integration_points(int_order, ipts, w, nint);
278  SF::get_transformed_pts(elem, lpts, fib);
279 
280  // Integration loop
281  for(int iidx=0; iidx < nint; iidx++)
282  {
283  double detj;
284  // get shape function and its spatial derivatives
285  SF::reference_shape(type, ipts[iidx], shape);
286  SF::jacobian_matrix(shape, nnodes, lpts, J);
287  SF::invert_jacobian_matrix(type, J, detj);
288 
289  double dx = w[iidx] * detj;
290  vol += dx;
291 
292  for (int k = 0; k < ndof; k++) {
293  double sk = shape[0][k];
294  for (int l = 0; l < ndof; l++) {
295  double sl = shape[0][l];
296  double val = sk * sl * dx;
297  buff[k][l] += val;
298 
299  if(val != val) have_nan = true;
300  }
301  }
302  }
303 
304  if(have_nan) {
305  print_element_info(elem, NULL, vol, NULL, "NaN detected: ");
306  exit(EXIT_FAILURE);
307  }
308 }
309 
310 void mass_integrator::dpn(mesh_int_t & row_dpn, mesh_int_t & col_dpn)
311 {
312  row_dpn = 1;
313  col_dpn = 1;
314 }
315 
316 } // namespace opencarp
317 
void dpn(mesh_int_t &row_dpn, mesh_int_t &col_dpn)
return (by reference) the row and column dimensions
cond_t g
rule to build conductivity tensor
Definition: fem_types.h:64
double z
Definition: SF_container.h:68
void get_conductivity(const double *evals, const SF::Point *f, const SF::Point *s, SF::dmat< double > &cond)
void get_transformed_pts(const element_view< T, S > &view, Point *loc_pts, Point &trsf_fibre)
physMaterial * material
material parameter description
Definition: fem_types.h:98
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
physMat_t material_type
ID of physics material.
Definition: fem_types.h:53
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)
Comfort class. Provides getter functions to access the mesh member variables more comfortably...
Definition: SF_fem_utils.h:704
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 set_size(const short irows, const short icols)
set the matrix dimensions
Definition: dense_mat.hpp:73
double ExVal[3]
extracellular conductivity eigenvalues
Definition: fem_types.h:62
#define ELEC_STIFFNESS
The nodal numbering of the reference mesh (the one stored on HD).
Definition: SF_container.h:189
size_t element_index() const
Get currently selected element index.
Definition: SF_fem_utils.h:883
double inner_prod(const Point &a, const Point &b)
Definition: SF_container.h:90
void get_conductivity_evals(const elecMaterial &emat, const bool is_bath, double *evals)
void assign(const dmat< S > &m)
copy a mtrix.
Definition: dense_mat.hpp:135
double InVal[3]
intracellular conductivity eigenvalues
Definition: fem_types.h:61
Point and vector struct.
Definition: SF_container.h:65
SF::vector< int > regionIDs
elemental region IDs (multiple tags are grouped in one region)
Definition: fem_types.h:114
int get_preferred_int_order(SF::elem_t &etype, int mat_type)
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
Point sheet() const
Get element sheet direction.
Definition: SF_fem_utils.h:860
Point fiber() const
Get element fiber direction.
Definition: SF_fem_utils.h:851
elem_t type() const
Getter function for the element type.
Definition: SF_fem_utils.h:772
vec3< V > cross(const vec3< V > &a, const vec3< V > &b)
Definition: vect.h:144
int mesh_int_t
Definition: SF_container.h:46
The element numbering of the reference mesh (the one stored on HD).
Definition: SF_container.h:192
S * data()
Definition: dense_mat.hpp:264
void print_points(SF::Point *pts, int npts)
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.
region based variations of arbitrary material parameters
Definition: fem_types.h:93
double BathVal[3]
bath conductivity eigenvalues
Definition: fem_types.h:63
void invert_jacobian_matrix(const elem_t type, double *J, double &detJ)
Definition: SF_fem_utils.h:633
void disp(const char *name)
Definition: dense_mat.hpp:254
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.
SF::vector< double > el_scale
optionally provided per-element params scale
Definition: fem_types.h:116
T num_nodes() const
Getter function for the number of nodes.
Definition: SF_fem_utils.h:762
void integration_points(const short order, Point *ip, double *w, int &nint) const
Definition: SF_fem_utils.h:915
elem_t
element type enum
Definition: SF_container.h:52
void dpn(mesh_int_t &row_dpn, mesh_int_t &col_dpn)
return (by reference) the row and column dimensions
bool has_sheet() const
Check if a sheet direction is present.
Definition: SF_fem_utils.h:873
void outer_prod(const Point &a, const Point &b, const double s, double *buff, const bool add=false)
Definition: SF_container.h:95
size_t size() const
The current size of the vector.
Definition: SF_vector.h:104
const T & global_node(short nidx) const
Access the connectivity information.
Definition: SF_fem_utils.h:806
double x
Definition: SF_container.h:66
double y
Definition: SF_container.h:67
#define ELEC_MASS
SF::vector< RegionSpecs > regions
array with region params
Definition: fem_types.h:115
size_t global_element_index() const
Get currently selected element index.
Definition: SF_fem_utils.h:893