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  // start dealing with units, first try
52  stim_info::units[I_tm] = "uA/cm^2";
53  stim_info::units[I_ex] = "uA/cm^3";
54  stim_info::units[V_ex] = "mV";
55  stim_info::units[GND_ex] = "mV";
56  stim_info::units[I_in] = "uA/cm^3";
57  stim_info::units[V_ex_ol] = "mV";
59  stim_info::units[I_lat] = "uA/cm^2";
60  stim_info::units[Vm_clmp] = "mV";
61  stim_info::units[Phie_pre] = "mV";
62 }
63 
64 
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 = IsVoltageStim_(cur_stim.crct) ? "mV" : "uA/cm^3";
257  //std::string sUnit = is_voltage((stim_t)cur_stim.crct.type) ? "mV" : "uA/cm^3";
258  std::string sUnit = stim_info::units[(stim_t)cur_stim.crct.type];
259  std::string tUnit = "ms";
260  wave.setUnits(tUnit,sUnit);
261 }
262 
263 
264 void get_stim_list(const char* str_list, std::vector<double> & stlist)
265 {
266  std::string tstim = str_list;
267  std::vector<std::string> stims;
268  split_string(tstim, ',', stims);
269 
270  stlist.resize(stims.size());
271  size_t widx = 0, err = 0;
272 
273  for(size_t i=0; i<stims.size(); i++) {
274  std::string & stim = stims[i];
275 
276  if(stim.size()) {
277  if(stim[0] == '+') {
278  double last = widx == 0 ? 0.0 : stlist[widx-1];
279  stim.erase(0, 1); // erase first char
280 
281  // we user sscanf so that we can actually determine if the string was converted
282  // into a double. atof does not allow for error-checking
283  double cur = -1.0;
284  size_t nread = sscanf(stim.c_str(), "%lf", &cur);
285  if(nread)
286  stlist[widx++] = cur + last;
287  } else {
288  double cur = -1.0;
289  size_t nread = sscanf(stim.c_str(), "%lf", &cur);
290  if(nread)
291  stlist[widx++] = cur;
292  }
293  }
294  }
295 
296  stlist.resize(widx);
297 
298  if(widx != stims.size())
299  log_msg(0, 4, 0, "%s warning: Some values of %s could not be converted in stim times!",
300  __func__, str_list);
301 }
302 
303 
306 void stim_protocol::setup(int idx, std::string name)
307 {
308  const Stim & cur_stim = param_globals::stim[idx];
309  const Protocol & ptcl = cur_stim.ptcl;
310 
311  if(strlen(ptcl.stimlist) == 0) {
312  start = ptcl.start, npls = ptcl.npls, pcl = ptcl.bcl;
313 
314  // add timer
315  timer_id = user_globals::tm_manager->add_eq_timer(ptcl.start,
316  param_globals::tend, npls, pcl, ptcl.duration, name.c_str());
317  } else {
318  std::vector<double> stims;
319  get_stim_list(ptcl.stimlist, stims);
320 
321  log_msg(0,0,ECHO | NONL, "stim %d: stimulating at times: ", idx);
322  for(double t : stims)
323  log_msg(0,0, ECHO | NONL, "%.2lf ", t);
324  log_msg(0,0,0, "");
325  log_msg(0,0,0, "stim %d: .bcl and .npls values will be ignored.", idx);
326 
327  timer_id = user_globals::tm_manager->add_neq_timer(stims, ptcl.duration, name.c_str());
328  }
329 }
330 
331 
334 void stim_physics::setup(int idx)
335 {
336  const Stim & cur_stim = param_globals::stim[idx];
337  type = stim_t(cur_stim.crct.type);
338  domain = stim_domain_t(cur_stim.elec.domain);
339  unit = stim_info::units[cur_stim.crct.type];
340  total_current = cur_stim.crct.total_current;
341 
342  const short dim = is_extra(stim_t(cur_stim.crct.type)) ? get_mesh_dim(extra_elec_msh) : get_mesh_dim(intra_elec_msh);
343 
344  switch(type) {
345  case I_tm:
346  scale = param_globals::operator_splitting ? user_globals::tm_manager->time_step : 1.0;
347  break;
348 
349  case I_ex:
350  scale = UM2_to_CM2 * (dim == 2 ? 1.0 : UM_to_CM);
351  break;
352 
353  default: scale = 1.0; break;
354  }
355 }
356 
357 
365 void sample_wave_form(stim_pulse& sp, int idx)
366 {
367  const Stimulus & curstim = param_globals::stimulus[idx];
368 
369  if(sp.wform == truncExpPulse)
370  {
371  // monophasic pulse, first phase lasts for entire pulse duration
372  sig::Pulse truncExp;
373  truncExp.duration = sp.duration;
374  truncExp.d1 = curstim.d1 * sp.duration; // relative to absolute duration of d1
375  truncExp.tau_edge = curstim.tau_edge;
376  truncExp.tau_plat = curstim.tau_plateau;
377  truncExp.decay = 5.;
378 
379  // from relative duration of d1 we infer mono- or biphasic
380  if(curstim.d1 == 1.) {
381  // monophasic pulse, first phase lasts for entire pulse duration
382  sig::monophasicTruncExpFunc fnc(truncExp);
383  fnc.sample(sp.wave);
384  }
385  else
386  {
387  // biphasic pulse, second phase lasts shorter than pulse duration
388  sig::biphasicTruncExpFunc fnc(truncExp);
389  fnc.sample(sp.wave);
390  }
391  }
392  else if(sp.wform == constPulse)
393  {
394  sig::constFunc cnst_fnc(1);
395  cnst_fnc.sample(sp.wave);
396  }
397  else
398  assert(0);
399 
400  /*
401  else if(sp.wform == squarePulse)
402  {
403  // generate step function using derived class
404  Step stepPars;
405  stepPars.trig = 0.25;
406  stepPars.rise = true;
407 
408  stepFunc step_func(stepPars);
409  s.set_labels("step_func");
410  step_func.sample(s);
411  s.write_trace();
412  }
413  else if(sp.wform == sinePulse)
414  {
415  // generate a sine wave
416  sineWave sinePars;
417  sinePars.frq = 1.;
418  sinePars.phase = 0.;
419  sineFunc sine(sinePars);
420  s.set_labels("sine_func");
421  sine.sample(s);
422  s.write_trace();
423  }
424  else if(sp.wform == APfootPulse)
425  {
426  // generate AP foot
427  APfoot footPars;
428  footPars.tau_f = s.duration()/5.; // use 5 time constants
429  APfootFunc APft(footPars);
430  s.set_labels("APfoot");
431  APft.sample(s);
432  s.write_trace();
433  }
434  else if(sp.wform == arbPulse)
435  {
436  }
437  else
438  log_msg(user_globals:: error);
439  */
440 }
441 
442 bool stimulus::value(double & v) const
443 {
444  if(user_globals::tm_manager->trigger(ptcl.timer_id) == false) {
445  // extracellular potential stimuli are always active, but return 0 when their
446  // protocol is not active. Use V_ex_ol for extracellular potential stimuli that
447  // get removed when they expire.
448  if(this->phys.type == V_ex || this->phys.type == GND_ex) {
449  v = 0.0;
450  return true;
451  }
452 
453  return false;
454  }
455  else {
456  int i = user_globals::tm_manager->trigger_elapse(ptcl.timer_id);
457  v = pulse.strength * phys.scale * pulse.wave.f[i];
458  return true;
459  }
460 }
461 
463 {
464  const Stim & curstim = param_globals::stim[idx];
465  mesh_t grid;
466 
467  // based on the stimulus domain type we select grid and physics
468  switch(curstim.crct.type) {
469  case I_ex:
470  case V_ex:
471  case V_ex_ol:
472  case GND_ex:
473  grid = extra_elec_msh; break;
474 
475  case Illum:
476  case Vm_clmp:
477  case I_tm:
478  grid = intra_elec_msh; break;
479 
480  case Ignore_stim: return;
481 
482  default:
483  log_msg(0,5,0, "stim_electrode::setup error: Can't determine domain from stim type! Aborting!", __func__);
484  EXIT(1);
485  }
486 
487  // get a logger from the physics
489 
490  // this are the criteria for choosing how we extract the stimulus electrode
491  bool vertex_file_given = strlen(curstim.elec.vtx_file) > 0;
492  bool tag_index_given = curstim.elec.geomID > -1;
493  // the mesh we need for computing the local vertex indices.
494  const sf_mesh & mesh = get_mesh(grid);
495 
496  if(vertex_file_given && tag_index_given)
497  log_msg(0,3,0, "%s warning: More than one stimulus electrode definintions set in electrode %d", __func__, idx);
498 
499  if(vertex_file_given) {
500  definition = def_t::file_based;
501  input_filename = curstim.elec.vtx_file;
502 
503  log_msg(logger, 0, 0, "Stimulus %d: Selecting vertices from file %s", idx, input_filename.c_str());
504 
505  set_dir(INPUT);
506  warn_when_passing_intra_vtx(input_filename);
507 
508  // we read the indices. they are being localized w.r.t. the provided numbering. In our
509  // case this is always the reference numbering.
510  if(curstim.elec.vtx_fcn)
511  read_indices_with_data(vertices, scaling, input_filename, mesh, SF::NBR_REF, true, 1, PETSC_COMM_WORLD);
512  else
513  read_indices(vertices, input_filename, mesh, SF::NBR_REF, true, PETSC_COMM_WORLD);
514 
515  int gnum_idx = get_global(vertices.size(), MPI_SUM);
516  if(gnum_idx == 0) {
517  log_msg(0, 5, 0, "Stimulus %d: Specified vertices are not in stimulus domain! Aborting!", idx);
518  EXIT(1);
519  }
520  }
521  else if(tag_index_given) {
522  definition = def_t::vol_based_tag;
523 
524  int tag = curstim.elec.geomID;
525  log_msg(logger, 0, 0, "Stimulus %d: Selecting vertices from tag %d", idx, tag);
526 
527  indices_from_region_tag(vertices, mesh, tag);
528  // we restrict the indices to the algebraic subset
529  SF::restrict_to_set(vertices, mesh.pl.algebraic_nodes());
530  }
531  else {
532  definition = def_t::vol_based_shape;
533  log_msg(logger, 0, 0, "Stimulus %d: Selecting vertices from shape.", idx);
534 
535  geom_shape shape;
536  shape.type = geom_shape::shape_t(curstim.elec.geom_type);
537  shape.p0 = curstim.elec.p0;
538  shape.p1 = curstim.elec.p1;
539  shape.radius = curstim.elec.radius;
540 
541  bool nodal = true;
542  indices_from_geom_shape(vertices, mesh, shape, nodal);
543  // we restrict the indices to the algebraic subset
544  SF::restrict_to_set(vertices, mesh.pl.algebraic_nodes());
545 
546  int gsize = vertices.size();
547  if(get_global(gsize, MPI_SUM) == 0) {
548  log_msg(0,5,0, "error: Empty stimulus[%d] electrode def! Aborting!", idx);
549  EXIT(1);
550  }
551  }
552 
553  if(curstim.elec.dump_vtx_file) {
554  SF::vector<mesh_int_t> glob_idx(vertices);
555  mesh.pl.globalize(glob_idx);
556 
557  SF::vector<mesh_int_t> srt_idx;
558  SF::sort_parallel(mesh.comm, glob_idx, srt_idx);
559 
560  size_t num_vtx = get_global(srt_idx.size(), MPI_SUM);
561  int rank = get_rank();
562 
563  FILE_SPEC f = NULL;
564  int err = 0;
565 
566  if(rank == 0) {
567  char dump_name[1024];
568 
569  if (strlen(curstim.name)) {
570  snprintf(dump_name, sizeof dump_name, "%s.vtx", curstim.name);
571  } else {
572  snprintf(dump_name, sizeof dump_name, "ELECTRODE_%d.vtx", idx);
573  }
574 
575  f = f_open(dump_name, "w");
576  if(!f) err++;
577  else {
578  fprintf(f->fd, "%zd\nextra\n", num_vtx);
579  }
580  }
581 
582  if(!get_global(err, MPI_SUM)) {
583  print_vector(mesh.comm, srt_idx, 1, f ? f->fd : NULL);
584  } else {
585  log_msg(0, 4, 0, "error: stimulus[%d] cannot be dumped!");
586  }
587 
588  // only root really does that
589  f_close(f);
590  }
591 }
592 
594 {
595  clear_active_dbc();
596 
597  for(const stimulus & s : stimuli) {
598  if(is_dbc(s.phys.type) && s.is_active()) {
599 
600  // the local indices of the dbc
601  const SF::vector<mesh_int_t> & dbc_idx = s.electrode.vertices;
602 
603  dbc_data* dbc_buff = new dbc_data();
604 
605  // the global petsc indices of the dbc
606  dbc_buff->nod = new SF::vector<SF_int>(dbc_idx.size());
607 
608  const SF::vector<mesh_int_t> & petsc_nbr = mat.mesh_ptr()->get_numbering(SF::NBR_PETSC);
609 
610  size_t widx = 0;
611  for(size_t i=0; i<dbc_idx.size(); i++)
612  (*dbc_buff->nod)[widx++] = petsc_nbr[dbc_idx[i]];
613 
614  sf_vec* dbc; SF::init_vector(&dbc, *mat.mesh_ptr(), 1, sf_vec::algebraic);
615  SF::init_vector(&dbc_buff->cntr, *mat.mesh_ptr(), 1, sf_vec::algebraic);
616 
617  if(s.electrode.scaling.size()) {
618  // we have spatially heterogenous dirichlet values
619  dbc->set(*dbc_buff->nod, s.electrode.scaling);
620  } else {
621  // we have spatially homogenous dirichlet values
622  dbc->set(*dbc_buff->nod, 1.0);
623  }
624 
625  mat.mult(*dbc, *dbc_buff->cntr);
626 
627  active_dbc[s.idx] = dbc_buff;
628  }
629  }
630 }
631 
633 {
634  // check if stim has come offline
635  for(const auto & d : active_dbc) {
636  if(stimuli[d.first].is_active() == false)
637  return true;
638  }
639 
640  // check if stim has come online
641  for(const stimulus & s : stimuli) {
642  if(is_dbc(s.phys.type) && s.is_active() && active_dbc.count(s.idx) == 0)
643  return true;
644  }
645 
646  return false;
647 }
648 
650 {
651  sf_vec* dbc;
652 
653  SF::init_vector(&dbc, *mat.mesh_ptr(), mat.dpn_row(), sf_vec::algebraic);
654 
655  for(auto it = active_dbc.begin(); it != active_dbc.end(); ++it) {
656  const SF::vector<SF_int> & dbc_nod = *it->second->nod;
657  // zero cols and rows in lhs mat
658  dbc->set(1.0);
659  dbc->set(dbc_nod, 0.0);
660  // multiplicate dbc vector from left and right to matrix in order to zero cols and rows
661  mat.mult_LR(*dbc, *dbc);
662  // now add 1.0 to the diagonal at the dirichlet locations
663  dbc->set(0.0);
664  dbc->set(dbc_nod, 1.0);
665  mat.diag_add(*dbc);
666  }
667 }
668 
670 {
671  for(auto it = active_dbc.begin(); it != active_dbc.end(); ++it) {
672  const int & dbc_idx = it->first;
673  const dbc_manager::dbc_data & dbc = *(it->second);
674  double strength = 0.0;
675  bool is_active = stimuli[dbc_idx].value(strength);
676 
677  // the whole idea of the dbc_manager and its active_dbc is
678  // based on only storing the active dbcs. if the stimulus associated
679  // to a dbc believed active is actually not active, we want to abort.
680  assert(is_active);
681 
682  rhs.add_scaled(*dbc.cntr, -strength);
683 
684  if(stimuli[dbc_idx].electrode.scaling.size()) {
685  SF::vector<SF_real> scaled_strength(stimuli[dbc_idx].electrode.scaling.size());
686 
687  size_t widx = 0;
688  for(SF_real s : stimuli[dbc_idx].electrode.scaling)
689  scaled_strength[widx++] = s * strength;
690 
691  rhs.set(*dbc.nod, scaled_strength);
692  } else {
693  rhs.set(*dbc.nod, strength);
694  }
695  }
696 }
697 
698 } // namespace opencarp
699 
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:306
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:203
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 voltage as stimulation
Definition: stimulate.cc:65
void globalize(vector< T > &lvec) const
Globalize local indices.
void restrict_to_set(vector< T > &v, const hashmap::unordered_set< T > &set)
FILE_SPEC logger
The logger of the physic, each physic should have one.
Definition: physics_types.h:64
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:669
#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)
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:593
bool value(double &v) const
Get the current value if the stimulus is active.
Definition: stimulate.cc:442
monophasic truncated exponentials (capacitive discharge)
Definition: signals.h:994
waveform_t wform
wave form of stimulus
Definition: stimulate.h:100
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:95
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
virtual void set(const vector< T > &idx, const vector< S > &vals, const bool additive=false)=0
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
Basic_physic * get_physics(physic_t p, bool error_if_missing)
Convinience function to get a physics.
Definition: sim_utils.cc:818
bool dbc_update()
check if dbcs have updated
Definition: stimulate.cc:632
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:99
sig::time_trace wave
wave form of stimulus pulse
Definition: stimulate.h:102
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:441
void get_stim_list(const char *str_list, std::vector< double > &stlist)
Definition: stimulate.cc:264
File descriptor struct.
Definition: basics.h:133
int set_dir(IO_t dest)
Definition: sim_utils.cc:446
#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:1255
void sample_wave_form(stim_pulse &sp, int idx)
sample a signal given in analytic form
Definition: stimulate.cc:365
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:84
int get_rank(MPI_Comm comm=PETSC_COMM_WORLD)
Definition: basics.h:276
void setup(int idx)
assign stimulus physics parameters
Definition: stimulate.cc:334
#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