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