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  // transition to new tilt-based shape defintion
128  s.pulse.tilt_ampl = curstim.s2;
129  s.pulse.tilt_time = curstim.d1;
130  s.pulse.strength = curstim.strength;
131 
132  // strength is not a scalar, we prescribe a different strength at every node
133  s.elec.vtx_fcn = curstim.vtx_fcn;
134 
135  // translate circuit
136  s.crct.type = curstim.stimtype;
137  s.crct.balance = curstim.balance;
138  s.crct.total_current = curstim.total_current;
139 
140  // translate electrode
141  s.elec.geomID = curstim.geometry;
142  // s.elec.domain = curstim.domain;
143  s.elec.vtx_file = strdup(curstim.vtx_file);
144  s.elec.dump_vtx_file = curstim.dump_vtx_file;
145 
146  // check whether we have a sound electrode geometry definition
147  s.elec.geom_type = 2; // should be region_t block;
148  s.elec.p0[0] = curstim.x0 - (curstim.ctr_def?curstim.xd/2.:0.);
149  s.elec.p0[1] = curstim.y0 - (curstim.ctr_def?curstim.yd/2.:0.);
150  s.elec.p0[2] = curstim.z0 - (curstim.ctr_def?curstim.zd/2.:0.);
151  s.elec.p1[0] = s.elec.p0[0] + curstim.xd;
152  s.elec.p1[1] = s.elec.p0[1] + curstim.yd;
153  s.elec.p1[2] = s.elec.p0[2] + curstim.zd;
154 
155  // cp overall electrode name
156  s.name = strdup(curstim.name);
157 }
158 
163 void stimulus::setup(int iidx)
164 {
165  idx = iidx;
166  const Stim & cur_stim = param_globals::stim[idx];
167 
168  // label stimulus
169  if(cur_stim.name != std::string(""))
170  name = cur_stim.name;
171  else
172  name = "Stimulus_" + std::to_string(idx);
173 
174  // set up stimulus pulse
175  pulse.setup(idx);
176 
177  // set up stimulation protocol
178  ptcl.setup(idx,name);
179 
180  // set up physics of applied stimulus
181  phys.setup(idx);
182 
183  // set up the spatial region of the stimulus
184  electrode.setup(idx);
185 }
186 
188 {
189  // extracellular potential stimuli are always active
190  if(this->phys.type == V_ex)
191  return true;
192 
193  return user_globals::tm_manager->trigger(ptcl.timer_id);
194 }
195 
196 
199 void stim_pulse::setup(int idx)
200 {
201  const Stim & cur_stim = param_globals::stim[idx];
202  const Pulse & pulse = cur_stim.pulse;
203 
204  // assign waveform type and parameters
205  waveform_t wvt = truncExpPulse; // default, we may expose this in prm as a choice
206 
207  // set waveform based on circuit type
208  switch(cur_stim.crct.type) {
209  case Vm_clmp:
210  case GND_ex:
211  wvt = constPulse; break;
212 
213  default:
214  if(pulse.file && strlen(pulse.file)) wvt = arbPulse;
215  else wvt = truncExpPulse;
216  break;
217  }
218 
219  assign(pulse.strength, cur_stim.ptcl.duration, param_globals::dt, wvt);
220 
221  // sample pulse shape
222  if(wvt == arbPulse) {
223  sig::time_trace file_wave;
224  int _err = 0;
225 
226  if(!get_rank()) {
227  bool unitize = true;
228 
229  update_cwd();
230  set_dir(INPUT);
231  _err = file_wave.read_trace(pulse.file, unitize);
232  set_dir(CURDIR);
233  }
234 
235  int err = get_global(_err, MPI_MIN);
236  if(err) {
238  "Failed reading pulse for stimulus[%d] from file %s.\n", idx, pulse.file);
239  }
240  else {
241  get_global(file_wave.f);
242  get_global(file_wave.t);
243 
244  file_wave.resample(this->wave);
245 
246  // check length of read in pulse and adjust stimulus settings accordingly
247  if(wave.duration() != duration)
248  duration = wave.duration();
249  }
250  }
251  else {
252  sample_wave_form(*this, idx);
253  }
254 
255  // add unit labels
256  std::string sUnit = stim_info::units[(stim_t)cur_stim.crct.type];
257  std::string tUnit = "ms";
258  wave.setUnits(tUnit,sUnit);
259 }
260 
261 
262 void get_stim_list(const char* str_list, std::vector<double> & stlist)
263 {
264  std::string tstim = str_list;
265  std::vector<std::string> stims;
266  split_string(tstim, ',', stims);
267 
268  stlist.resize(stims.size());
269  size_t widx = 0, err = 0;
270 
271  for(size_t i=0; i<stims.size(); i++) {
272  std::string & stim = stims[i];
273 
274  if(stim.size()) {
275  if(stim[0] == '+') {
276  double last = widx == 0 ? 0.0 : stlist[widx-1];
277  stim.erase(0, 1); // erase first char
278 
279  // we user sscanf so that we can actually determine if the string was converted
280  // into a double. atof does not allow for error-checking
281  double cur = -1.0;
282  size_t nread = sscanf(stim.c_str(), "%lf", &cur);
283  if(nread)
284  stlist[widx++] = cur + last;
285  } else {
286  double cur = -1.0;
287  size_t nread = sscanf(stim.c_str(), "%lf", &cur);
288  if(nread)
289  stlist[widx++] = cur;
290  }
291  }
292  }
293 
294  stlist.resize(widx);
295 
296  if(widx != stims.size())
297  log_msg(0, 4, 0, "%s warning: Some values of %s could not be converted in stim times!",
298  __func__, str_list);
299 }
300 
301 
304 void stim_protocol::setup(int idx, std::string name)
305 {
306  const Stim & cur_stim = param_globals::stim[idx];
307  const Protocol & ptcl = cur_stim.ptcl;
308 
309  if(strlen(ptcl.stimlist) == 0) {
310  start = ptcl.start, npls = ptcl.npls, pcl = ptcl.bcl;
311 
312  // add timer
313  timer_id = user_globals::tm_manager->add_eq_timer(ptcl.start,
314  param_globals::tend, npls, pcl, ptcl.duration, name.c_str());
315  } else {
316  std::vector<double> stims;
317  get_stim_list(ptcl.stimlist, stims);
318 
319  log_msg(0,0,ECHO | NONL, "stim %d: stimulating at times: ", idx);
320  for(double t : stims)
321  log_msg(0,0, ECHO | NONL, "%.2lf ", t);
322  log_msg(0,0,0, "");
323  log_msg(0,0,0, "stim %d: .bcl and .npls values will be ignored.", idx);
324 
325  timer_id = user_globals::tm_manager->add_neq_timer(stims, ptcl.duration, name.c_str());
326  }
327 }
328 
329 
332 void stim_physics::setup(int idx)
333 {
334  const Stim & cur_stim = param_globals::stim[idx];
335  type = stim_t(cur_stim.crct.type);
336  domain = stim_domain_t(cur_stim.elec.domain);
337  unit = stim_info::units[cur_stim.crct.type];
338  total_current = cur_stim.crct.total_current;
339 
340  const short dim = is_extra(stim_t(cur_stim.crct.type)) ? get_mesh_dim(extra_elec_msh) : get_mesh_dim(intra_elec_msh);
341 
342  switch(type) {
343  case I_tm:
344  scale = param_globals::operator_splitting ? user_globals::tm_manager->time_step : 1.0;
345  break;
346 
347  case I_ex:
348  scale = UM2_to_CM2 * (dim == 2 ? 1.0 : UM_to_CM);
349  break;
350 
351  default: scale = 1.0; break;
352  }
353 }
354 
355 
363 void sample_wave_form(stim_pulse& sp, int idx)
364 {
365  const Stimulus & curstim = param_globals::stimulus[idx];
366 
367  if(sp.wform == truncExpPulse)
368  {
369  // monophasic pulse, first phase lasts for entire pulse duration
370  sig::Pulse truncExp;
371  truncExp.duration = sp.duration;
372  truncExp.d1 = curstim.d1 * sp.duration; // relative to absolute duration of d1
373  truncExp.tau_edge = curstim.tau_edge;
374  truncExp.tau_plat = curstim.tau_plateau;
375  truncExp.decay = 5.;
376 
377  // from relative duration of d1 we infer mono- or biphasic
378  if(curstim.d1 == 1.) {
379  // monophasic pulse, first phase lasts for entire pulse duration
380  sig::monophasicTruncExpFunc fnc(truncExp);
381  fnc.sample(sp.wave);
382  }
383  else
384  {
385  // biphasic pulse, second phase lasts shorter than pulse duration
386  sig::biphasicTruncExpFunc fnc(truncExp);
387  fnc.sample(sp.wave);
388  }
389  }
390  else if(sp.wform == constPulse)
391  {
392  sig::constFunc cnst_fnc(1);
393  cnst_fnc.sample(sp.wave);
394  }
395  else
396  assert(0);
397 
398  /*
399  else if(sp.wform == squarePulse)
400  {
401  // generate step function using derived class
402  Step stepPars;
403  stepPars.trig = 0.25;
404  stepPars.rise = true;
405 
406  stepFunc step_func(stepPars);
407  s.set_labels("step_func");
408  step_func.sample(s);
409  s.write_trace();
410  }
411  else if(sp.wform == sinePulse)
412  {
413  // generate a sine wave
414  sineWave sinePars;
415  sinePars.frq = 1.;
416  sinePars.phase = 0.;
417  sineFunc sine(sinePars);
418  s.set_labels("sine_func");
419  sine.sample(s);
420  s.write_trace();
421  }
422  else if(sp.wform == APfootPulse)
423  {
424  // generate AP foot
425  APfoot footPars;
426  footPars.tau_f = s.duration()/5.; // use 5 time constants
427  APfootFunc APft(footPars);
428  s.set_labels("APfoot");
429  APft.sample(s);
430  s.write_trace();
431  }
432  else if(sp.wform == arbPulse)
433  {
434  }
435  else
436  log_msg(user_globals:: error);
437  */
438 }
439 
440 bool stimulus::value(double & v) const
441 {
442  if(user_globals::tm_manager->trigger(ptcl.timer_id) == false) {
443  // extracellular potential stimuli are always active, but return 0 when their
444  // protocol is not active. Use V_ex_ol for extracellular potential stimuli that
445  // get removed when they expire.
446  if(this->phys.type == V_ex || this->phys.type == GND_ex) {
447  v = 0.0;
448  return true;
449  }
450 
451  return false;
452  }
453  else {
454  int i = user_globals::tm_manager->trigger_elapse(ptcl.timer_id);
455  v = pulse.strength * phys.scale * pulse.wave.f[i];
456  return true;
457  }
458 }
459 
461 {
462  const Stim & curstim = param_globals::stim[idx];
463  mesh_t grid;
464 
465  // based on the stimulus domain type we select grid and physics
466  switch(curstim.crct.type) {
467  case I_ex:
468  case V_ex:
469  case V_ex_ol:
470  case GND_ex:
471  grid = extra_elec_msh; break;
472 
473  case Illum:
474  case Vm_clmp:
475  case I_tm:
476  grid = intra_elec_msh; break;
477 
478  case Ignore_stim: return;
479 
480  default:
481  log_msg(0,5,0, "stim_electrode::setup error: Can't determine domain from stim type! Aborting!", __func__);
482  EXIT(1);
483  }
484 
485  // get a logger from the physics
486  FILE_SPEC logger = NULL;
487 
488  // this are the criteria for choosing how we extract the stimulus electrode
489  bool vertex_file_given = strlen(curstim.elec.vtx_file) > 0;
490  bool tag_index_given = curstim.elec.geomID > -1;
491  // the mesh we need for computing the local vertex indices.
492  const sf_mesh & mesh = get_mesh(grid);
493 
494  if(vertex_file_given && tag_index_given)
495  log_msg(0,3,0, "%s warning: More than one stimulus electrode definintions set in electrode %d", __func__, idx);
496 
497  if(vertex_file_given) {
498  definition = def_t::file_based;
499  input_filename = curstim.elec.vtx_file;
500 
501  log_msg(logger, 0, 0, "Stimulus %d: Selecting vertices from file %s", idx, input_filename.c_str());
502 
503  set_dir(INPUT);
504  warn_when_passing_intra_vtx(input_filename);
505 
506  // we read the indices. they are being localized w.r.t. the provided numbering. In our
507  // case this is always the reference numbering.
508  if(curstim.elec.vtx_fcn)
509  read_indices_with_data(vertices, scaling, input_filename, mesh, SF::NBR_REF, true, 1, PETSC_COMM_WORLD);
510  else
511  read_indices(vertices, input_filename, mesh, SF::NBR_REF, true, PETSC_COMM_WORLD);
512 
513  int gnum_idx = get_global(vertices.size(), MPI_SUM);
514  if(gnum_idx == 0) {
515  log_msg(0, 5, 0, "Stimulus %d: Specified vertices are not in stimulus domain! Aborting!", idx);
516  EXIT(1);
517  }
518  }
519  else if(tag_index_given) {
520  definition = def_t::vol_based_tag;
521 
522  int tag = curstim.elec.geomID;
523  log_msg(logger, 0, 0, "Stimulus %d: Selecting vertices from tag %d", idx, tag);
524 
525  indices_from_region_tag(vertices, mesh, tag);
526  // we restrict the indices to the algebraic subset
527  SF::restrict_to_set(vertices, mesh.pl.algebraic_nodes());
528  }
529  else {
530  definition = def_t::vol_based_shape;
531  log_msg(logger, 0, 0, "Stimulus %d: Selecting vertices from shape.", idx);
532 
533  geom_shape shape;
534  shape.type = geom_shape::shape_t(curstim.elec.geom_type);
535  shape.p0 = curstim.elec.p0;
536  shape.p1 = curstim.elec.p1;
537  shape.radius = curstim.elec.radius;
538 
539  bool nodal = true;
540  indices_from_geom_shape(vertices, mesh, shape, nodal);
541  // we restrict the indices to the algebraic subset
542  SF::restrict_to_set(vertices, mesh.pl.algebraic_nodes());
543 
544  int gsize = vertices.size();
545  if(get_global(gsize, MPI_SUM) == 0) {
546  log_msg(0,5,0, "error: Empty stimulus[%d] electrode def! Aborting!", idx);
547  EXIT(1);
548  }
549  }
550 
551  if(curstim.elec.dump_vtx_file) {
552  SF::vector<mesh_int_t> glob_idx(vertices);
553  mesh.pl.globalize(glob_idx);
554 
555  SF::vector<mesh_int_t> srt_idx;
556  SF::sort_parallel(mesh.comm, glob_idx, srt_idx);
557 
558  size_t num_vtx = get_global(srt_idx.size(), MPI_SUM);
559  int rank = get_rank();
560 
561  FILE_SPEC f = NULL;
562  int err = 0;
563 
564  if(rank == 0) {
565  char dump_name[1024];
566 
567  if (strlen(curstim.name)) {
568  snprintf(dump_name, sizeof dump_name, "%s.vtx", curstim.name);
569  } else {
570  snprintf(dump_name, sizeof dump_name, "ELECTRODE_%d.vtx", idx);
571  }
572 
573  f = f_open(dump_name, "w");
574  if(!f) err++;
575  else {
576  fprintf(f->fd, "%zd\nextra\n", num_vtx);
577  }
578  }
579 
580  if(!get_global(err, MPI_SUM)) {
581  print_vector(mesh.comm, srt_idx, 1, f ? f->fd : NULL);
582  } else {
583  log_msg(0, 4, 0, "error: stimulus[%d] cannot be dumped!");
584  }
585 
586  // only root really does that
587  f_close(f);
588  }
589 }
590 
592 {
593  clear_active_dbc();
594 
595  for(const stimulus & s : stimuli) {
596  if(is_dbc(s.phys.type) && s.is_active()) {
597 
598  // the local indices of the dbc
599  const SF::vector<mesh_int_t> & dbc_idx = s.electrode.vertices;
600 
601  dbc_data* dbc_buff = new dbc_data();
602 
603  // the global petsc indices of the dbc
604  dbc_buff->nod = new SF::vector<SF_int>(dbc_idx.size());
605 
606  const SF::vector<mesh_int_t> & petsc_nbr = mat.mesh_ptr()->get_numbering(SF::NBR_PETSC);
607 
608  size_t widx = 0;
609  for(size_t i=0; i<dbc_idx.size(); i++)
610  (*dbc_buff->nod)[widx++] = petsc_nbr[dbc_idx[i]];
611 
612  sf_vec* dbc; SF::init_vector(&dbc, *mat.mesh_ptr(), 1, sf_vec::algebraic);
613  SF::init_vector(&dbc_buff->cntr, *mat.mesh_ptr(), 1, sf_vec::algebraic);
614 
615  if(s.electrode.scaling.size()) {
616  // we have spatially heterogenous dirichlet values
617  dbc->set(*dbc_buff->nod, s.electrode.scaling);
618  } else {
619  // we have spatially homogenous dirichlet values
620  dbc->set(*dbc_buff->nod, 1.0);
621  }
622 
623  mat.mult(*dbc, *dbc_buff->cntr);
624 
625  active_dbc[s.idx] = dbc_buff;
626  }
627  }
628 }
629 
631 {
632  // check if stim has come offline
633  for(const auto & d : active_dbc) {
634  if(stimuli[d.first].is_active() == false)
635  return true;
636  }
637 
638  // check if stim has come online
639  for(const stimulus & s : stimuli) {
640  if(is_dbc(s.phys.type) && s.is_active() && active_dbc.count(s.idx) == 0)
641  return true;
642  }
643 
644  return false;
645 }
646 
648 {
649  sf_vec* dbc;
650 
651  SF::init_vector(&dbc, *mat.mesh_ptr(), mat.dpn_row(), sf_vec::algebraic);
652 
653  for(auto it = active_dbc.begin(); it != active_dbc.end(); ++it) {
654  const SF::vector<SF_int> & dbc_nod = *it->second->nod;
655  // zero cols and rows in lhs mat
656  dbc->set(1.0);
657  dbc->set(dbc_nod, 0.0);
658  // multiplicate dbc vector from left and right to matrix in order to zero cols and rows
659  mat.mult_LR(*dbc, *dbc);
660  // now add 1.0 to the diagonal at the dirichlet locations
661  dbc->set(0.0);
662  dbc->set(dbc_nod, 1.0);
663  mat.diag_add(*dbc);
664  }
665 }
666 
668 {
669  for(auto it = active_dbc.begin(); it != active_dbc.end(); ++it) {
670  const int & dbc_idx = it->first;
671  const dbc_manager::dbc_data & dbc = *(it->second);
672  double strength = 0.0;
673  bool is_active = stimuli[dbc_idx].value(strength);
674 
675  // the whole idea of the dbc_manager and its active_dbc is
676  // based on only storing the active dbcs. if the stimulus associated
677  // to a dbc believed active is actually not active, we want to abort.
678  assert(is_active);
679 
680  rhs.add_scaled(*dbc.cntr, -strength);
681 
682  if(stimuli[dbc_idx].electrode.scaling.size()) {
683  SF::vector<SF_real> scaled_strength(stimuli[dbc_idx].electrode.scaling.size());
684 
685  size_t widx = 0;
686  for(SF_real s : stimuli[dbc_idx].electrode.scaling)
687  scaled_strength[widx++] = s * strength;
688 
689  rhs.set(*dbc.nod, scaled_strength);
690  } else {
691  rhs.set(*dbc.nod, strength);
692  }
693  }
694 }
695 
696 } // namespace opencarp
697 
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:1068
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:304
virtual void add_scaled(const abstract_vector< T, S > &vec, S k)=0
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.
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:667
#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:591
bool value(double &v) const
Get the current value if the stimulus is active.
Definition: stimulate.cc:440
monophasic truncated exponentials (capacitive discharge)
Definition: signals.h:994
waveform_t wform
wave form of stimulus
Definition: stimulate.h:94
void setup(int id)
Setup from a param stimulus index.
Definition: stimulate.cc:199
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:187
biphasic truncated exponentials (capacitive discharge)
Definition: signals.h:1060
virtual SF::vector< sReal > & sample(time_trace &trc)
Definition: signals.h:1002
Time tracing class.
Definition: signals.h:142
int decay
edge decay time (multiples of tau_edge)
Definition: signals.h:826
virtual SF::vector< sReal > & sample(time_trace &trc)
Definition: signals.h:919
bool dbc_update()
check if dbcs have updated
Definition: stimulate.cc:630
constant function
Definition: signals.h:911
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:163
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:262
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:363
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
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:332
#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