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 <fstream>
26 #include <string>
27 #include <cmath>
28 #include "basics.h"
29 
30 #include "MULTI_ION_IF.h"
31 
32 #include "trace.h"
33 #include "clamp.h"
34 #include "ap_analyzer.h"
35 #include "restitute.h"
36 #include "stretch.h"
37 #include "sv_init.h"
38 #include "bench_utils.h"
39 #include "target.h"
40 #include "Lapicque.h"
41 
42 #include "libgen.h"
43 #include "build_info.h"
44 
45 #include "petsc_utils.h" //TODO for EXIT, FPRINT*
46 #include "SF_init.h" // for SF::init_xxx()
47 
48 
49 // globals
50 namespace opencarp {
51 namespace user_globals {
55  std::map<mesh_t, sf_mesh> mesh_reg;
57  std::map<SF::quadruple<int>, SF::index_mapping<mesh_int_t> > map_reg;
58  // /// the physics
59  // std::map<physic_t, Basic_physic*> physics_reg;
62  // /// important solution vectors from different physics
63  // std::map<datavec_t, sf_vec*> datavec_reg;
64 } // namespace user_globals
65 } // namespace opencarp
66 
67 using namespace opencarp;
68 using namespace limpet;
69 
70 #undef __FUNCT__
71 #define __FUNCT__ "main"
72 
73 int main(int argc, char *argv[]) {
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  // determine mode
120  bool RRC = params.RRC_mode_counter;
121  bool Lapicque_crv = params.StimThresh_mode_counter;
122  bool restitute = params.restitute_given;
123 
124  // assign command line params and dependent variables
125  double dt = params.dt_arg;
126  double bcl = Lapicque_crv ? params.Lap_bcl_arg : params.bcl_arg;
127  double stim_start = Lapicque_crv ? 0. : params.stim_start_arg;
128  int nstim = Lapicque_crv ? params.Lap_numstim_arg : params.bcl_arg;
129  double stim_dur = params.stim_dur_arg;
130  double stim_charge = params.stim_curr_arg*dt;
131  double dt_out = params.dt_out_arg;
132  double start_out = params.start_out_arg;
133  int numNode = params.num_arg;
134  int stim_assign = params.stim_assign_flag;
135  bool analyze_AP = params.APstatistics_given || params.restitute_given;
136  bool validate = params.validate_given;
137  bool APclamp = params.AP_clamp_file_given;
138  bool do_trace = restitute ? params.res_trace_flag : (!params.no_trace_flag);
139  TrgList stim_lst;
140  float duration = determine_duration(&params, &stim_lst);
141 
142  nstim = nstim ? nstim : -1;
143  io.w2file = validate ? 1 : params.fout_given;
144  io.wbin = validate ? 1 : params.bin_flag;
145 
146  if(get_size() > numNode) {
147  numNode = get_size();
148 
149  log_msg(NULL, 3, 0, "Warning: Number of nodes is less than number of processes used!");
150  log_msg(NULL, 3, 0, "Setting number of nodes to %d.", numNode);
151  }
152 
153  // stimulate with trace from file
154  trace stim_trace;
155  bool tr_stim = false;
156  if (params.stim_file_given) {
157  char *fname = dupstr(params.stim_file_arg);
158  read_trace(&stim_trace, fname);
159  resample_trace(&stim_trace, dt);
160  tr_stim = true;
161  stim_dur = stim_trace.dur;
162  if (bcl < stim_trace.dur)
163  log_msg(NULL, 4, 0, "BCL less than stimulus duration!");
164  }
165 #define RRC_MAX_N 50
166  if( RRC ) {
167  mk_RRC_trace(&stim_trace, params.stim_curr_arg,params.RRC_delay_arg,
168  -1., params.RRC_clamp_dur_arg );
169  resample_trace(&stim_trace, dt);
170  tr_stim = true;
171  stim_dur = stim_trace.dur;
172  nstim = RRC_MAX_N;
173  bcl = stim_dur+10.;
174  duration = nstim*bcl;
175  }
176 
177  // illuminate with trace from file
178  trace light_trace;
179  bool tr_light = false;
180  if(params.light_file_given) {
181  char* fname = dupstr(params.light_file_arg);
182  read_trace(&light_trace, fname);
183  resample_trace(&light_trace, dt);
184  tr_light = true;
185 
186  int n_clipped_to_0 = 0;
187  for(int i=0; i<light_trace.N; i++) {
188  if( light_trace.s[i] < 0.0 ) {
189  light_trace.s[i] = 0.0;
190  n_clipped_to_0++;
191  }
192  }
193  if( n_clipped_to_0 ) {
194  FPRINTF(WORLD stderr, "WARNING: %d irradiance values in %s were negative (clipped to 0.0)\n", n_clipped_to_0, fname);
195  }
196  }
197 
198  // state variable clamp
199  Clamp *sv_cl;
200  int SVclamp = process_sv_clamps(params.clamp_SVs_arg, params.SV_clamp_files_arg, &sv_cl, dt);
201 
202  // action potential clamp
203  Clamp ap_cl;
204  if (APclamp){
205  const char *Vm_clamp = "Vm";
206  initialize_sv_clamp(&ap_cl, Vm_clamp, params.AP_clamp_file_arg, dt);
207  }
208 
209  // voltage clamp experiment
210  Clamp cl;
211  bool IsVclamp = initialize_clamp(&cl, params.clamp_arg, params.clamp_ini_arg,
212  params.clamp_start_arg, params.clamp_dur_arg,
213  params.clamp_file_arg,
214  !params.clamp_ini_given ? CLAMP_TM_IDX : 0, &duration);
215  if (IsVclamp) {
216  if (APclamp) {
217  log_msg(NULL, 0, 0, "info: cannot clamp voltage and have AP clamp at same time");
218  exit(1);
219  }
220  if (cl.transient) {
221  log_msg(NULL, 0, 0, "info: Vm will be clamped to %f mV over the interval [%f, %f]",
222  params.clamp_arg, params.clamp_start_arg, params.clamp_start_arg + params.clamp_dur_arg);
223  } else {
224  log_msg(NULL, 0, 0, "info: Vm will be clamped from %f to %f mV over the interval [%f, %f]",
225  params.clamp_ini_arg, params.clamp_arg, params.clamp_start_arg,
226  params.clamp_start_arg + params.clamp_dur_arg);
227  }
228  }
229 
230  action_potential AP;
231  if (analyze_AP) initialize_AP_analysis(&AP);
232 
233  Regions = static_cast<char *>(calloc(numNode, sizeof(char) ));
234 
235  SF::init_vector(&Vmv, numNode, -1);
236  SF::init_vector(&I_ion, Vmv);
237 
238  int *stim_list = static_cast<int *>(malloc(numNode*sizeof(int) ));
239  for (int i = 0; i < numNode; i++) {
240  Regions[i] = R1;
241  stim_list[i] = i;
242  }
243 
244  for (unsigned int i = 0; i < params.load_module_given; i++) {
245 #ifdef HAVE_DLOPEN
246  load_ionic_module(params.load_module_arg[i]);
247  if (!params.imp_given) {
248 
249  char *imp = strdup(basename(params.load_module_arg[i]) );
250  char *dot = strrchr(imp, '.');
251  if (dot)
252  *dot = 0;
253  params.imp_arg = imp;
254  }
255 #else // ifdef HAVE_DLOPEN
256  log_msg(NULL, 5, 0, "Compile with USE_DLOPEN to support dynamic module loading.");
257  EXIT(-1);
258 #endif // ifdef HAVE_DLOPEN
259  }
260 
261  if (get_ion_type(std::string(params.imp_arg)) == NULL) {
262  fprintf(stderr, "Illegal IMP specified: %s\n", params.imp_arg);
263  exit(1);
264  } else {
265  RegionDef = get_ion_type(std::string(params.imp_arg));
266  }
267  if (!get_plug_flag(params.plug_in_arg, &num_plugins, RegionPlug)) {
268  fprintf(stderr, "Illegal plugin specified: %s\n", params.plug_in_arg);
269  exit(1);
270  }
271  // Fetch target enum from command line string
272  Target target = get_target_from_string(std::string(params.target_arg));
273  // Automatic target selection
274  if (target == Target::AUTO) {
275  target = RegionDef->select_target(target);
276  }
277  // Check whether target exists and was generated for the given model and plugin
278  if (target == Target::UNKNOWN) {
279  fprintf(stderr, "Unkown target: %s\n", params.target_arg);
280  fprintf(stderr, "Available targets are: %s\n", get_target_list_string().c_str());
281  exit(1);
282  }
283  // Check if the main imp was generated for the requested target
284  if (RegionDef->select_target(target) == Target::UNKNOWN) {
285  fprintf(stderr, "Model %s was not generated for target %s\n",
286  params.imp_arg, get_string_from_target(target).c_str());
287  exit(1);
288  }
289  // Check the same thing if a plugin is specified
290  if (!RegionPlug.empty()) {
291  if (RegionPlug[0].get().select_target(target) == Target::UNKNOWN) {
292  fprintf(stderr, "Plugin %s was not generated for target %s\n",
293  params.plug_in_arg, get_string_from_target(target).c_str());
294  exit(1);
295  }
296  }
297 
298  // data structure to manage multiple IIF's
299  MULTI_IF MIIF;
300  MULTI_IF doppel, *cMIIF = &MIIF;
301  bool doppel_created = false;
302 
303  MIIF.N_IIF = num_region;
304  MIIF.iontypes = {*RegionDef};
305  MIIF.targets = {target};
306  MIIF.IIFmask = Regions;
307  MIIF.numplugs = &num_plugins;
308  MIIF.plugtypes = {RegionPlug};
309  MIIF.numNode = Vmv->lsize();
310  MIIF.gdata[Vm] = Vmv;
311  MIIF.gdata[Iion] = I_ion;
312 
313  // A manually imposed voltage protocol (--clamp / --clamp-file) owns Vm: it is updated
314  // externally, so the ionic model must not integrate the membrane equation for Vm.
315  MIIF.extUpdateVm = IsVclamp;
316 
317  double setup_time = timing(t2, t1);
318  update_timing(timings+SETUP_IDX, setup_time);
319 
320  MIIF.initialize_MIIF();
321 
322  if (params.list_imps_flag || params.plugin_outputs_flag) {
323  print_models(params.plugin_outputs_flag);
324  exit(0);
325  }
326 
327  if (params.imp_info_flag) {
328  print_param_help(RegionDef, RegionPlug);
329  exit(0);
330  }
331 
332  fprintf(stderr, "\n*** GIT tag: %s\n", GIT_COMMIT_TAG);
333  fprintf(stderr, "*** GIT hash: %s\n", GIT_COMMIT_HASH);
334  fprintf(stderr, "*** GIT repo: %s\n", GIT_PATH);
335  fprintf(stderr, "*** dependency commits: %s\n\n", SUBREPO_COMMITS);
336 
337  if (params.buildinfo_flag) {
338  fprintf(stderr, "\n*** -buildinfo flag is deprecated and will be removed.\n");
339  fprintf( stderr, "\n*** The build information is printed by default. \n\n");
340  exit(0);
341  }
342 
343  MIIF.IIF[0]->tune(params.imp_par_arg, params.plug_in_arg, params.plug_par_arg);//FIXME
344 
345  // now compute the tables
346  MIIF.initialize_currents(dt, 1);
347 
348  if (analyze_AP) {
349  // initialize AP analyzer
350  double Vm0 = getCellVal(MIIF.gdata[Vm], 0);
351  for (int i = 0; i < VM_HIST_LEN; i++)
352  AP.vm_trc[i] = Vm0;
353 
354  char ap_stats_fname[1024];
355  snprintf(ap_stats_fname, sizeof ap_stats_fname, "%s_AP_stats.dat", params.fout_arg);
356  AP.stats = fopen(ap_stats_fname, "wt");
357  print_AP_stats_header(&AP, AP.stats);
358  }
359 
360  restitution r;
361  int n_dopple;
362  double *t_dopple;
363  if (restitute) {
364  char *res_file = dupstr(params.res_file_arg);
365  restitution_trigger_list(res_file, &r, params.restitute_arg, &n_dopple, &t_dopple);
366  if (r.dur > duration) duration = r.dur;
367 
368  // output restitution data
369  char ap_rstats_fname[1024];
370  snprintf(ap_rstats_fname, sizeof ap_rstats_fname, "%s_APD_restitution.dat", params.fout_arg);
371  AP.rstats = fopen(ap_rstats_fname, "wt");
372  print_AP_stats_header(&AP, AP.rstats);
373  }
374 
375  // read initial state vector from file and/or assignment
376  double t = 0.;
377  // if (params.restore_given)
378  // t = MIIF.restore_state( params.restore_arg, &pl, true);
379 
380  // illumination stuff
381  double light_irrad = params.light_irrad_arg;
382  double light_start = params.light_start_arg;
383  double light_end = params.duration_arg;
384  int light_nstim = params.light_numstim_arg;
385  double light_bcl = params.light_bcl_arg;
386  double light_dur = params.light_dur_arg;
387 
388  if(light_irrad < 0) {
389  FPRINTF(WORLD stderr, "WARNING: irradiance value %.3f makes no sense (clipped to 0.0)\n", light_irrad);
390  light_irrad = 0.0;
391  }
392  TrgList light_lst;
393  if(params.light_times_given) //user specified list
394  determine_stim_list(params.light_times_arg, &light_lst, false);
395 
396  if (params.SV_init_given)
397  initial_SVs(&MIIF, params.SV_init_arg, params.imp_arg, params.plug_in_arg, numNode);
398 
399  timer_manager tmo(dt, t, duration);
400  tmo.timers.resize(N_TIMERS);
401  tmo.initialize_eq_timer(start_out, t+duration, 0, dt_out, 0., CON_TM_IDX, "CON_TIMER");
402  tmo.initialize_eq_timer(start_out, t+duration, 0, dt_out, 0., SVD_TM_IDX, "SVD_TIMER");
403 
404  if (params.save_time_arg)
405  tmo.initialize_singlestep_timer(params.save_time_arg, 0., STA_TM_IDX, "STA_TIMER");
406 
407  if (params.save_ini_time_given)
408  tmo.initialize_singlestep_timer(params.save_ini_time_arg, 0., SSV_TM_IDX, "SSV_TIMER");
409 
410  if( RRC )
411  tmo.initialize_eq_timer( 0, duration-1, RRC_MAX_N, bcl, 1., DOPPLE_TM_IDX, "DOPPLE_TIMER");
412  if( Lapicque_crv )
413  tmo.initialize_singlestep_timer( nstim*bcl, 0, DOPPLE_TM_IDX, "DOPPLE_TIMER");
414 
415  // use a trigger for stimulation
416  if (params.restitute_given) {
417  std::vector<double> trg;
418  trg.assign(r.trigs.lst, r.trigs.lst + r.trigs.n);
419  tmo.initialize_neq_timer(trg, stim_dur, STM_TM_IDX, "STIM_TIMER");
420 
421  if (params.res_state_vector_given) {
422  trg.assign(r.saveState.lst, r.saveState.lst + r.saveState.n);
423  tmo.initialize_neq_timer(trg, 0, RES_SAVE_TM_IDX, "RES_SAVE_TIMER");
424  }
425 
426  if (!strcmp(params.restitute_arg, "S1S2f") ) {
427  trg.assign(t_dopple, t_dopple + n_dopple);
428  tmo.initialize_neq_timer(trg, 1, DOPPLE_TM_IDX, "DOPPLE_TIMER");
429  }
430  }
431  else if (params.stim_times_given) {
432  std::vector<double> trg;
433  trg.assign(stim_lst.lst, stim_lst.lst + stim_lst.n);
434  tmo.initialize_neq_timer(trg, stim_dur, STM_TM_IDX, "STIM_TIMER");
435  }
436  else {
437  tmo.initialize_eq_timer(stim_start, stim_start+duration, nstim, bcl, stim_dur, STM_TM_IDX, "STIM_TIMER");
438  }
439 
440  // illumination timers
441  if (MIIF.gdata[illum] != NULL && !tr_light) {
442  if (params.light_times_given) {
443  std::vector<double> trg;
444  trg.assign(light_lst.lst, light_lst.lst + light_lst.n);
445  tmo.initialize_neq_timer(trg, light_dur, LIGHT_TM_IDX, "LIGHT_TIMER", NULL);
446  } else {
447  tmo.initialize_eq_timer(light_start, light_end, light_nstim,
448  light_bcl, light_dur, LIGHT_TM_IDX, "LIGHT_TIMER", NULL);
449  }
450  }
451 
452  tmo.initialize_eq_timer(start_out, t+duration, 0, dt_out, 0, TRACE_TM_IDX, "TRACE_TIMER");
453 
454  if (IsVclamp && cl.transient)
455  tmo.initialize_singlestep_timer(params.clamp_start_arg, params.clamp_dur_arg, CLAMP_TM_IDX, "CLAMP_TIMER");
456  if (!restitute && params.doppel_on_given) {
457  tmo.initialize_singlestep_timer(params.doppel_on_arg, params.doppel_dur_arg, DOPPLE_TM_IDX, "DOPPLE_TIMER");
458  }
459 
460  // read initial state from single cell file and spread it out to all cells
461  if (params.read_ini_file_given) {
462  if (read_sv(&MIIF, R1, params.read_ini_file_arg) != 0) {
463  return 1; // exit with error code.
464  }
465  }
466 
467  // stretch experiment
468  stretch s;
469  memset(&s, 0, sizeof(stretch));
470  if (params.strain_given && MIIF.use_stretch()) {
471  initializePulseStretch(params.strain_arg, params.strain_time_arg,
472  params.strain_dur_arg, params.strain_rate_arg,
473  params.strain_rate_arg, &s);
474 
475  sf_vec* lambdavec; SF::init_vector(&lambdavec, MIIF.gdata[Vm]);
476  MIIF.gdata[Lambda] = lambdavec;
477  SF_real *l = lambdavec->ptr();
478 
479  for (int i = 0; i < lambdavec->lsize(); i++)
480  l[i] = s.pulse.sr;
481 
482  lambdavec->release_ptr(l);
483  }
484 
485  // Lapique curve
486  Lapicque *lapicque = NULL;
487  if( Lapicque_crv ) lapicque = new Lapicque(&tmo,params.Lap_min_arg,params.Lap_max_arg,
488  params.Lapicque_arg, params.thresh_tol_arg, params.thresh_tm_arg );
489 
490  // set up assigning stimulus current to ion concentrations
491  std::stringstream stim_species(params.stim_species_arg);
492  std::stringstream stim_ratios(params.stim_ratios_arg);
493  std::string specie, ratio;
494  std::vector<std::string> species_list;
495  std::vector<float> ratios_list;
496  float ratio_sum = 0;
497  if (stim_assign) {
498  // split stim_species by ":" and then iterate over them
499  while(std::getline(stim_species, specie, ':'))
500  {
501  species_list.push_back(specie);
502  }
503  while(std::getline(stim_ratios, ratio, ':'))
504  {
505  ratios_list.push_back(std::stof(ratio));
506  }
507  if (species_list.size() != ratios_list.size()) {
508  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);
509  exit(1);
510  }
511  for (auto i : ratios_list)
512  ratio_sum += i;
513  if (std::abs(ratio_sum - 1) >= 0.001)
514  log_msg(NULL, 3, 0, "Warning: Sum of ion species ratios for stimulus assignement != 1. Normalizing to sum %.1f.", ratio_sum);
515  }
516 
517  // dumping of state variables goes here
518  char sv_dump_names[1024];
519  if (!params.fout_given && !params.validate_flag)
520  snprintf(sv_dump_names, sizeof sv_dump_names, "%s_%s", params.fout_arg, params.imp_arg);
521  else
522  strcpy(sv_dump_names, params.fout_arg);
523 
524  if (params.validate_flag) {
525  dump_all(&MIIF, R1, params.imp_arg, params.plug_in_arg, t, dt, sv_dump_names);
526  } else {
527  MIIF.sv_dump_add_by_name_list(R1, params.imp_arg, sv_dump_names,
528  params.imp_sv_dump_arg, params.plug_in_arg,
529  params.plug_sv_dump_arg, t, dt_out);
530  }
531 
532  // dumping of LUTs
533  if (params.dump_lut_flag)
534  MIIF.dump_luts_MIIF(true);
535 
536  // initialize tracing
537  int trace_nodes[] = {0};
538  if (do_trace)
539  open_trace(&MIIF, sizeof(trace_nodes)/sizeof(trace_nodes[0]), trace_nodes, &params.trace_no_arg, NULL);
540 
541  // dumping global vectors to file?
542  open_globalvec_dump(fhdls, &gvd, &MIIF, sv_dump_names, &io);
543 
544  // initialization time
545  double ini_time = timing(t3, t2);
546  update_timing(timings + INIT_IDX, ini_time);
547  if (tmo.timers[DOPPLE_TM_IDX] != nullptr) {
548  doppel_MIIF(&MIIF, &doppel);
549  doppel_created = true;
550  }
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 if( Lapicque_crv ) {
586  if( lapicque->init(DOPPLE_TM_IDX, STM_TM_IDX) ){
587  bool fire = cMIIF->gdata[Vm]->get(0) >= params.vthreshold_arg;
588  float stimcurr = stim_charge/dt;
589  if( lapicque->check( fire, stimcurr, DOPPLE_TM_IDX, STM_TM_IDX ) )
590  break;
591  stim_charge = stimcurr*dt;
592  doppel_update(&doppel, &MIIF);
593  } else {
594  doppel_update(&MIIF, &doppel);
595  }
596  } else {
597  doppel_update(&MIIF, &doppel);
598  cMIIF = &doppel;
599  }
600  }
601 
602  cMIIF->extUpdateVm = IsVclamp; // external Vm control whenever a manual protocol is active
603 
604  if (IsVclamp)
605  clamp_signal(cMIIF, &cl, &tmo);
606 
607  if (APclamp)
608  AP_clamp(&ap_cl, &tmo, cMIIF->gdata[Vm], tmo.triggered_now(STM_TM_IDX) );
609 
610  for (int i = 0; i < SVclamp; i++) {
611  bool trigger;
612  if (params.SV_I_trigger_flag)
613  trigger = tmo.triggered_now(STM_TM_IDX);
614  else
615  trigger = !tmo.d_time;
616 
617  sv_clamp(sv_cl+i, &tmo, cMIIF, trigger);
618  }
619 
620  // output selected state variables
621  cMIIF->dump_svs(tmo.timers[SVD_TM_IDX]);
622  globalvec_dump(fhdls, &gvd, cMIIF, &tmo, &io, numNode);
623 
624  // save state of all cells to binary file
625  if (tmo.trigger(STA_TM_IDX))
626  cMIIF->dump_state(params.save_file_arg, tmo.time, mesh_t::unset_msh, false, 0);
627 
628  // save state of cell 0 to text file
629  if (tmo.trigger(SSV_TM_IDX))
630  save_sv(cMIIF, R1, params.save_ini_file_arg);
631 
632  // save state within an restitution protocol
633  if (tmo.trigger(RES_SAVE_TM_IDX))
634  restitution_save_sv(cMIIF, R1, &r, &AP);
635 
636  // add stim current
637  if (tmo.trigger(STM_TM_IDX)) {
638  gpu_Vm_update = 0;
639  if (tr_stim) {
640  stim_charge = stim_trace.s[tmo.trigger_elapse(STM_TM_IDX)]*dt;
641  }
642  else if (params.stim_volt_given) {
643  SF_real *d = cMIIF->gdata[Vm]->ptr();
644  // peak_vm = last_vm = d[0];
645  stim_charge = (params.stim_volt_arg-d[0])/params.resistance_arg*dt;
646  cMIIF->gdata[Vm]->release_ptr(d);
647  }
648 
649 #ifndef ALGEBRAIC
650  *(cMIIF->gdata[Vm]) += stim_charge;
651 #endif // ifndef ALGEBRAIC
652 
653  if (stim_assign) {
654  int iIon = 0;
655  for (auto ion : species_list)
656  {
657  cMIIF->transmem_stim_species(-stim_charge*(ratios_list[iIon]/ratio_sum), ion.c_str(), params.surface_to_volume_arg, stim_list, numNode);
658  FPRINTF(WORLD stderr, "[stim_assign] @ t = %.3f ms; charge: %f pA/pF*ms; type: %s\n", tmo.time, stim_charge, ion.c_str());
659  iIon++;
660  }
661  }
662  }
663 
664  if (MIIF.gdata[illum] != NULL) {
665  MIIF.gdata[illum]->set(tmo.trigger(LIGHT_TM_IDX) ? light_irrad : 0.0);
666 
667  if(tmo.triggered_now(LIGHT_TM_IDX) && light_irrad) {
668  FPRINTF(WORLD stderr, "[stim] illum ON @ t = %.3f ms; E_e = %.3f mW/mm^2\n", tmo.time, light_irrad);
669  }
670  if(tmo.trigger_end(LIGHT_TM_IDX) && light_irrad) {
671  FPRINTF(WORLD stderr, "[stim] illum OFF @ t = %.3f ms\n", tmo.time);
672  }
673  }
674 
675  // strain
676  if (params.strain_given && cMIIF->use_stretch())
677  apply_stretch(cMIIF, &s, &tmo);
678 
679  if (analyze_AP) {
680  if (restitute) AP.pmat = r.trigs.pmat[AP.beat];
681  check_events(getCellVal(cMIIF->gdata[Vm], 0), &AP, &tmo);
682  }
683 
684  get_time(t5);
685 
686 
687  if (is_gpu(target)) {
688  if (gpu_Vm_update == 0) {
689  cMIIF->compute_ionic_current(1, 1);
690  } else if (gpu_Vm_update == 1) {
691  cMIIF->compute_ionic_current(fabs(counter - 0.0) < epsilon || fabs(counter - 1.0) < epsilon , fabs((counter+dt) - 1.0) < epsilon);
692  }
693  }
694  else {
695  cMIIF->compute_ionic_current();
696  }
697 
698  double ode_time = timing(t6, t5);
699  update_timing(timings+ODE_IDX, ode_time);
700 
701  if (do_trace && tmo.trigger(TRACE_TM_IDX))
702  dump_trace(cMIIF, t);
703 
704  if (!restitute && !RRC && !Lapicque_crv && tmo.trigger_end(DOPPLE_TM_IDX) ) {
705  log_msg(NULL, 0, 0, "[%f] Switching back to original ion structure\n", tmo.time);
706  cMIIF = &MIIF;
707  }
708 
709  tmo.update_timers();
710 
711  double loop_time = timing(t7, t4);
712  update_timing(timings+LOOP_IDX, loop_time);
713 
714  gpu_Vm_update = 1;
715  counter = counter + dt;
716  if ( fabs(counter - 1.0) < epsilon ) {
717  counter = 0.0;
718  }
719 
720  if( RRC && tmo.trigger(STM_TM_IDX) &&
721  tmo.trigger_elapse(STM_TM_IDX)*tmo.time_step>params.RRC_delay_arg ){
722  auto data = cMIIF->gdata[Vm]->ptr();
723  if( *data <= params.RRC_repol_arg )
724  RRC_repol = true;
725  cMIIF->gdata[Vm]->release_ptr(data);
726  }
727  }//loop end
728 
729  // loop_current time
730  double main_time = timing(t8, t3);
731 
732  // write header so that we know which files belong together
733  const char *ExpID = params.fout_arg;
734  write_dump_header(&gvd, &MIIF.svd, ExpID);
735 
736  // clean up everything
737  if( Lapicque_crv ) {
738  if( params.Lap_file_given ) {
739  std::ofstream ofs(params.Lap_file_arg);
740  lapicque->print(ofs);
741  } else
742  lapicque->print(std::cout);
743  }
744  if (tr_stim) free_trace(&stim_trace);
745  if (analyze_AP) cleanup_AP_analysis(&AP);
746  if (do_trace) close_trace(&MIIF);
747  MIIF.close_svs_dumps();
748  if (doppel_created)
749  free_doppel(&doppel);
750  MIIF.free_MIIF();
751 
752  close_globalvec_dump(fhdls, &gvd, &io);
753 
754  log_msg(NULL, 0, 0, "\n\n\nAll done!\n\n");
755 
756  // print timing data
757  event_timing *t_setup = timings+SETUP_IDX;
758  log_msg(NULL, 0, 0, "setup time %.6f s", (double)t_setup->tot);
759  event_timing *t_init = timings+INIT_IDX;
760  log_msg(NULL, 0, 0, "initialization time %.6f s", (double)t_init->tot);
761  event_timing *t_loop = timings+LOOP_IDX;
762  log_msg(NULL, 0, 0, "main loop time %.6f s", (double)t_loop->tot);
763  event_timing *t_ode = timings+ODE_IDX;
764  log_msg(NULL, 0, 0, "total ode time %.6f s\n", (double)t_ode->tot);
765 
766  log_msg(NULL, 0, 0, "mn/avg/mx loop time %.6f %.6f %.6f ms",
767  (double)t_loop->mn*1e3, (double)t_loop->avg*1e3, (double)t_loop->mx*1e3);
768  log_msg(NULL, 0, 0, "mn/avg/mx ODE time %.6f %.6f %.6f ms",
769  (double)t_ode->mn*1e3, (double)t_ode->avg*1e3, (double)t_ode->mx*1e3);
770 
771  log_msg(NULL, 0, 0, "real time factor %.6f\n\n", duration/(t_ode->tot*1e3) );
772 
773 
774  CHKERRQ(PetscFinalize());
775  return 0;
776 } // 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:73
#define RRC_MAX_N
bool update(float &val, bool success)
update search
Definition: bench_utils.h:32
void print(std::ostream &)
Definition: Lapicque.cc:67
bool init(int, int)
initial first step
Definition: Lapicque.cc:87
bool check(bool fire, float &, int, int)
check if the Lapicque curve is finished and update current
Definition: Lapicque.cc:45
virtual void get(const vector< T > &idx, S *out)=0
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:59
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:63
bool trigger(int ID) const
Definition: timer_utils.h:152
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:64
void initialize_singlestep_timer(double tg, double idur, int ID, const char *iname, const char *poolname=nullptr)
Definition: timer_utils.h:142
int trigger_elapse(int ID) const
Definition: timer_utils.h:173
bool trigger_end(int ID) const
Definition: timer_utils.h:165
std::vector< base_timer * > timers
vector containing individual timers
Definition: timer_utils.h:70
double time
current time
Definition: timer_utils.h:62
bool triggered_now(int ID) const
Definition: timer_utils.h:157
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