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