openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
electrics_eikonal.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 "electrics_eikonal.h"
28 #include "basics.h"
29 #include "petsc_utils.h"
30 #include "timers.h"
31 #include "stimulate.h"
32 
33 #include "SF_init.h"
34 
35 namespace opencarp
36 {
37 
39 {
40  double t1, t2;
41  get_time(t1);
42 
43  set_dir(OUTPUT);
44 
45  // open logger
46  logger = f_open("eikonal.log", param_globals::experiment != 4 ? "w" : "r");
47 
48  // setup mappings between extra and intra grids, algebraic and nodal,
49  // and between PETSc and canonical orderings
50  setup_mappings();
51 
52  eik_tech = static_cast<Eikonal::eikonal_t>(param_globals::dream.solve);
53 
54  // the ionic physics is currently triggered from inside the Electrics to have tighter
55  // control over it.
56  switch (eik_tech) {
57  case EIKONAL: break;
58  default:
59  ion.logger = logger;
60  ion.initialize();
61  }
62 
63  // set up Intracellular tissue
64  set_elec_tissue_properties(mtype, intra_grid, logger);
65  region_mask(intra_elec_msh, mtype[intra_grid].regions, mtype[intra_grid].regionIDs, true, "gregion_i");
66 
67  // add electrics timer for time stepping, add to time stepper tool (TS)
68  double global_time = user_globals::tm_manager->time;
69  timer_idx = user_globals::tm_manager->add_eq_timer(global_time, param_globals::tend, 0,
70  param_globals::dt, 0, "elec::ref_dt", "TS");
71 
72  // electrics stimuli setup
73  setup_stimuli();
74 
75  // set up the linear equation systems. this needs to happen after the stimuli have been
76  // set up, since we need boundary condition info
77  setup_solvers();
78 
79  // the next setup steps require the solvers to be set up, since they use the matrices
80  // generated by those
81 
82  // initialize the LATs detector
83  switch (eik_tech) {
84  case EIKONAL: break; // not available for pure eikonal solve
85  default:
87  }
88 
89  // prepare the electrics output. we skip it if we do post-processing
90  if (param_globals::experiment != EXP_POSTPROCESS)
91  setup_output();
92 
93  if (param_globals::prepacing_bcl > 0)
94  prepace();
95 
96  this->initialize_time += timing(t2, t1);
97 }
98 
99 void Eikonal::set_elec_tissue_properties(MaterialType* mtype, Eikonal::grid_t g, FILE_SPEC logger)
100 {
101  MaterialType* m = mtype + g;
102 
103  // initialize random conductivity fluctuation structure with PrM values
104  m->regions.resize(param_globals::num_gregions);
105 
106  const char* grid_name = g == Eikonal::intra_grid ? "intracellular" : "extracellular";
107  log_msg(logger, 0, 0, "Setting up %s tissue poperties for %d regions ..", grid_name,
108  param_globals::num_gregions);
109 
110  char buf[64];
111  RegionSpecs* reg = m->regions.data();
112 
113  for (size_t i = 0; i < m->regions.size(); i++, reg++) {
114  if (!strcmp(param_globals::gregion[i].name, "")) {
115  snprintf(buf, sizeof buf, ", gregion_%d", int(i));
116  param_globals::gregion[i].name = dupstr(buf);
117  }
118 
119  reg->regname = strdup(param_globals::gregion[i].name);
120  reg->regID = i;
121  reg->nsubregs = param_globals::gregion[i].num_IDs;
122  if (!reg->nsubregs)
123  reg->subregtags = NULL;
124  else {
125  reg->subregtags = new int[reg->nsubregs];
126 
127  for (int j = 0; j < reg->nsubregs; j++)
128  reg->subregtags[j] = param_globals::gregion[i].ID[j];
129  }
130 
131  // describe material in given region
132  elecMaterial* emat = new elecMaterial();
133  emat->material_type = ElecMat;
134 
135  emat->InVal[0] = param_globals::gregion[i].g_il;
136  emat->InVal[1] = param_globals::gregion[i].g_it;
137  emat->InVal[2] = param_globals::gregion[i].g_in;
138 
139  emat->ExVal[0] = param_globals::gregion[i].g_el;
140  emat->ExVal[1] = param_globals::gregion[i].g_et;
141  emat->ExVal[2] = param_globals::gregion[i].g_en;
142 
143  emat->BathVal[0] = param_globals::gregion[i].g_bath;
144  emat->BathVal[1] = param_globals::gregion[i].g_bath;
145  emat->BathVal[2] = param_globals::gregion[i].g_bath;
146 
147  // convert units from S/m -> mS/um
148  for (int j = 0; j < 3; j++) {
149  emat->InVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
150  emat->ExVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
151  emat->BathVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
152  }
153  reg->material = emat;
154  }
155 
156  if ((g == Eikonal::intra_grid && strlen(param_globals::gi_scale_vec)) ||
157  (g == Eikonal::extra_grid && strlen(param_globals::ge_scale_vec))) {
159  sf_mesh& mesh = get_mesh(mt);
160  const char* file = g == Eikonal::intra_grid ? param_globals::gi_scale_vec : param_globals::ge_scale_vec;
161 
162  size_t num_file_entries = SF::root_count_ascii_lines(file, mesh.comm);
163 
164  if (num_file_entries != mesh.g_numelem)
165  log_msg(0, 4, 0, "%s warning: number of %s conductivity scaling entries does not match number of elements!",
166  __func__, get_mesh_type_name(mt));
167 
168  // set up parallel element vector and read data
169  sf_vec* escale;
170  SF::init_vector(&escale, get_mesh(mt), 1, sf_vec::elemwise);
171  escale->read_ascii(g == Eikonal::intra_grid ? param_globals::gi_scale_vec : param_globals::ge_scale_vec);
172 
173  if (get_size() > 1) {
174  // set up element vector permutation and permute
175  if (get_permutation(mt, ELEM_PETSC_TO_CANONICAL, 1) == NULL) {
177  }
179  sc(*escale, false);
180  }
181 
182  // copy data into SF::vector
183  SF_real* p = escale->ptr();
184  m->el_scale.assign(p, p + escale->lsize());
185  escale->release_ptr(p);
186  }
187 }
188 
189 void Eikonal::setup_mappings()
190 {
191  bool intra_exits = mesh_is_registered(intra_elec_msh), extra_exists = mesh_is_registered(extra_elec_msh);
192  assert(intra_exits);
193  const int dpn = 1;
194 
195  // It may be that another physic (e.g. ionic models) has already computed the intracellular mappings,
196  // thus we first test their existence
197  if (get_scattering(intra_elec_msh, ALG_TO_NODAL, dpn) == NULL) {
198  log_msg(logger, 0, 0, "%s: Setting up intracellular algebraic-to-nodal scattering.", __func__);
200  }
202  log_msg(logger, 0, 0, "%s: Setting up intracellular PETSc to canonical permutation.", __func__);
204  }
205 }
206 
208 {
209  double t1, t2;
210  get_time(t1);
211 
212  switch (eik_tech) {
213  case EIKONAL: solve_EIKONAL(); break;
214  case DREAM: solve_DREAM(); break;
215  default: solve_RE(); break;
216  }
217 
219  // output lin solver stats
221  }
222  this->compute_time += timing(t2, t1);
223 
224  // since the traces have their own timing, we check for trace dumps in the compute step loop
227 }
228 
230 {
231  double t1, t2;
232  get_time(t1);
233 
234  switch (eik_tech) {
235  case EIKONAL: break;
236  default:
238  output_manager_time.write_data(); // does not exist in EIKONAL
239  }
240 
241  if (do_output_eikonal) {
243  do_output_eikonal = false;
244  }
245 
246  double curtime = timing(t2, t1);
247  this->output_time += curtime;
248 
249  IO_stats.calls++;
250  IO_stats.tot_time += curtime;
251 
254 }
255 
260 {
261  // close logger
262  f_close(logger);
263 
264  switch (eik_tech) {
265  case EIKONAL:
267  break;
268  default:
269  // output LAT data
273  ion.destroy();
274  }
275 }
276 
277 void Eikonal::setup_stimuli()
278 {
279  // initialize basic stim info data (used units, supported types, etc)
280  init_stim_info();
281  bool dumpTrace = true;
282 
283  if (dumpTrace) set_dir(OUTPUT);
284 
285  stimuli.resize(param_globals::num_stim);
286  for (int i = 0; i < param_globals::num_stim; i++) {
287  // construct new stimulus
288  stimulus& s = stimuli[i];
289 
290  if (param_globals::stim[i].crct.type != 0 && param_globals::stim[i].crct.type != 9) {
291  // In the Eikonal class, only the intracellular domain is registered.
292  // Therefore, only I_tm and Vm_clmp are compatible.
293  log_msg(NULL, 5, 0, "%s error: stimulus of type %i is incompatible with the eikonal model! Use I_tm or Vm_clmp instead. Aborting!", __func__, s.phys.type);
294  EXIT(EXIT_FAILURE);
295  }
296 
298  s.translate(i);
299 
300  s.setup(i);
301 
302  log_msg(NULL, 2, 0, "Only geometry, start time, npls, and bcl of stim[%i] are used", i);
303 
304  if (dumpTrace && get_rank() == 0)
305  s.pulse.wave.write_trace(s.name + ".trc");
306  }
307 
309 }
310 
311 void Eikonal::stimulate_intracellular()
312 {
314 
315  // iterate over stimuli
316  for (stimulus& s : stimuli) {
317  if (s.is_active()) {
318  // for active stimuli, deal with the stimuli-type specific stimulus application
319  switch (s.phys.type) {
320  case I_tm: {
321  if (param_globals::operator_splitting) {
322  apply_stim_to_vector(s, *ps.Vmv, true);
323  } else {
324  SF_real Cm = 1.0;
326  SF_real sc = tm.time_step / Cm;
327 
328  ps.Irhs->set(0.0);
329  apply_stim_to_vector(s, *ps.Irhs, true);
330 
331  *ps.tmp_i1 = *ps.IIon;
332  *ps.tmp_i1 -= *ps.Irhs;
333  *ps.tmp_i1 *= sc; // tmp_i1 = sc * (IIon - Irhs)
334 
335  // add ionic, transmembrane and intracellular currents to rhs
336  if (param_globals::parab_solve != parabolic_solver::EXPLICIT)
337  ps.mass_i->mult(*ps.tmp_i1, *ps.Irhs);
338  else
339  *ps.Irhs = *ps.tmp_i1;
340  }
341  break;
342  }
343 
344  case Illum: {
345  sf_vec* illum_vec = ion.miif->gdata[limpet::illum];
346 
347  if (illum_vec == NULL) {
348  log_msg(0, 5, 0, "Cannot apply illumination stim: global vector not present!");
349  EXIT(EXIT_FAILURE);
350  } else {
351  apply_stim_to_vector(s, *illum_vec, false);
352  }
353 
354  break;
355  }
356 
357  default: break;
358  }
359  }
360  }
361 }
362 
363 void Eikonal::clamp_Vm()
364 {
365  for (stimulus& s : stimuli) {
366  if (s.phys.type == Vm_clmp && s.is_active())
368  }
369 }
370 
371 void Eikonal::setup_output()
372 {
373  int rank = get_rank();
374  SF::vector<mesh_int_t>* restr_i = NULL;
375  SF::vector<mesh_int_t>* restr_e = NULL;
376  set_dir(OUTPUT);
377 
378  setup_dataout(param_globals::dataout_i, param_globals::dataout_i_vtx, intra_elec_msh,
379  restr_i, param_globals::num_io_nodes > 0);
380 
381  if (param_globals::dataout_i) {
382  switch (eik_tech) {
383  case EIKONAL:
384  output_manager_cycle.register_output(eik_solver.AT, intra_elec_msh, 1, param_globals::dream.output.atfile, "ms", restr_i);
385  break;
386  case DREAM:
387  output_manager_time.register_output(parab_solver.Vmv, intra_elec_msh, 1, param_globals::vofile, "mV", restr_i);
388  output_manager_cycle.register_output(eik_solver.AT, intra_elec_msh, 1, param_globals::dream.output.atfile, "ms", restr_i);
389  output_manager_cycle.register_output(eik_solver.RT, intra_elec_msh, 1, param_globals::dream.output.rtfile, "ms", restr_i);
390  if (strcmp(param_globals::dream.output.idifffile, "") != 0) {
391  output_manager_time.register_output(eik_solver.Idiff, intra_elec_msh, 1, param_globals::dream.output.idifffile, "muA/cm2", restr_i);
392  }
393  break;
394  default:
395  output_manager_time.register_output(parab_solver.Vmv, intra_elec_msh, 1, param_globals::vofile, "mV", restr_i);
396  output_manager_cycle.register_output(eik_solver.AT, intra_elec_msh, 1, param_globals::dream.output.atfile, "ms", restr_i);
397  if (strcmp(param_globals::dream.output.idifffile, "") != 0) {
398  output_manager_time.register_output(eik_solver.Idiff, intra_elec_msh, 1, param_globals::dream.output.idifffile, "muA/cm2", restr_i);
399  }
400  }
401  }
402 
404 
405  if (param_globals::num_trace) {
406  sf_mesh& imesh = get_mesh(intra_elec_msh);
407  open_trace(ion.miif, param_globals::num_trace, param_globals::trace_node, NULL, &imesh);
408  }
409 
410  // initialize generic logger for IO timings per time_dt
411  IO_stats.init_logger("IO_stats.dat");
412 }
413 
414 void Eikonal::dump_matrices()
415 {
416  std::string bsname = param_globals::dump_basename;
417  std::string fn;
418 
419  set_dir(OUTPUT);
420 
421  // dump monodomain matrices
422  if (param_globals::parab_solve == 1) {
423  // using Crank-Nicolson
424  fn = bsname + "_Ki_CN.bin";
425  parab_solver.lhs_parab->write(fn.c_str());
426  }
427  fn = bsname + "_Ki.bin";
428  parab_solver.rhs_parab->write(fn.c_str());
429 
430  fn = bsname + "_Mi.bin";
431  parab_solver.mass_i->write(fn.c_str());
432 }
433 
436 double Eikonal::timer_val(const int timer_id)
437 {
438  // determine
439  int sidx = stimidx_from_timeridx(stimuli, timer_id);
440  double val = 0.0;
441  if (sidx != -1) {
442  stimuli[sidx].value(val);
443  } else
444  val = std::nan("NaN");
445 
446  return val;
447 }
448 
451 std::string Eikonal::timer_unit(const int timer_id)
452 {
453  int sidx = stimidx_from_timeridx(stimuli, timer_id);
454  std::string s_unit;
455 
456  if (sidx != -1)
457  // found a timer-linked stimulus
458  s_unit = stimuli[sidx].pulse.wave.f_unit;
459 
460  return s_unit;
461 }
462 
463 void Eikonal::setup_solvers()
464 {
465  set_dir(OUTPUT);
466 
467  switch (eik_tech) {
468  case EIKONAL:
469  eik_solver.init();
471  break;
472  default:
473  parab_solver.init();
474  parab_solver.rebuild_matrices(mtype, *ion.miif, logger);
475  eik_solver.init();
477  if (param_globals::dump2MatLab) dump_matrices();
478  }
479 }
480 
481 void Eikonal::checkpointing()
482 {
484 
485  // regular user selected state save
486  if (tm.trigger(iotm_chkpt_list)) {
487  char save_fnm[1024];
488  const char* tsav_ext = get_tsav_ext(tm.time);
489 
490  snprintf(save_fnm, sizeof save_fnm, "%s.%s.roe", param_globals::write_statef, tsav_ext);
491 
492  ion.miif->dump_state(save_fnm, tm.time, intra_elec_msh, false, GIT_COMMIT_COUNT);
493  eik_solver.save_eikonal_state(tsav_ext);
494  }
495 
496  // checkpointing based on interval
497  if (tm.trigger(iotm_chkpt_intv)) {
498  char save_fnm[1024];
499  snprintf(save_fnm, sizeof save_fnm, "checkpoint.%.1f.roe", tm.time);
500  ion.miif->dump_state(save_fnm, tm.time, intra_elec_msh, false, GIT_COMMIT_COUNT);
501  }
502 }
503 
504 void Eikonal::prepace()
505 {
506  // Default stimulation parameters for the single cells
507  // To be eventually be replaced by user input or info from bench
508  float stimdur = 1.;
509  float stimstr = 60.;
510 
511  log_msg(NULL, 0, 0, "Using activation times from file %s to distribute prepacing states\n",
512  param_globals::prepacing_lats);
513  log_msg(NULL, 1, 0, "Assuming stimulus strength %f uA/uF with duration %f ms for prepacing\n",
514  stimstr, stimdur);
515 
517  limpet::MULTI_IF* miif = elec->ion.miif;
518 
519  const sf_mesh& mesh = get_mesh(intra_elec_msh);
520  sf_vec* read_lats;
521  SF::init_vector(&read_lats, mesh, 1, sf_vec::algebraic);
522 
523  // read in the global distributed vector of all activation times
524  set_dir(INPUT);
525  size_t numread = read_lats->read_ascii(param_globals::prepacing_lats);
526  if (numread == 0) {
527  log_msg(NULL, 5, 0, "Failed reading required LATs! Skipping prepacing!");
528  return;
529  }
530  set_dir(OUTPUT);
531 
533  assert(sc != NULL);
534 
535  // permute in-place to petsc permutation
536  bool forward = false;
537  (*sc)(*read_lats, forward);
538 
539  // take care of negative LAT values
540  {
541  SF_real* lp = read_lats->ptr();
542  for (int i = 0; i < read_lats->lsize(); i++)
543  if (lp[i] < 0.0) lp[i] = param_globals::tend + 10.0;
544 
545  read_lats->release_ptr(lp);
546  }
547 
548  // make LATs relative and figure out the first LAT
549  // so we know when to save state of each point
550  SF_real LATmin = read_lats->min();
551 
552  if (LATmin < 0.0) {
553  log_msg(0, 3, 0, "LAT data is not complete. Skipping prepacing.");
554  return;
555  }
556 
557  SF_real offset = floor(LATmin / param_globals::prepacing_bcl) * param_globals::prepacing_bcl;
558  SF_real last_tm = param_globals::prepacing_bcl * param_globals::prepacing_beats;
559 
560  // compute read_lats[i] = last_tm - (read_lats[i] - offset)
561  *read_lats += -offset;
562  *read_lats *= -1.;
563  *read_lats += last_tm;
564 
565  miif->getRealData();
566  SF_real* save_tm = read_lats->ptr();
567  SF_real* vm = miif->gdata[limpet::Vm]->ptr();
568 
569  for (int ii = 0; ii < miif->N_IIF; ii++) {
570  if (!miif->N_Nodes[ii]) continue;
571 
572  // create sorted array of save times.
573  SF::vector<SF::mixed_tuple<double, int>> sorted_save(miif->N_Nodes[ii]); // v1 = time, v2 = index
574  for (int kk = 0; kk < miif->N_Nodes[ii]; kk++) {
575  sorted_save[kk].v1 = save_tm[miif->NodeLists[ii][kk]];
576  sorted_save[kk].v2 = kk;
577  }
578  std::sort(sorted_save.begin(), sorted_save.end());
579 
580  size_t lastidx = sorted_save.size() - 1;
581  int paced = sorted_save[lastidx].v2; // IMP index of latest node
582  int csav = 0;
583 
584  for (double t = 0; t < sorted_save[lastidx].v1; t += param_globals::dt) {
585  if (fmod(t, param_globals::prepacing_bcl) < stimdur &&
586  t < param_globals::prepacing_bcl * param_globals::prepacing_beats - 1)
587  miif->ldata[ii][limpet::Vm][paced] += stimstr * param_globals::dt;
588 
589  compute_IIF(*miif->IIF[ii], miif->ldata[ii], paced);
590 
591  // Vm update always happens now outside of the imp
592  miif->ldata[ii][limpet::Vm][paced] -= miif->ldata[ii][limpet::Iion][paced] * param_globals::dt;
593  vm[miif->NodeLists[ii][paced]] = miif->ldata[ii][limpet::Vm][paced];
594 
595  while (csav < miif->N_Nodes[ii] - 1 && t >= sorted_save[csav].v1)
596  dup_IMP_node_state(*miif->IIF[ii], paced, sorted_save[csav++].v2, miif->ldata[ii]);
597  }
598 
599  // get nodes which may be tied for last
600  while (csav < miif->N_Nodes[ii] - 1)
601  dup_IMP_node_state(*miif->IIF[ii], paced, sorted_save[csav++].v2, miif->ldata[ii]);
602  // ipdate global Vm vector
603  for (int k = 0; k < miif->N_Nodes[ii]; k++) vm[miif->NodeLists[ii][k]] = miif->ldata[ii][limpet::Vm][k];
604  }
605 
606  read_lats->release_ptr(save_tm);
607  miif->gdata[limpet::Vm]->release_ptr(vm);
608  miif->releaseRealData();
609 }
610 
611 void Eikonal::solve_EIKONAL()
612 {
613  if (user_globals::tm_manager->time == 0) {
614  eik_solver.FIM();
617  do_output_eikonal = true;
618  }
619 }
620 
621 void Eikonal::solve_RE()
622 {
623  if (user_globals::tm_manager->time == 0) {
624  eik_solver.FIM();
627  do_output_eikonal = true;
628  }
629  solve_RD();
630 }
631 
632 void Eikonal::solve_DREAM()
633 {
635  solve_RD();
636  } else {
637  if (user_globals::tm_manager->time != 0) {
639  }
640 
641  eik_solver.cycFIM();
643 
646  do_output_eikonal = true;
647  }
648 }
649 
650 void Eikonal::solve_RD()
651 {
652  // if requested, we checkpoint the current state
653  checkpointing();
654 
655  // activation checking
656  const double time = user_globals::tm_manager->time,
657  time_step = user_globals::tm_manager->time_step;
658 
659  lat.check_acts(time);
660  lat.check_quiescence(time, time_step);
661 
662  clamp_Vm();
663 
664  *parab_solver.old_vm = *parab_solver.Vmv; // needed in step D of DREAM
665 
666  // compute ionics update
667  ion.compute_step();
668 
669  // Compute the I diff current
670  *eik_solver.Idiff *= 0;
673 
674  if (eik_tech == REp) {
676  }
677 
678  switch (eik_tech) {
680  default: break;
681  }
682 
683  clamp_Vm();
684 }
685 
687 {
688  if (param_globals::output_level > 1) log_msg(0, 0, 0, "\n *** Initializing Eikonal Solver ***\n");
689 
690  if (get_size() > 1) {
691  log_msg(NULL, 5, 0, "The DREAM does not run in parallel. Aborting!");
692  EXIT(EXIT_FAILURE);
693  }
694 
695  stats.init_logger("eik_stats.dat");
696 
697  if (param_globals::dream.output.debugNode >= 0) {
698  nodeData.idX = param_globals::dream.output.debugNode;
699  char buf[256];
700  snprintf(buf, sizeof buf, "node_%d.dat", nodeData.idX);
701  nodeData.init_logger(buf);
702  }
703 
704  // currently only used for output
705  const sf_mesh& mesh = get_mesh(intra_elec_msh);
706  const int dpn = 1;
707  SF::init_vector(&Idiff, mesh, dpn, sf_vec::algebraic);
708  SF::init_vector(&AT, mesh, dpn, sf_vec::algebraic);
709  SF::init_vector(&RT, mesh, dpn, sf_vec::algebraic);
710 
711  num_pts = mesh.l_numpts;
712 
713  e2n_con = mesh.con; // Connectivity vector with nodes that belong to each element
714  // The 4 nodes from index 4j to 4j+3 belong to the same element for 0<=j<num elements
715  elem_start = mesh.dsp; // For the i_th element elem_start[j] stores its starting position in the "connect" vector
716 
717  cnt_from_dsp(elem_start, e2n_cnt);
718  transpose_connectivity(e2n_cnt, e2n_con, n2e_cnt, n2e_con);
719  dsp_from_cnt(n2e_cnt, n2e_dsp);
720 
721  x_coord.resize(num_pts);
722  y_coord.resize(num_pts);
723  z_coord.resize(num_pts);
724  fiber_node.resize(num_pts * 3, 0);
725  sheet_node.resize(num_pts * 3, 0);
726 
727  for (int i = 0; i < ((mesh.xyz.size() + 1) / 3); i++) {
728  x_coord[i] = mesh.xyz[3 * i + 0];
729  y_coord[i] = mesh.xyz[3 * i + 1];
730  z_coord[i] = mesh.xyz[3 * i + 2];
731  }
732 
733  diff_cur.resize(num_pts);
734 
735  D.set_size(3, 3);
736  iD.set_size(3, 3);
737 
738  List.resize(num_pts, 0); // List of active nodes
739  T_A.resize(num_pts, inf); // Activation times
740  D_I.resize(num_pts, 0); // Diastolic Intervals
741  T_R.resize(num_pts, -400); // Recovery times
742  TA_old.resize(num_pts, inf); // Activation Time is the previous cycle
743  num_changes.resize(num_pts, 0); // Number of Times entered in the List per current LAT
744  nReadded2List.resize(num_pts, 0); // Number of Times entered in the List per current LAT
745  stim_status.resize(num_pts, 0); // Status of nodes
746 
747  CVnodes.resize(num_pts, 0);
748  p_cvrest.resize(num_pts);
749  k_cvrest.resize(num_pts);
750  j_cvrest.resize(num_pts);
751  l_cvrest.resize(num_pts);
752  cv_ar_t.resize(num_pts, -1);
753  cv_ar_n.resize(num_pts, -1);
754 
755  StimulusPoints.resize(num_pts * 10, -1); // Nodes that have initial stimulus
756  StimulusTimes.resize(num_pts * 10, -1); // Times when the initial nodes are stimulated
757 
758  switch (mesh.type[0]) {
759  case 0:
760  numPtsxElem = 4;
761  break;
762 
763  case 6:
764  numPtsxElem = 3;
765  break;
766 
767  case 7:
768  numPtsxElem = 2;
769  break;
770 
771  default:
772  log_msg(0, 5, 0, "Error: Type of element is not compatible with this version of the eikonal model. Use tetrahedra or triangles");
773  EXIT(EXIT_FAILURE);
774  break;
775  }
776 
777  if (strlen(param_globals::start_statef) > 0) load_state_file();
778 
779  create_node_to_node_graph();
780 
781  translate_stim_to_eikonal();
782 
783  init_diffusion_current();
784 }
785 
786 void eikonal_solver::bubble_sort(SF::vector<SF_real>& Array, SF::vector<mesh_int_t>& IndexArray, int& length)
787 {
788  SF_real Temp_a;
789  mesh_int_t Temp_i;
790  for (int i = 0; i < length - 1; i++) {
791  for (int j = 0; j < length - i - 1; j++) {
792  if (Array[j] > Array[j + 1]) {
793  Temp_a = Array[j];
794  Array[j] = Array[j + 1];
795  Array[j + 1] = Temp_a;
796  Temp_i = IndexArray[j];
797  IndexArray[j] = IndexArray[j + 1];
798  IndexArray[j + 1] = Temp_i;
799  }
800  }
801  }
802 }
803 
805 {
806  bool act_is_in_safety_window, empty_list_and_illegal_stimulus, empty_stimulus, stimulus_is_in_safety_window;
807 
808  act_is_in_safety_window = (actMIN > param_globals::dream.tau_s) && (actMIN - time > param_globals::dream.tau_s);
809  empty_stimulus = (StimulusPoints[Index_currStim] == -1);
810  stimulus_is_in_safety_window = (StimulusTimes[Index_currStim] - time > param_globals::dream.tau_s);
811  empty_list_and_illegal_stimulus = (sum(List) == 0) && (empty_stimulus || stimulus_is_in_safety_window);
812 
813  return act_is_in_safety_window || empty_list_and_illegal_stimulus;
814 }
815 
817 {
818  const sf_mesh& mesh = get_mesh(intra_elec_msh);
819  SF::vector<mesh_int_t> vertices;
820  for (int num_g = 0; num_g < param_globals::num_gregions; num_g++) {
821  for (int num_id = 0; num_id <= param_globals::gregion[num_g].num_IDs; num_id++) {
822  indices_from_region_tag(vertices, mesh, param_globals::gregion[num_g].ID[num_id]);
823  for (int i = 0; i < vertices.size(); i++) {
824  CVnodes[vertices[i]] = param_globals::gregion[num_g].dream.vel_l;
825  cv_ar_t[vertices[i]] = param_globals::gregion[num_g].dream.vel_l / param_globals::gregion[num_g].dream.vel_t;
826  if (twoFib) {
827  cv_ar_n[vertices[i]] = param_globals::gregion[num_g].dream.vel_l / param_globals::gregion[num_g].dream.vel_n;
828  }
829  }
830  }
831  }
832  CVnodes_mod = CVnodes;
833 
834  for (int num_i = 0; num_i < param_globals::num_imp_regions; num_i++) {
835  for (int num_id = 0; num_id <= param_globals::imp_region[num_i].num_IDs; num_id++) {
836  indices_from_region_tag(vertices, mesh, param_globals::imp_region[num_i].ID[num_id]);
837  for (int i = 0; i < vertices.size(); i++) {
838  p_cvrest[vertices[i]] = param_globals::imp_region[num_i].dream.CVrest.rho;
839  k_cvrest[vertices[i]] = param_globals::imp_region[num_i].dream.CVrest.kappa;
840  j_cvrest[vertices[i]] = param_globals::imp_region[num_i].dream.CVrest.theta;
841  l_cvrest[vertices[i]] = param_globals::imp_region[num_i].dream.CVrest.psi;
842  }
843  }
844  }
845 
846  if (strlen(param_globals::cv_scale_vec)) {
847  mesh_t mt = intra_elec_msh;
848  const char* file = param_globals::cv_scale_vec;
849 
850  size_t num_file_entries = SF::root_count_ascii_lines(file, mesh.comm);
851 
852  if (num_file_entries != mesh.g_numpts)
853  log_msg(0, 2, 0, "%s warning: number of %s conductivity scaling entries does not match number of points!,%i,%i",
854  __func__, get_mesh_type_name(mt), num_file_entries, mesh.g_numpts);
855 
856  // set up parallel point vector and read data
857  sf_vec* escale;
858  SF::init_vector(&escale, mesh, 1, sf_vec::nodal);
859  escale->read_ascii(file);
860 
861  if (get_size() > 1) {
862  // set up point vector permutation and permute
863  if (get_permutation(mt, PETSC_TO_CANONICAL, 1) == NULL) {
865  }
867  sc(*escale, false);
868  }
869 
870  // copy data into SF::vector
871  SF::vector<double> nd_scale;
872  SF_real* p = escale->ptr();
873  nd_scale.assign(p, p + escale->lsize());
874  escale->release_ptr(p);
875 
876  for (int num_g = 0; num_g < param_globals::num_gregions; num_g++) {
877  for (int num_id = 0; num_id < param_globals::gregion[num_g].num_IDs; num_id++) {
878  indices_from_region_tag(vertices, mesh, param_globals::gregion[num_g].ID[num_id]);
879  for (int i = 0; i < vertices.size(); i++) {
880  CVnodes[vertices[i]] *= nd_scale[vertices[i]];
881  }
882  }
883  }
884  CVnodes_mod = CVnodes;
885  }
886 } // Close set initial CV
887 
888 void eikonal_solver::update_Ta_in_active_list()
889 {
890  bool First_in_List = 1;
891 
892  for (size_t j = 0; j < List.size(); j++) {
893  if (T_A[j] < 0) continue;
894  if (List[j] == 1 && First_in_List) {
895  actMIN = T_A[j];
896  actMAX = T_A[j];
897  First_in_List = 0;
898  continue;
899  }
900  if (List[j] == 1) {
901  if (T_A[j] < actMIN) actMIN = T_A[j];
902  if (T_A[j] > actMAX) actMAX = T_A[j];
903  }
904  }
905 }
906 
908 {
909  for (size_t j = 0; j < List.size(); j++) {
910  if (T_A[j] < user_globals::tm_manager->time) {
911  TA_old[j] = T_A[j];
912  stim_status[j] = 0;
913  nReadded2List[j] = 0;
914 
915  if (List[j] == 1) List[j] = 0;
916  }
917  }
918 }
919 
920 SF_real eikonal_solver::compute_H(mesh_int_t& indX, SF_real& T_A_indX)
921 {
922  float delay = 0;
923  if (stim_status[indX] == 2) delay = 5;
924 
925  if (T_A_indX == inf || (T_R[indX] + delay) == -400 || abs(T_A_indX - (TA_old[indX])) < 2) {
926  return CVnodes[indX];
927  } else if ((T_R[indX]) > T_A_indX) {
928  return CVnodes[indX];
929  } else {
930  float DI_indX = T_A_indX - (T_R[indX] + delay);
931  D_I[indX] = DI_indX;
932 
933  float Factor_DI;
934  float denom = log(p_cvrest[indX]) / l_cvrest[indX];
935  Factor_DI = 1 - p_cvrest[indX] * exp(-(DI_indX + k_cvrest[indX]) * denom);
936 
937  if (Factor_DI > 0) {
938  if (DI_indX < j_cvrest[indX]) {
939  return -CVnodes[indX] * Factor_DI;
940  } else {
941  return CVnodes[indX] * Factor_DI;
942  }
943  } else {
944  return 0;
945  }
946  }
947 }
948 
949 SF_real eikonal_solver::compute_coherence(mesh_int_t& indX)
950 {
951  SF_real p = T_A[indX];
952  int iter_ch = 0;
953  SF_real q = compute_F(indX, p);
954  while (abs(p - q) >= param_globals::dream.fim.tol && iter_ch < param_globals::dream.fim.max_coh) {
955  p = q;
956  q = compute_F(indX, p);
957  iter_ch++;
958  }
959  if (compute_H(indX, q) < 0) return T_A[indX];
960  return q;
961 }
962 
964 {
965  bool EmpList = sum(List) == 0;
966 
967  if (EmpList) {
968  for (size_t j = Index_currStim; j < StimulusTimes.size(); j++) {
969  if ((StimulusPoints[j] == -1) || (StimulusTimes[j] > StimulusTimes[Index_currStim])) {
970  Index_currStim = j;
971  break;
972  }
973 
974  mesh_int_t indNode = StimulusPoints[j];
975  SF_real TimeSt = StimulusTimes[j];
976 
977  bool cond2add = add_node_neighbor_to_list(T_R[indNode], T_A[indNode], TimeSt);
978  if (cond2add && compute_H(indNode, TimeSt) > 0) {
979  if (T_A[indNode] < TimeSt) {
980  CVnodes_mod[indNode] = -CVnodes_mod[indNode];
981  }
982  T_A[indNode] = TimeSt;
983  stim_status[indNode] = 1;
984  stats.bc_status = true;
985 
986  if (param_globals::dream.output.debugNode == indNode) {
987  nodeData.T_A = TimeSt;
988  nodeData.update_status(node_stats::in, node_stats::stim);
989  }
990 
991  for (int ii = n2n_dsp[indNode]; ii < n2n_dsp[indNode + 1]; ii++) {
992  mesh_int_t indXNB = n2n_connect[ii];
993  if (List[indXNB] == 0) {
994  List[indXNB] = 1;
995  if (param_globals::dream.output.debugNode == indXNB) {
996  nodeData.update_status(node_stats::in, node_stats::nbn);
997  nodeData.idXNB = indNode;
998  nodeData.nbn_T_A = TimeSt;
999  }
1000  }
1001  }
1002  }
1003 
1004  if (cond2add && compute_H(indNode, TimeSt) <= 0) {
1005  stim_status[indNode] = 2;
1006  }
1007  }
1008 
1009  // After NB of initial points are assigned remove initial points from the list if they were added in the previous loop.
1010  for (size_t j = 0; j < StimulusPoints.size(); j++) {
1011  if ((StimulusPoints[j] == -1) && (StimulusTimes[j] > StimulusTimes[Index_currStim])) continue;
1012  if (List[StimulusPoints[j]] == 1) {
1013  List[StimulusPoints[j]] = 0;
1014  if (param_globals::dream.output.debugNode == StimulusPoints[j]) nodeData.update_status(node_stats::out, node_stats::stim);
1015  }
1016  }
1017 
1018  if (StimulusTimes[Index_currStim] != -1) actMAX = StimulusTimes[Index_currStim];
1019  } else {
1020  for (size_t i = Index_currStim; i < StimulusTimes.size(); i++) {
1021  mesh_int_t indNode = StimulusPoints[i];
1022  SF_real TimeSt = StimulusTimes[i];
1023 
1024  if (indNode == -1) continue;
1025 
1026  if (TimeSt <= actMAX) {
1027  bool cond2add = add_node_neighbor_to_list(T_R[indNode], T_A[indNode], TimeSt);
1028  if (cond2add && compute_H(indNode, TimeSt) > 0) {
1029  T_A[indNode] = TimeSt;
1030  stim_status[indNode] = 1;
1031  stats.bc_status = true;
1032 
1033  if (param_globals::dream.output.debugNode == indNode) {
1034  nodeData.T_A = TimeSt;
1035  nodeData.update_status(node_stats::in, node_stats::stim);
1036  }
1037 
1038  for (int ii = n2n_dsp[indNode]; ii < n2n_dsp[indNode + 1]; ii++) {
1039  mesh_int_t indXNB = n2n_connect[ii];
1040  if (List[indXNB] == 0) {
1041  List[indXNB] = 1;
1042  if (param_globals::dream.output.debugNode == indXNB) {
1043  nodeData.update_status(node_stats::in, node_stats::nbn);
1044  nodeData.idXNB = indNode;
1045  nodeData.nbn_T_A = TimeSt;
1046  }
1047  }
1048  }
1049 
1050  for (size_t j = 0; j < StimulusPoints.size(); j++) {
1051  if ((StimulusPoints[j] == -1) && (StimulusTimes[j] > StimulusTimes[Index_currStim])) continue;
1052  if (List[StimulusPoints[j]] == 1) {
1053  List[StimulusPoints[j]] = 0;
1054  if (param_globals::dream.output.debugNode == StimulusPoints[j]) nodeData.update_status(node_stats::out, node_stats::stim);
1055  }
1056  }
1057  }
1058  if (cond2add && compute_H(indNode, TimeSt) <= 0) {
1059  stim_status[indNode] = 2;
1060  }
1061 
1062  } else {
1063  Index_currStim = i;
1064 
1065  break;
1066  }
1067  }
1068  }
1069 
1070 } // end compute stimulus
1071 
1073 {
1074  int niter = 0;
1075  double t1, t0;
1076  get_time(t0);
1077 
1078  for (size_t i = Index_currStim; i < StimulusTimes.size(); i++) {
1079  mesh_int_t indNode = StimulusPoints[i];
1080  SF_real TimeSt = StimulusTimes[i];
1081  if (StimulusPoints[i] == -1) break;
1082 
1083  T_A[indNode] = TimeSt;
1084 
1085  for (size_t j = n2n_dsp[indNode]; j < n2n_dsp[indNode + 1]; j++) {
1086  if (T_A[n2n_connect[j]] == inf) {
1087  List[n2n_connect[j]] = 1;
1088  }
1089  }
1090  }
1091 
1092  do {
1093  for (mesh_int_t indX = 0; indX < List.size(); indX++) {
1094  if (List[indX] == 0) continue;
1095 
1096  SF_real p = T_A[indX];
1097  SF_real q = compute_F(indX, p);
1098  T_A[indX] = q;
1099 
1100  if (abs(p - q) < param_globals::dream.fim.tol) {
1101  for (int ii = n2n_dsp[indX]; ii < n2n_dsp[indX + 1]; ii++) {
1102  mesh_int_t indXNB = n2n_connect[ii];
1103  if (List[indXNB] == 1) continue;
1104  p = T_A[indXNB];
1105  q = compute_F(indXNB, p);
1106  if (p > q) {
1107  T_A[indXNB] = q;
1108  List[indXNB] = 1;
1109  }
1110  }
1111  List[indX] = 0;
1112  }
1113  }
1114 
1115  update_Ta_in_active_list();
1116  niter++;
1117 
1118  if (niter > param_globals::dream.fim.max_iter) break;
1119 
1120  } while (sum(List) > 0);
1121 
1122  // copy for igb output
1123  double* atc = AT->ptr();
1124  for (mesh_int_t i = 0; i < List.size(); i++) {
1125  if (T_A[i] == inf) {
1126  // for better visualization in meshalyzer
1127  atc[i] = -1;
1128  } else {
1129  atc[i] = T_A[i];
1130  }
1131  }
1132  AT->release_ptr(atc);
1133 
1134  // treat solver statistics
1135  auto dur = timing(t1, t0);
1136  stats.slvtime_A += dur;
1137  stats.update_iter(niter);
1138  stats.minAT = actMIN;
1139  stats.maxAT = actMAX;
1140  stats.activeList = sum(List);
1141  stats.update_cli(user_globals::tm_manager->time, false);
1142 }
1143 
1145 {
1146  int niter = 0;
1147  double t1, t0;
1148  get_time(t0);
1149 
1150  if (sum(List) == 0) {
1151  compute_bc();
1152  }
1153 
1154  time2stop_eikonal = param_globals::dream.tau_inc;
1155  float maxadvance = user_globals::tm_manager->time + param_globals::dream.tau_s + param_globals::dream.tau_inc + param_globals::dream.tau_max;
1156 
1157  do {
1158  if (StimulusTimes[Index_currStim] <= maxadvance) {
1159  compute_bc();
1160  }
1161 
1162  for (mesh_int_t indX = 0; indX < List.size(); indX++) {
1163  SF_real p = T_A[indX];
1164  SF_real q;
1165 
1166  if (List[indX] == 0) continue;
1167 
1168  q = compute_coherence(indX);
1169 
1170  T_A[indX] = q;
1171  num_changes[indX] = num_changes[indX] + 1;
1172 
1173  if (q > maxadvance) continue;
1174 
1175  if (abs(p - q) < param_globals::dream.fim.tol || (num_changes[indX] > param_globals::dream.fim.max_iter) || (q == inf || p == inf)) {
1176  for (int ii = n2n_dsp[indX]; ii < n2n_dsp[indX + 1]; ii++) {
1177  mesh_int_t indXNB = n2n_connect[ii];
1178 
1179  if (List[indXNB] == 1) {
1180  continue;
1181  }
1182 
1183  SF_real pNB = T_A[indXNB];
1184  SF_real qNB;
1185 
1186  qNB = compute_coherence(indXNB);
1187 
1188  bool node_is_valid = add_node_neighbor_to_list(T_R[indXNB], pNB, qNB) && qNB != inf && qNB > user_globals::tm_manager->time;
1189  // This second condition is there to be able to add a node to the list when a new valid activation time
1190  // is found but a reentry would be blocked by the L2 parameter. Normally this condition does not make or brake the
1191  // simulation but would leave individual nodes inactivated, which is not ideal.
1192  bool ignore_L2_if_valid = node_is_valid && qNB > pNB && nReadded2List[indXNB] >= param_globals::dream.fim.max_addpt;
1193 
1194  if (node_is_valid && (nReadded2List[indXNB] < param_globals::dream.fim.max_addpt) || ignore_L2_if_valid) {
1195  if (qNB > pNB) {
1196  nReadded2List[indXNB] = 0;
1197  }
1198  T_A[indXNB] = qNB;
1199  nReadded2List[indXNB]++;
1200  num_changes[indXNB] = 0;
1201  List[indXNB] = 1;
1202  if (param_globals::dream.output.debugNode == indXNB) {
1203  nodeData.update_status(node_stats::in, node_stats::nbn);
1204  nodeData.idXNB = indX;
1205  nodeData.nbn_T_A = q;
1206  }
1207  }
1208  }
1209 
1210  List[indX] = 0;
1211  if (param_globals::dream.output.debugNode == indX) {
1212  nodeData.update_status(node_stats::out, node_stats::conv);
1213  }
1214  }
1215  }
1216 
1217  SF_real actMIN_old = actMIN;
1218  SF_real progress_time;
1219 
1220  update_Ta_in_active_list();
1221 
1222  if (actMIN > actMIN_old) {
1223  progress_time = actMIN - actMIN_old;
1224  } else {
1225  progress_time = 0;
1226  }
1227 
1228  time2stop_eikonal -= progress_time;
1229  niter++;
1230 
1231  } while (time2stop_eikonal > 0 && sum(List) > 0);
1232 
1233  if (sum(List) == 0) {
1234  if (StimulusTimes[Index_currStim] != -1) actMIN = StimulusTimes[Index_currStim];
1235  }
1236 
1237  // copy for igb output
1238  double* atc = AT->ptr();
1239  double* rpt = RT->ptr();
1240  for (mesh_int_t i = 0; i < List.size(); i++) {
1241  if (T_A[i] == inf) {
1242  // for better visualization in meshalyzer
1243  atc[i] = -1;
1244  } else {
1245  atc[i] = T_A[i];
1246  }
1247  rpt[i] = T_R[i];
1248  }
1249  AT->release_ptr(atc);
1250  RT->release_ptr(rpt);
1251 
1252  // treat solver statistics
1253  auto dur = timing(t1, t0);
1254  stats.slvtime_A += dur;
1255  stats.update_iter(niter);
1256  stats.minAT = actMIN;
1257  stats.maxAT = actMAX;
1258  stats.activeList = sum(List);
1259  stats.update_cli(user_globals::tm_manager->time, false);
1260 
1261  // treat node stats
1262  if (param_globals::dream.output.debugNode >= 0) {
1263  nodeData.T_A = T_A[nodeData.idX];
1264  nodeData.T_R = T_R[nodeData.idX];
1265  nodeData.D_I = D_I[nodeData.idX];
1266  }
1267 
1268 } // close iterate list
1269 
1270 SF_real eikonal_solver::compute_F(mesh_int_t& indX, SF_real& T_A_indX)
1271 {
1272  const double time = user_globals::tm_manager->time;
1273  TA = inf;
1274  SF::vector<mesh_int_t> Winner_Element(3, -1);
1275  CVnodes_mod[indX] = compute_H(indX, T_A_indX);
1276 
1277  CV_l = abs(CVnodes_mod[indX]);
1278 
1279  if (CV_l == 0) {
1280  return TA;
1281  }
1282 
1283  SF::Point fib, she;
1284 
1285  if (twoFib) {
1286  fib.x = fiber_node[3 * indX + 0];
1287  fib.y = fiber_node[3 * indX + 1];
1288  fib.z = fiber_node[3 * indX + 2];
1289 
1290  she.x = sheet_node[3 * indX + 0];
1291  she.y = sheet_node[3 * indX + 1];
1292  she.z = sheet_node[3 * indX + 2];
1293 
1294  SF_real ani_ratio_t2 = cv_ar_t[indX] * cv_ar_t[indX];
1295  SF_real ani_ratio_n2 = cv_ar_n[indX] * cv_ar_n[indX];
1296  SF::Point n = cross(fib, she);
1297  SF::outer_prod(fib, fib, 1, D[0], false);
1298  SF::outer_prod(she, she, 1 / ani_ratio_t2, D[0], true);
1299  SF::outer_prod(n, n, 1 / ani_ratio_n2, D[0], true);
1300 
1301  iD = invert_3x3(D);
1302 
1303  } else {
1304  fib.x = fiber_node[3 * indX + 0];
1305  fib.y = fiber_node[3 * indX + 1];
1306  fib.z = fiber_node[3 * indX + 2];
1307 
1308  SF_real ani_ratio2 = cv_ar_t[indX] * cv_ar_t[indX];
1309  SF::outer_prod(fib, fib, (ani_ratio2 - 1) / ani_ratio2, D[0]);
1310  D[0][0] += (1 / ani_ratio2), D[1][1] += (1 / ani_ratio2), D[2][2] += (1 / ani_ratio2);
1311  iD = invert_3x3(D);
1312  }
1313 
1314  if (numPtsxElem == 4) {
1315  // Solve for Tetrahedron
1316  for (int e_i = n2e_dsp[indX]; e_i < n2e_dsp[indX + 1]; e_i++) {
1317  int Elem_i = n2e_con[e_i];
1318  mesh_int_t indEle = elem_start[Elem_i];
1319 
1320  std::vector<mesh_int_t> Elem_cur = {e2n_con[indEle], e2n_con[indEle + 1], e2n_con[indEle + 2], e2n_con[indEle + 3]};
1321  std::remove(Elem_cur.begin(), Elem_cur.end(), indX); // intentionally used to move indX to the end of Elem_cur but not to be erased
1322 
1323  condign1 = T_A[Elem_cur[0]] == inf || T_A[Elem_cur[1]] == inf || T_A[Elem_cur[2]] == inf || (T_A[Elem_cur[0]] < time) || (T_A[Elem_cur[1]] < time) || (T_A[Elem_cur[2]] < time);
1324  condign2 = (T_A[Elem_cur[0]] < T_R[Elem_cur[0]]) || (T_A[Elem_cur[1]] < T_R[Elem_cur[1]]) || (T_A[Elem_cur[2]] < T_R[Elem_cur[2]]);
1325  condign3 = CV_l == 0;
1326  condign4 = stim_status[indX] == 2 && (stim_status[Elem_cur[0]] == 1 || stim_status[Elem_cur[1]] == 1 || stim_status[Elem_cur[2]] == 1);
1327 
1328  if (!(condign1 || condign2 || condign3 || condign4)) {
1329  SF_real temp3 = solve_tet(indX, Elem_cur);
1330 
1331  if (temp3 < TA && temp3 > T_R[indX] && compute_H(indX, temp3) > 0) {
1332  Winner_Element[0] = Elem_cur[0];
1333  Winner_Element[1] = Elem_cur[1];
1334  Winner_Element[2] = Elem_cur[2];
1335  TA = temp3;
1336  }
1337  }
1338 
1339  std::vector<mesh_int_t> Triang = {-1, -1};
1340  // Solve triangles in Tetrahedra
1341  for (int cases = 0; cases < 3; cases++) {
1342  switch (cases) {
1343  case 0:
1344  Triang[0] = Elem_cur[0];
1345  Triang[1] = Elem_cur[1];
1346  break;
1347 
1348  case 1:
1349  Triang[0] = Elem_cur[0];
1350  Triang[1] = Elem_cur[2];
1351  break;
1352 
1353  case 2:
1354  Triang[0] = Elem_cur[1];
1355  Triang[1] = Elem_cur[2];
1356  break;
1357 
1358  default:
1359  break;
1360  }
1361 
1362  condign1 = T_A[Triang[0]] == inf || T_A[Triang[1]] == inf || (T_A[Triang[0]] < time) || (T_A[Triang[1]] < time);
1363  condign2 = (T_A[Triang[0]] < T_R[Triang[0]]) || (T_A[Triang[1]] < T_R[Triang[1]]); //||abs(T_A[Triang[0]] - TA_old[Triang[0]])<2||abs(T_A[Triang[1]] - TA_old[Triang[1]])<2;
1364  condign3 = CV_l == 0;
1365  condign4 = stim_status[indX] == 2 && (stim_status[Triang[0]] == 1 || stim_status[Triang[1]] == 1);
1366 
1367  if (!(condign1 || condign2 || condign3 || condign4)) {
1368  SF_real temp2 = solve_tri(indX, Triang);
1369 
1370  if (temp2 < TA && temp2 > T_R[indX] && compute_H(indX, temp2) > 0) {
1371  Winner_Element[0] = Triang[0];
1372  Winner_Element[1] = Triang[1];
1373  Winner_Element[2] = -1;
1374  TA = temp2;
1375  }
1376  }
1377  }
1378  }
1379  } // numPtsxElem == 4
1380 
1381  if (numPtsxElem == 3) {
1382  for (int e_i = n2e_dsp[indX]; e_i < n2e_dsp[indX + 1]; e_i++) {
1383  int Elem_i = n2e_con[e_i];
1384  mesh_int_t indEle = elem_start[Elem_i];
1385 
1386  std::vector<mesh_int_t> Elem_cur = {e2n_con[indEle], e2n_con[indEle + 1], e2n_con[indEle + 2]};
1387  std::remove(Elem_cur.begin(), Elem_cur.end(), indX); // intentionally used to move indX to the end of Elem_cur but not to be erased
1388 
1389  condign1 = T_A[Elem_cur[0]] == inf || T_A[Elem_cur[1]] == inf || (T_A[Elem_cur[0]] < time) || (T_A[Elem_cur[1]] < time); //||abs(T_A[Elem_cur[0]] - TA_old[Elem_cur[0]])<2||abs(T_A[Elem_cur[1]] - TA_old[Elem_cur[1]])<2;
1390  condign2 = (T_A[Elem_cur[0]] < T_R[Elem_cur[0]]) || (T_A[Elem_cur[1]] < T_R[Elem_cur[1]]);
1391  condign3 = CV_l == 0;
1392  condign4 = stim_status[indX] == 2 && (stim_status[Elem_cur[0]] == 1 || stim_status[Elem_cur[1]] == 1);
1393 
1394  if (condign1 || condign2 || condign3 || condign4) continue;
1395 
1396  SF_real temp2 = solve_tri(indX, Elem_cur);
1397 
1398  if (TA > temp2 && temp2 > T_R[indX] && compute_H(indX, temp2) > 0) {
1399  TA = temp2;
1400  }
1401  }
1402  } // numPtsxElem == 3
1403 
1404  // Solve for NB Nodes
1405  for (int ii = n2n_dsp[indX]; ii < n2n_dsp[indX + 1]; ii++) {
1406  mesh_int_t indXNB = n2n_connect[ii];
1407  condign4 = stim_status[indX] == 2 && (stim_status[indXNB] == 1);
1408 
1409  if (!(CV_l == 0 || T_A[indXNB] < T_R[indXNB] || T_A[indXNB] == inf || condign4 || T_A[indXNB] < time)) {
1410  SF_real temp1 = solve_edges(indX, indXNB);
1411 
1412  if (temp1 < TA && temp1 > T_R[indX] && compute_H(indX, temp1) > 0) {
1413  Winner_Element[0] = indXNB;
1414  Winner_Element[1] = -1;
1415  Winner_Element[2] = -1;
1416  TA = temp1;
1417  }
1418  }
1419  }
1420 
1421  if (TA == inf) {
1422  Winner_Element[0] = -1;
1423  Winner_Element[1] = -1;
1424  Winner_Element[2] = -1;
1425  TA = T_A[indX];
1426  }
1427 
1428  std::sort(Winner_Element.begin(), Winner_Element.end());
1429  if (compute_H(indX, TA) == 0) TA = inf;
1430 
1431  return TA;
1432 
1433 } // Close Function_F
1434 
1435 SF_real eikonal_solver::solve_tet(mesh_int_t& indx3, std::vector<mesh_int_t>& Elem_cur)
1436 {
1437  SF_real TA_th = inf;
1438 
1439  SF::vector<SF_real> X(3), Y(3), Z(3), W(3);
1440  SF::vector<SF_real> XY(3), XZ(3), XW(3);
1441 
1442  int indy3 = Elem_cur[0], indz3 = Elem_cur[1], indw3 = Elem_cur[2];
1443 
1444  X[0] = x_coord[indx3];
1445  X[1] = y_coord[indx3];
1446  X[2] = z_coord[indx3];
1447 
1448  Y[0] = x_coord[indy3];
1449  Y[1] = y_coord[indy3];
1450  Y[2] = z_coord[indy3];
1451 
1452  Z[0] = x_coord[indz3];
1453  Z[1] = y_coord[indz3];
1454  Z[2] = z_coord[indz3];
1455 
1456  W[0] = x_coord[indw3];
1457  W[1] = y_coord[indw3];
1458  W[2] = z_coord[indw3];
1459 
1460  XY[0] = Y[0] - X[0];
1461  XZ[0] = Z[0] - X[0];
1462  XW[0] = W[0] - X[0];
1463  XY[1] = Y[1] - X[1];
1464  XZ[1] = Z[1] - X[1];
1465  XW[1] = W[1] - X[1];
1466  XY[2] = Y[2] - X[2];
1467  XZ[2] = Z[2] - X[2];
1468  XW[2] = W[2] - X[2];
1469 
1470  c1 = mult_VtMV(XY, iD, XY);
1471  c2 = mult_VtMV(XZ, iD, XZ);
1472  c3 = mult_VtMV(XW, iD, XW);
1473  c4 = 2 * (mult_VtMV(XY, iD, XZ)); //+mult_VtMV(XZ,iD,XY);
1474  c5 = 2 * mult_VtMV(XY, iD, XW); //+mult_VtMV(XW,iD,XY);
1475  c6 = 2 * mult_VtMV(XW, iD, XZ); //+mult_VtMV(XZ,iD,XW);
1476 
1477  a1 = 4 * c2 * c3 - 2 * c2 * c5 - 2 * c3 * c4 + c4 * c6 + c5 * c6 - c6 * c6;
1478  s1 = 4 * c1 * c2 + 4 * c1 * c3 + 4 * c2 * c3 - 4 * c1 * c6 - 4 * c2 * c5 - 4 * c3 * c4 + 2 * c4 * c5 + 2 * c4 * c6 + 2 * c5 * c6 - c4 * c4 - c5 * c5 - c6 * c6;
1479  SF_real s_1 = 1 / s1;
1480  s2 = c1 * c6 * c6 + c2 * c5 * c5 + c3 * c4 * c4 - 4 * c1 * c2 * c3 - c4 * c5 * c6;
1481  s3 = 4 * c1 * c6 - 4 * c1 * c3 - 4 * c2 * c3 - 4 * c1 * c2 + 4 * c2 * c5 + 4 * c3 * c4 - 2 * c4 * c5 - 2 * c4 * c6 - 2 * c5 * c6 + c4 * c4 + c5 * c5 + c6 * c6 + 4 * CV_l * CV_l * T_A[indy3] * T_A[indy3] * c2 + 4 * CV_l * CV_l * T_A[indy3] * T_A[indy3] * c3 - 4 * CV_l * CV_l * T_A[indy3] * T_A[indy3] * c6 + 4 * CV_l * CV_l * T_A[indz3] * T_A[indz3] * c1 + 4 * CV_l * CV_l * T_A[indz3] * T_A[indz3] * c3 - 4 * CV_l * CV_l * T_A[indz3] * T_A[indz3] * c5 + 4 * CV_l * CV_l * T_A[indw3] * T_A[indw3] * c1 + 4 * CV_l * CV_l * T_A[indw3] * T_A[indw3] * c2 - 4 * CV_l * CV_l * T_A[indw3] * T_A[indw3] * c4 - 8 * CV_l * CV_l * T_A[indy3] * T_A[indz3] * c3 - 4 * CV_l * CV_l * T_A[indy3] * T_A[indz3] * c4 + 4 * CV_l * CV_l * T_A[indy3] * T_A[indz3] * c5 + 4 * CV_l * CV_l * T_A[indy3] * T_A[indz3] * c6 - 8 * CV_l * CV_l * T_A[indy3] * T_A[indw3] * c2 + 4 * CV_l * CV_l * T_A[indy3] * T_A[indw3] * c4 - 4 * CV_l * CV_l * T_A[indy3] * T_A[indw3] * c5 + 4 * CV_l * CV_l * T_A[indy3] * T_A[indw3] * c6 - 8 * CV_l * CV_l * T_A[indz3] * T_A[indw3] * c1 + 4 * CV_l * CV_l * T_A[indz3] * T_A[indw3] * c4 + 4 * CV_l * CV_l * T_A[indz3] * T_A[indw3] * c5 - 4 * CV_l * CV_l * T_A[indz3] * T_A[indw3] * c6;
1482  a2 = 2 * CV_l * T_A[indy3] * c2 + 2 * CV_l * T_A[indy3] * c3 - 2 * CV_l * T_A[indy3] * c6 - 2 * CV_l * T_A[indz3] * c3 - CV_l * T_A[indz3] * c4 + CV_l * T_A[indz3] * c5 + CV_l * T_A[indz3] * c6 - 2 * CV_l * T_A[indw3] * c2 + CV_l * T_A[indw3] * c4 - CV_l * T_A[indw3] * c5 + CV_l * T_A[indw3] * c6;
1483  s4 = 4 * c1 * c2 + 4 * c1 * c3 + 4 * c2 * c3 - 4 * c1 * c6 - 4 * c2 * c5 - 4 * c3 * c4 + 2 * c4 * c5 + 2 * c4 * c6 + 2 * c5 * c6 - c4 * c4 - c5 * c5 - c6 * c6;
1484  b1 = 4 * c1 * c3 - 2 * c1 * c6 - 2 * c3 * c4 + c4 * c5 + c5 * c6 - c5 * c5;
1485  b2 = CV_l * T_A[indy3] * c5 - CV_l * T_A[indy3] * c4 - 2 * CV_l * T_A[indy3] * c3 + CV_l * T_A[indy3] * c6 + 2 * CV_l * T_A[indz3] * c1 + 2 * CV_l * T_A[indz3] * c3 - 2 * CV_l * T_A[indz3] * c5 - 2 * CV_l * T_A[indw3] * c1 + CV_l * T_A[indw3] * c4 + CV_l * T_A[indw3] * c5 - CV_l * T_A[indw3] * c6;
1486 
1487  SF_real s2_s3 = s2 / s3;
1488  SF_real LTas = a1 * s_1;
1489  SF_real LTbs = b1 * s_1;
1490 
1491  SF_real RTa = (2 * sqrt(s2_s3) * (a2)) / (s4);
1492  SF_real RTb = (2 * sqrt(s2_s3) * (b2)) / (s4);
1493 
1494  SF_real tol = 1e-24;
1495 
1496  if (abs(s1) > tol && abs(s3) > tol && abs(s4) > tol && (s2 / s3) > 0) {
1497  a = LTas - RTa;
1498  b = LTbs - RTb;
1499 
1500  if ((a > 0) && (a < 1) && (b > 0) && (b < 1) && ((1 - a - b) > 0) && ((1 - a - b) < 1)) {
1501  TA_th = std::min(TA_th, compute_AT_at_node_in_tet(a, b, indx3, indy3, indz3, indw3));
1502  }
1503 
1504  a = LTas + RTa;
1505  b = LTbs + RTb;
1506 
1507  if ((a > 0) && (a < 1) && (b > 0) && (b < 1) && ((1 - a - b) > 0) && ((1 - a - b) < 1)) {
1508  TA_th = std::min(TA_th, compute_AT_at_node_in_tet(a, b, indx3, indy3, indz3, indw3));
1509  }
1510  }
1511 
1512  return TA_th;
1513 }
1514 
1515 SF_real eikonal_solver::solve_tri(mesh_int_t& indx, std::vector<mesh_int_t>& Elem_cur)
1516 {
1517  SF_real TA_tr = inf;
1518 
1519  SF::vector<SF_real> X(3), Y(3), Z(3);
1520  SF::vector<SF_real> XY(3), XZ(3);
1521 
1522  int indy = Elem_cur[0], indz = Elem_cur[1];
1523 
1524  X[0] = x_coord[indx];
1525  X[1] = y_coord[indx];
1526  X[2] = z_coord[indx];
1527 
1528  Y[0] = x_coord[indy];
1529  Y[1] = y_coord[indy];
1530  Y[2] = z_coord[indy];
1531 
1532  Z[0] = x_coord[indz];
1533  Z[1] = y_coord[indz];
1534  Z[2] = z_coord[indz];
1535 
1536  XY[0] = Y[0] - X[0];
1537  XZ[0] = Z[0] - X[0];
1538  XY[1] = Y[1] - X[1];
1539  XZ[1] = Z[1] - X[1];
1540  XY[2] = Y[2] - X[2];
1541  XZ[2] = Z[2] - X[2];
1542 
1543  C = mult_VtMV(XZ, iD, XZ);
1544  A = mult_VtMV(XY, iD, XY) - 2 * mult_VtMV(XZ, iD, XY) + C;
1545  B = 2 * mult_VtMV(XY, iD, XZ) - 2 * C;
1546 
1547  if (abs(T_A[indy] - T_A[indz]) < 1e-5) {
1548  p1 = -B / (2 * A);
1549  if (p1 > 0 && p1 < 1) {
1550  TA_tr = std::min(compute_AT_at_node(p1, indx, indy, indz), TA_tr);
1551  }
1552  }
1553 
1554  lam = ((T_A[indz] - T_A[indy]) * (T_A[indz] - T_A[indy])) * 4 * (CV_l * CV_l);
1555  ae = (4 * (A * A)) - lam * A;
1556  be = 4 * A * B - lam * B;
1557  ce = (B * B) - lam * C;
1558  det = (be * be) - (4 * ae * ce);
1559 
1560  if (det >= 0) {
1561  p1 = (-be + sqrt(det)) / (2 * ae);
1562 
1563  if (p1 > 0 && p1 < 1) {
1564  TA_tr = std::min(compute_AT_at_node(p1, indx, indy, indz), TA_tr);
1565  }
1566 
1567  if (det > 0) {
1568  p2 = (-be - sqrt(det)) / (2 * ae);
1569 
1570  if (p2 > 0 && p2 < 1) {
1571  TA_tr = std::min(compute_AT_at_node(p2, indx, indy, indz), TA_tr);
1572  }
1573  }
1574  }
1575 
1576  return TA_tr;
1577 }
1578 
1579 SF_real eikonal_solver::solve_edges(mesh_int_t& indX, mesh_int_t& indY)
1580 {
1581  SF::vector<SF_real> X(3), Y(3), XY(3);
1582 
1583  X[0] = x_coord[indX];
1584  X[1] = y_coord[indX];
1585  X[2] = z_coord[indX];
1586  Y[0] = x_coord[indY];
1587  Y[1] = y_coord[indY];
1588  Y[2] = z_coord[indY];
1589 
1590  XY[0] = Y[0] - X[0];
1591  XY[1] = Y[1] - X[1];
1592  XY[2] = Y[2] - X[2];
1593 
1594  Num = mult_VtMV(XY, iD, XY);
1595  Num = sqrt(Num);
1596  Frac = Num / CV_l;
1597 
1598  SF_real TAn = T_A[indY] + Frac;
1599 
1600  return TAn;
1601 } // end Solve_Edges
1602 
1603 bool eikonal_solver::add_node_neighbor_to_list(SF_real& RT, SF_real& oldTA, SF_real& newTA)
1604 {
1605  bool isNewTABetweenRTAndOldTA = (RT < newTA) && (newTA < oldTA);
1606  bool isNewTABiggerRTBiggerOldTA = (oldTA < RT) && (RT < newTA);
1607 
1608  return (isNewTABetweenRTAndOldTA) || (isNewTABiggerRTBiggerOldTA);
1609 }
1610 
1611 SF_real eikonal_solver::mult_VtMV(SF::vector<SF_real>& X, SF::dmat<double>& iD, SF::vector<SF_real>& Y)
1612 {
1613  SF_real result = 0;
1614 
1615  if (X[0] == Y[0] && X[1] == Y[1] && X[2] == Y[2]) {
1616  result = iD[0][0] * X[0] * X[0] + 2 * iD[0][1] * X[0] * X[1] + iD[1][1] * X[1] * X[1] + 2 * iD[0][2] * X[0] * X[2] + iD[2][2] * X[2] * X[2] + 2 * iD[1][2] * X[1] * X[2];
1617  } else {
1618  result = iD[0][0] * X[0] * Y[0] + iD[0][1] * (X[0] * Y[1] + X[1] * Y[0]) + iD[1][1] * X[1] * Y[1] + iD[0][2] * (X[0] * Y[2] + X[2] * Y[0]) + iD[2][2] * X[2] * Y[2] + iD[1][2] * (X[1] * X[2] + X[2] * Y[1]);
1619  }
1620 
1621  return result;
1622 
1623 } // mult_VtMV
1624 
1625 SF_real eikonal_solver::compute_AT_at_node(SF_real& pp, mesh_int_t& indxf1, mesh_int_t& indyf1, mesh_int_t& indzf1)
1626 {
1627  if (pp == 0) {
1628  Num = sqrt(C);
1629  Frac = Num / CV_l;
1630  return T_A[indzf1] + Frac;
1631 
1632  } else if (pp == 1) {
1633  Num = sqrt(A + B + C);
1634  Frac = Num / CV_l;
1635  return T_A[indyf1] + Frac;
1636 
1637  } else {
1638  Num = sqrt(A * pp * pp + B * pp + C);
1639  Frac = Num / CV_l;
1640  TA_ori = T_A[indyf1] * pp + T_A[indzf1] * (1 - pp);
1641  return TA_ori + Frac;
1642  }
1643 }
1644 
1645 SF_real eikonal_solver::compute_AT_at_node_in_tet(SF_real& pf2, SF_real& qf2, mesh_int_t indxf2, mesh_int_t indyf2, mesh_int_t indzf2, mesh_int_t indwf2)
1646 {
1647  Num = c1 * (pf2 * pf2) + c2 * (qf2 * qf2) + c3 * ((1 - pf2 - qf2) * (1 - pf2 - qf2)) + c4 * (pf2 * qf2) + c5 * (pf2 * (1 - pf2 - qf2)) + c6 * (qf2 * (1 - pf2 - qf2));
1648  Num = sqrt(Num);
1649  Frac = Num / CV_l;
1650  TA_ori = T_A[indyf2] * pf2 + T_A[indzf2] * qf2 + T_A[indwf2] * (1 - pf2 - qf2);
1651  return TA_ori + Frac;
1652 }
1653 
1654 void eikonal_solver::save_eikonal_state(const char* tsav_ext)
1655 {
1656  if (get_rank() == 0) {
1657  FILE* file_writestate;
1658  char buffer_writestate[1024];
1659  snprintf(buffer_writestate, sizeof buffer_writestate, "%s.%s.roe.dat", param_globals::write_statef, tsav_ext);
1660  file_writestate = fopen(buffer_writestate, "w");
1661  for (size_t jjj = 0; jjj < List.size(); jjj++) {
1662  fprintf(file_writestate, "%d %d %d %f %f %f %f \n", List[jjj], num_changes[jjj], nReadded2List[jjj], T_A[jjj], T_R[jjj], TA_old[jjj], D_I[jjj]);
1663  }
1664 
1665  fclose(file_writestate);
1666  }
1667 }
1668 
1670 {
1671  SF_real* old_ptr = Vmv_old.ptr();
1672  SF_real* new_ptr = Vmv.ptr();
1673 
1674  for (int ind_nodes = 0; ind_nodes < num_pts; ind_nodes++) {
1675  double prev_R = T_R[ind_nodes];
1676 
1677  if (old_ptr[ind_nodes] >= param_globals::dream.repol_time_thresh && new_ptr[ind_nodes] < param_globals::dream.repol_time_thresh) {
1678  T_R[ind_nodes] = time;
1679  }
1680  }
1681  Vmv_old.release_ptr(old_ptr);
1682  Vmv.release_ptr(new_ptr);
1683 }
1684 
1686 {
1687  double t1, t0;
1688  get_time(t0);
1689 
1690  #ifdef _OPENMP
1691  int max_threads = omp_get_max_threads();
1692  omp_set_num_threads(1); // with the current setup of this function, it is better to run it in serial.
1693  #endif
1694 
1695  limpet::MULTI_IF* miif = ion.miif;
1696 
1697  const double time = user_globals::tm_manager->time,
1699 
1700  for (int i = 0; i < miif->N_IIF; i++) {
1701  if (!miif->N_Nodes[i]) continue;
1702  limpet::IonIfBase* pIF = ion.miif->IIF[i];
1703  limpet::IonIfBase* IIF_old = pIF->get_type().make_ion_if(pIF->get_target(),
1704  pIF->get_num_node(),
1705  ion.miif->plugtypes[i]);
1706  IIF_old->copy_SVs_from(*pIF, false);
1707 
1708  int current = 0;
1709  do {
1710  int ind_gb = (miif->NodeLists[i][current]);
1711  // Global index of current node;
1712  // save states at the moment
1713  double Vm_old = miif->ldata[i][limpet::Vm][current];
1714  double Iion_old = miif->ldata[i][limpet::Iion][current];
1715  double prev_R = T_R[ind_gb];
1716 
1717  bool cond_2upd = T_R[ind_gb] <= T_A[ind_gb] && T_A[ind_gb] != inf && Vm_old > param_globals::dream.repol_time_thresh;
1718 
1719  if (cond_2upd) {
1720  double elapsed_time = 0;
1721  double prev_Vm = miif->ldata[i][limpet::Vm][current];
1722 
1723  do {
1724  elapsed_time += dt;
1725  pIF->compute(current, current + 1, miif->ldata[i]);
1726  miif->ldata[i][limpet::Vm][current] -= miif->ldata[i][limpet::Iion][current] * param_globals::dt;
1727  if (miif->ldata[i][limpet::Vm][current] < param_globals::dream.repol_time_thresh) {
1728  T_R[ind_gb] = time + elapsed_time;
1729  break;
1730  }
1731  } while (elapsed_time < 600);
1732 
1733  // Restore old states
1734  miif->ldata[i][limpet::Vm][current] = Vm_old;
1735  miif->ldata[i][limpet::Iion][current] = Iion_old;
1736  }
1737  current++;
1738  } while (current < miif->N_Nodes[i]);
1739  pIF->copy_SVs_from(*IIF_old, false);
1740  }
1741 
1742  #ifdef _OPENMP
1743  omp_set_num_threads(max_threads); // restore max threads for parabolic solver
1744  #endif
1745 
1746  // treat solver statistics
1747  auto dur = timing(t1, t0);
1748  stats.slvtime_D += dur;
1749 }
1750 
1752 {
1753  double t1, t0;
1754  get_time(t0);
1755 
1756  SF_real* c = Idiff->ptr();
1757  SF_real* v = vm.ptr();
1758  SF_real dT;
1759  double e_on, e_off;
1760 
1761  int rank = get_rank();
1762  const sf_mesh& mesh = get_mesh(intra_elec_msh);
1763  const SF::vector<mesh_int_t>& alg_nod = mesh.pl.algebraic_nodes();
1764 
1765  for (size_t j = 0; j < alg_nod.size(); j++) {
1766  mesh_int_t loc_nodal_idx = alg_nod[j];
1767  mesh_int_t loc_petsc_idx = local_nodal_to_local_petsc(mesh, rank, loc_nodal_idx);
1768 
1769  if ((time >= T_A[loc_nodal_idx]) && ((T_A[loc_nodal_idx] + 5) >= time) && (List[loc_nodal_idx] == 0)) {
1770  dT = time - T_A[loc_nodal_idx];
1771 
1772  switch (diff_cur[loc_nodal_idx].model) {
1773  case GAUSS:
1774  c[loc_petsc_idx] = diff_cur[loc_nodal_idx].alpha_1 * exp(-((dT - diff_cur[loc_nodal_idx].beta_1) / diff_cur[loc_nodal_idx].gamma_1) * ((dT - diff_cur[loc_nodal_idx].beta_1) / diff_cur[loc_nodal_idx].gamma_1))
1775  + diff_cur[loc_nodal_idx].alpha_2 * exp(-((dT - diff_cur[loc_nodal_idx].beta_2) / diff_cur[loc_nodal_idx].gamma_2) * ((dT - diff_cur[loc_nodal_idx].beta_2) / diff_cur[loc_nodal_idx].gamma_2))
1776  + diff_cur[loc_nodal_idx].alpha_3 * exp(-((dT - diff_cur[loc_nodal_idx].beta_3) / diff_cur[loc_nodal_idx].gamma_3) * ((dT - diff_cur[loc_nodal_idx].beta_3) / diff_cur[loc_nodal_idx].gamma_3));
1777  break;
1778  default:
1779  e_on = (dT >= 0) ? 1.0 : 0.0;
1780  e_off = (v[loc_petsc_idx] < diff_cur[loc_nodal_idx].V_th) ? 1.0 : 0.0;
1781  c[loc_petsc_idx] = diff_cur[loc_nodal_idx].A_F / diff_cur[loc_nodal_idx].tau_F * exp(dT / diff_cur[loc_nodal_idx].tau_F) * e_on * e_off;
1782  }
1783  } // end if
1784  } // end for loop
1785 
1786  Idiff->release_ptr(c);
1787  vm.release_ptr(v);
1788 
1789  // treat solver statistics
1790  auto dur = timing(t1, t0);
1791  stats.slvtime_B += dur;
1792 }
1793 
1794 void eikonal_solver::load_state_file()
1795 {
1796  set_dir(INPUT);
1797  FILE* file_startstate;
1798  char buffer_startstate[strlen(param_globals::start_statef) + 10];
1799 
1800  snprintf(buffer_startstate, sizeof buffer_startstate, "%s.dat", param_globals::start_statef);
1801  file_startstate = fopen(buffer_startstate, "r");
1802 
1803  if (file_startstate == NULL) {
1804  log_msg(NULL, 5, 0, "Not able to open state file: %s", buffer_startstate);
1805  } else if (param_globals::output_level) {
1806  log_msg(NULL, 0, 0, "Open state file for eikonal model: %s", buffer_startstate);
1807  }
1808 
1809  int ListVal, numChangesVal, numChanges2Val;
1810  float T_AVal, T_RVal, TA_oldVal, D_IVal, PCLVal;
1811  int index = 0;
1812 
1813  while (fscanf(file_startstate, "%d %d %d %f %f %f %f", &ListVal, &numChangesVal, &numChanges2Val, &T_AVal, &T_RVal, &TA_oldVal, &D_IVal) == 7) {
1814  List[index] = ListVal;
1815  num_changes[index] = numChangesVal;
1816  nReadded2List[index] = numChanges2Val;
1817  T_A[index] = T_AVal;
1818  T_R[index] = T_RVal;
1819  TA_old[index] = TA_oldVal;
1820  D_I[index] = D_IVal;
1821  ++index;
1822  }
1823  if (param_globals::output_level) log_msg(NULL, 0, 0, "Number of nodes in active list: %i", sum(List));
1824 
1825  fclose(file_startstate);
1826 }
1827 
1828 void eikonal_solver::init_diffusion_current()
1829 {
1830  const sf_mesh& mesh = get_mesh(intra_elec_msh);
1831  SF::vector<mesh_int_t> vertices;
1832 
1833  for (int num_i = 0; num_i < param_globals::num_imp_regions; num_i++) {
1834  for (int num_id = 0; num_id <= param_globals::imp_region[num_i].num_IDs; num_id++) {
1835  indices_from_region_tag(vertices, mesh, param_globals::imp_region[num_i].ID[num_id]);
1836  for (int i = 0; i < vertices.size(); i++) {
1837  diff_cur[vertices[i]].model = static_cast<eikonal_solver::Idiff_t>(param_globals::imp_region[num_i].dream.Idiff.model);
1838  switch (diff_cur[vertices[i]].model) {
1839  case GAUSS:
1840  diff_cur[vertices[i]].alpha_1 = param_globals::imp_region[num_i].dream.Idiff.alpha_i[0];
1841  diff_cur[vertices[i]].alpha_2 = param_globals::imp_region[num_i].dream.Idiff.alpha_i[1];
1842  diff_cur[vertices[i]].alpha_3 = param_globals::imp_region[num_i].dream.Idiff.alpha_i[2];
1843  diff_cur[vertices[i]].beta_1 = param_globals::imp_region[num_i].dream.Idiff.beta_i[0];
1844  diff_cur[vertices[i]].beta_2 = param_globals::imp_region[num_i].dream.Idiff.beta_i[1];
1845  diff_cur[vertices[i]].beta_3 = param_globals::imp_region[num_i].dream.Idiff.beta_i[2];
1846  diff_cur[vertices[i]].gamma_1 = param_globals::imp_region[num_i].dream.Idiff.gamma_i[0];
1847  diff_cur[vertices[i]].gamma_2 = param_globals::imp_region[num_i].dream.Idiff.gamma_i[1];
1848  diff_cur[vertices[i]].gamma_3 = param_globals::imp_region[num_i].dream.Idiff.gamma_i[2];
1849  break;
1850  default:
1851  diff_cur[vertices[i]].A_F = param_globals::imp_region[num_i].dream.Idiff.A_F;
1852  diff_cur[vertices[i]].tau_F = param_globals::imp_region[num_i].dream.Idiff.tau_F;
1853  diff_cur[vertices[i]].V_th = param_globals::imp_region[num_i].dream.Idiff.V_th;
1854  };
1855  }
1856  }
1857  }
1858 }
1859 
1860 void eikonal_solver::translate_stim_to_eikonal()
1861 {
1862  // Set stimulus for eikonal model
1863  if (param_globals::output_level) log_msg(NULL, 0, 0, "Initializing stimuli for eikonal model ...");
1864 
1865  SF::vector<SF_real> StartStimulus;
1866  SF::vector<mesh_int_t> IndexStimulus;
1867  int number_stimulus = param_globals::num_stim;
1868  int total_stimuli = 0;
1869  int count_stm = 0;
1870  int count = 0;
1871  Index_currStim = 0;
1872 
1873  for (stimulus s : *stimuliRef) {
1874  total_stimuli += s.ptcl.npls;
1875  for (int idx_pls = 0; idx_pls < s.ptcl.npls; idx_pls++) {
1876  SF_real start_time = s.ptcl.start + s.ptcl.pcl * idx_pls;
1877  StartStimulus.push_back(start_time);
1878  IndexStimulus.push_back(s.idx);
1879  count_stm++;
1880  }
1881  }
1882  // sort by start times in ascending order
1883  bubble_sort(StartStimulus, IndexStimulus, total_stimuli);
1884 
1885  for (int ind_stim = 0; ind_stim < total_stimuli; ind_stim++) {
1886  stimulus s = (*stimuliRef)[IndexStimulus[ind_stim]];
1887  // Loop over vertices for each defined stim
1888  for (int ind_nodes = 0; ind_nodes < s.electrode.vertices.size(); ind_nodes++) {
1889  StimulusPoints[count] = s.electrode.vertices[ind_nodes];
1890  StimulusTimes[count] = StartStimulus[ind_stim];
1891  count++;
1892  }
1893  }
1894  // Add -1 at the end to indicate we ran out of stimulations
1895  StimulusPoints.resize(count + 2, -1); // Nodes that have initial stimulus
1896  StimulusTimes.resize(count + 2, -1); // Times when the initial nodes are stimulated
1897 }
1898 
1899 void eikonal_solver::create_node_to_node_graph()
1900 {
1901  // Create the Nodes to Nodes Graph (Per Each node the graph finds the NB nodes)
1902  n2n_dsp.resize(num_pts + 1, 0);
1903  const sf_mesh& mesh = get_mesh(intra_elec_msh);
1904  twoFib = mesh.she.size() > 0;
1905 
1906  for (int point_idx = 0; point_idx < num_pts; point_idx++) {
1907  int numNBElem = n2e_dsp[point_idx + 1] - n2e_dsp[point_idx];
1908  std::vector<mesh_int_t> NNNodesxNodes(numNBElem * numPtsxElem);
1909 
1910  for (int eedsp = 0; eedsp < numNBElem; eedsp++) {
1911  int currElem = n2e_con[n2e_dsp[point_idx] + eedsp];
1912 
1913  if (twoFib) {
1914  fiber_node[3 * point_idx] = mesh.fib[3 * currElem];
1915  fiber_node[3 * point_idx + 1] = mesh.fib[3 * currElem + 1];
1916  fiber_node[3 * point_idx + 2] = mesh.fib[3 * currElem + 2];
1917 
1918  sheet_node[3 * point_idx] = mesh.she[3 * currElem];
1919  sheet_node[3 * point_idx + 1] = mesh.she[3 * currElem + 1];
1920  sheet_node[3 * point_idx + 2] = mesh.she[3 * currElem + 2];
1921 
1922  } else {
1923  fiber_node[3 * point_idx] = mesh.fib[3 * currElem];
1924  fiber_node[3 * point_idx + 1] = mesh.fib[3 * currElem + 1];
1925  fiber_node[3 * point_idx + 2] = mesh.fib[3 * currElem + 2];
1926  }
1927 
1928  for (int nndsp = 0; nndsp < numPtsxElem; nndsp++) {
1929  NNNodesxNodes[eedsp * numPtsxElem + nndsp] = e2n_con[elem_start[currElem] + nndsp];
1930  }
1931  }
1932 
1933  std::sort(NNNodesxNodes.begin(), NNNodesxNodes.end());
1934  auto it = std::unique(NNNodesxNodes.begin(), NNNodesxNodes.end());
1935  NNNodesxNodes.erase(it, NNNodesxNodes.end());
1936  it = std::remove(NNNodesxNodes.begin(), NNNodesxNodes.end(), point_idx);
1937  NNNodesxNodes.erase(it, NNNodesxNodes.end());
1938  int max = NNNodesxNodes.size();
1939 
1940  n2n_dsp[point_idx + 1] = n2n_dsp[point_idx] + NNNodesxNodes.size();
1941 
1942  n2n_connect.insert(n2n_connect.end(), NNNodesxNodes.begin(), NNNodesxNodes.end());
1943  }
1944 
1945  n2n_dsp[num_pts] = n2n_connect.size();
1946 }
1947 
1949 {
1950  logger = f_open(filename, "w");
1951 
1952  const char* h1 = " ------ ---------- ---------- ------- ------- | List logic ----- -------- | Neighbor node ---- |";
1953  const char* h2 = " cycle AT old AT RT DI | Status Entry Exit | ID AT |";
1954 
1955  if (logger == NULL)
1956  log_msg(NULL, 3, 0, "%s error: Could not open file %s in %s. Turning off logging.\n",
1957  __func__, filename);
1958  else {
1959  log_msg(logger, 0, 0, "%s", h1);
1960  log_msg(logger, 0, 0, "%s", h2);
1961  }
1962 }
1963 
1964 void node_stats::log_stats(double time, bool cflg)
1965 {
1966  if (!this->logger) return;
1967 
1968  char abuf[256];
1969  char bbuf[256];
1970  char cbuf[256];
1971 
1972  // create nicer output in logger for inf, -inf, nan
1973  std::ostringstream oss_TA, oss_TA_, oss_TR, oss_DI, oss_nbnTA;
1974  oss_TA << this->T_A;
1975  oss_TA_ << this->T_A_;
1976  oss_TR << this->T_R;
1977  oss_DI << this->D_I;
1978  oss_nbnTA << this->nbn_T_A;
1979 
1980  if (this->idXNB == std::numeric_limits<Int>::min()) {
1981  snprintf(cbuf, sizeof cbuf, "%7s %10s", "-", "-");
1982  } else {
1983  snprintf(cbuf, sizeof cbuf, "%7d %10s", this->idXNB, oss_nbnTA.str().c_str());
1984  }
1985 
1986  snprintf(abuf, sizeof abuf, "%6d %10s %10s %7s %7s", this->cycle, oss_TA.str().c_str(), oss_TA_.str().c_str(), oss_TR.str().c_str(), oss_DI.str().c_str());
1987  snprintf(bbuf, sizeof bbuf, "%7s %8s %8s", this->status, this->reasonIn, this->reasonOut);
1988 
1989  unsigned char flag = cflg ? ECHO : 0;
1990  log_msg(this->logger, 0, flag | FLUSH | NONL, "%9.3f %s | %s | %s |\n", time, abuf, bbuf, cbuf);
1991 
1992  this->reasonIn = "-";
1993  this->reasonOut = "-";
1994  this->T_A_ = this->T_A;
1995  this->T_A = std::numeric_limits<double>::quiet_NaN();
1996  this->T_R = std::numeric_limits<double>::quiet_NaN();
1997  this->D_I = std::numeric_limits<double>::quiet_NaN();
1998  this->idXNB = std::numeric_limits<Int>::min();
1999  this->nbn_T_A = std::numeric_limits<double>::quiet_NaN();
2000  this->cycle++;
2001 }
2002 
2004 {
2005  // directly convert enums to strings for logging
2006  const char* stat_str;
2007  const char* reas_str;
2008  switch (s) {
2009  case node_stats::in: stat_str = "in"; break;
2010  case node_stats::out: stat_str = "out"; break;
2011  }
2012 
2013  switch (r) {
2014  case node_stats::none: reas_str = "-"; break;
2015  case node_stats::nbn: reas_str = "nbn"; break;
2016  case node_stats::conv: reas_str = "conv"; break;
2017  case node_stats::stim: reas_str = "stim"; break;
2018  }
2019 
2020  this->status = stat_str;
2021  if (s == in) {
2022  this->reasonIn = reas_str;
2023  } else {
2024  this->reasonOut = reas_str;
2025  }
2026 }
2027 
2028 } // namespace opencarp
constexpr T min(T a, T b)
Definition: ion_type.h:33
SF::scattering * get_permutation(const int mesh_id, const int perm_id, const int dpn)
Get the PETSC to canonical permutation scattering for a given mesh and number of dpn.
int ** NodeLists
local partitioned node lists for each IMP stored
Definition: MULTI_ION_IF.h:201
stim_electrode electrode
electrode geometry
Definition: stimulate.h:168
void init()
Initialize vectors and variables in the eikonal_solver class.
int * subregtags
FEM tags forming this region.
Definition: fem_types.h:97
char * regname
name of region
Definition: fem_types.h:94
void log_stats(double tm, bool cflg)
Definition: timers.cc:93
virtual void release_ptr(S *&p)=0
void FIM()
Standard fast iterative method to solve eikonal equation with active list approach.
double z
Definition: SF_container.h:68
#define FLUSH
Definition: basics.h:311
stim_pulse pulse
stimulus wave form
Definition: stimulate.h:165
char * dupstr(const char *old_str)
Definition: basics.cc:44
description of materal properties in a mesh
Definition: fem_types.h:110
virtual void add_scaled(const abstract_vector< T, S > &vec, S k)=0
physMaterial * material
material parameter description
Definition: fem_types.h:98
sf_vec * Irhs
weighted transmembrane currents
Definition: electrics.h:105
eikonal_solver eik_solver
Solver for the eikonal equation.
LAT_detector lat
the activation time detector
MPI_Comm comm
the parallel mesh is defined on a MPI world
Definition: SF_container.h:392
void set_stimuli(SF::vector< stimulus > &stimuli)
Simple setter for stimulus vector.
igb_output_manager output_manager_cycle
constexpr T max(T a, T b)
Definition: ion_type.h:31
parabolic_solver parab_solver
Solver for the parabolic bidomain equation.
#define PETSC_TO_CANONICAL
Permute algebraic data from PETSC to canonical ordering.
Definition: sf_interface.h:74
const IonType & get_type() const
Gets this IMP&#39;s model type.
Definition: ION_IF.cc:144
physMat_t material_type
ID of physics material.
Definition: fem_types.h:53
vector< T > dsp
connectivity starting index of each element
Definition: SF_container.h:404
sf_vec * phie_dummy
no elliptic solver needed, but we need a dummy for phie to use parabolic solver
overlapping_layout< T > pl
nodal parallel layout
Definition: SF_container.h:417
vector< S > fib
fiber direction
Definition: SF_container.h:408
int calls
# calls for this interval, this is incremented externally
Definition: timers.h:70
virtual void set(const vector< T > &idx, const vector< S > &vals, const bool additive=false, const bool local=false)=0
void update_status(enum status s, enum reason r)
void cnt_from_dsp(const std::vector< T > &dsp, std::vector< T > &cnt)
Compute counts from displacements.
Definition: kdpart.hpp:149
void compute_diffusion_current(const double &time, sf_vec &vm)
Compute the diffusion current approximation I_diff in all activated nodes. Only converged nodes are c...
eikonal_solver_stats stats
void solve(sf_vec &phie_i)
Definition: electrics.cc:1235
void initialize()
Definition: ionics.cc:60
virtual T lsize() const =0
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
double ExVal[3]
extracellular conductivity eigenvalues
Definition: fem_types.h:62
const char * name
The name of the physic, each physic should have one.
Definition: physics_types.h:62
sf_mat * lhs_parab
lhs matrix (CN) to solve parabolic
Definition: electrics.h:111
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
SF::vector< mesh_int_t > vertices
Definition: stimulate.h:152
size_t read_ascii(FILE *fd)
double time_step
global reference time step
Definition: timer_utils.h:78
void cycFIM()
Implementation of the cyclical fast iterative method used in step A of the DREAM model.
void count(const vector< T > &data, vector< S > &cnt)
Count number of occurrences of indices.
Definition: SF_vector.h:332
const char * get_mesh_type_name(mesh_t t)
get a char* to the name of a mesh type
Definition: sf_interface.cc:46
#define ECHO
Definition: basics.h:308
vector< S > xyz
node cooridnates
Definition: SF_container.h:415
vector< T > con
Definition: SF_container.h:400
Target get_target() const
Definition: ION_IF.h:338
sf_vec * tmp_i1
scratch vector for i-grid
Definition: electrics.h:103
void log_stats(double tm, bool cflg)
mesh_t
The enum identifying the different meshes we might want to load.
Definition: sf_interface.h:58
T & push_back(T val)
Definition: SF_vector.h:283
std::vector< IonTypeList > plugtypes
plugins types for each region
Definition: MULTI_ION_IF.h:215
bool trigger(int ID) const
Definition: timer_utils.h:166
double InVal[3]
intracellular conductivity eigenvalues
Definition: fem_types.h:61
int N_IIF
how many different IIF&#39;s
Definition: MULTI_ION_IF.h:211
void init_stim_info(void)
uses voltage for stimulation
Definition: stimulate.cc:49
double timer_val(const int timer_id)
figure out current value of a signal linked to a given timer
Point and vector struct.
Definition: SF_container.h:65
void close_files_and_cleanup()
close file descriptors
Definition: sim_utils.cc:1527
void init_logger(const char *filename)
Definition: timers.cc:77
void init_vector(SF::abstract_vector< T, S > **vec)
Definition: SF_init.h:99
virtual void copy_SVs_from(IonIfBase &other, bool alloc)=0
Copies the state variables of an IMP.
void region_mask(mesh_t meshspec, SF::vector< RegionSpecs > &regspec, SF::vector< int > &regionIDs, bool mask_elem, const char *reglist)
classify elements/points as belonging to a region
Definition: ionics.cc:362
std::vector< IonIfBase * > IIF
array of IIF&#39;s
Definition: MULTI_ION_IF.h:202
#define ELEM_PETSC_TO_CANONICAL
Permute algebraic element data from PETSC to canonical ordering.
Definition: sf_interface.h:76
bool determine_model_to_run(double &time)
Determine the next model to run in the alternation between RD and Eikonal.
const T * end() const
Pointer to the vector&#39;s end.
Definition: SF_vector.h:128
void initialize()
Initialize the Eikonal class.
generic_timing_stats IO_stats
vec3< V > cross(const vec3< V > &a, const vec3< V > &b)
Definition: vect.h:144
void compute_IIF(limpet::IonIfBase &pIF, limpet::GlobalData_t **impdata, int n)
Definition: ionics.cc:464
void output(vector< int > nodes, IGBheader *h, char *ofname, enum_format of, int t0, int t1, int stride, bool explode, float scale)
Definition: IGBextract.cc:208
SF::scattering * get_scattering(const int from, const int to, const SF::SF_nbr nbr, const int dpn)
Get a scattering from the global scatter registry.
virtual S * ptr()=0
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
bool using_legacy_stimuli
flag storing whether legacy stimuli are used
Definition: main.cc:58
void init(sf_vec &vm, sf_vec &phie, int offset, enum physic_t=elec_phys)
initializes all datastructs after electric solver setup
Definition: electrics.cc:1466
virtual void mult(const abstract_vector< T, S > &x, abstract_vector< T, S > &b) const =0
void dump_state(char *, float, opencarp::mesh_t gid, bool, unsigned int)
void transpose_connectivity(const vector< T > &a_cnt, const vector< T > &a_con, vector< T > &b_cnt, vector< T > &b_con)
Transpose CRS matrix graph A into B.
sf_vec * Vmv
global Vm vector
Definition: electrics.h:99
double tot_time
total time, this is incremented externally
Definition: timers.h:72
int mesh_int_t
Definition: SF_container.h:46
void log_stats(double time, bool cflg)
Definition: timers.cc:130
void clean_list()
Clean the list of nodes by resetting their status and tracking changes based on the time step of the ...
virtual void write(const char *filename) const =0
void assemble_sv_gvec(gvec_data &gvecs, limpet::MULTI_IF *miif)
Definition: ionics.cc:637
double time
current time
Definition: timer_utils.h:76
Represents the ionic model and plug-in (IMP) data structure.
Definition: ION_IF.h:138
virtual IonIfBase * make_ion_if(Target target, int num_node, const std::vector< std::reference_wrapper< IonType >> &plugins) const =0
Generate an IonIf object from this type.
void destroy()
Currently we only need to close the file logger.
region based variations of arbitrary material parameters
Definition: fem_types.h:93
GlobalData_t *** ldata
data local to each IMP
Definition: MULTI_ION_IF.h:205
int check_acts(double tm)
check activations at sim time tm
Definition: electrics.cc:1586
double BathVal[3]
bath conductivity eigenvalues
Definition: fem_types.h:63
std::string name
label stimulus
Definition: stimulate.h:164
int timer_idx
the timer index received from the timer manager
Definition: physics_types.h:66
MaterialType mtype[2]
the material types of intra_grid and extra_grid grids.
void write_data()
write registered data to disk
Definition: sim_utils.cc:1480
void compute(int start, int end, GlobalData_t **data)
Perform ionic model computation for 1 time step.
Definition: ION_IF.cc:270
sf_mesh & get_mesh(const mesh_t gt)
Get a mesh by specifying the gridID.
Definition: sf_interface.cc:33
SF::scattering * register_permutation(const int mesh_id, const int perm_id, const int dpn)
Register a permutation between two orderings for a mesh.
void set_initial_cv()
Set the initial conduction velocity (CV) parameters for the nodes in the mesh.
SF::vector< stimulus > stimuli
the electrical stimuli
const char * get_tsav_ext(double time)
Definition: electrics.cc:831
#define ALG_TO_NODAL
Scatter algebraic to nodal.
Definition: sf_interface.h:72
gvec_data gvec
datastruct holding global IMP state variable output
SF::vector< double > el_scale
optionally provided per-element params scale
Definition: fem_types.h:116
void dsp_from_cnt(const std::vector< T > &cnt, std::vector< T > &dsp)
Compute displacements from counts.
Definition: kdpart.hpp:140
void get_time(double &tm)
Definition: basics.h:436
Basic_physic * get_physics(physic_t p, bool error_if_missing)
Convinience function to get a physics.
Definition: sim_utils.cc:864
#define EXP_POSTPROCESS
Definition: sim_utils.h:161
int write_trace()
write traces to file
Definition: signals.h:701
T sum(const vector< T > &vec)
Compute sum of a vector&#39;s entries.
Definition: SF_vector.h:340
void open_trace(MULTI_IF *MIIF, int n_traceNodes, int *traceNodes, int *label, opencarp::sf_mesh *imesh)
Set up ionic model traces at some global node numbers.
int * N_Nodes
#nodes for each IMP
Definition: MULTI_ION_IF.h:200
sf_vec * old_vm
older Vm needed for 2nd order dT
Definition: electrics.h:101
void destroy()
Definition: ionics.cc:52
size_t root_count_ascii_lines(std::string file, MPI_Comm comm)
count the lines in a ascii file
int check_quiescence(double tm, double dt)
check for quiescence
Definition: electrics.cc:1641
V timing(V &t2, const V &t1)
Definition: basics.h:448
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 rebuild_matrices(MaterialType *mtype, limpet::MULTI_IF &miif, FILE_SPEC logger)
Definition: electrics.cc:1132
int regID
region ID
Definition: fem_types.h:95
File descriptor struct.
Definition: basics.h:133
void outer_prod(const Point &a, const Point &b, const double s, double *buff, const bool add=false)
Definition: SF_container.h:95
opencarp::sf_vec * gdata[NUM_IMP_DATA_TYPES]
data used by all IMPs
Definition: MULTI_ION_IF.h:216
void update_repolarization_times_from_rd(sf_vec &Vmv, sf_vec &Vmv_old, double time)
Checks if Vm crosses the repolarization threshold during each time step of the parabolic solver and u...
int set_dir(IO_t dest)
Definition: sim_utils.cc:483
void dup_IMP_node_state(IonIfBase &IF, int from, int to, GlobalData_t **localdata)
size_t size() const
The current size of the vector.
Definition: SF_vector.h:104
void log_stats(double tm, bool cflg)
Definition: timers.cc:27
int nsubregs
#subregions forming this region
Definition: fem_types.h:96
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
void init_sv_gvec(gvec_data &GVs, limpet::MULTI_IF *miif, sf_vec &tmpl, igb_output_manager &output_manager)
Definition: ionics.cc:566
int get_num_node() const
Gets the number of nodes handled by this IMP.
Definition: ION_IF.cc:148
bool mesh_is_registered(const mesh_t gt)
check wheter a SF mesh is set
Definition: sf_interface.cc:59
stim_t type
type of stimulus
Definition: stimulate.h:136
void save_eikonal_state(const char *tsav_ext)
Save the current state of variables related to the Eikonal simulation to a file to initialize a futur...
size_t l_numpts
local number of points
Definition: SF_container.h:389
lin_solver_stats stats
Definition: electrics.h:120
double x
Definition: SF_container.h:66
void output_initial_activations()
output one nodal vector of initial activation time
Definition: electrics.cc:1750
double y
Definition: SF_container.h:67
void dump_trace(MULTI_IF *MIIF, limpet::Real time)
size_t g_numpts
global number of points
Definition: SF_container.h:388
igb_output_manager output_manager_time
class handling the igb output
const T * begin() const
Pointer to the vector&#39;s start.
Definition: SF_vector.h:116
void f_close(FILE_SPEC &f)
Close a FILE_SPEC.
Definition: basics.cc:162
void compute_step()
Definition: ionics.cc:35
limpet::MULTI_IF * miif
Definition: ionics.h:66
sf_mat * mass_i
lumped for parabolic problem
Definition: electrics.h:109
sf_vec * IIon
ionic currents
Definition: electrics.h:98
void register_output(sf_vec *inp_data, const mesh_t inp_meshid, const int dpn, const char *name, const char *units, const SF::vector< mesh_int_t > *idx=NULL, bool elem_data=false)
Register a data vector for output.
Definition: sim_utils.cc:1447
void assign(InputIterator s, InputIterator e)
Assign a memory range.
Definition: SF_vector.h:161
const vector< T > & algebraic_nodes() const
Getter function for the local indices forming the local algebraic node set.
vector< S > she
sheet direction
Definition: SF_container.h:409
Diffusion Reaction Eikonal Alternant Model (DREAM) based on the electrics physics class...
size_t g_numelem
global number of elements
Definition: SF_container.h:386
double SF_real
Use the general double as real type.
Definition: SF_globals.h:38
Basic utility structs and functions, mostly IO related.
std::string timer_unit(const int timer_id)
figure out units of a signal linked to a given timer
void translate(int id)
convert legacy definitions to new format
Definition: stimulate.cc:100
stim_physics phys
physics of stimulus
Definition: stimulate.h:167
void apply_stim_to_vector(const stimulus &s, sf_vec &vec, bool add)
Definition: electrics.cc:428
SF::vector< RegionSpecs > regions
array with region params
Definition: fem_types.h:115
void init_logger(const char *filename)
void setup_dataout(const int dataout, std::string dataout_vtx, mesh_t grid, SF::vector< mesh_int_t > *&restr, bool async)
Definition: electrics.cc:588
void resize(size_t n)
Resize a vector.
Definition: SF_vector.h:209
vector< elem_t > type
element type
Definition: SF_container.h:406
int get_size(MPI_Comm comm=PETSC_COMM_WORLD)
Definition: basics.h:290
SF::scattering * register_scattering(const int from, const int to, const SF::SF_nbr nbr, const int dpn)
Register a scattering between to grids, or between algebraic and nodal representation of data on the ...
Definition: sf_interface.cc:65
void invert_3x3(S *ele, S &det)
Definition: dense_mat.hpp:452
int stimidx_from_timeridx(const SF::vector< stimulus > &stimuli, const int timer_id)
determine link between timer and stimulus
Definition: electrics.cc:747
void update_repolarization_times(const Ionics &ion)
Estimates initial repolarization times (T_R) in Step D of DREAM.
sf_mat * rhs_parab
rhs matrix to solve parabolic
Definition: electrics.h:110
centralize time managment and output triggering
Definition: timer_utils.h:73
int get_rank(MPI_Comm comm=PETSC_COMM_WORLD)
Definition: basics.h:276
T local_nodal_to_local_petsc(const meshdata< T, S > &mesh, int rank, T local_nodal)
void compute_bc()
Computes and updates boundary counditions in cycFIM.
grid_t
An electrics grid identifier to distinguish between intra and extra grids.
FILE_SPEC f_open(const char *fname, const char *mode)
Open a FILE_SPEC.
Definition: basics.cc:135
Container for a PETSc VecScatter.