openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
stimulate.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 <typeinfo> //only needed to be used for typeid
28 #include "stimulate.h"
29 #include "electrics.h"
30 
31 #include "SF_init.h" // for SF::init_xxx()
32 #include "petsc_utils.h" // TODO for EXIT
33 #include "petscviewer.h" //nur für MatView
34 #include "petscmat.h"
35 
36 namespace opencarp {
37 
38 // stimulus convenience functions
39 // group stimulus types
40 namespace stim_info {
41  std::set<int> voltage_stim {V_ex, GND_ex, V_ex_ol};
42  std::set<int> current_stim {I_tm, I_ex, I_in, I_lat};
43  std::set<int> dbc_stim {V_ex, GND_ex, V_ex_ol};
44  std::map<int,std::string> units;
45 } // namespace stim_info
46 
47 namespace user_globals {
48 
49  extern timer_manager* tm_manager;
50 } // namespace user_globals
51 
52 void init_stim_info(void)
53 {
54  // start dealing with units, first try
55  stim_info::units[I_tm] = "uA/cm^2";
56  stim_info::units[I_ex] = "uA/cm^3";
57  stim_info::units[V_ex] = "mV";
58  stim_info::units[GND_ex] = "mV";
59  stim_info::units[I_in] = "uA/cm^3";
60  stim_info::units[V_ex_ol] = "mV";
62  stim_info::units[I_lat] = "uA/cm^2";
63  stim_info::units[Vm_clmp] = "mV";
64  stim_info::units[Phie_pre] = "mV";
65 }
66 
67 
68 bool is_voltage(stim_t type)
69 {
70  return stim_info::voltage_stim.count(type);
71 }
72 
74 bool is_current(stim_t type)
75 {
76  return stim_info::current_stim.count(type);
77 }
78 
79 bool is_dbc(stim_t type)
80 {
81  return stim_info::dbc_stim.count(type);
82 }
83 
84 bool is_extra(stim_t type)
85 {
86  switch(type) {
87  default:
88  case I_tm:
89  case Vm_clmp:
90  return false;
91 
92  case I_ex:
93  case V_ex:
94  case V_ex_ol:
95  case GND_ex:
96  return true;
97  }
98 }
99 
100 
103 void stimulus::translate(int iidx)
104 {
105  idx = iidx;
106 
107  // translate protocol
108  Stim & s = param_globals::stim[idx];
109  Stimulus & curstim = param_globals::stimulus[idx];
110 
111  s.ptcl.bcl = curstim.bcl;
112  s.ptcl.npls = curstim.npls;
113  s.ptcl.start = curstim.start;
114  s.ptcl.duration = curstim.duration;
115 
116  // translate pulse
117  // create a name, old stimulus structure lacks this feature, no naming of pulse waveforms
118  std::string pulse_name;
119  pulse_name = "pulse_" + std::to_string(idx);
120  s.pulse.name = (char*) calloc(pulse_name.length()+1, sizeof(char));
121  strcpy(s.pulse.name, pulse_name.c_str());
122 
123  // old stimulus treats any pulse as truncated exponential shape
124  s.pulse.shape = truncExpPulse;
125  s.pulse.file = strdup(curstim.pulse_file);
126  s.pulse.bias = curstim.bias;
127  s.pulse.tau_plateau = curstim.tau_plateau;
128  s.pulse.tau_edge = curstim.tau_edge;
129  s.pulse.s2 = curstim.s2;
130  // transition to new tilt-based shape defintion
131  s.pulse.tilt_ampl = curstim.s2;
132  s.pulse.tilt_time = curstim.d1;
133  s.pulse.strength = curstim.strength;
134 
135  // strength is not a scalar, we prescribe a different strength at every node
136  s.elec.vtx_fcn = curstim.vtx_fcn;
137 
138  // translate circuit
139  s.crct.type = curstim.stimtype;
140  s.crct.balance = curstim.balance;
141  s.crct.total_current = curstim.total_current;
142 
143  // translate electrode
144  s.elec.geomID = curstim.geometry;
145  // s.elec.domain = curstim.domain;
146  s.elec.vtx_file = strdup(curstim.vtx_file);
147  s.elec.dump_vtx_file = curstim.dump_vtx_file;
148 
149  // check whether we have a sound electrode geometry definition
150  s.elec.geom_type = 2; // should be region_t block;
151  s.elec.p0[0] = curstim.x0 - (curstim.ctr_def?curstim.xd/2.:0.);
152  s.elec.p0[1] = curstim.y0 - (curstim.ctr_def?curstim.yd/2.:0.);
153  s.elec.p0[2] = curstim.z0 - (curstim.ctr_def?curstim.zd/2.:0.);
154  s.elec.p1[0] = s.elec.p0[0] + curstim.xd;
155  s.elec.p1[1] = s.elec.p0[1] + curstim.yd;
156  s.elec.p1[2] = s.elec.p0[2] + curstim.zd;
157 
158  // cp overall electrode name
159  s.name = strdup(curstim.name);
160 }
161 
166 void stimulus::setup(int iidx)
167 {
168  idx = iidx;
169  const Stim & cur_stim = param_globals::stim[idx];
170 
171  // label stimulus
172  if(cur_stim.name != std::string(""))
173  name = cur_stim.name;
174  else
175  name = "Stimulus_" + std::to_string(idx);
176 
177  // set up stimulus pulse
178  pulse.setup(idx);
179 
180  // set up stimulation protocol
181  ptcl.setup(idx,name);
182 
183  // set up physics of applied stimulus
184  phys.setup(idx);
185 
186  // set up the spatial region of the stimulus
188 }
189 
191 {
192  // extracellular potential stimuli are always active
193  if(this->phys.type == V_ex)
194  return true;
195 
197 }
198 
199 
202 void stim_pulse::setup(int idx)
203 {
204  const Stim & cur_stim = param_globals::stim[idx];
205  const Pulse & pulse = cur_stim.pulse;
206 
207  // assign waveform type and parameters
208  waveform_t wvt = truncExpPulse; // default, we may expose this in prm as a choice
209  if(strcmp(pulse.file,""))
210  wvt = arbPulse;
211 
212  assign(pulse.strength, cur_stim.ptcl.duration, param_globals::dt, wvt);
213 
214  // sample pulse shape
215  if(wvt == arbPulse) {
216  sig::time_trace file_wave;
217  int _err = 0;
218 
219  if(!get_rank()) {
220  bool unitize = true;
221 
222  update_cwd();
223  set_dir(INPUT);
224  _err = file_wave.read_trace(pulse.file, unitize);
225  set_dir(CURDIR);
226  }
227 
228  int err = get_global(_err, MPI_MIN);
229  if(err) {
231  "Failed reading pulse for stimulus[%d] from file %s.\n", idx, pulse.file);
232  }
233  else {
234  get_global(file_wave.f);
235  get_global(file_wave.t);
236 
237  file_wave.resample(this->wave);
238 
239  // check length of read in pulse and adjust stimulus settings accordingly
240  if(wave.duration() != duration)
241  duration = wave.duration();
242  }
243  }
244  else {
245  sample_wave_form(*this, idx);
246  }
247 
248  // add unit labels
249  //std::string sUnit = IsVoltageStim_(cur_stim.crct) ? "mV" : "uA/cm^3";
250  //std::string sUnit = is_voltage((stim_t)cur_stim.crct.type) ? "mV" : "uA/cm^3";
251  std::string sUnit = stim_info::units[(stim_t)cur_stim.crct.type];
252  std::string tUnit = "ms";
253  wave.setUnits(tUnit,sUnit);
254 }
255 
256 
257 void get_stim_list(const char* str_list, std::vector<double> & stlist)
258 {
259  std::string tstim = str_list;
260  std::vector<std::string> stims;
261  split_string(tstim, ',', stims);
262 
263  stlist.resize(stims.size());
264  size_t widx = 0, err = 0;
265 
266  for(size_t i=0; i<stims.size(); i++) {
267  std::string & stim = stims[i];
268 
269  if(stim.size()) {
270  if(stim[0] == '+') {
271  double last = widx == 0 ? 0.0 : stlist[widx-1];
272  stim.erase(0, 1); // erase first char
273 
274  // we user sscanf so that we can actually determine if the string was converted
275  // into a double. atof does not allow for error-checking
276  double cur = -1.0;
277  size_t nread = sscanf(stim.c_str(), "%lf", &cur);
278  if(nread)
279  stlist[widx++] = cur + last;
280  } else {
281  double cur = -1.0;
282  size_t nread = sscanf(stim.c_str(), "%lf", &cur);
283  if(nread)
284  stlist[widx++] = cur;
285  }
286  }
287  }
288 
289  stlist.resize(widx);
290 
291  if(widx != stims.size())
292  log_msg(0, 4, 0, "%s warning: Some values of %s could not be converted in stim times!",
293  __func__, str_list);
294 }
295 
296 
299 void stim_protocol::setup(int idx, std::string name)
300 {
301  const Stim & cur_stim = param_globals::stim[idx];
302  const Protocol & ptcl = cur_stim.ptcl;
303 
304  if(strlen(ptcl.stimlist) == 0) {
305  start = ptcl.start, npls = ptcl.npls, pcl = ptcl.bcl;
306 
307  // add timer
309  param_globals::tend, npls, pcl, ptcl.duration, name.c_str());
310  } else {
311  std::vector<double> stims;
312  get_stim_list(ptcl.stimlist, stims);
313 
314  log_msg(0,0,ECHO | NONL, "stim %d: stimulating at times: ", idx);
315  for(double t : stims)
316  log_msg(0,0, ECHO | NONL, "%.2lf ", t);
317  log_msg(0,0,0, "");
318  log_msg(0,0,0, "stim %d: .bcl and .npls values will be ignored.", idx);
319 
320  timer_id = user_globals::tm_manager->add_neq_timer(stims, ptcl.duration, name.c_str());
321  }
322 }
323 
324 
327 void stim_physics::setup(int idx)
328 {
329  const Stim & cur_stim = param_globals::stim[idx];
330  type = stim_t(cur_stim.crct.type);
331  domain = stim_domain_t(cur_stim.elec.domain);
332  unit = stim_info::units[cur_stim.crct.type];
333  total_current = cur_stim.crct.total_current;
334 
335  const short dim = is_extra(stim_t(cur_stim.crct.type)) ? get_mesh_dim(extra_elec_msh) : get_mesh_dim(intra_elec_msh);
336 
337  switch(type) {
338  case I_tm:
339  scale = param_globals::operator_splitting ? user_globals::tm_manager->time_step : 1.0;
340  break;
341 
342  case I_ex:
343  scale = UM2_to_CM2 * (dim == 2 ? 1.0 : UM_to_CM);
344  break;
345 
346  default: scale = 1.0; break;
347  }
348 }
349 
350 
358 void sample_wave_form(stim_pulse& sp, int idx)
359 {
360  const Stimulus & curstim = param_globals::stimulus[idx];
361 
362  if(sp.wform == truncExpPulse)
363  {
364  // monophasic pulse, first phase lasts for entire pulse duration
365  sig::Pulse truncExp;
366  truncExp.duration = sp.duration;
367  truncExp.d1 = curstim.d1 * sp.duration; // relative to absolute duration of d1
368  truncExp.tau_edge = curstim.tau_edge;
369  truncExp.tau_plat = curstim.tau_plateau;
370  truncExp.decay = 5.;
371 
372  // from relative duration of d1 we infer mono- or biphasic
373  if(curstim.d1 == 1.) {
374  // monophasic pulse, first phase lasts for entire pulse duration
375  sig::monophasicTruncExpFunc fnc(truncExp);
376  fnc.sample(sp.wave);
377  }
378  else
379  {
380  // biphasic pulse, second phase lasts shorter than pulse duration
381  sig::biphasicTruncExpFunc fnc(truncExp);
382  fnc.sample(sp.wave);
383  }
384  }
385  else if(sp.wform == constPulse)
386  {
387  sig::constFunc cnst_fnc(1);
388  cnst_fnc.sample(sp.wave);
389  }
390  else
391  assert(0);
392 
393  /*
394  else if(sp.wform == squarePulse)
395  {
396  // generate step function using derived class
397  Step stepPars;
398  stepPars.trig = 0.25;
399  stepPars.rise = true;
400 
401  stepFunc step_func(stepPars);
402  s.set_labels("step_func");
403  step_func.sample(s);
404  s.write_trace();
405  }
406  else if(sp.wform == sinePulse)
407  {
408  // generate a sine wave
409  sineWave sinePars;
410  sinePars.frq = 1.;
411  sinePars.phase = 0.;
412  sineFunc sine(sinePars);
413  s.set_labels("sine_func");
414  sine.sample(s);
415  s.write_trace();
416  }
417  else if(sp.wform == APfootPulse)
418  {
419  // generate AP foot
420  APfoot footPars;
421  footPars.tau_f = s.duration()/5.; // use 5 time constants
422  APfootFunc APft(footPars);
423  s.set_labels("APfoot");
424  APft.sample(s);
425  s.write_trace();
426  }
427  else if(sp.wform == arbPulse)
428  {
429  }
430  else
431  log_msg(user_globals:: error);
432  */
433 }
434 
435 bool stimulus::value(double & v) const
436 {
437  if(user_globals::tm_manager->trigger(ptcl.timer_id) == false) {
438  // extracellular potential stimuli are always active, but return 0 when their
439  // protocol is not active. Use V_ex_ol for extracellular potential stimuli that
440  // get removed when they expire.
441  if(this->phys.type == V_ex || this->phys.type == GND_ex) {
442  v = 0.0;
443  return true;
444  }
445 
446  return false;
447  }
448  else {
450  v = pulse.strength * phys.scale * pulse.wave.f[i];
451  return true;
452  }
453 }
454 
456 {
457  const Stim & curstim = param_globals::stim[idx];
458  mesh_t grid;
459 
460  // based on the stimulus domain type we select grid and physics
461  switch(curstim.crct.type) {
462  case I_ex:
463  case V_ex:
464  case V_ex_ol:
465  case GND_ex:
466  grid = extra_elec_msh; break;
467 
468  case Illum:
469  case Vm_clmp:
470  case I_tm:
471  grid = intra_elec_msh; break;
472 
473  case Ignore_stim: return;
474 
475  default:
476  log_msg(0,5,0, "stim_electrode::setup error: Can't determine domain from stim type! Aborting!", __func__);
477  EXIT(1);
478  }
479 
480  // get a logger from the physics
482 
483  // this are the criteria for choosing how we extract the stimulus electrode
484  bool vertex_file_given = strlen(curstim.elec.vtx_file) > 0;
485  bool tag_index_given = curstim.elec.geomID > -1;
486  // the mesh we need for computing the local vertex indices.
487  const sf_mesh & mesh = get_mesh(grid);
488 
489  if(vertex_file_given && tag_index_given)
490  log_msg(0,3,0, "%s warning: More than one stimulus electrode definintions set in electrode %d", __func__, idx);
491 
492  if(vertex_file_given) {
493  definition = def_t::file_based;
494  input_filename = curstim.elec.vtx_file;
495 
496  log_msg(logger, 0, 0, "Stimulus %d: Selecting vertices from file %s", idx, input_filename.c_str());
497 
498  set_dir(INPUT);
500 
501  // we read the indices. they are being localized w.r.t. the provided numbering. In our
502  // case this is always the reference numbering.
503  if(curstim.elec.vtx_fcn)
504  read_indices_with_data(vertices, scaling, input_filename, mesh, SF::NBR_REF, true, 1, PETSC_COMM_WORLD);
505  else
506  read_indices(vertices, input_filename, mesh, SF::NBR_REF, true, PETSC_COMM_WORLD);
507 
508  int gnum_idx = get_global(vertices.size(), MPI_SUM);
509  if(gnum_idx == 0) {
510  log_msg(0, 5, 0, "Stimulus %d: Specified vertices are not in stimulus domain! Aborting!", idx);
511  EXIT(1);
512  }
513  }
514  else if(tag_index_given) {
515  definition = def_t::vol_based_tag;
516 
517  int tag = curstim.elec.geomID;
518  log_msg(logger, 0, 0, "Stimulus %d: Selecting vertices from tag %d", idx, tag);
519 
520  indices_from_region_tag(vertices, mesh, tag);
521  // we restrict the indices to the algebraic subset
522  SF::restrict_to_set(vertices, mesh.pl.algebraic_nodes());
523  }
524  else {
525  definition = def_t::vol_based_shape;
526  log_msg(logger, 0, 0, "Stimulus %d: Selecting vertices from shape.", idx);
527 
528  geom_shape shape;
529  shape.type = geom_shape::shape_t(curstim.elec.geom_type);
530  shape.p0 = curstim.elec.p0;
531  shape.p1 = curstim.elec.p1;
532  shape.radius = curstim.elec.radius;
533 
534  bool nodal = true;
535  indices_from_geom_shape(vertices, mesh, shape, nodal);
536  // we restrict the indices to the algebraic subset
537  SF::restrict_to_set(vertices, mesh.pl.algebraic_nodes());
538 
539  int gsize = vertices.size();
540  if(get_global(gsize, MPI_SUM) == 0) {
541  log_msg(0,5,0, "error: Empty stimulus[%d] electrode def! Aborting!", idx);
542  EXIT(1);
543  }
544  }
545 
546  if(curstim.elec.dump_vtx_file) {
548  mesh.pl.globalize(glob_idx);
549 
550  SF::vector<mesh_int_t> srt_idx;
551  SF::sort_parallel(mesh.comm, glob_idx, srt_idx);
552 
553  size_t num_vtx = get_global(srt_idx.size(), MPI_SUM);
554  int rank = get_rank();
555 
556  FILE_SPEC f = NULL;
557  int err = 0;
558 
559  if(rank == 0) {
560  char dump_name[1024];
561 
562  if (strlen(curstim.name)) {
563  snprintf(dump_name, sizeof dump_name, "%s.vtx", curstim.name);
564  } else {
565  snprintf(dump_name, sizeof dump_name, "ELECTRODE_%d.vtx", idx);
566  }
567 
568  f = f_open(dump_name, "w");
569  if(!f) err++;
570  else {
571  fprintf(f->fd, "%zd\nextra\n", num_vtx);
572  }
573  }
574 
575  if(!get_global(err, MPI_SUM)) {
576  print_vector(mesh.comm, srt_idx, 1, f ? f->fd : NULL);
577  } else {
578  log_msg(0, 4, 0, "error: stimulus[%d] cannot be dumped!");
579  }
580 
581  // only root really does that
582  f_close(f);
583  }
584 }
585 
587 {
588  clear_active_dbc();
589 
590  for(const stimulus & s : stimuli) {
591  if(is_dbc(s.phys.type) && s.is_active()) {
592 
593  // the local indices of the dbc
594  const SF::vector<mesh_int_t> & dbc_idx = s.electrode.vertices;
595 
596  dbc_data* dbc_buff = new dbc_data();
597 
598  // the global petsc indices of the dbc
599  dbc_buff->nod = new SF::vector<SF_int>(dbc_idx.size());
600 
602 
603  size_t widx = 0;
604  for(size_t i=0; i<dbc_idx.size(); i++)
605  (*dbc_buff->nod)[widx++] = petsc_nbr[dbc_idx[i]];
606 
608  SF::init_vector(&dbc_buff->cntr, *mat.mesh_ptr(), 1, sf_vec::algebraic);
609 
610  if(s.electrode.scaling.size()) {
611  // we have spatially heterogenous dirichlet values
612  dbc->set(*dbc_buff->nod, s.electrode.scaling);
613  } else {
614  // we have spatially homogenous dirichlet values
615  dbc->set(*dbc_buff->nod, 1.0);
616  }
617 // if (typeid(mat).name == MaterialType): // only for output
618 // MatView(mat, PETSC_VIEWER_STDOUT_WORLD);
619  //std::exit(0);
620  std::string outName0 = "output_dbc.mat";
621 // log_msg(0,0,0, "OutName0: %s", outName0.c_str());
622  dbc->write_ascii(outName0.c_str(), 0);
623 
624  char outName1[128];
625  snprintf(outName1, sizeof(outName1), "outputMatrix1_stim%d", s.idx);
626 // log_msg(0, 0, 0, "OutName1: %s", outName1);
627  mat.write(outName1);
628 
629 // std::string outName1 = "outputMatrix1.mat";
630 // log_msg(0, 0, 0, "OutName1: %s", outName1.c_str());
631 // mat.write(outName1.c_str());
632 
633  mat.mult(*dbc, *dbc_buff->cntr);
634 
635  char outName2[128];
636  snprintf(outName2, sizeof(outName2), "outputMatrix2_stim%d", s.idx);
637 // log_msg(0, 0, 0, "OutName2: %s", outName2);
638  mat.write(outName2);
639 
640 // std::string outName2 = "outputMatrix2.mat";
641 // log_msg(0, 0, 0, "OutName2: %s", outName2.c_str());
642 // mat.write(outName2.c_str());
643 
644  //MatView((dynamic_cast<SF::petsc_matrix<int, double>>(mat)).data, PETSC_VIEWER_STDOUT_WORLD);
645 // if (typeid(mat) == MaterialType): // only for output
646 // MatView(mat, PETSC_STDOUT_WORLD);
647 
648  // PetscErrorCode MatView(mat, PETSC_VIEWER_STDOUT_SELF);
649 
650  active_dbc[s.idx] = dbc_buff;
651  }
652  }
653 }
654 
656 {
657  // check if stim has come offline
658  for(const auto & d : active_dbc) {
659  if(stimuli[d.first].is_active() == false)
660  return true;
661  }
662 
663  // check if stim has come online
664  for(const stimulus & s : stimuli) {
665  if(is_dbc(s.phys.type) && s.is_active() && active_dbc.count(s.idx) == 0)
666  return true;
667  }
668 
669  return false;
670 }
671 
673 {
675 
676  for(auto it = active_dbc.begin(); it != active_dbc.end(); ++it) {
677  const SF::vector<SF_int> & dbc_nod = *it->second->nod;
678  // zero cols and rows in lhs mat
679  dbc->set(1.0);
680  dbc->set(dbc_nod, 0.0);
681  // multiplicate dbc vector from left and right to matrix in order to zero cols and rows
682 // std::string outNameelhs1 = "outputMatrixelhs1.mat";
683  char outNameelhs1[128];
684  snprintf(outNameelhs1, sizeof(outNameelhs1), "outputMatrixelhs1_stim%d", it->first);
685 // log_msg(0, 0, 0, "OutName1: %s", outNameelhs1);
686  mat.write(outNameelhs1);
687 // log_msg(0, 0, 0, "OutName1: %s", outNameelhs1.c_str());
688 // mat.write(outNameelhs1.c_str());
689  mat.mult_LR(*dbc, *dbc);
690 
691  char outNameelhs2[128];
692  snprintf(outNameelhs2, sizeof(outNameelhs2), "outputMatrixelhs2_stim%d", it->first);
693 // log_msg(0, 0, 0, "OutName2: %s", outNameelhs2);
694  mat.write(outNameelhs2);
695 
696 
697 // std::string outNameelhs2 = "outputMatrixelhs2.mat";
698 // log_msg(0, 0, 0, "OutName2: %s", outNameelhs2.c_str());
699 // mat.write(outNameelhs2.c_str());
700  // now add 1.0 to the diagonal at the dirichlet locations
701  dbc->set(0.0);
702  dbc->set(dbc_nod, 1.0);
703  mat.diag_add(*dbc);
704 
705  char outNameelhs3[128];
706  snprintf(outNameelhs3, sizeof(outNameelhs3), "outputMatrixelhs3_stim%d", it->first);
707 // log_msg(0, 0, 0, "OutName3: %s", outNameelhs3);
708  mat.write(outNameelhs3);
709 // std::string outNameelhs3 = "outputMatrixelhs3.mat";
710 // log_msg(0, 0, 0, "OutName3: %s", outNameelhs3.c_str());
711 // mat.write(outNameelhs3.c_str());
712 
713  }
714 }
715 
717 {
718  PetscReal *c = rhs.ptr();
719  for(auto it = active_dbc.begin(); it != active_dbc.end(); ++it) {
720  const int & dbc_idx = it->first;
721  const dbc_manager::dbc_data & dbc = *(it->second);
722  double strength = 0.0;
723  bool is_active = stimuli[dbc_idx].value(strength);
724 
725  // the whole idea of the dbc_manager and its active_dbc is
726  // based on only storing the active dbcs. if the stimulus associated
727  // to a dbc believed active is actually not active, we want to abort.
728  assert(is_active);
729 
730  rhs.add_scaled(*dbc.cntr, -strength);
731 
732  if(stimuli[dbc_idx].electrode.scaling.size()) {
733  SF::vector<SF_real> scaled_strength(stimuli[dbc_idx].electrode.scaling.size());
734 
735  size_t widx = 0;
736  for(SF_real s : stimuli[dbc_idx].electrode.scaling)
737  scaled_strength[widx++] = s * strength;
738 
739  rhs.set(*dbc.nod, scaled_strength);
740  } else {
741  rhs.set(*dbc.nod, strength);
742  }
743  }
744 }
745 
746 } // namespace opencarp
747 
double SF_real
Use the general double as real type.
Definition: SF_globals.h:38
#define MAX_LOG_LEVEL
Definition: basics.h:315
#define ECHO
Definition: basics.h:308
#define NONL
Definition: basics.h:312
const meshdata< mesh_int_t, mesh_real_t > * mesh_ptr() const
virtual void mult(const abstract_vector< T, S > &x, abstract_vector< T, S > &b) const =0
virtual void mult_LR(const abstract_vector< T, S > &L, const abstract_vector< T, S > &R)=0
virtual void write(const char *filename) const =0
virtual void diag_add(const abstract_vector< T, S > &diag)=0
virtual S * ptr()=0
size_t write_ascii(const char *file, bool write_header)
virtual void set(const vector< T > &idx, const vector< S > &vals, const bool additive=false)=0
virtual void add_scaled(const abstract_vector< T, S > &vec, S k)=0
overlapping_layout< T > pl
nodal parallel layout
Definition: SF_container.h:419
MPI_Comm comm
the parallel mesh is defined on a MPI world
Definition: SF_container.h:393
vector< T > & get_numbering(SF_nbr nbr_type)
Get the vector defining a certain numbering.
Definition: SF_container.h:454
size_t size() const
The current size of the vector.
Definition: SF_vector.h:104
FILE_SPEC logger
The logger of the physic, each physic should have one.
Definition: physics_types.h:64
std::map< int, dbc_data * > active_dbc
the DBCs that are currently active
Definition: stimulate.h:217
sf_mat & mat
the matrix we link the dbc_manager to
Definition: stimulate.h:213
void enforce_dbc_rhs(sf_vec &rhs)
Definition: stimulate.cc:716
void recompute_dbcs()
recompute the dbc data.
Definition: stimulate.cc:586
const SF::vector< stimulus > & stimuli
the stimuli we link the dbc_manager to
Definition: stimulate.h:215
bool dbc_update()
check if dbcs have updated
Definition: stimulate.cc:655
class to store shape definitions
Definition: basics.h:381
sReal duration
pulse duration, default is 1 ms
Definition: signals.h:821
float tau_plat
time constant governing plateau of pulse
Definition: signals.h:825
float tau_edge
time constant for leading/trailing edges
Definition: signals.h:824
int decay
edge decay time (multiples of tau_edge)
Definition: signals.h:826
double d1
duration of first sub-pulse in [ms] (zero with monophasic pulse)
Definition: signals.h:823
biphasic truncated exponentials (capacitive discharge)
Definition: signals.h:1061
virtual SF::vector< sReal > & sample(time_trace &trc)
Definition: signals.h:1068
constant function
Definition: signals.h:912
virtual SF::vector< sReal > & sample(time_trace &trc)
Definition: signals.h:919
monophasic truncated exponentials (capacitive discharge)
Definition: signals.h:995
virtual SF::vector< sReal > & sample(time_trace &trc)
Definition: signals.h:1002
Time tracing class.
Definition: signals.h:142
SF::vector< sReal > t
time axis
Definition: signals.h:148
void resample(time_trace &trc)
Definition: signals.h:434
SF::vector< sReal > f
store function values of trace
Definition: signals.h:147
void setUnits(std::string _t_unit, std::string _f_unit)
Definition: signals.h:245
int read_trace(const std::string fname)
determine duration of a signal stored in file
Definition: signals.h:623
SF::vector< mesh_int_t > vertices
Definition: stimulate.h:158
std::string input_filename
Definition: stimulate.h:160
SF::vector< SF_real > scaling
Definition: stimulate.h:159
void setup(int idx)
assign stimulus physics parameters
Definition: stimulate.cc:327
double scale
internal unit conversion scaling
Definition: stimulate.h:145
bool total_current
whether we apply total current scaling
Definition: stimulate.h:146
stim_t type
type of stimulus
Definition: stimulate.h:142
std::string unit
physical units of stimulus
Definition: stimulate.h:144
stim_domain_t domain
applied in intra- or extracellular space
Definition: stimulate.h:143
int timer_id
timer for stimulus
Definition: stimulate.h:127
int npls
number of stimulus pulses
Definition: stimulate.h:125
double pcl
pacing cycle length
Definition: stimulate.h:126
double start
start time of protocol
Definition: stimulate.h:124
void setup(int idx, std::string name)
Setup from a param stimulus index.
Definition: stimulate.cc:299
define the wave form of a stimulation pulse
Definition: stimulate.h:96
sig::time_trace wave
wave form of stimulus pulse
Definition: stimulate.h:102
void assign(double _strength, double _duration, double _dt, waveform_t _wform)
Definition: stimulate.h:104
void setup(int id)
Setup from a param stimulus index.
Definition: stimulate.cc:202
double duration
duration of stimulus
Definition: stimulate.h:99
waveform_t wform
wave form of stimulus
Definition: stimulate.h:100
double strength
strength of stimulus
Definition: stimulate.h:98
stim_protocol ptcl
applied stimulation protocol used
Definition: stimulate.h:172
int idx
index in global input stimulus array
Definition: stimulate.h:169
stim_electrode electrode
electrode geometry
Definition: stimulate.h:174
stim_pulse pulse
stimulus wave form
Definition: stimulate.h:171
void translate(int id)
convert legacy definitions to new format
Definition: stimulate.cc:103
bool is_active() const
Return whether stim is active.
Definition: stimulate.cc:190
void setup(int idx)
Setup from a param stimulus index.
Definition: stimulate.cc:166
stim_physics phys
physics of stimulus
Definition: stimulate.h:173
bool value(double &v) const
Get the current value if the stimulus is active.
Definition: stimulate.cc:435
std::string name
label stimulus
Definition: stimulate.h:170
centralize time managment and output triggering
Definition: timer_utils.h:73
bool trigger(int ID) const
Definition: timer_utils.h:166
double time_step
global reference time step
Definition: timer_utils.h:78
int add_neq_timer(const std::vector< double > &itrig, double idur, const char *iname, const char *poolname=nullptr)
Definition: timer_utils.cc:92
int add_eq_timer(double istart, double iend, int ntrig, double iintv, double idur, const char *iname, const char *poolname=nullptr)
Add a equidistant step timer to the array of timers.
Definition: timer_utils.cc:78
int trigger_elapse(int ID) const
Definition: timer_utils.h:186
Tissue level electrics, main Electrics physics class.
void print_vector(MPI_Comm comm, const vector< T > &vec, const short dpn, FILE *fd)
void sort_parallel(MPI_Comm comm, const vector< T > &idx, vector< T > &out_idx)
Sort index values parallel ascending across the ranks.
void restrict_to_set(vector< T > &v, const hashmap::unordered_set< T > &set)
void init_vector(SF::abstract_vector< T, S > **vec)
Definition: SF_init.h:122
@ NBR_PETSC
PETSc numbering of nodes.
Definition: SF_container.h:192
@ NBR_REF
The nodal numbering of the reference mesh (the one stored on HD).
Definition: SF_container.h:190
std::set< int > current_stim
Definition: stimulate.cc:42
std::set< int > dbc_stim
Definition: stimulate.cc:43
std::map< int, std::string > units
Definition: stimulate.cc:44
std::set< int > voltage_stim
Definition: stimulate.cc:41
timer_manager * tm_manager
a manager for the various physics timers
Definition: main.cc:52
std::map< physic_t, Basic_physic * > physics_reg
the physics
Definition: main.cc:50
void sample_wave_form(stim_pulse &sp, int idx)
sample a signal given in analytic form
Definition: stimulate.cc:358
sf_mesh & get_mesh(const mesh_t gt)
Get a mesh by specifying the gridID.
Definition: sf_interface.cc:33
bool is_dbc(stim_t type)
whether stimulus is a dirichlet type. implies boundary conditions on matrix
Definition: stimulate.cc:79
@ constPulse
Definition: stimulate.h:79
@ truncExpPulse
Definition: stimulate.h:79
@ arbPulse
Definition: stimulate.h:79
int set_dir(IO_t dest)
Definition: sim_utils.cc:452
int get_rank(MPI_Comm comm=PETSC_COMM_WORLD)
Definition: basics.h:276
short get_mesh_dim(mesh_t id)
get (lowest) dimension of the mesh used in the experiment
Definition: sim_utils.cc:1261
T get_global(T in, MPI_Op OP, MPI_Comm comm=PETSC_COMM_WORLD)
Do a global reduction on a variable.
Definition: basics.h:233
@ Phie_pre
Definition: stimulate.h:83
@ Ignore_stim
Definition: stimulate.h:83
@ I_tm_grad
Definition: stimulate.h:83
@ V_ex_ol
Definition: stimulate.h:83
@ GND_ex
Definition: stimulate.h:83
@ Vm_clmp
Definition: stimulate.h:83
void init_stim_info(void)
Definition: stimulate.cc:52
bool is_extra(stim_t type)
whether stimulus is on extra grid (or on intra)
Definition: stimulate.cc:84
FILE_SPEC f_open(const char *fname, const char *mode)
Open a FILE_SPEC.
Definition: basics.cc:135
bool is_current(stim_t type)
uses current as stimulation
Definition: stimulate.cc:74
void warn_when_passing_intra_vtx(const std::string filename)
Definition: fem_utils.cc:224
stim_domain_t
Definition: stimulate.h:84
@ CURDIR
Definition: sim_utils.h:52
void split_string(const std::string &input, const char s, STRVEC &list)
Split a string holding a character-seperated list into a vector of strings.
Definition: basics.h:103
void indices_from_region_tag(SF::vector< mesh_int_t > &idx, const sf_mesh &mesh, const int tag)
Populate vertex data with the vertices of a given tag region.
Definition: fem_utils.cc:155
void indices_from_geom_shape(SF::vector< mesh_int_t > &idx, const sf_mesh &mesh, const geom_shape shape, const bool nodal)
Populate vertex data with the vertices inside a defined box shape.
Definition: fem_utils.cc:169
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:60
@ extra_elec_msh
Definition: sf_interface.h:62
@ intra_elec_msh
Definition: sf_interface.h:61
Basic_physic * get_physics(physic_t p, bool error_if_missing)
Convinience function to get a physics.
Definition: sim_utils.cc:824
void read_indices_with_data(SF::vector< T > &idx, SF::vector< S > &dat, const std::string filename, const hashmap::unordered_map< mesh_int_t, mesh_int_t > &dd_map, const int dpn, MPI_Comm comm)
like read_indices, but with associated data for each index
Definition: fem_utils.h:269
void read_indices(SF::vector< T > &idx, const std::string filename, const hashmap::unordered_map< mesh_int_t, mesh_int_t > &dd_map, MPI_Comm comm)
Read indices from a file.
Definition: fem_utils.h:120
void get_stim_list(const char *str_list, std::vector< double > &stlist)
Definition: stimulate.cc:257
void update_cwd()
save the current working directory to curdir so that we can switch back to it if needed.
Definition: sim_utils.cc:447
void f_close(FILE_SPEC &f)
Close a FILE_SPEC.
Definition: basics.cc:162
bool is_voltage(stim_t type)
uses voltage as stimulation
Definition: stimulate.cc:68
#define UM_to_CM
convert um to cm
Definition: physics_types.h:36
#define UM2_to_CM2
convert um^2 to cm^2
Definition: physics_types.h:35
Electrical stimulation functions.
SF::vector< SF_int > * nod
Definition: stimulate.h:203
File descriptor struct.
Definition: basics.h:133