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