openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
bench.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 #define PrMGLOBAL
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include "cmdline.h"
31 #include <time.h>
32 #include <sys/time.h>
33 #include <sys/resource.h>
34 #include <unistd.h>
35 #include <limits.h>
36 #include <math.h>
37 #include <stdbool.h>
38 #include <vector>
39 #include <sstream>
40 #include <string>
41 #include <cmath>
42 #include "basics.h"
43 
44 #include "MULTI_ION_IF.h"
45 
46 #include "trace.h"
47 #include "clamp.h"
48 #include "ap_analyzer.h"
49 #include "restitute.h"
50 #include "stretch.h"
51 #include "sv_init.h"
52 #include "bench_utils.h"
53 #include "target.h"
54 
55 #include "libgen.h"
56 #include "build_info.h"
57 
58 #include "petsc_utils.h" //TODO for EXIT, FPRINT*
59 #include "SF_init.h" // for SF::init_xxx()
60 
61 
62 // globals
63 namespace opencarp {
64 namespace user_globals {
68  std::map<mesh_t, sf_mesh> mesh_reg;
70  std::map<SF::quadruple<int>, SF::index_mapping<mesh_int_t> > map_reg;
71  // /// the physics
72  // std::map<physic_t, Basic_physic*> physics_reg;
75  // /// important solution vectors from different physics
76  // std::map<datavec_t, sf_vec*> datavec_reg;
77 } // namespace user_globals
78 } // namespace opencarp
79 
80 using namespace opencarp;
81 using namespace limpet;
82 
83 #undef __FUNCT__
84 #define __FUNCT__ "main"
85 
86 int main(int argc, char *argv[]) {
87 
88  double dt = 10.e-3; // time step needed for the setup of the LUT's (ms)
89 
90  // specify the number of regions and their names
91  const int num_region = 1;
92  enum { R1 }
93  Region;
94 
95  // specifiy the ionic model for each region
96  IonType* RegionDef;
97 
98  // specify the plugins for each region
99  int num_plugins;
100  IonTypeList RegionPlug = {};
101 
102  // specify the region for each node
103  char *Regions;
104 
105 
106  struct gengetopt_args_info params;
107 
108  // we allocate the vectors on the heap since they will be freed by the MIIF
109  sf_vec* Vmv;
110  sf_vec* I_ion;
111 
112  FILE *fhdls[NUM_IMP_DATA_TYPES+1];
113  GVEC_DUMP gvd;
114  IOCtrl io;
115 
116  char *PetscDBfile = COMPAT_PETSC_NULLPTR; // could be used to pass in ~/.petscrc
117  char *help_msg = COMPAT_PETSC_NULLPTR;
118  initialize_PETSc(&argc, argv, PetscDBfile, help_msg);
119  COMPAT_PetscOptionsInsertString("-options_left no");
120 
121  if (cmdline_parser(argc, argv, &params) != 0)
122  exit(1);
123 
124  // performance measurements
125  double t1, t2, t3, t4, t5, t6, t7, t8;
126  struct timeval tt1;
127 
128  event_timing timings[N_TIMINGS];
129  initialize_timings(timings);
130 
131  // start measurementstrace_IA
132  get_time(t1);
133 
134  // assign command line params and depending variables
135  dt = params.dt_arg;
136  double stim_charge = params.stim_curr_arg*dt;
137  double bcl = params.bcl_arg;
138  double stim_start = params.stim_start_arg;
139  double stim_dur = params.stim_dur_arg;
140  int nstim = params.numstim_arg ? params.numstim_arg : -1;
141  double dt_out = params.dt_out_arg;
142  double start_out = params.start_out_arg;
143  int numNode = params.num_arg;
144  int stim_assign = params.stim_assign_flag;
145  bool analyze_AP = params.APstatistics_given || params.restitute_given;
146  bool restitute = params.restitute_given;
147  bool validate = params.validate_given;
148  bool APclamp = params.AP_clamp_file_given;
149  bool do_trace = restitute ? params.res_trace_flag : (!params.no_trace_flag);
150  bool RRC = params.RRC_mode_counter;
151  TrgList stim_lst;
152  float duration = determine_duration(&params, &stim_lst);
153 
154  io.w2file = validate ? 1 : params.fout_given;
155  io.wbin = validate ? 1 : params.bin_flag;
156 
157  if(get_size() > numNode) {
158  numNode = get_size();
159 
160  log_msg(NULL, 3, 0, "Warning: Number of nodes is less than number of processes used!");
161  log_msg(NULL, 3, 0, "Setting number of nodes to %d.", numNode);
162  }
163 
164  // stimulate with trace from file
165  trace stim_trace;
166  bool tr_stim = false;
167  if (params.stim_file_given) {
168  char *fname = dupstr(params.stim_file_arg);
169  read_trace(&stim_trace, fname);
170  resample_trace(&stim_trace, dt);
171  tr_stim = true;
172  stim_dur = stim_trace.dur;
173  if (bcl < stim_trace.dur)
174  log_msg(NULL, 4, 0, "BCL less than stimulus duration!");
175  }
176 #define RRC_MAX_N 50
177  if( RRC ) {
178  mk_RRC_trace(&stim_trace, params.stim_curr_arg,params.RRC_delay_arg,
179  -1., params.RRC_clamp_dur_arg );
180  resample_trace(&stim_trace, dt);
181  tr_stim = true;
182  stim_dur = stim_trace.dur;
183  nstim = RRC_MAX_N;
184  bcl = stim_dur+10.;
185  duration = nstim*bcl;
186  }
187 
188  // illuminate with trace from file
189  trace light_trace;
190  bool tr_light = false;
191  if(params.light_file_given) {
192  char* fname = dupstr(params.light_file_arg);
193  read_trace(&light_trace, fname);
194  resample_trace(&light_trace, dt);
195  tr_light = true;
196 
197  int n_clipped_to_0 = 0;
198  for(int i=0; i<light_trace.N; i++) {
199  if( light_trace.s[i] < 0.0 ) {
200  light_trace.s[i] = 0.0;
201  n_clipped_to_0++;
202  }
203  }
204  if( n_clipped_to_0 ) {
205  FPRINTF(WORLD stderr, "WARNING: %d irradiance values in %s were negative (clipped to 0.0)\n", n_clipped_to_0, fname);
206  }
207  }
208 
209  // state variable clamp
210  Clamp *sv_cl;
211  int SVclamp = process_sv_clamps(params.clamp_SVs_arg, params.SV_clamp_files_arg, &sv_cl, dt);
212 
213  // action potential clamp
214  Clamp ap_cl;
215  if (APclamp){
216  const char *Vm_clamp = "Vm";
217  initialize_sv_clamp(&ap_cl, Vm_clamp, params.AP_clamp_file_arg, dt);
218  }
219 
220  // voltage clamp experiment
221  Clamp cl;
222  bool IsVclamp = initialize_clamp(&cl, params.clamp_arg, params.clamp_ini_arg,
223  params.clamp_start_arg, params.clamp_dur_arg,
224  params.clamp_file_arg,
225  !params.clamp_ini_given ? CLAMP_TM_IDX : 0, &duration);
226  if (IsVclamp) {
227  if (APclamp) {
228  log_msg(NULL, 0, 0, "info: cannot clamp voltage and have AP clamp at same time");
229  exit(1);
230  }
231  if (cl.transient) {
232  log_msg(NULL, 0, 0, "info: Vm will be clamped to %f mV over the interval [%f, %f]",
233  params.clamp_arg, params.clamp_start_arg, params.clamp_start_arg + params.clamp_dur_arg);
234  } else {
235  log_msg(NULL, 0, 0, "info: Vm will be clamped from %f to %f mV over the interval [%f, %f]",
236  params.clamp_ini_arg, params.clamp_arg, params.clamp_start_arg,
237  params.clamp_start_arg + params.clamp_dur_arg);
238  }
239  }
240 
241  action_potential AP;
242  if (analyze_AP) initialize_AP_analysis(&AP);
243 
244  Regions = static_cast<char *>(calloc(numNode, sizeof(char) ));
245 
246  SF::init_vector(&Vmv, numNode, -1);
247  SF::init_vector(&I_ion, Vmv);
248 
249  int *stim_list = static_cast<int *>(malloc(numNode*sizeof(int) ));
250  for (int i = 0; i < numNode; i++) {
251  Regions[i] = R1;
252  stim_list[i] = i;
253  }
254 
255  for (unsigned int i = 0; i < params.load_module_given; i++) {
256 #ifdef HAVE_DLOPEN
257  load_ionic_module(params.load_module_arg[i]);
258  if (!params.imp_given) {
259 
260  char *imp = strdup(basename(params.load_module_arg[i]) );
261  char *dot = strrchr(imp, '.');
262  if (dot)
263  *dot = 0;
264  params.imp_arg = imp;
265  }
266 #else // ifdef HAVE_DLOPEN
267  log_msg(NULL, 5, 0, "Compile with USE_DLOPEN to support dynamic module loading.");
268  EXIT(-1);
269 #endif // ifdef HAVE_DLOPEN
270  }
271 
272  if (get_ion_type(std::string(params.imp_arg)) == NULL) {
273  fprintf(stderr, "Illegal IMP specified: %s\n", params.imp_arg);
274  exit(1);
275  } else {
276  RegionDef = get_ion_type(std::string(params.imp_arg));
277  }
278  if (!get_plug_flag(params.plug_in_arg, &num_plugins, RegionPlug)) {
279  fprintf(stderr, "Illegal plugin specified: %s\n", params.plug_in_arg);
280  exit(1);
281  }
282  // Fetch target enum from command line string
283  Target target = get_target_from_string(std::string(params.target_arg));
284  // Automatic target selection
285  if (target == Target::AUTO) {
286  target = RegionDef->select_target(target);
287  }
288  // Check whether target exists and was generated for the given model and plugin
289  if (target == Target::UNKNOWN) {
290  fprintf(stderr, "Unkown target: %s\n", params.target_arg);
291  fprintf(stderr, "Available targets are: %s\n", get_target_list_string().c_str());
292  exit(1);
293  }
294  // Check if the main imp was generated for the requested target
295  if (RegionDef->select_target(target) == Target::UNKNOWN) {
296  fprintf(stderr, "Model %s was not generated for target %s\n",
297  params.imp_arg, get_string_from_target(target).c_str());
298  exit(1);
299  }
300  // Check the same thing if a plugin is specified
301  if (!RegionPlug.empty()) {
302  if (RegionPlug[0].get().select_target(target) == Target::UNKNOWN) {
303  fprintf(stderr, "Plugin %s was not generated for target %s\n",
304  params.plug_in_arg, get_string_from_target(target).c_str());
305  exit(1);
306  }
307  }
308 
309  // data structure to manage multiple IIF's
310  MULTI_IF MIIF;
311  MULTI_IF doppel, *cMIIF = &MIIF;
312 
313  MIIF.N_IIF = num_region;
314  MIIF.iontypes = {*RegionDef};
315  MIIF.targets = {target};
316  MIIF.IIFmask = Regions;
317  MIIF.numplugs = &num_plugins;
318  MIIF.plugtypes = {RegionPlug};
319  MIIF.numNode = Vmv->lsize();
320  MIIF.gdata[Vm] = Vmv;
321  MIIF.gdata[Iion] = I_ion;
322 
323  // A manually imposed voltage protocol (--clamp / --clamp-file) owns Vm: it is updated
324  // externally, so the ionic model must not integrate the membrane equation for Vm.
325  MIIF.extUpdateVm = IsVclamp;
326 
327  double setup_time = timing(t2, t1);
328  update_timing(timings+SETUP_IDX, setup_time);
329 
330  MIIF.initialize_MIIF();
331 
332  if (params.list_imps_flag || params.plugin_outputs_flag) {
333  print_models(params.plugin_outputs_flag);
334  exit(0);
335  }
336 
337  if (params.imp_info_flag) {
338  print_param_help(RegionDef, RegionPlug);
339  exit(0);
340  }
341 
342  fprintf(stderr, "\n*** GIT tag: %s\n", GIT_COMMIT_TAG);
343  fprintf(stderr, "*** GIT hash: %s\n", GIT_COMMIT_HASH);
344  fprintf(stderr, "*** GIT repo: %s\n", GIT_PATH);
345  fprintf(stderr, "*** dependency commits: %s\n\n", SUBREPO_COMMITS);
346 
347  if (params.buildinfo_flag) {
348  fprintf(stderr, "\n*** -buildinfo flag is deprecated and will be removed.\n");
349  fprintf( stderr, "\n*** The build information is printed by default. \n\n");
350  exit(0);
351  }
352 
353  MIIF.IIF[0]->tune(params.imp_par_arg, params.plug_in_arg, params.plug_par_arg);//FIXME
354 
355  // now compute the tables
356  MIIF.initialize_currents(dt, 1);
357 
358  if (analyze_AP) {
359  // initialize AP analyzer
360  double Vm0 = getCellVal(MIIF.gdata[Vm], 0);
361  for (int i = 0; i < VM_HIST_LEN; i++)
362  AP.vm_trc[i] = Vm0;
363 
364  char ap_stats_fname[1024];
365  snprintf(ap_stats_fname, sizeof ap_stats_fname, "%s_AP_stats.dat", params.fout_arg);
366  AP.stats = fopen(ap_stats_fname, "wt");
367  print_AP_stats_header(&AP, AP.stats);
368  }
369 
370  restitution r;
371  int n_dopple;
372  double *t_dopple;
373  if (restitute) {
374  char *res_file = dupstr(params.res_file_arg);
375  restitution_trigger_list(res_file, &r, params.restitute_arg, &n_dopple, &t_dopple);
376  if (r.dur > duration) duration = r.dur;
377 
378  // output restitution data
379  char ap_rstats_fname[1024];
380  snprintf(ap_rstats_fname, sizeof ap_rstats_fname, "%s_APD_restitution.dat", params.fout_arg);
381  AP.rstats = fopen(ap_rstats_fname, "wt");
382  print_AP_stats_header(&AP, AP.rstats);
383  }
384 
385  // read initial state vector from file and/or assignment
386  double t = 0.;
387  // if (params.restore_given)
388  // t = MIIF.restore_state( params.restore_arg, &pl, true);
389 
390  // illumination stuff
391  double light_irrad = params.light_irrad_arg;
392  double light_start = params.light_start_arg;
393  double light_end = params.duration_arg;
394  int light_nstim = params.light_numstim_arg;
395  double light_bcl = params.light_bcl_arg;
396  double light_dur = params.light_dur_arg;
397 
398  if(light_irrad < 0) {
399  FPRINTF(WORLD stderr, "WARNING: irradiance value %.3f makes no sense (clipped to 0.0)\n", light_irrad);
400  light_irrad = 0.0;
401  }
402  TrgList light_lst;
403  if(params.light_times_given) //user specified list
404  determine_stim_list(params.light_times_arg, &light_lst, false);
405 
406  if (params.SV_init_given)
407  initial_SVs(&MIIF, params.SV_init_arg, params.imp_arg, params.plug_in_arg, numNode);
408 
409  timer_manager tmo(dt, t, duration);
410  tmo.timers.resize(N_TIMERS);
411  tmo.initialize_eq_timer(start_out, t+duration, 0, dt_out, 0., CON_TM_IDX, "CON_TIMER");
412  tmo.initialize_eq_timer(start_out, t+duration, 0, dt_out, 0., SVD_TM_IDX, "SVD_TIMER");
413 
414  if (params.save_time_arg)
415  tmo.initialize_singlestep_timer(params.save_time_arg, 0., STA_TM_IDX, "STA_TIMER");
416 
417  if (params.save_ini_time_given)
418  tmo.initialize_singlestep_timer(params.save_ini_time_arg, 0., SSV_TM_IDX, "SSV_TIMER");
419 
420  if( RRC )
421  tmo.initialize_eq_timer( 0, duration-1, RRC_MAX_N, bcl, 1., DOPPLE_TM_IDX, "DOPPLE_TIMER");
422  //
423  // use a trigger for stimulation
424  if (params.restitute_given) {
425  std::vector<double> trg;
426  trg.assign(r.trigs.lst, r.trigs.lst + r.trigs.n);
427  tmo.initialize_neq_timer(trg, stim_dur, STM_TM_IDX, "STIM_TIMER");
428 
429  if (params.res_state_vector_given) {
430  trg.assign(r.saveState.lst, r.saveState.lst + r.saveState.n);
431  tmo.initialize_neq_timer(trg, 0, RES_SAVE_TM_IDX, "RES_SAVE_TIMER");
432  }
433 
434  if (!strcmp(params.restitute_arg, "S1S2f") ) {
435  trg.assign(t_dopple, t_dopple + n_dopple);
436  tmo.initialize_neq_timer(trg, 1, DOPPLE_TM_IDX, "DOPPLE_TIMER");
437  }
438  }
439  else if (params.stim_times_given) {
440  std::vector<double> trg;
441  trg.assign(stim_lst.lst, stim_lst.lst + stim_lst.n);
442  tmo.initialize_neq_timer(trg, stim_dur, STM_TM_IDX, "STIM_TIMER");
443  }
444  else {
445  tmo.initialize_eq_timer(stim_start, stim_start+duration, nstim, bcl, stim_dur, STM_TM_IDX, "STIM_TIMER");
446  }
447 
448  // illumination timers
449  if (MIIF.gdata[illum] != NULL && !tr_light) {
450  if (params.light_times_given) {
451  std::vector<double> trg;
452  trg.assign(light_lst.lst, light_lst.lst + light_lst.n);
453  tmo.initialize_neq_timer(trg, light_dur, LIGHT_TM_IDX, "LIGHT_TIMER", NULL);
454  } else {
455  tmo.initialize_eq_timer(light_start, light_end, light_nstim,
456  light_bcl, light_dur, LIGHT_TM_IDX, "LIGHT_TIMER", NULL);
457  }
458  }
459 
460  tmo.initialize_eq_timer(start_out, t+duration, 0, dt_out, 0, TRACE_TM_IDX, "TRACE_TIMER");
461 
462  if (IsVclamp && cl.transient)
463  tmo.initialize_singlestep_timer(params.clamp_start_arg, params.clamp_dur_arg, CLAMP_TM_IDX, "CLAMP_TIMER");
464  if (!restitute && params.doppel_on_given) {
465  tmo.initialize_singlestep_timer(params.doppel_on_arg, params.doppel_dur_arg, DOPPLE_TM_IDX, "DOPPLE_TIMER");
466  }
467 
468  // read initial state from single cell file and spread it out to all cells
469  if (params.read_ini_file_given) {
470  if (read_sv(&MIIF, R1, params.read_ini_file_arg) != 0) {
471  return 1; // exit with error code.
472  }
473  }
474 
475  // stretch experiment
476  stretch s;
477  memset(&s, 0, sizeof(stretch));
478  if (params.strain_given && MIIF.use_stretch()) {
479  initializePulseStretch(params.strain_arg, params.strain_time_arg,
480  params.strain_dur_arg, params.strain_rate_arg,
481  params.strain_rate_arg, &s);
482 
483  sf_vec* lambdavec; SF::init_vector(&lambdavec, MIIF.gdata[Vm]);
484  MIIF.gdata[Lambda] = lambdavec;
485  SF_real *l = lambdavec->ptr();
486 
487  for (int i = 0; i < lambdavec->lsize(); i++)
488  l[i] = s.pulse.sr;
489 
490  lambdavec->release_ptr(l);
491  }
492 
493  // set up assigning stimulus current to ion concentrations
494  std::stringstream stim_species(params.stim_species_arg);
495  std::stringstream stim_ratios(params.stim_ratios_arg);
496  std::string specie, ratio;
497  std::vector<std::string> species_list;
498  std::vector<float> ratios_list;
499  float ratio_sum = 0;
500  if (stim_assign) {
501  // split stim_species by ":" and then iterate over them
502  while(std::getline(stim_species, specie, ':'))
503  {
504  species_list.push_back(specie);
505  }
506  while(std::getline(stim_ratios, ratio, ':'))
507  {
508  ratios_list.push_back(std::stof(ratio));
509  }
510  if (species_list.size() != ratios_list.size()) {
511  fprintf(stderr, "Number of ion species for which stimulus should be assigned does not match number of ratio entries: %s | %s\n", params.stim_species_arg, params.stim_ratios_arg);
512  exit(1);
513  }
514  for (auto i : ratios_list)
515  ratio_sum += i;
516  if (std::abs(ratio_sum - 1) >= 0.001)
517  log_msg(NULL, 3, 0, "Warning: Sum of ion species ratios for stimulus assignement != 1. Normalizing to sum %.1f.", ratio_sum);
518  }
519 
520  // dumping of state variables goes here
521  char sv_dump_names[1024];
522  if (!params.fout_given && !params.validate_flag)
523  snprintf(sv_dump_names, sizeof sv_dump_names, "%s_%s", params.fout_arg, params.imp_arg);
524  else
525  strcpy(sv_dump_names, params.fout_arg);
526 
527  if (params.validate_flag) {
528  dump_all(&MIIF, R1, params.imp_arg, params.plug_in_arg, t, dt, sv_dump_names);
529  } else {
530  MIIF.sv_dump_add_by_name_list(R1, params.imp_arg, sv_dump_names,
531  params.imp_sv_dump_arg, params.plug_in_arg,
532  params.plug_sv_dump_arg, t, dt_out);
533  }
534 
535  // dumping of LUTs
536  if (params.dump_lut_flag)
537  MIIF.dump_luts_MIIF(true);
538 
539  // initialize tracing
540  int trace_nodes[] = {0};
541  if (do_trace)
542  open_trace(&MIIF, sizeof(trace_nodes)/sizeof(trace_nodes[0]), trace_nodes, &params.trace_no_arg, NULL);
543 
544  // dumping global vectors to file?
545  open_globalvec_dump(fhdls, &gvd, &MIIF, sv_dump_names, &io);
546 
547  // initialization time
548  double ini_time = timing(t3, t2);
549  update_timing(timings + INIT_IDX, ini_time);
550  doppel_MIIF(&MIIF, &doppel);
551 
552  int gpu_Vm_update = 1;
553  double counter = 0.0;
554  double epsilon = 0.0000001f;
555  Bisect RRC_find( 0, INFINITY, params.RRC_tol_arg );
556  float RRC_est = params.stim_curr_arg/10.;
557  bool RRC_repol;
558 
559  /***********************************************/
560  /***************** MAIN LOOP *******************/
561  /***********************************************/
562 
563  // Print simulation target
564  fprintf(stderr, "Running simulation on target %s\n",
565  get_string_from_target(target).c_str());
566  while (!tmo.elapsed())
567  {
568  get_time(t4);
569  t = tmo.time;
570 
571 
572  if (tmo.triggered_now(DOPPLE_TM_IDX) ) {
573  printf("[%f] Switching to new ion structure\n", tmo.time);
574  if( RRC ) {
575  doppel_update(&doppel, &MIIF);
576  if( tmo.timers[DOPPLE_TM_IDX]->trigger_count>1 ) {
577  if( RRC_find.update( RRC_est, !RRC_repol ) ){
578  std::cout << "\nRRC found: " << RRC_est << std::endl;
579  break;
580  }
581  }
582  set_trace_amp( &stim_trace, RRC_est, params.RRC_delay_arg );
583  std::cout << RRC_est << " " << RRC_repol << std::endl;
584  RRC_repol = false;
585  } else
586  doppel_update(&doppel, &MIIF);
587  }
588 
589  cMIIF->extUpdateVm = IsVclamp; // external Vm control whenever a manual protocol is active
590 
591  if (IsVclamp)
592  clamp_signal(cMIIF, &cl, &tmo);
593 
594  if (APclamp)
595  AP_clamp(&ap_cl, &tmo, cMIIF->gdata[Vm], tmo.triggered_now(STM_TM_IDX) );
596 
597  for (int i = 0; i < SVclamp; i++) {
598  bool trigger;
599  if (params.SV_I_trigger_flag)
600  trigger = tmo.triggered_now(STM_TM_IDX);
601  else
602  trigger = !tmo.d_time;
603 
604  sv_clamp(sv_cl+i, &tmo, cMIIF, trigger);
605  }
606 
607  // output selected state variables
608  cMIIF->dump_svs(tmo.timers[SVD_TM_IDX]);
609  globalvec_dump(fhdls, &gvd, cMIIF, &tmo, &io, numNode);
610 
611  // save state of all cells to binary file
612  if (tmo.trigger(STA_TM_IDX))
613  cMIIF->dump_state(params.save_file_arg, tmo.time, mesh_t::unset_msh, false, 0);
614 
615  // save state of cell 0 to text file
616  if (tmo.trigger(SSV_TM_IDX))
617  save_sv(cMIIF, R1, params.save_ini_file_arg);
618 
619  // save state within an restitution protocol
620  if (tmo.trigger(RES_SAVE_TM_IDX))
621  restitution_save_sv(cMIIF, R1, &r, &AP);
622 
623  // add stim current
624  if (tmo.trigger(STM_TM_IDX)) {
625  gpu_Vm_update = 0;
626  if (tr_stim) {
627  stim_charge = stim_trace.s[tmo.trigger_elapse(STM_TM_IDX)]*dt;
628  }
629  else if (params.stim_volt_given) {
630  SF_real *d = cMIIF->gdata[Vm]->ptr();
631  // peak_vm = last_vm = d[0];
632  stim_charge = (params.stim_volt_arg-d[0])/params.resistance_arg*dt;
633  cMIIF->gdata[Vm]->release_ptr(d);
634  }
635 
636 #ifndef ALGEBRAIC
637  *(cMIIF->gdata[Vm]) += stim_charge;
638 #endif // ifndef ALGEBRAIC
639 
640  if (stim_assign) {
641  int iIon = 0;
642  for (auto ion : species_list)
643  {
644  cMIIF->transmem_stim_species(-stim_charge*(ratios_list[iIon]/ratio_sum), ion.c_str(), params.surface_to_volume_arg, stim_list, numNode);
645  FPRINTF(WORLD stderr, "[stim_assign] @ t = %.3f ms; charge: %f pA/pF*ms; type: %s\n", tmo.time, stim_charge, ion.c_str());
646  iIon++;
647  }
648  }
649  }
650 
651  if (MIIF.gdata[illum] != NULL) {
652  MIIF.gdata[illum]->set(tmo.trigger(LIGHT_TM_IDX) ? light_irrad : 0.0);
653 
654  if(tmo.triggered_now(LIGHT_TM_IDX) && light_irrad) {
655  FPRINTF(WORLD stderr, "[stim] illum ON @ t = %.3f ms; E_e = %.3f mW/mm^2\n", tmo.time, light_irrad);
656  }
657  if(tmo.trigger_end(LIGHT_TM_IDX) && light_irrad) {
658  FPRINTF(WORLD stderr, "[stim] illum OFF @ t = %.3f ms\n", tmo.time);
659  }
660  }
661 
662  // strain
663  if (params.strain_given && cMIIF->use_stretch())
664  apply_stretch(cMIIF, &s, &tmo);
665 
666  if (analyze_AP) {
667  if (restitute) AP.pmat = r.trigs.pmat[AP.beat];
668  check_events(getCellVal(cMIIF->gdata[Vm], 0), &AP, &tmo);
669  }
670 
671  get_time(t5);
672 
673 
674  if (is_gpu(target)) {
675  if (gpu_Vm_update == 0) {
676  cMIIF->compute_ionic_current(1, 1);
677  } else if (gpu_Vm_update == 1) {
678  cMIIF->compute_ionic_current(fabs(counter - 0.0) < epsilon || fabs(counter - 1.0) < epsilon , fabs((counter+dt) - 1.0) < epsilon);
679  }
680  }
681  else {
682  cMIIF->compute_ionic_current();
683  }
684 
685  double ode_time = timing(t6, t5);
686  update_timing(timings+ODE_IDX, ode_time);
687 
688  if (do_trace && tmo.trigger(TRACE_TM_IDX))
689  dump_trace(cMIIF, t);
690 
691  if (!restitute && !RRC && tmo.trigger_end(DOPPLE_TM_IDX) ) {
692  log_msg(NULL, 0, 0, "[%f] Switching back to original ion structure\n", tmo.time);
693  cMIIF = &MIIF;
694  }
695 
696  tmo.update_timers();
697 
698  double loop_time = timing(t7, t4);
699  update_timing(timings+LOOP_IDX, loop_time);
700 
701  gpu_Vm_update = 1;
702  counter = counter + dt;
703  if ( fabs(counter - 1.0) < epsilon ) {
704  counter = 0.0;
705  }
706 
707  if( RRC && tmo.trigger(STM_TM_IDX) &&
708  tmo.trigger_elapse(STM_TM_IDX)*tmo.time_step>params.RRC_delay_arg ){
709  auto data = cMIIF->gdata[Vm]->ptr();
710  if( *data <= params.RRC_repol_arg )
711  RRC_repol = true;
712  cMIIF->gdata[Vm]->release_ptr(data);
713  }
714  }//loop end
715 
716  // loop_current time
717  double main_time = timing(t8, t3);
718 
719  // write header so that we know which files belong together
720  const char *ExpID = params.fout_arg;
721  write_dump_header(&gvd, &MIIF.svd, ExpID);
722 
723  // clean up everything
724  if (tr_stim) free_trace(&stim_trace);
725  if (analyze_AP) cleanup_AP_analysis(&AP);
726  if (do_trace) close_trace(&MIIF);
727  MIIF.close_svs_dumps();
728  free_doppel(&doppel);
729  MIIF.free_MIIF();
730 
731  close_globalvec_dump(fhdls, &gvd, &io);
732 
733  log_msg(NULL, 0, 0, "\n\n\nAll done!\n\n");
734 
735  // print timing data
736  event_timing *t_setup = timings+SETUP_IDX;
737  log_msg(NULL, 0, 0, "setup time %.6f s", (double)t_setup->tot);
738  event_timing *t_init = timings+INIT_IDX;
739  log_msg(NULL, 0, 0, "initialization time %.6f s", (double)t_init->tot);
740  event_timing *t_loop = timings+LOOP_IDX;
741  log_msg(NULL, 0, 0, "main loop time %.6f s", (double)t_loop->tot);
742  event_timing *t_ode = timings+ODE_IDX;
743  log_msg(NULL, 0, 0, "total ode time %.6f s\n", (double)t_ode->tot);
744 
745  log_msg(NULL, 0, 0, "mn/avg/mx loop time %.6f %.6f %.6f ms",
746  (double)t_loop->mn*1e3, (double)t_loop->avg*1e3, (double)t_loop->mx*1e3);
747  log_msg(NULL, 0, 0, "mn/avg/mx ODE time %.6f %.6f %.6f ms",
748  (double)t_ode->mn*1e3, (double)t_ode->avg*1e3, (double)t_ode->mx*1e3);
749 
750  log_msg(NULL, 0, 0, "real time factor %.6f\n\n", duration/(t_ode->tot*1e3) );
751 
752 
753  CHKERRQ(PetscFinalize());
754  return 0;
755 } // main
Define multiple ionic models to be used in different regions.
opencarp::real_t SF_real
Global scalar type.
Definition: SF_globals.h:33
#define VM_HIST_LEN
Definition: ap_analyzer.h:87
Basic utility structs and functions, mostly IO related.
int main(int argc, char *argv[])
Definition: bench.cc:86
#define RRC_MAX_N
bool update(float &val, bool success)
update search
Definition: bench_utils.h:47
virtual S * ptr()=0
virtual void release_ptr(S *&p)=0
virtual T lsize() const =0
virtual void set(const vector< T > &idx, const vector< S > &vals, const bool additive=false, const bool local=false)=0
The scatterer registry class.
Abstract class representing an ionic model type.
Definition: ion_type.h:59
virtual Target select_target(Target target) const =0
Gets a supported target from the given target.
bool extUpdateVm
flag indicating update function for Vm
Definition: MULTI_ION_IF.h:219
std::vector< IonIfBase * > IIF
array of IIF's
Definition: MULTI_ION_IF.h:213
opencarp::sf_vec * gdata[NUM_IMP_DATA_TYPES]
data used by all IMPs
Definition: MULTI_ION_IF.h:227
void sv_dump_add_by_name_list(int, char *, char *, char *, char *, char *, double, double)
node_count_t numNode
local number of nodes
Definition: MULTI_ION_IF.h:221
size_t dump_svs(opencarp::base_timer *)
int * numplugs
number of plugins for each region
Definition: MULTI_ION_IF.h:220
std::vector< Target > targets
target for each region
Definition: MULTI_ION_IF.h:224
std::vector< IonTypeList > plugtypes
plugins types for each region
Definition: MULTI_ION_IF.h:226
IonTypeList iontypes
type for each region
Definition: MULTI_ION_IF.h:223
void dump_state(char *, float, opencarp::mesh_t gid, bool, unsigned int)
SV_DUMP svd
state variable dump
Definition: MULTI_ION_IF.h:214
void transmem_stim_species(float, const char *, float, int *, int)
void initialize_currents(double, int)
int N_IIF
how many different IIF's
Definition: MULTI_ION_IF.h:222
void compute_ionic_current(bool flag_send=1, bool flag_receive=1)
GPU kernel to emulate the add_scaled call made to adjust the Vm values when the update to Vm is not m...
void dump_luts_MIIF(bool)
IIF_Mask_t * IIFmask
region for each node
Definition: MULTI_ION_IF.h:225
centralize time managment and output triggering
Definition: timer_utils.h:73
void initialize_neq_timer(const std::vector< double > &itrig, double idur, int ID, const char *iname, const char *poolname=nullptr)
Definition: timer_utils.cc:63
long d_time
current time instance index
Definition: timer_utils.h:77
bool trigger(int ID) const
Definition: timer_utils.h:166
void initialize_eq_timer(double istart, double iend, int ntrig, double iintv, double idur, int ID, const char *iname, const char *poolname=nullptr)
Definition: timer_utils.cc:48
double time_step
global reference time step
Definition: timer_utils.h:78
void initialize_singlestep_timer(double tg, double idur, int ID, const char *iname, const char *poolname=nullptr)
Definition: timer_utils.h:156
int trigger_elapse(int ID) const
Definition: timer_utils.h:186
bool trigger_end(int ID) const
Definition: timer_utils.h:178
std::vector< base_timer * > timers
vector containing individual timers
Definition: timer_utils.h:84
double time
current time
Definition: timer_utils.h:76
bool triggered_now(int ID) const
Definition: timer_utils.h:171
void init_vector(SF::abstract_vector< T, S > **vec)
Definition: SF_init.h:107
int mk_RRC_trace(trace *tr, float st, float delay, float clamp, float dur)
make a trace for a repolarization reserve current calculation
Definition: trace.cc:63
void initial_SVs(MULTI_IF *miif, char *SVs, char *imp, char *plgins, int num)
Definition: bench_utils.cc:412
void doppel_MIIF(MULTI_IF *orig, MULTI_IF *miif_doppel)
int write_dump_header(GVEC_DUMP *gvd, SV_DUMP *svd, const char *ExpID)
Definition: bench_utils.cc:135
int get_plug_flag(char *plgstr, int *out_num_plugins, IonTypeList &out_plugins)
void doppel_update(MULTI_IF *orig, MULTI_IF *miif_doppel)
void globalvec_dump(FILE **fhdls, GVEC_DUMP *gvd, MULTI_IF *pMIIF, timer_manager *tmo, IOCtrl *io, int numNode)
Definition: bench_utils.cc:272
void resample_trace(trace *tr, double dt)
Definition: trace.cc:162
int load_ionic_module(const char *)
void free_trace(trace *tr)
Definition: trace.cc:123
void clamp_signal(MULTI_IF *pMIIF, Clamp *cl, timer_manager *tm)
Definition: clamp.cc:69
Target
enum that represents different targets to run ionic models on.
Definition: target.h:45
@ AUTO
Definition: target.h:46
@ UNKNOWN
special value to handle unknown targets
Definition: target.h:47
void set_trace_amp(trace *tr, float val, float whenceforth)
change the amplitude of part of a trace
Definition: trace.cc:43
void sv_clamp(Clamp *cl, timer_manager *tm, MULTI_IF *miif, bool trigger)
Definition: clamp.cc:138
@ LOOP_IDX
timing for main loop (including IO and ODE solve)
Definition: bench_utils.h:120
@ ODE_IDX
timing for ODE solve
Definition: bench_utils.h:121
@ INIT_IDX
timing for initialization
Definition: bench_utils.h:119
@ N_TIMINGS
number of benchmark timings we use
Definition: bench_utils.h:122
@ SETUP_IDX
timing for setup phase
Definition: bench_utils.h:118
std::string get_string_from_target(Target const target)
Get a string representation of a given target.
Definition: target.cc:49
void dump_all(MULTI_IF *MIIF, int reg, char *imp, char *plugs, double t, double ddt, char *fout)
Definition: bench_utils.cc:344
void close_globalvec_dump(FILE **fhdls, GVEC_DUMP *gvd, IOCtrl *io)
Definition: bench_utils.cc:315
IonType * get_ion_type(const std::string &name)
int initialize_AP_analysis(action_potential *AP)
Definition: ap_analyzer.cc:71
void restitution_trigger_list(char *r_file, restitution *r, char *protocol, int *n_dop, double **t_dop)
Definition: restitute.cc:45
void save_sv(MULTI_IF *, int, const char *)
double getCellVal(sf_vec *v, int ind)
Definition: bench_utils.cc:392
std::string get_target_list_string()
Returns a string containing the list of available targets.
Definition: target.cc:58
void apply_stretch(MULTI_IF *miif, stretch *s, timer_manager *tm)
Definition: stretch.cc:55
bool is_gpu(Target const target)
Checks if this is a GPU target.
Definition: target.cc:67
void restitution_save_sv(MULTI_IF *miif, int R1, restitution *r, action_potential *AP)
Definition: restitute.cc:275
@ CON_TM_IDX
Definition: bench_utils.h:71
@ DOPPLE_TM_IDX
Definition: bench_utils.h:79
@ LIGHT_TM_IDX
Definition: bench_utils.h:76
@ STM_TM_IDX
Definition: bench_utils.h:75
@ TRACE_TM_IDX
Definition: bench_utils.h:77
@ N_TIMERS
Definition: bench_utils.h:81
@ SVD_TM_IDX
Definition: bench_utils.h:72
@ CLAMP_TM_IDX
Definition: bench_utils.h:78
@ RES_SAVE_TM_IDX
Definition: bench_utils.h:80
@ STA_TM_IDX
Definition: bench_utils.h:73
@ SSV_TM_IDX
Definition: bench_utils.h:74
void print_AP_stats_header(action_potential *AP, FILE *outbuf)
Definition: ap_analyzer.cc:526
void initialize_timings(event_timing *t)
Definition: bench_utils.cc:366
bool initialize_clamp(Clamp *cl, double cl_val, double ini_val, double start, double dur, const char *f, int trans, float *duration)
Definition: clamp.cc:45
void initializePulseStretch(float strain, float onset, float duration, float rise, float fall, stretch *s)
Definition: stretch.cc:38
void determine_stim_list(char *stl, TrgList *trg, bool DIAs)
Definition: bench_utils.cc:101
void cleanup_AP_analysis(action_potential *AP)
Definition: ap_analyzer.cc:169
std::vector< std::reference_wrapper< IonType > > IonTypeList
Definition: ion_type.h:291
float determine_duration(struct gengetopt_args_info *p, TrgList *stim_lst)
determine time of last stimulus
Definition: bench_utils.cc:503
int process_sv_clamps(char *SVs, char *files, Clamp **clamps, double dt)
Definition: clamp.cc:197
int read_trace(trace *tr, const char *name)
Definition: trace.cc:90
void open_globalvec_dump(FILE **fhdls, GVEC_DUMP *gvd, MULTI_IF *pMIIF, char *base_name, IOCtrl *io)
Definition: bench_utils.cc:176
void print_models(bool)
int read_sv(MULTI_IF *, int, const char *)
void close_trace(MULTI_IF *MIIF)
void dump_trace(MULTI_IF *MIIF, limpet::Real time)
void update_timing(event_timing *t, double event_duration)
Definition: bench_utils.cc:376
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.
void free_doppel(MULTI_IF *m)
void initialize_sv_clamp(Clamp *cl, const char *sv, char *file, double dt)
Definition: clamp.cc:117
bool check_events(double vm, action_potential *AP, timer_manager *tm)
Definition: ap_analyzer.cc:263
void print_param_help(IonType *im, IonTypeList &plugs)
Definition: bench_utils.cc:468
Target get_target_from_string(std::string const str)
Returns a value from the Target enum from a given string.
Definition: target.cc:39
void AP_clamp(Clamp *cl, timer_manager *tm, sf_vec *v, bool trigger)
Definition: clamp.cc:178
timer_manager * tm_manager
a manager for the various physics timers
Definition: main.cc:55
std::map< SF::quadruple< int >, SF::index_mapping< mesh_int_t > > map_reg
Registriy for the inter domain mappings.
Definition: main.cc:51
SF::scatter_registry scatter_reg
Registry for the different scatter objects.
Definition: main.cc:47
std::map< mesh_t, sf_mesh > mesh_reg
Registry for the different meshes used in a multi-physics simulation.
Definition: main.cc:49
V dot(const vec3< V > &p1, const vec3< V > &p2)
Definition: vect.h:125
char * dupstr(const char *old_str)
Definition: basics.cc:44
void log_msg(FILE_SPEC out, int level, unsigned char flag, const char *fmt,...)
Definition: basics.cc:72
void get_time(double &tm)
Definition: basics.h:444
int get_size(MPI_Comm comm=PETSC_COMM_WORLD)
Definition: basics.h:298
V timing(V &t2, const V &t1)
Definition: basics.h:456
#define Vm_clamp
Definition: stimulate.h:46
int transient
Definition: clamp.h:37
char wbin
write to file in binary format
Definition: bench_utils.h:93
char w2file
write to file
Definition: bench_utils.h:92
int n
number of pulses required for protocol
Definition: restitute.h:29
double * lst
store instants of pulse delivery
Definition: restitute.h:30
bool * pmat
store flag to indicate prematurity
Definition: restitute.h:31
bool pmat
indiacte premature AP
Definition: ap_analyzer.h:91
int beat
beat counter
Definition: ap_analyzer.h:89
FILE * rstats
output restitution statistics only
Definition: ap_analyzer.h:104
FILE * stats
output statistics for each AP
Definition: ap_analyzer.h:103
double vm_trc[VM_HIST_LEN]
Definition: ap_analyzer.h:100
double avg
average duration of event
Definition: bench_utils.h:112
double mx
maximum duration of event
Definition: bench_utils.h:111
double mn
minimum duration of event
Definition: bench_utils.h:110
double tot
total duration of all events
Definition: bench_utils.h:113
TrgList saveState
instants at wich we save state vectors
Definition: restitute.h:51
double dur
total duration of protocol
Definition: restitute.h:52
TrgList trigs
trigger list for defining stim sequence
Definition: restitute.h:50
manage input, output, resampling of traces
Definition: trace.h:25
double dur
duration
Definition: trace.h:29
float * s
samples
Definition: trace.h:28
int N
number of samples
Definition: trace.h:26
Defines valid targets for an ionic model to run on and an allocator for allocating memory on a specif...
pulseStretch pulse
Definition: stretch.h:52