openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
sim_utils.cc
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // openCARP is an open cardiac electrophysiology simulator.
3 //
4 // Copyright (C) 2020 openCARP project
5 //
6 // This program is licensed under the openCARP Academic Public License (APL)
7 // v1.0: You can use and redistribute it and/or modify it in non-commercial
8 // academic environments under the terms of APL as published by the openCARP
9 // project v1.0, or (at your option) any later version. Commercial use requires
10 // a commercial license (info@opencarp.org).
11 //
12 // This program is distributed without any warranty; see the openCARP APL for
13 // more details.
14 //
15 // You should have received a copy of the openCARP APL along with this program
16 // and can find it online: http://www.opencarp.org/license
17 // ----------------------------------------------------------------------------
18 
27 #include "basics.h"
28 #include "sim_utils.h"
29 #include "fem.h"
30 #include "physics.h"
31 #include "async_io.h"
32 #include "SF_init.h"
33 #ifdef WITH_POWERCAPPING
34 #include <vector>
35 #include "powercapping.h"
36 #endif
37 
38 #include <libgen.h>
39 #include <fstream>
40 #include <iomanip>
41 
42 
43 namespace opencarp {
44 
45 static char input_dir[1024], // directory from which to read input
46  output_dir[1024], // directory to which to write results
47  postproc_dir[1024], // postprocessing directory
48  current_dir[1024]; // current directory
49 
50 void parse_params_cpy(int argc, char** argv)
51 {
52  // copy the command line parameters that are for carp (i.e. before the first "+")
53  int cpy_argc = argc;
54  SF::vector<char*> cpy_argv(cpy_argc, NULL);
55 
56  for(int i=0; i < cpy_argc; i++) {
57  if(strcmp(argv[i], "+") != 0) {
58  cpy_argv[i] = dupstr(argv[i]);
59  }
60  else {
61  cpy_argc = i;
62  break;
63  }
64  }
65 
66  // launch param on the carp command line parameters
67  int param_argc = cpy_argc;
68  int status;
69 
70  do {
71  status = param(PARAMETERS, &param_argc, cpy_argv.data());
72  if ( status==PrMERROR||status==PrMFATAL )
73  fprintf( stderr, "\n*** Error reading parameters\n\n");
74  else if ( status == PrMQUIT )
75  fprintf( stderr, "\n*** Quitting by user's request\n\n");
76  } while (status == PrMERROR);
77 
78  // if --output-setup then loop over PARAMETERS
79  // output value of each PARAMETER
80  if (param_globals::output_setup)
81  param_output(PARAMETERS, 1);
82 
83  // free the parameters
84  for(int i=0; i<param_argc; i++)
85  free(cpy_argv[i]);
86 
87  // exit on error
88  if (status & PrMERROR) exit(status);
89 
90 }
91 
92 
94 {
95  // here all the physics can be registered to the physics registry
96  // then they should be processed automatically
100 #if WITH_EMI_MODEL
101  user_globals::physics_reg[emi_phys] = new EMI();
102 #else
103  log_msg(NULL, 5, ECHO, "The EMI model was not compiled for this binary.\n");
104  exit(EXIT_FAILURE);
105 #endif
106  }
107 
110 
113 }
114 
116 {
117  log_msg(0,0,0, "\n *** Initializing physics ***\n");
118 
119  //load in the external imp modules
120 #ifdef HAVE_DLOPEN
121  for (int ii = 0; ii < param_globals::num_external_imp; ii++) {
122  int loading_succeeded = limpet::load_ionic_module(param_globals::external_imp[ii]);
123  assert(loading_succeeded);
124  }
125 #else
126  if(param_globals::num_external_imp)
127  log_msg(NULL, 4, ECHO,"Loading of external LIMPET modules not enabled.\n"
128  "Recompile with DLOPEN set.\n" );
129 #endif
130 
131  // init physics via Basic_physic interface
132  for(auto it : user_globals::physics_reg) {
133  Basic_physic* p = it.second;
134  log_msg(NULL, 0, 0, "Initializing %s ..", p->name);
135  p->initialize();
136  }
137 }
138 
140 {
141  log_msg(0,0,0, "\n *** Destroying physics ***\n");
142 
143  for(auto it : user_globals::physics_reg) {
144  Basic_physic* p = it.second;
145  log_msg(NULL, 0, 0, "Destroying %s ..", p->name);
146  p->destroy();
147  }
148 }
149 
150 // ignore_extracellular stim must be moved to stimulate.cc to be able
151 // to use all defined set operations instead of defines
152 
166 void ignore_extracellular_stim(Stimulus *st, int ns, int ignore)
167 {
168  // needs to be switch to stim enum types defined in stimulate.h
169  for ( int i=0; i<ns; i++ ) {
170  int turn_off = 0;
171  turn_off += (st[i].stimtype == Extracellular_Ground) && (ignore & NO_EXTRA_GND);
172  turn_off += (IsExtraV(st[i])) && (ignore & NO_EXTRA_V);
173  turn_off += (st[i].stimtype==Extracellular_I) && (ignore & NO_EXTRA_I);
174 
175  if (turn_off) {
176  st[i].stimtype = Ignore_Stim;
177  log_msg( NULL, 1, 0, "Extracellular stimulus %d ignored for monodomain", i );
178  } else if ( st[i].stimtype==Intracellular_I ) {
179  st[i].stimtype = Transmembrane_I;
180  log_msg( NULL, 1, 0, "Intracellular stimulus %d converted to transmembrane", i );
181  }
182  }
183 }
184 
192 int set_ignore_flags( int mode )
193 {
194  if(mode==MONODOMAIN)
195  return STM_IGNORE_MONODOMAIN;
196  if(mode==BIDOMAIN)
197  return STM_IGNORE_BIDOMAIN;
198  if(mode==PSEUDO_BIDM)
199  return STM_IGNORE_PSEUDO_BIDM;
200 
201  return IGNORE_NONE;
202 }
203 
204 
215 {
216  if(param_globals::floating_ground)
217  return;
218 
219  Stimulus* s = param_globals::stimulus;
220 
221  for(int i=0; i < param_globals::num_stim; i++) {
222  if(s[i].stimtype == Extracellular_Ground ||
223  s[i].stimtype == Extracellular_V ||
224  s[i].stimtype == Extracellular_V_OL)
225  return;
226  }
227 
228  // for now we only warn, although we should actually stop the run
229  log_msg( NULL, 4, 0,"Elliptic system is singular!\n"
230  "Either set floating_ground=1 or use an explicit ground:voltage (stimulus[X].stimtype=3)\n"
231  "Do not trust the elliptic solution of this simulation run!\n");
232 }
233 
235 {
236  printf("\n""*** GIT tag: %s\n", GIT_COMMIT_TAG);
237  printf( "*** GIT hash: %s\n", GIT_COMMIT_HASH);
238  printf( "*** GIT repo: %s\n", GIT_PATH);
239  printf( "*** dependency commits: %s\n\n", SUBREPO_COMMITS);
240 }
241 
243 {
244  // convert time steps to milliseconds
245  param_globals::dt /= 1000.;
246 
247  // check parab-solve solution method
248  if(param_globals::mass_lumping == 0 && param_globals::parab_solve==0) {
249  log_msg(NULL, 2, ECHO,
250  "Warning: explicit solve not possible without mass lumping. \n"
251  "Switching to Crank-Nicolson!\n\n");
252 
253  param_globals::parab_solve = 1;
254  }
255 
256  // check if we have to modify stimuli based on used bidomain setting
257  if(!param_globals::extracell_monodomain_stim)
258  ignore_extracellular_stim(param_globals::stimulus, param_globals::num_stim,
259  set_ignore_flags(param_globals::bidomain));
260 
261  // check nullspace if necessary
262  // if((param_globals::bidomain==BIDOMAIN) ||
263  // (param_globals::bidomain==PSEUDO_BIDM))
264  // check_nullspace_ok();
265 
266  if(param_globals::t_sentinel > 0 && param_globals::sentinel_ID < 0 ) {
267  log_msg(0,4,0, "Warning: t_sentinel is set but no sentinel_ID has been specified; check_quiescence() behavior may not be as expected");
268  }
269 
270  if(param_globals::num_external_imp > 0 ) {
271  for(int ext_imp_i = 0; ext_imp_i < param_globals::num_external_imp; ext_imp_i++) {
272  if(param_globals::external_imp[ext_imp_i][0] != '/') {
273  log_msg(0,5,0, "external_imp[%d] error: absolute paths must be used for .so file loading (\'%s\')",
274  ext_imp_i, param_globals::external_imp[ext_imp_i]);
275  EXIT(1);
276  }
277  }
278  }
279 
280  if(param_globals::experiment == EXP_LAPLACE && param_globals::bidomain != 1) {
281  log_msg(0,4,0, "Warning: Laplace experiment mode requires bidomain = 1. Setting bidomain = 1.");
282  param_globals::bidomain = 1;
283  }
284 
285  if(param_globals::num_phys_regions == 0) {
286  log_msg(0,4,0, "Warning: No physics region defined! Please set phys_region parameters to correctly define physics.");
287 
288  if(param_globals::experiment != EXP_LAPLACE) {
289  log_msg(0,4,0, "Intra-elec and Extra-elec domains will be derived from fibers.\n");
290  param_globals::num_phys_regions = param_globals::bidomain ? 2 : 1;
291  param_globals::phys_region = (p_region*) malloc(param_globals::num_phys_regions * sizeof(p_region));
292  param_globals::phys_region[0].ptype = PHYSREG_INTRA_ELEC;
293  param_globals::phys_region[0].name = strdup("Autogenerated intracellular Electrics");
294  param_globals::phys_region[0].num_IDs = 0;
295 
296  if(param_globals::bidomain) {
297  param_globals::phys_region[1].ptype = PHYSREG_EXTRA_ELEC;
298  param_globals::phys_region[1].name = strdup("Autogenerated extracellular Electrics");
299  param_globals::phys_region[1].num_IDs = 0;
300  }
301  } else {
302  log_msg(0,4,0, "Laplace domain will be derived from fibers.\n");
303  param_globals::num_phys_regions = 1;
304  param_globals::phys_region = (p_region*) malloc(param_globals::num_phys_regions * sizeof(p_region));
305  param_globals::phys_region[0].ptype = PHYSREG_LAPLACE;
306  param_globals::phys_region[0].name = strdup("Autogenerated Laplace");
307  param_globals::phys_region[0].num_IDs = 0;
308  }
309  }
310 
311  if(param_globals::experiment == EXP_LAPLACE && !phys_defined(PHYSREG_LAPLACE)) {
312  log_msg(0,4,0, "Warning: Laplace experiment mode requires a laplace physics regions defined.");
313 
314  int idx = -1;
315  if((idx = get_phys_index(PHYSREG_EXTRA_ELEC)) > -1) {
316  log_msg(0,4,0, "Converting the defined extracellular-electrics-region to laplace-region.");
317  param_globals::phys_region[idx].ptype = PHYSREG_LAPLACE;
318  } else if ((idx = get_phys_index(PHYSREG_INTRA_ELEC)) > -1) {
319  log_msg(0,4,0, "Converting the defined intracellular-electrics-region to laplace-region.");
320  param_globals::phys_region[idx].ptype = PHYSREG_LAPLACE;
321  } else {
322  param_globals::num_phys_regions += 1;
323  param_globals::phys_region = (p_region*) realloc(param_globals::phys_region, param_globals::num_phys_regions * sizeof(p_region));
324 
325  param_globals::phys_region[param_globals::num_phys_regions - 1].ptype = PHYSREG_LAPLACE;
326  param_globals::phys_region[param_globals::num_phys_regions - 1].name = strdup("Autogenerated Laplace");
327  param_globals::phys_region[param_globals::num_phys_regions - 1].num_IDs = 0;
328  }
329  }
330 
331 #ifndef WITH_PARMETIS
332  if(param_globals::pstrat == 1) {
333  log_msg(0,3,0, "openCARP was built without Parmetis support. Swithing to KDtree.");
334  param_globals::pstrat = 2;
335  }
336 #endif
337 
338  // check if we have the legacy stimuli or the new stimuli defined by the user
339  bool legacy_stim_set = false, new_stim_set = false;
340 
341  for(int i=0; i<param_globals::num_stim; i++) {
342  Stimulus & legacy_stim = param_globals::stimulus[i];
343  Stim & new_stim = param_globals::stim[i];
344 
345  if(legacy_stim.stimtype || legacy_stim.strength)
346  legacy_stim_set = true;
347 
348  if(new_stim.crct.type || new_stim.pulse.strength)
349  new_stim_set = true;
350  }
351 
352  if(legacy_stim_set || new_stim_set) {
353  if(legacy_stim_set && new_stim_set) {
354  log_msg(0,4,0, "Warning: Legacy stimuli and default stimuli are defined. Only default stimuli will be used!");
355  }
356  else if (legacy_stim_set) {
357  log_msg(0,1,0, "Warning: Legacy stimuli defined. Please consider switching to stimulus definition \"stim[]\"!");
359  }
360  }
361  else {
362  log_msg(0,4,0, "Warning: No potential or current stimuli found!");
363  }
364 }
365 
366 void set_io_dirs(char *sim_ID, char *pp_ID, IO_t init)
367 {
368  int flg = 0, err = 0, rank = get_rank();
369 
370  char *ptr = getcwd(current_dir, 1024);
371  if (ptr == NULL) err++;
372  ptr = getcwd(input_dir, 1024);
373  if (ptr == NULL) err++;
374  //if (param_globals::experiment == 4 && post_processing_opts == MECHANIC_POSTPROCESS)
375  // { sim_ID = param_globals::ppID; param_globals::ppID = "POSTPROC_DIR"; }
376 
377  // output directory
378  if (rank == 0) {
379  if (strcmp(sim_ID, "OUTPUT_DIR")) {
380  if (mkdir(sim_ID, 0775)) { // rwxrwxr-x
381  if (errno == EEXIST ) {
382  log_msg(NULL, 2, 0, "Output directory exists: %s\n", sim_ID);
383  } else {
384  log_msg(NULL, 5, 0, "Unable to make output directory\n");
385  flg = 1;
386  }
387  }
388  } else if (mkdir(sim_ID, 0775) && errno != EEXIST) {
389  log_msg(NULL, 5, 0, "Unable to make output directory\n");
390  flg = 1;
391  }
392  }
393 
394  // terminate?
395  if(get_global(flg, MPI_SUM)) { EXIT(-1); }
396 
397  err += chdir(sim_ID);
398  ptr = getcwd(output_dir, 1024);
399  if (ptr == NULL) err++;
400 
401  // terminate?
402  if(get_global(err, MPI_SUM)) { EXIT(-1); }
403 
404  err += chdir(output_dir);
405 
406  // postprocessing directory
407  if (rank == 0 && (param_globals::experiment==EXP_POSTPROCESS)) {
408 
409  if (strcmp(param_globals::ppID, "POSTPROC_DIR")) {
410  if (mkdir(param_globals::ppID, 0775)) { // rwxrwxr-x
411  if (errno == EEXIST ) {
412  log_msg(NULL, 2, ECHO, "Postprocessing directory exists: %s\n\n", param_globals::ppID);
413  } else {
414  log_msg(NULL, 5, ECHO, "Unable to make postprocessing directory\n\n");
415  flg = 1;
416  }
417  }
418  } else if (mkdir(param_globals::ppID, 0775) && errno != EEXIST) {
419  log_msg(NULL, 5, ECHO, "Unable to make postprocessing directory\n\n");
420  flg = 1;
421  }
422 
423  }
424 
425  if(get_global(flg, MPI_SUM)) { EXIT(-1); }
426 
427  err += chdir(param_globals::ppID);
428  ptr = getcwd(postproc_dir, 1024);
429  if (ptr == NULL) err++;
430  err = chdir(output_dir);
431  if(get_global(err, MPI_SUM)) { EXIT(-1); }
432 
433  err = set_dir(init);
434  if(get_global(err, MPI_SUM)) { EXIT(-1); }
435 }
436 
437 bool setup_IO(int argc, char **argv)
438 {
439  bool io_node = false;
440  int psize = get_size(), prank = get_rank();
441 
442  if (param_globals::num_io_nodes > 0) {
443  // Can't do async IO with only one core
444  if (get_size() == 1) {
445  log_msg(NULL, 5, 0, "You cannot run with async IO on only one core.\n");
446  EXIT(EXIT_FAILURE);
447  }
448  // Can't do async IO with more IO cores than compute cores
449  if (2 * param_globals::num_io_nodes >= psize) {
450  log_msg(NULL, 5, 0, "The number of IO cores be less " "than the number of compute cores.");
451  EXIT(EXIT_FAILURE);
452  }
453 #if 0
454  if (param_globals::num_PS_nodes && param_globals::num_io_nodes > param_globals::num_PS_nodes) {
455  LOG_MSG(NULL, 5, 0,
456  "The number of IO cores (%d) should not "
457  "exceed the number of PS compute cores (%d).\n",
458  param_globals::num_io_nodes, param_globals::num_PS_nodes);
459  EXIT(-1);
460  }
461 #endif
462  // root IO node is global node 0
463  io_node = prank < param_globals::num_io_nodes;
464 
465  MPI_Comm comm;
466  MPI_Comm_split(PETSC_COMM_WORLD, io_node, get_rank(), &comm);
467  MPI_Comm_set_name(comm, io_node ? "IO" : "compute");
468 
469  PETSC_COMM_WORLD = comm; // either the compute world or IO world
470 
471  prank = get_rank();
472 
473  MPI_Intercomm_create(comm, 0, MPI_COMM_WORLD, io_node ? param_globals::num_io_nodes : 0,
475 
477  log_msg(NULL, 4, 0, "Global node %d, Comm rank %d != Intercomm rank %d\n",
478  get_rank(MPI_COMM_WORLD), get_rank(PETSC_COMM_WORLD),
480  } else
481  MPI_Comm_set_name(PETSC_COMM_WORLD, "compute");
482 
483  set_io_dirs(param_globals::simID, param_globals::ppID, OUTPUT);
484 
485  if((io_node || !param_globals::num_io_nodes) && !prank)
486  output_parameter_file("parameters.par", argc, argv);
487 
488  return io_node;
489 }
491 {
492  getcwd(current_dir, 1024);
493 }
494 
495 int set_dir(IO_t dest)
496 {
497  int err;
498 
499  if (dest==OUTPUT) err = chdir(output_dir);
500  else if (dest==POSTPROC) err = chdir(postproc_dir);
501  else if (dest==CURDIR) err = chdir(current_dir);
502  else err = chdir(input_dir);
503 
504  return err;
505 }
506 
508 {
509  // if we restart from a checkpoint, the timer_manager will be notified at a later stage
510  double start_time = 0.0;
511  user_globals::tm_manager = new timer_manager(param_globals::dt, start_time, param_globals::tend);
512 
513  double end_time = param_globals::tend;
515 
516  if(param_globals::experiment == EXP_LAPLACE) {
517  tm.initialize_singlestep_timer(tm.time, 0, iotm_console, "IO (console)", nullptr);
518  tm.initialize_singlestep_timer(tm.time, 0, iotm_state_var, "IO (state vars)", nullptr);
519  tm.initialize_singlestep_timer(tm.time, 0, iotm_spacedt, "IO (spacedt)", nullptr);
520  }
521  else {
522  tm.initialize_eq_timer(tm.time, end_time, 0, param_globals::timedt, 0, iotm_console, "IO (console)");
523  tm.initialize_eq_timer(tm.time, end_time, 0, param_globals::spacedt, 0, iotm_state_var, "IO (state vars)");
524  tm.initialize_eq_timer(tm.time, end_time, 0, param_globals::spacedt, 0, iotm_spacedt, "IO (spacedt)");
525  }
526 
527  if(param_globals::num_tsav) {
528  std::vector<double> trig(param_globals::num_tsav);
529  for(size_t i=0; i<trig.size(); i++) trig[i] = param_globals::tsav[i];
530 
531  tm.initialize_neq_timer(trig, 0, iotm_chkpt_list, "instance checkpointing");
532  }
533 
534  if(param_globals::chkpt_intv)
535  tm.initialize_eq_timer(param_globals::chkpt_start, param_globals::chkpt_stop, 0,
536  param_globals::chkpt_intv, 0, iotm_chkpt_intv, "interval checkpointing");
537 
538  if(param_globals::num_trace)
539  tm.initialize_eq_timer(tm.time, end_time, 0, param_globals::tracedt, 0, iotm_trace, "IO (node trace)");
540 }
541 
542 #ifdef WITH_POWERCAPPING
543 void basic_powercapping_setup()
544 {
545  user_globals::pc_manager = new powercapping_manager(param_globals::powcap_backend, param_globals::powcap_backend_policy, param_globals::powcap_metrics_backend, param_globals::powcap_policy);
546 }
547 #endif
548 
549 void get_protocol_column_widths(std::vector<int> & col_width, std::vector<int> & used_timer_ids)
550 {
551  char buff[256];
552  const short padding = 4;
553  Electrics* elec = (Electrics*) get_physics(elec_phys, false);
554 
555  do {
556  snprintf(buff, sizeof buff, "%.3lf", user_globals::tm_manager->time);
557  if(col_width[0] < int(strlen(buff)+padding))
558  col_width[0] = strlen(buff)+padding;
559 
560  snprintf(buff, sizeof buff, "%.3ld", user_globals::tm_manager->d_time);
561  if(col_width[1] < int(strlen(buff)+padding))
562  col_width[1] = strlen(buff)+padding;
563 
564  int col = 2;
565  for (size_t tid = 0; tid < used_timer_ids.size(); tid++)
566  {
567  int timer_id = used_timer_ids[tid];
569 
570  if(t->d_trigger_dur && elec) {
571  // figure out value of signal linked to this timer
572  double val = 0.;
573 
574  // determine timer linked to which physics, for now we deal with electrics only
575  val = elec->timer_val(timer_id);
576 
577  snprintf(buff, sizeof buff, "%.3lf", val);
578  if(col_width[col] < int(strlen(buff)+padding))
579  col_width[col] = strlen(buff)+padding;
580  }
581  col++;
582  }
583 
584  // advance time
586  } while (!user_globals::tm_manager->elapsed());
587 
589 }
592 int plot_protocols(const char *fname)
593 {
594  int err = {0};
595  std::ofstream fh;
596  const char* smpl_endl = "\n";
597 
598  if(!get_rank()) {
599  fh.open(fname);
600 
601  // If we couldn't open the output file stream for writing
602  if (!fh) {
603  // Print an error and exit
604  log_msg(0,5,0,"Protocol file %s could not be opened for writing!\n", fname);
605  err = -1;
606  }
607  }
608 
609  // broadcast and return if err
610  if(get_global(err, MPI_SUM))
611  return err;
612 
613  // only rank 0 writes
614  if(!get_rank()) {
615 
616  // collect timer information, label, short label, unit
617  std::vector<std::string> col_labels = {"time", "tick"};
618  std::vector<std::string> col_short_labels = {"A", "B"};
619  std::vector<std::string> col_unit_labels = {"ms", "--" };
620  std::vector<int> col_width = {4, 4};
621 
622  char c_label = {'C'};
623  std::string label = {""};
624  std::string unit = {""};
625 
626  // here we store the IDs of the timers that we care about. currently this are the IO and TS timers
627  // and the electricts timers
628  std::vector<int> used_timer_ids;
629  std::vector<int> used_stim_ids;
630 
631  Electrics* elec = (Electrics*) get_physics(elec_phys, false);
632  if(elec) {
633  int sidx = 0;
634  for(const stimulus & s : elec->stimuli) {
635  if(s.ptcl.timer_id > -1) {
637  if(t) {
638  used_timer_ids.push_back(s.ptcl.timer_id);
639  used_stim_ids.push_back(sidx);
640  }
641  }
642 
643  sidx++;
644  }
645  }
646 
647  // determine longest timer label
648  int mx_llen = 0;
649  for (size_t tid = 0; tid < used_timer_ids.size(); tid++)
650  {
651  int timer_id = used_timer_ids[tid];
653 
654  int llen = strlen(t->name);
655  mx_llen = llen > mx_llen ? llen : mx_llen;
656  }
657 
658  for (size_t tid = 0; tid < used_timer_ids.size(); tid++)
659  {
660  int timer_id = used_timer_ids[tid];
662 
663  col_labels.push_back(t->name);
664  label = c_label;
665  col_short_labels.push_back(label);
666 
667  if(elec) {
668  // search physics for signals linked to timer
669  unit = elec->timer_unit(timer_id);
670  if(unit.empty()) unit = "--";
671  col_unit_labels.push_back(unit);
672  col_width.push_back(4);
673  }
674  c_label++;
675  }
676 
677  get_protocol_column_widths(col_width, used_timer_ids);
678 
679  // print header + legend first
680  fh << "# Protocol header\n#\n" << "# Legend:\n";
681  for(size_t i = 0; i<col_short_labels.size(); i++)
682  {
683  fh << "#" << std::setw(2) << col_short_labels[i] << " = " << std::setw(mx_llen) << col_labels[i];
684  fh << " [" << std::setw(10) << col_unit_labels[i] << "]";
685 
686  if(i >= 2 && used_stim_ids[i-2] > -1) {
687  stimulus & s = elec->stimuli[used_stim_ids[i-2]];
688 
689  if (is_potential(s.phys.type)) {
690  if(s.phys.type == GND_ex)
691  fh << " ground stim" << smpl_endl;
692  else
693  fh << " applied: " << std::to_string(s.pulse.strength) << smpl_endl;
694  } else {
695  fh << smpl_endl;
696  }
697  } else {
698  fh << smpl_endl;
699  }
700  }
701 
702  // plot column short labels
703  fh << "#";
704  for(size_t i = 0; i<col_short_labels.size(); i++)
705  fh << std::setw(col_width[i] - 3) << col_short_labels[i].c_str() << std::setw(3) << " ";
706 
707  // plot column units
708  fh << smpl_endl << "#";
709  for(size_t i = 0; i<col_unit_labels.size(); i++)
710  fh << "[" << std::setw(col_width[i]-2) << col_unit_labels[i].c_str() << "]";
711 
712  // step through simulated time period
713  fh << smpl_endl << std::fixed;
714  do {
715  // time and discrete time
716  fh << std::setw(col_width[0]) << std::setprecision(3) << user_globals::tm_manager->time;
717  fh << std::setw(col_width[1]) << user_globals::tm_manager->d_time;
718 
719  // iterate over all timers
720  int col = 2;
721  for (size_t tid = 0; tid < used_timer_ids.size(); tid++)
722  {
723  int timer_id = used_timer_ids[tid];
725 
726  // type of timer: plain trigger or trigger linked to signal
727  if(!t->d_trigger_dur) {
728  int On = t->triggered ? 1 : 0;
729  fh << std::setw(col_width[col]) << On;
730  } else if(elec) {
731  // figure out value of signal linked to this timer
732  double val = 0.;
733 
734  // determine timer linked to which physics, for now we deal with electrics only
735  val = elec->timer_val(timer_id);
736 
737  fh << std::setw(col_width[col]) << std::setprecision(3) << val;
738  }
739  col++;
740  }
741 
742  fh << smpl_endl;
743 
744  // advance time
746  } while (!user_globals::tm_manager->elapsed());
747 
748  fh.close();
749 
750  // reset timer to start before actual simulation
752  }
753 
754  return err;
755 }
756 
758 {
759  const char* h1_prog = "PROG\t----- \t----\t-------\t-------|";
760  const char* h2_prog = "time\t%%comp\ttime\t ctime \t ETA |";
761  const char* h1_wc = "\tELAPS |";
762  const char* h2_wc = "\twc |";
763 
764  p.start = get_time();
765  p.last = p.start;
766 
767  log_msg(NULL, 0, 0, "%s", h1_prog );
768  log_msg(NULL, 0, NONL, "%s", h2_prog );
769  log_msg(NULL, 0, 0, "" );
770 }
771 
772 
773 void time_to_string(float time, char* str, short str_size)
774 {
775  int req_hours = ((int)(time)) / 3600;
776  int req_min = (((int)(time)) % 3600) / 60;
777  int req_sec = (((int)(time)) % 3600) % 60;
778 
779  snprintf(str, str_size, "%d:%02d:%02d", req_hours, req_min, req_sec);
780 }
781 
783 {
784 
785  float progress = 100.*(tm.time - tm.start) / (tm.end - tm.start);
786  float elapsed_time = timing(p.curr, p.start);
787  float req_time = (elapsed_time / progress) * (100.0f - progress);
788 
789  if(progress == 0.0f)
790  req_time = 0.0f;
791 
792  char elapsed_time_str[256];
793  char req_time_str[256];
794  time_to_string(elapsed_time, elapsed_time_str, 255);
795  time_to_string(req_time, req_time_str, 255);
796 
797  log_msg( NULL, 0, NONL, "%.2f\t%.1f\t%.1f\t%s\t%s",
798  tm.time,
799  progress,
800  (float)(p.curr - p.last),
801  elapsed_time_str,
802  req_time_str);
803 
804  p.last = p.curr;
805 
806  // we add an empty string for newline and flush
807  log_msg( NULL, 0, ECHO | FLUSH, "");
808 }
809 
810 void simulate()
811 {
812  // here we want to include all time dependent physics, to check if we have any of those
813  bool have_timedependent_phys = (phys_defined(PHYSREG_INTRA_ELEC) || phys_defined(PHYSREG_EIKONAL) || phys_defined(PHYSREG_EMI));
814 
815  if(!have_timedependent_phys) {
816  log_msg(0,0,0, "\n no time-dependent physics region registered, skipping simulate loop..\n");
817  return;
818  }
819 
820  log_msg(0,0,0, "\n *** Launching simulation ***\n");
821 
822  set_dir(OUTPUT);
823 
824  if(param_globals::dump_protocol)
825  plot_protocols("protocol.trc");
826 
827  prog_stats prog;
829  init_console_output(tm, prog);
830 
831 #ifdef WITH_POWERCAPPING
832  basic_powercapping_setup();
833  powercapping_manager &pc = *user_globals::pc_manager;
834 #endif
835 
836  // main loop
837  do {
838  // console output
839  if(tm.trigger(iotm_console)) {
840  // print console
841  update_console_output(tm, prog);
842  }
843 
844 #ifdef WITH_POWERCAPPING
845  std::vector<int> flops_per_rank;
846 
847  // TODO: fill vector with expected flops per MPI rank for upcoming iteration
848 #endif
849 
850 #ifdef WITH_POWERCAPPING
851  pc.iteration_begin(flops_per_rank);
852 #endif
853 
854  // in order to be closer to carpentry we first do output and then compute the solution
855  // for the next time slice ..
856  if (tm.trigger(iotm_spacedt)) {
857  for(const auto & it : user_globals::physics_reg) {
858  it.second->output_step();
859  }
860  }
861 #ifdef WITH_POWERCAPPING
862  pc.sample("output step");
863 #endif
864 
865  // compute step
866  for(const auto & it : user_globals::physics_reg) {
867  Basic_physic* p = it.second;
868  if (tm.trigger(p->timer_idx))
869  p->compute_step();
870 #ifdef WITH_POWERCAPPING
871  pc.sample(p->name);
872 #endif
873  }
874 
875 #ifdef WITH_POWERCAPPING
876  pc.iteration_end(flops_per_rank);
877 #endif
878 
879  // advance time
880  tm.update_timers();
881  } while (!tm.elapsed());
882 
883  log_msg(0,0,0, "\n\nTimings of individual physics:");
884  log_msg(0,0,0, "------------------------------\n");
885 
886  for(const auto & it : user_globals::physics_reg) {
887  Basic_physic* p = it.second;
888  p->output_timings();
889  }
890 
891 }
892 
894 {
895  if(param_globals::post_processing_opts & RECOVER_PHIE) {
896  log_msg(NULL,0,ECHO,"\nPOSTPROCESSOR: Recovering Phie ...");
897  log_msg(NULL,0,ECHO, "----------------------------------\n");
898 
899  // do postprocessing
900  int err = postproc_recover_phie();
901 
902  if(!err) {
903  log_msg(NULL,0,ECHO,"\n-----------------------------------------");
904  log_msg(NULL,0,ECHO, "POSTPROCESSOR: Successfully recoverd Phie.\n");
905  }
906  }
907 }
908 
909 Basic_physic* get_physics(physic_t p, bool error_if_missing)
910 {
911  auto it = user_globals::physics_reg.find(p);
912 
913  if(it != user_globals::physics_reg.end()) {
914  return it->second;
915  } else {
916  if(error_if_missing) {
917  log_msg(0,5,0, "%s error: required physic is not active! Usually this is due to an inconsistent experiment configuration. Aborting!", __func__);
918  EXIT(EXIT_FAILURE);
919  }
920 
921  return NULL;
922  }
923 }
924 
926 {
927  sf_vec* ret = NULL;
928 
930  ret = user_globals::datavec_reg[d];
931 
932  return ret;
933 }
934 
936 {
937  if(user_globals::datavec_reg.count(d) == 0) {
938  user_globals::datavec_reg[d] = dat;
939  }
940  else {
941  log_msg(0,5,0, "%s warning: trying to register already registered data vector.", __func__);
942  }
943 }
944 
946 {
947  std::map<mesh_t, sf_mesh> & mesh_registry = user_globals::mesh_reg;
948 
949  // This is the initial grid we read the hard-disk data into
950  mesh_registry[reference_msh] = sf_mesh();
951  // we specify the MPI communicator for the reference mesh,
952  // all derived meshes will get this comminicator automatically
953  mesh_registry[reference_msh].comm = PETSC_COMM_WORLD;
954 
955  auto register_new_mesh = [&] (mesh_t mt, int pidx) {
956  if(!mesh_registry.count(mt)) {
957  mesh_registry[mt] = sf_mesh();
958  mesh_registry[mt].name = param_globals::phys_region[pidx].name;
959  }
960  return &mesh_registry[mt];
961  };
962 
963  // based on cli parameters we determine which grids need to be defined
964  for(int i=0; i<param_globals::num_phys_regions; i++)
965  {
966  sf_mesh* curmesh = NULL;
967  // register mesh type
968  switch(param_globals::phys_region[i].ptype) {
969  case PHYSREG_EIKONAL:
970  case PHYSREG_INTRA_ELEC:
971  curmesh = register_new_mesh(intra_elec_msh, i);
972  break;
973 
974  case PHYSREG_LAPLACE:
975  case PHYSREG_EXTRA_ELEC:
976  curmesh = register_new_mesh(extra_elec_msh, i);
977  break;
978 #if WITH_EMI_MODEL
979  case PHYSREG_EMI:
980  curmesh = register_new_mesh(emi_msh, i);
981  break;
982 #endif
983 
984  default:
985  log_msg(0,5,0, "Unsupported mesh type %d! Aborting!", param_globals::phys_region[i].ptype);
986  EXIT(EXIT_FAILURE);
987  }
988 
989  if(curmesh) {
990  // set mesh unique tags
991  for(int j=0; j<param_globals::phys_region[i].num_IDs; j++)
992  curmesh->extr_tag.insert(param_globals::phys_region[i].ID[j]);
993  }
994  }
995 }
996 
1007 void retag_elements(sf_mesh & mesh, TagRegion *tagRegs, int ntr)
1008 {
1009  if(ntr == 0) return;
1010  // checkTagRegDefs(ntr, tagRegs);
1011 
1013 
1014  for (int i=0; i<ntr; i++) {
1015  tagreg_t type = tagreg_t(tagRegs[i].type);
1016  SF::vector<mesh_int_t> elem_indices;
1017 
1018  if (type == tagreg_list)
1019  read_indices(elem_indices, tagRegs[i].elemfile, ref_eidx, mesh.comm);
1020  else {
1021  geom_shape shape;
1022  shape.type = geom_shape::shape_t(tagRegs[i].type);
1023  shape.p0.x = tagRegs[i].p0[0];
1024  shape.p0.y = tagRegs[i].p0[1];
1025  shape.p0.z = tagRegs[i].p0[2];
1026  shape.p1.x = tagRegs[i].p1[0];
1027  shape.p1.y = tagRegs[i].p1[1];
1028  shape.p1.z = tagRegs[i].p1[2];
1029  shape.radius = tagRegs[i].radius;
1030 
1031  bool nodal = false;
1032  indices_from_geom_shape(elem_indices, mesh, shape, nodal);
1033  }
1034 
1035  if(get_global((long int)elem_indices.size(), MPI_SUM, mesh.comm) == 0)
1036  log_msg(0,3,0,"Tag region %d is empty", i);
1037 
1038  for(size_t j=0; j<elem_indices.size(); j++)
1039  mesh.tag[elem_indices[j]] = tagRegs[i].tag;
1040  }
1041 
1042  // output the vector?
1043  if(strlen(param_globals::retagfile))
1044  {
1045  update_cwd();
1046  set_dir(OUTPUT);
1047 
1048  int dpn = 1;
1049  SF::write_data_ascii(mesh.comm, ref_eidx, mesh.tag, param_globals::retagfile, dpn);
1050 
1051  // Set dir back to what is was prior to retagfile output
1052  set_dir(CURDIR);
1053  }
1054 }
1055 
1056 size_t renormalise_fibres(SF::vector<mesh_real_t> &fib, size_t l_numelem)
1057 {
1058  size_t renormalised_count = 0;
1059 
1060  // using pragma omp without global OMP control can lead to massive compute stalls,
1061  // as all cores may be already occupied by MPI, thus they become oversubscribed. Once
1062  // there is a global OMP control in place, we can activate this parallel for again. -Aurel, 20.01.2022
1063  // #pragma omp parallel for schedule(static) reduction(+ : renormalised_count)
1064  for (size_t i = 0; i < l_numelem; i++)
1065  {
1066  const mesh_real_t f0 = fib[3*i+0], f1 = fib[3*i+1], f2 = fib[3*i+2];
1067  mesh_real_t fibre_len = sqrt(f0*f0 + f1*f1 + f2*f2);
1068 
1069  if (fibre_len && fabs(fibre_len - 1) > 1e-3) {
1070  fib[3 * i + 0] /= fibre_len;
1071  fib[3 * i + 1] /= fibre_len;
1072  fib[3 * i + 2] /= fibre_len;
1073  renormalised_count++;
1074  }
1075  }
1076 
1077  return renormalised_count;
1078 }
1079 
1080 void setup_meshes(bool require_fibers=true)
1081 {
1082  log_msg(0,0,0, "\n *** Processing meshes ***\n");
1083 
1084  const std::string basename = param_globals::meshname;
1085  const int verb = param_globals::output_level;
1086  std::map<mesh_t, sf_mesh> & mesh_registry = user_globals::mesh_reg;
1087  assert(mesh_registry.count(reference_msh) == 1); // There must be a reference mesh
1088 
1089  set_dir(INPUT);
1090 
1091  // we always read into the reference mesh
1092  sf_mesh & ref_mesh = mesh_registry[reference_msh];
1093  MPI_Comm comm = ref_mesh.comm;
1094 
1095  int size, rank;
1096  double t1, t2, s1, s2;
1097  MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
1098 
1100  SF::vector<mesh_int_t> ptsidx;
1101 
1102  // we add pointers to the meshes that need vertex cooridnates to this list
1103  std::list< sf_mesh* > ptsread_list;
1104 
1105  // read element mesh data
1106  t1 = MPI_Wtime(); s1 = t1;
1107  if(verb) log_msg(NULL, 0, 0,"Reading reference mesh: %s.*", basename.c_str());
1108 
1109  SF::read_elements(ref_mesh, basename, require_fibers);
1110  SF::read_points(basename, comm, pts, ptsidx);
1111 
1112  t2 = MPI_Wtime();
1113  if(verb) log_msg(NULL, 0, 0, "Done in %f sec.", float(t2 - t1));
1114 
1115  bool check_fibre_normality = true;
1116  if (check_fibre_normality and ref_mesh.fib.size()>0) {
1117  t1 = MPI_Wtime();
1118 
1119  // make sure that all fibre vectors have unit length
1120  size_t l_num_fixed_fib = renormalise_fibres(ref_mesh.fib, ref_mesh.l_numelem);
1121 
1122  size_t l_num_fixed_she = 0;
1123  if (ref_mesh.she.size() > 0)
1124  l_num_fixed_she = renormalise_fibres(ref_mesh.she, ref_mesh.l_numelem);
1125 
1126  unsigned long fixed[2] = {(unsigned long) l_num_fixed_fib, (unsigned long) l_num_fixed_she};
1127  MPI_Allreduce(MPI_IN_PLACE, fixed, 2, MPI_UNSIGNED_LONG, MPI_SUM, comm);
1128 
1129  if (fixed[0] + fixed[1] > 0)
1130  log_msg(NULL, 0, 0, "Renormalised %ld longitudinal and %ld sheet-transverse fibre vectors.", fixed[0], fixed[1]);
1131 
1132  t2 = MPI_Wtime();
1133  if(verb) log_msg(NULL, 0, 0, "Done in %f sec.", float(t2 - t1));
1134  }
1135 
1136  if(param_globals::numtagreg > 0) {
1137  log_msg(0, 0, 0, "Re-tagging reference mesh");
1138 
1139  // the retagging requires vertex coordinates, as such we need to read them into
1140  // the reference mesh
1141  ptsread_list.push_back(&ref_mesh);
1142  SF::insert_points(pts, ptsidx, ptsread_list);
1143 
1144  retag_elements(ref_mesh, param_globals::tagreg, param_globals::numtagreg);
1145 
1146  // we clear the list of meshet to receive vertices
1147  ptsread_list.clear();
1148  }
1149 
1150  if(verb) log_msg(NULL, 0, 0, "Processing submeshes");
1151 
1152  for(auto it = mesh_registry.begin(); it != mesh_registry.end(); ++it) {
1153  mesh_t grid_type = it->first;
1154  sf_mesh & submesh = it->second;
1155 
1156  if(grid_type != reference_msh) {
1157  if(verb > 1) log_msg(NULL, 0, 0, "\nSubmesh name: %s", submesh.name.c_str());
1158  t1 = MPI_Wtime();
1159 
1160  if(submesh.extr_tag.size() && grid_type != emi_msh)
1161  extract_tagbased(ref_mesh, submesh);
1162  else {
1163  // all submeshes should be defined on sets of tags, for backwards compatibility
1164  // we do a fiber based intra_elec_msh extraction if no tags are provided. Also, we
1165  // could do special treatments of any other physics type here. It would defeat
1166  // the purpose of the tag-based design, though. -Aurel
1167  switch(grid_type) {
1168  case emi_msh:
1169  case intra_elec_msh: extract_myocardium(ref_mesh, submesh, require_fibers); break;
1170  default: extract_tagbased(ref_mesh, submesh); break;
1171  }
1172  }
1173  t2 = MPI_Wtime();
1174  if(verb > 1) log_msg(NULL, 0, 0, "Extraction done in %f sec.", float(t2 - t1));
1175 
1176  ptsread_list.push_back(&submesh);
1177  }
1178  }
1179 
1180  // KDtree partitioning requires the coordinates to be present in the mesh data
1181  if(param_globals::pstrat == 2)
1182  SF::insert_points(pts, ptsidx, ptsread_list);
1183 
1184  for(auto it = mesh_registry.begin(); it != mesh_registry.end(); ++it)
1185  {
1186  mesh_t grid_type = it->first;
1187  sf_mesh & submesh = it->second;
1188  if(grid_type != reference_msh && grid_type!=emi_msh) {
1189  if(verb > 2) log_msg(NULL, 0, 0, "\nSubmesh name: %s", submesh.name.c_str());
1191 
1192  // generate partitioning vector
1193  t1 = MPI_Wtime();
1194  switch(param_globals::pstrat) {
1195  case 0:
1196  if(verb > 2) log_msg(NULL, 0, 0, "Using linear partitioning ..");
1197  break;
1198 
1199 #ifdef WITH_PARMETIS
1200  case 1:
1201  {
1202  if(verb > 2) log_msg(NULL, 0, 0, "Using Parmetis partitioner ..");
1203  SF::parmetis_partitioner<mesh_int_t, mesh_real_t> partitioner(param_globals::pstrat_imbalance, 2);
1204  partitioner(submesh, part);
1205  break;
1206  }
1207 #endif
1208  default:
1209  case 2: {
1210  if(verb > 2) log_msg(NULL, 0, 0, "Using KDtree partitioner ..");
1212  partitioner(submesh, part);
1213  break;
1214  }
1215  }
1216  t2 = MPI_Wtime();
1217  if(verb > 2) log_msg(NULL, 0, 0, "Partitioning done in %f sec.", float(t2 - t1));
1218 
1219  if(param_globals::pstrat > 0) {
1220  if(param_globals::gridout_p) {
1221  std::string out_name = get_basename(param_globals::meshname);
1222  if(grid_type == intra_elec_msh) out_name += "_i.part.dat";
1223  else if(grid_type == extra_elec_msh) out_name += "_e.part.dat";
1224 
1225  set_dir(OUTPUT);
1226  log_msg(0,0,0, "Writing \"%s\" partitioning to: %s", submesh.name.c_str(), out_name.c_str());
1227  write_data_ascii(submesh.comm, submesh.get_numbering(SF::NBR_ELEM_REF), part, out_name);
1228  }
1229 
1230  t1 = MPI_Wtime();
1231  SF::redistribute_elements(submesh, part);
1232  t2 = MPI_Wtime();
1233  if(verb > 2) log_msg(NULL, 0, 0, "Redistributing done in %f sec.", float(t2 - t1));
1234  }
1235 
1236  t1 = MPI_Wtime();
1238  sm_numbering(submesh);
1239  t2 = MPI_Wtime();
1240  if(verb > 2) log_msg(NULL, 0, 0, "Canonical numbering done in %f sec.", float(t2 - t1));
1241 
1242  t1 = MPI_Wtime();
1243  submesh.generate_par_layout();
1244  SF::petsc_numbering<mesh_int_t, mesh_real_t> p_numbering(submesh.pl);
1245  p_numbering(submesh);
1246  t2 = MPI_Wtime();
1247  if(verb > 2) log_msg(NULL, 0, 0, "PETSc numbering done in %f sec.", float(t2 - t1));
1248  if(verb > 2) print_DD_info(submesh);
1249  }
1250  }
1251 
1252  SF::insert_points(pts, ptsidx, ptsread_list);
1253  ref_mesh.clear_data();
1254 
1255  s2 = MPI_Wtime();
1256  if(verb) log_msg(NULL, 0, 0, "All done in %f sec.", float(s2 - s1));
1257 }
1258 
1259 
1261 {
1262  bool write_intra_elec = mesh_is_registered(intra_elec_msh) && param_globals::gridout_i;
1263  bool write_extra_elec = mesh_is_registered(extra_elec_msh) && param_globals::gridout_e;
1264 
1265  set_dir(OUTPUT);
1266  std::string output_base = get_basename(param_globals::meshname);
1267 
1268  if(write_intra_elec) {
1269  sf_mesh & mesh = get_mesh(intra_elec_msh);
1270 
1271  if(param_globals::gridout_i & 1) {
1272  sf_mesh surfmesh;
1273  if(param_globals::output_level > 1)
1274  log_msg(0,0,0, "Computing \"%s\" surface ..", mesh.name.c_str());
1275  compute_surface_mesh(mesh, SF::NBR_SUBMESH, surfmesh);
1276 
1277  std::string output_file = output_base + "_i.surf";
1278  log_msg(0,0,0, "Writing \"%s\" surface: %s", mesh.name.c_str(), output_file.c_str());
1279  write_surface(surfmesh, output_file);
1280  }
1281  if(param_globals::gridout_i & 2) {
1282  bool write_binary = false;
1283 
1284  std::string output_file = output_base + "_i";
1285  log_msg(0,0,0, "Writing \"%s\" mesh: %s", mesh.name.c_str(), output_file.c_str());
1286  write_mesh_parallel(mesh, write_binary, output_file.c_str());
1287  }
1288  }
1289 
1290  if(write_extra_elec) {
1291  sf_mesh & mesh = get_mesh(extra_elec_msh);
1292 
1293  if(param_globals::gridout_e & 1) {
1294  sf_mesh surfmesh;
1295  if(param_globals::output_level > 1)
1296  log_msg(0,0,0, "Computing \"%s\" surface ..", mesh.name.c_str());
1297 
1298  compute_surface_mesh(mesh, SF::NBR_SUBMESH, surfmesh);
1299  std::string output_file = output_base + "_e.surf";
1300  log_msg(0,0,0, "Writing \"%s\" surface: %s", mesh.name.c_str(), output_file.c_str());
1301  write_surface(surfmesh, output_file);
1302  }
1303  if(param_globals::gridout_e & 2) {
1304  bool write_binary = false;
1305  std::string output_file = output_base + "_e";
1306  log_msg(0,0,0, "Writing \"%s\" mesh: %s", mesh.name.c_str(), output_file.c_str());
1307  write_mesh_parallel(mesh, write_binary, output_file.c_str());
1308  }
1309  }
1310 }
1311 
1312 [[noreturn]] void cleanup_and_exit()
1313 {
1314  destroy_physics();
1316 
1317  param_clean();
1318  PetscFinalize();
1319 
1320  // close petsc error FD
1323 
1324  exit(EXIT_SUCCESS);
1325 }
1326 
1327 char* get_file_dir(const char* file)
1328 {
1329  char* filecopy = dupstr(file);
1330  char* dir = dupstr(dirname(filecopy));
1331 
1332  free(filecopy);
1333  return dir;
1334 }
1335 
1337 {
1338  int rank = get_rank();
1339  set_dir(OUTPUT);
1340 
1341  if(rank == 0) {
1342  // we open a error log file handle and set it as petsc stderr
1343  user_globals::petsc_error_fd = fopen("petsc_err_log.txt", "w");
1344  PETSC_STDERR = user_globals::petsc_error_fd;
1345  }
1346  else {
1347  PetscErrorPrintf = PetscErrorPrintfNone;
1348  }
1349 }
1350 
1352 {
1353  const sf_mesh & mesh = get_mesh(id);
1355  short mindim = 3;
1356 
1357  for(size_t eidx = 0; eidx < mesh.l_numelem; eidx++) {
1358  view.set_elem(eidx);
1359  short cdim = view.dimension();
1360  if(mindim < cdim) mindim = cdim;
1361  }
1362 
1363  mindim = get_global(mindim, MPI_MIN, mesh.comm);
1364 
1365  return mindim;
1366 }
1367 
1369  const mesh_t inp_meshid,
1370  const int dpn,
1371  const char* name,
1372  const char* units,
1373  const SF::vector<mesh_int_t>* idx,
1374  bool elem_data)
1375 {
1376  sync_io_item IO;
1377 
1378  IO.data = inp_data;
1379  IO.elem_flag = elem_data;
1380  IO.restr_idx = idx;
1381 
1382  IGBheader regigb;
1384  const int num_io = tm.timers[iotm_spacedt]->numIOs;
1385  int err = 0;
1386 
1387  int gsize = inp_data->gsize();
1388 
1389  // if we are restricting, we have to compute the restricted global size
1390  if(idx != NULL)
1391  gsize = get_global(int(idx->size()), MPI_SUM);
1392 
1393  regigb.x(gsize / dpn);
1394  regigb.dim_x(regigb.x()-1);
1395  regigb.inc_x(1);
1396 
1397  regigb.y(1); regigb.z(1);
1398  regigb.t(num_io);
1399  regigb.dim_t(tm.end-tm.start);
1400 
1401  switch(dpn) {
1402  default:
1403  case 1: regigb.type(IGB_FLOAT); break;
1404  case 3: regigb.type(IGB_VEC3_f); break;
1405  case 4: regigb.type(IGB_VEC4_f); break;
1406  case 9: regigb.type(IGB_VEC9_f); break;
1407  }
1408 
1409  regigb.unites_x("um"); regigb.unites_y("um"); regigb.unites_z("um");
1410  regigb.unites_t("ms");
1411  regigb.unites(units);
1412 
1413  regigb.inc_t(param_globals::spacedt);
1414 
1415  if(get_rank() == 0) {
1416  FILE_SPEC file = f_open(name, "w");
1417  if(file != NULL) {
1418  regigb.fileptr(file->fd);
1419  regigb.write();
1420  delete file;
1421  }
1422  else err++;
1423  }
1424 
1425  err = get_global(err, MPI_SUM);
1426  if(err) {
1427  log_msg(0,5,0, "%s error: Could not set up data output! Aborting!", __func__);
1428  EXIT(1);
1429  }
1430 
1431  IO.igb = regigb;
1432 
1433  SF::mixed_tuple<mesh_t, int> mesh_spec = {inp_meshid, dpn};
1434  IO.spec = mesh_spec;
1435 
1436  if(elem_data) {
1437  if(buffmap_elem.find(mesh_spec) == buffmap_elem.end()) {
1438  sf_vec *inp_copy; SF::init_vector(&inp_copy, inp_data);
1439  buffmap_elem[mesh_spec] = inp_copy;
1440  }
1441  } else {
1442  if(buffmap.find(mesh_spec) == buffmap.end()) {
1443  sf_vec *inp_copy; SF::init_vector(&inp_copy, inp_data);
1444  buffmap[mesh_spec] = inp_copy;
1445  }
1446  }
1447 
1448  this->sync_IOs.push_back(IO);
1449 }
1450 
1451 void igb_output_manager::register_output_async(sf_vec* inp_data,
1452  const mesh_t inp_meshid,
1453  const int dpn,
1454  const char* name,
1455  const char* units,
1456  const SF::vector<mesh_int_t>* idx,
1457  bool elem_data)
1458 {
1459  sf_mesh & mesh = get_mesh(inp_meshid);
1460  SF::vector<mesh_int_t> ioidx;
1461  int rank = get_rank();
1462 
1463  async_io_item IO;
1464  IO.data = inp_data;
1465  IO.restr_idx = idx;
1466 
1467  if(elem_data) {
1469  ioidx.resize(mesh.l_numelem);
1470  for(size_t i=0; i<mesh.l_numelem; i++)
1471  ioidx[i] = nbr[i];
1472  } else {
1473  const SF::vector<mesh_int_t> & alg_nod = mesh.pl.algebraic_nodes();
1475 
1476  if(idx == NULL) {
1477  ioidx.resize(alg_nod.size());
1478 
1479  for(size_t i=0; i<alg_nod.size(); i++)
1480  ioidx[i] = nbr[alg_nod[i]];
1481  } else {
1482  ioidx.resize(idx->size());
1483  IO.restr_petsc_idx.resize(idx->size());
1484 
1485  for(size_t i=0; i<idx->size(); i++) {
1486  mesh_int_t loc_nodal = (*idx)[i];
1487  ioidx[i] = nbr[loc_nodal];
1488  IO.restr_petsc_idx[i] = local_nodal_to_local_petsc(mesh, rank, loc_nodal);
1489  }
1490  }
1491  }
1492 
1493  int id = async::COMPUTE_register_output(ioidx, dpn, name, units);
1494  IO.IO_id = id;
1495 
1496  this->async_IOs.push_back(IO);
1497 }
1498 
1500  const mesh_t inp_meshid,
1501  const int dpn,
1502  const char* name,
1503  const char* units,
1504  const SF::vector<mesh_int_t>* idx,
1505  bool elem_data)
1506 {
1507  if(param_globals::num_io_nodes == 0)
1508  register_output_sync(inp_data, inp_meshid, dpn, name, units, idx, elem_data);
1509  else
1510  register_output_async(inp_data, inp_meshid, dpn, name, units, idx, elem_data);
1511 }
1512 
1513 sf_vec* igb_output_manager::fill_output_buffer(const sync_io_item & it)
1514 {
1515  const SF::mixed_tuple<mesh_t, int> & cspec = it.spec;
1516  sf_vec* data_vec = it.data;
1517 
1518  bool have_perm = it.elem_flag ? have_permutation(cspec.v1, ELEM_PETSC_TO_CANONICAL, cspec.v2):
1519  have_permutation(cspec.v1, PETSC_TO_CANONICAL, cspec.v2);
1520 
1521  if(have_perm) {
1522  sf_vec* perm_vec = it.elem_flag ? this->buffmap_elem[cspec] : this->buffmap[cspec];
1524  get_permutation(cspec.v1, PETSC_TO_CANONICAL, cspec.v2);
1525  sc->forward(*data_vec, *perm_vec);
1526  return perm_vec;
1527  } else {
1528  return data_vec;
1529  }
1530 }
1531 
1533 {
1534  SF::vector<float> restr_buff;
1535  int rank = get_rank();
1536  // loop over registered datasets and root-write one by one
1537  //
1538  for (sync_io_item & it : sync_IOs) {
1539  // write to associated file descriptor
1540  FILE* fd = static_cast<FILE*>(it.igb.fileptr());
1541 
1542  // fill the output buffer
1543  sf_vec* buff = fill_output_buffer(it);
1544 
1545  if(it.restr_idx == NULL) {
1546  buff->write_binary<float>(fd);
1547  } else {
1548  const SF::vector<mesh_int_t> & idx = *it.restr_idx;
1549  SF_real* p = buff->ptr();
1550 
1551  restr_buff.resize(idx.size()); restr_buff.resize(0);
1552 
1553  for(mesh_int_t ii : idx)
1554  restr_buff.push_back(p[ii]);
1555 
1556  root_write(fd, restr_buff, PETSC_COMM_WORLD);
1557  buff->release_ptr(p);
1558  }
1559  }
1560 
1561  // do all A-synchronous output:
1562  // loop over IDs received from the IO nodes and trigger async output
1563  //
1564  for (async_io_item & it : async_IOs) {
1565  SF_real* p = it.data->ptr();
1566  int ls = it.data->lsize();
1567  int id = it.IO_id;
1568 
1569  if(it.restr_idx == NULL)
1570  async::COMPUTE_do_output(p, ls, id);
1571  else {
1572  async::COMPUTE_do_output(p, it.restr_petsc_idx, id);
1573  }
1574 
1575  it.data->release_ptr(p);
1576  }
1577 }
1578 
1580 {
1581  if(get_rank() == 0) {
1582  // loop over registered datasets and close fd
1583  for(sync_io_item & it : sync_IOs) {
1584  FILE* fd = static_cast<FILE*>(it.igb.fileptr());
1585  fclose(fd);
1586  }
1587  }
1588 
1589  for(auto it = buffmap.begin(); it != buffmap.end(); ++it)
1590  delete it->second;
1591 
1592  for(auto it = buffmap_elem.begin(); it != buffmap_elem.end(); ++it)
1593  delete it->second;
1594 
1595  // we resize the arrays and clear the maps so that we are safe when calling
1596  // close_files_and_cleanup multiple times.
1597  sync_IOs.resize(0);
1598  async_IOs.resize(0);
1599  buffmap.clear();
1600  buffmap_elem.clear();
1601 }
1602 
1604 {
1605  for(sync_io_item & it : sync_IOs) {
1606  if(it.data == vec)
1607  return &it.igb;
1608  }
1609 
1610  return NULL;
1611 }
1612 
1613 void output_parameter_file(const char *fname, int argc, char **argv)
1614 {
1615  const int max_line_len = 128;
1616  const char* file_sep = "#=======================================================";
1617 
1618  // make sure only root executes this function
1619  if(get_rank() != 0)
1620  return;
1621 
1622  FILE_SPEC out = f_open(fname, "w");
1623  fprintf(out->fd, "# CARP GIT commit hash: %s\n", GIT_COMMIT_HASH);
1624  fprintf(out->fd, "# dependency hashes: %s\n", SUBREPO_COMMITS);
1625  fprintf(out->fd, "\n");
1626 
1627  // output the command line
1628  char line[8196] = "# ";
1629 
1630  for (int j=0; j<argc; j++) {
1631  strcat(line, argv[j]);
1632  if(strlen(line) > max_line_len) {
1633  fprintf(out->fd, "%s\n", line);
1634  strcpy(line, "# ");
1635  } else
1636  strcat(line, " ");
1637  }
1638 
1639  fprintf(out->fd, "%s\n\n", line);
1640  set_dir(INPUT);
1641 
1642  // convert command line to a par file
1643  for (int i=1; i<argc; i++) {
1644  std::string argument(argv[i]);
1645  if (argument == "+F" || argument.find("_options_file")!= std::string::npos) {
1646 
1647  std::string init = "";
1648  if (argument.find("_options_file")!= std::string::npos) {
1649  fprintf(out->fd, "%s = %s\n", argument.substr(1).c_str(), argv[i+1]);
1650  init = "#";
1651  }
1652  fprintf(out->fd, "%s>>\n", file_sep);
1653  // import par files
1654  i++;
1655  fprintf(out->fd, "## %s ##\n", argv[i]);
1656  FILE *in = fopen(argv[i], "r");
1657  while (fgets(line, 8196, in))
1658  fprintf(out->fd, "%s%s", init.c_str(), line);
1659  fclose(in);
1660  fprintf(out->fd, "\n##END of %s\n", argv[i]);
1661  fprintf(out->fd, "%s<<\n\n", file_sep);
1662  }
1663  else if(argv[i][0] == '-')
1664  {
1665  bool prelast = (i==argc-1);
1666  bool paramFollows = !prelast && ((argv[i+1][0] != '-') ||
1667  ((argv[i+1][1] >= '0') && (argv[i+1][1] <= '9')));
1668 
1669  // strip leading hyphens from command line opts
1670  // assume options do not start with numbers
1671  if(paramFollows) {
1672  // nonflag option
1673  char *optcpy = strdup(argv[i+1]);
1674  char *front = optcpy;
1675  // strip {} if present for arrays of values
1676  while(*front==' ' && *front) front++;
1677  if(*front=='{') {
1678  while(*++front == ' ');
1679  char *back = optcpy+strlen(optcpy)-1;
1680  while(*back==' ' && back>front) back--;
1681  if(*back == '}')
1682  *back = '\0';
1683  }
1684  if (strstr(front, "=") != nullptr) // if "=" is find then we need ""
1685  fprintf(out->fd, "%-40s= \"%s\"\n", argv[i]+1, front);
1686  else
1687  fprintf(out->fd, "%-40s= %s\n", argv[i]+1, front);
1688  free(optcpy);
1689  i++;
1690  }
1691  else // a flag was specified
1692  fprintf(out->fd, "%-40s= 1\n", argv[i]);
1693  }
1694  }
1695  f_close(out);
1696 }
1697 
1698 void savequit()
1699 {
1700  if(!get_physics(elec_phys)) return;
1701 
1702  set_dir(OUTPUT);
1703 
1704  double time = user_globals::tm_manager->time;
1705  char save_fn[512];
1706 
1707  snprintf(save_fn, sizeof save_fn, "exit.save.%.3f.roe", time);
1708  log_msg(NULL, 0, 0, "savequit called at time %g\n", time);
1709 
1711  elec->ion.miif->dump_state(save_fn, time, intra_elec_msh, false, GIT_COMMIT_COUNT);
1712 
1713  cleanup_and_exit();
1714 }
1715 
1716 } // namespace opencarp
1717 
int mesh_int_t
Definition: SF_container.h:46
float mesh_real_t
Definition: SF_container.h:47
double SF_real
Use the general double as real type.
Definition: SF_globals.h:38
Async IO functions.
Basic utility structs and functions, mostly IO related.
#define FLUSH
Definition: basics.h:327
#define ECHO
Definition: basics.h:324
#define NONL
Definition: basics.h:328
virtual S * ptr()=0
virtual void release_ptr(S *&p)=0
virtual T gsize() const =0
size_t write_binary(FILE *fd)
Write a vector to HD in binary. File descriptor is already set up.
virtual T lsize() const =0
Comfort class. Provides getter functions to access the mesh member variables more comfortably.
Definition: SF_fem_utils.h:704
void set_elem(size_t eidx)
Set the view to a new element.
Definition: SF_fem_utils.h:731
short dimension() const
Definition: SF_fem_utils.h:922
void clear_data()
Clear the mesh data from memory.
Definition: SF_container.h:552
overlapping_layout< T > pl
nodal parallel layout
Definition: SF_container.h:429
vector< S > she
sheet direction
Definition: SF_container.h:421
vector< S > fib
fiber direction
Definition: SF_container.h:420
size_t l_numelem
local number of elements
Definition: SF_container.h:399
std::string name
the mesh name
Definition: SF_container.h:407
void generate_par_layout()
Set up the parallel layout.
Definition: SF_container.h:538
MPI_Comm comm
the parallel mesh is defined on a MPI world
Definition: SF_container.h:404
vector< T > & get_numbering(SF_nbr nbr_type)
Get the vector defining a certain numbering.
Definition: SF_container.h:464
vector< T > tag
element tag
Definition: SF_container.h:417
hashmap::unordered_set< int > extr_tag
the element tags based on which the mesh has been extracted
Definition: SF_container.h:424
Functor class generating a numbering optimized for PETSc.
Definition: SF_numbering.h:231
void free_scatterings()
Free the registered scatterings.
Container for a PETSc VecScatter.
void forward(abstract_vector< T, S > &in, abstract_vector< T, S > &out, bool add=false)
Forward scattering.
Functor class applying a submesh renumbering.
Definition: SF_numbering.h:68
A vector storing arbitrary data.
Definition: SF_vector.h:43
size_t size() const
The current size of the vector.
Definition: SF_vector.h:104
void resize(size_t n)
Resize a vector.
Definition: SF_vector.h:209
T * data()
Pointer to the vector's start.
Definition: SF_vector.h:91
T & push_back(T val)
Definition: SF_vector.h:283
size_t size() const
Definition: hashmap.hpp:1152
void insert(InputIterator first, InputIterator last)
Definition: hashmap.hpp:1048
void dump_state(char *, float, opencarp::mesh_t gid, bool, unsigned int)
The abstract physics interface we can use to trigger all physics.
Definition: physics_types.h:59
virtual void destroy()=0
int timer_idx
the timer index received from the timer manager
Definition: physics_types.h:66
virtual void output_timings()
Definition: physics_types.h:76
virtual void compute_step()=0
virtual void initialize()=0
const char * name
The name of the physic, each physic should have one.
Definition: physics_types.h:62
SF::vector< stimulus > stimuli
the electrical stimuli
Definition: electrics.h:263
std::string timer_unit(const int timer_id)
figure out units of a signal linked to a given timer
Definition: electrics.cc:793
double timer_val(const int timer_id)
figure out current value of a signal linked to a given timer
Definition: electrics.cc:777
void dim_t(float a)
Definition: IGBheader.h:333
void unites_x(const char *a)
Definition: IGBheader.h:345
void unites_z(const char *a)
Definition: IGBheader.h:351
void unites(const char *a)
Definition: IGBheader.h:357
void unites_y(const char *a)
Definition: IGBheader.h:348
void unites_t(const char *a)
Definition: IGBheader.h:354
void fileptr(gzFile f)
Definition: IGBheader.cc:336
void dim_x(float a)
Definition: IGBheader.h:324
void inc_t(float a)
Definition: IGBheader.h:321
void inc_x(float a)
Definition: IGBheader.h:312
limpet::MULTI_IF * miif
Definition: ionics.h:66
class to store shape definitions
Definition: basics.h:397
std::map< SF::mixed_tuple< mesh_t, int >, sf_vec * > buffmap_elem
Definition: sim_utils.h:327
IGBheader * get_igb_header(const sf_vec *vec)
Get the pointer to the igb header for a vector that was registered for output.
Definition: sim_utils.cc:1603
void write_data()
write registered data to disk
Definition: sim_utils.cc:1532
SF::vector< async_io_item > async_IOs
Definition: sim_utils.h:324
void register_output_sync(sf_vec *inp_data, const mesh_t inp_meshid, const int dpn, const char *name, const char *units, const SF::vector< mesh_int_t > *idx=NULL, bool elem_data=false)
Definition: sim_utils.cc:1368
std::map< SF::mixed_tuple< mesh_t, int >, sf_vec * > buffmap
map data spec -> PETSc vector buffer
Definition: sim_utils.h:326
void close_files_and_cleanup()
close file descriptors
Definition: sim_utils.cc:1579
void register_output(sf_vec *inp_data, const mesh_t inp_meshid, const int dpn, const char *name, const char *units, const SF::vector< mesh_int_t > *idx=NULL, bool elem_data=false)
Register a data vector for output.
Definition: sim_utils.cc:1499
SF::vector< sync_io_item > sync_IOs
Definition: sim_utils.h:323
stim_t type
type of stimulus
Definition: stimulate.h:137
int timer_id
timer for stimulus
Definition: stimulate.h:122
double strength
strength of stimulus
Definition: stimulate.h:93
stim_protocol ptcl
applied stimulation protocol used
Definition: stimulate.h:168
stim_pulse pulse
stimulus wave form
Definition: stimulate.h:167
stim_physics phys
physics of stimulus
Definition: stimulate.h:169
centralize time managment and output triggering
Definition: timer_utils.h:73
void initialize_neq_timer(const std::vector< double > &itrig, double idur, int ID, const char *iname, const char *poolname=nullptr)
Definition: timer_utils.cc:63
double end
final time
Definition: timer_utils.h:81
long d_time
current time instance index
Definition: timer_utils.h:77
bool trigger(int ID) const
Definition: timer_utils.h:166
void initialize_eq_timer(double istart, double iend, int ntrig, double iintv, double idur, int ID, const char *iname, const char *poolname=nullptr)
Definition: timer_utils.cc:48
void reset_timers()
Reset time in timer_manager and then reset registered timers.
Definition: timer_utils.h:115
double start
initial time (nonzero when restarting)
Definition: timer_utils.h:79
void initialize_singlestep_timer(double tg, double idur, int ID, const char *iname, const char *poolname=nullptr)
Definition: timer_utils.h:156
std::vector< base_timer * > timers
vector containing individual timers
Definition: timer_utils.h:84
double time
current time
Definition: timer_utils.h:76
Base class for tracking progress.
Definition: progress.hpp:39
Top-level header of FEM module.
void extract_tagbased(const meshdata< T, S > &mesh, meshdata< T, S > &submesh)
Extract a submesh based on element tags.
void write_data_ascii(const MPI_Comm comm, const vector< T > &idx, const vector< S > &data, std::string file, short dpn=1)
void compute_surface_mesh(const meshdata< T, S > &mesh, const SF_nbr numbering, const hashmap::unordered_set< T > &tags, meshdata< T, S > &surfmesh)
Compute the surface of a given mesh.
void print_DD_info(const meshdata< T, S > &mesh)
Print some basic information on the domain decomposition of a mesh.
void read_points(const std::string basename, const MPI_Comm comm, vector< S > &pts, vector< T > &ptsidx)
Read the points and insert them into a list of meshes.
Definition: SF_mesh_io.h:824
void write_surface(const meshdata< T, S > &surfmesh, std::string surffile)
Definition: SF_mesh_io.h:712
void count(const vector< T > &data, vector< S > &cnt)
Count number of occurrences of indices.
Definition: SF_vector.h:332
void insert_points(const vector< S > &pts, const vector< T > &ptsidx, std::list< meshdata< T, S > * > &meshlist)
Insert the points from the read-in buffers into a list of distributed meshes.
Definition: SF_mesh_io.h:894
void redistribute_elements(meshdata< T, S > &mesh, meshdata< T, S > &sendbuff, vector< T > &part)
Redistribute the element data of a parallel mesh among the ranks based on a partitioning.
T local_nodal_to_local_petsc(const meshdata< T, S > &mesh, int rank, T local_nodal)
void init_vector(SF::abstract_vector< T, S > **vec)
Definition: SF_init.h:99
void extract_myocardium(const meshdata< T, S > &mesh, meshdata< T, S > &submesh, bool require_fibers=true)
Extract the myocardium submesh.
void write_mesh_parallel(const meshdata< T, S > &mesh, bool binary, std::string basename)
Write a parallel mesh to harddisk without gathering it on one rank.
size_t root_write(FILE *fd, const vector< V > &vec, MPI_Comm comm)
Write vector data binary to disk.
void read_elements(meshdata< T, S > &mesh, std::string basename, bool require_fibers=true)
Read the element data (elements and fibers) of a CARP mesh.
Definition: SF_mesh_io.h:518
@ NBR_PETSC
PETSc numbering of nodes.
Definition: SF_container.h:203
@ NBR_ELEM_REF
The element numbering of the reference mesh (the one stored on HD).
Definition: SF_container.h:204
@ NBR_SUBMESH
Submesh nodal numbering: The globally ascending sorted reference indices are reindexed.
Definition: SF_container.h:202
@ NBR_ELEM_SUBMESH
Submesh element numbering: The globally ascending sorted reference indices are reindexed.
Definition: SF_container.h:205
int load_ionic_module(const char *)
void COMPUTE_do_output(SF_real *dat, const int lsize, const int IO_id)
Definition: async_io.cc:396
int COMPUTE_register_output(const SF::vector< mesh_int_t > &idx, const int dpn, const char *name, const char *units)
Definition: async_io.cc:104
std::map< int, std::string > units
Definition: stimulate.cc:41
MPI_Comm IO_Intercomm
Communicator between IO and compute worlds.
Definition: main.cc:66
FILE * petsc_error_fd
file descriptor for petsc error output
Definition: main.cc:62
timer_manager * tm_manager
a manager for the various physics timers
Definition: main.cc:58
std::map< datavec_t, sf_vec * > datavec_reg
important solution vectors from different physics
Definition: main.cc:60
bool using_legacy_stimuli
flag storing whether legacy stimuli are used
Definition: main.cc:64
SF::scatter_registry scatter_reg
Registry for the different scatter objects.
Definition: main.cc:50
std::map< mesh_t, sf_mesh > mesh_reg
Registry for the different meshes used in a multi-physics simulation.
Definition: main.cc:52
std::map< physic_t, Basic_physic * > physics_reg
the physics
Definition: main.cc:56
void time_to_string(float time, char *str, short str_size)
Definition: sim_utils.cc:773
physic_t
Identifier for the different physics we want to set up.
Definition: physics_types.h:51
@ iotm_chkpt_list
Definition: timer_utils.h:44
@ iotm_console
Definition: timer_utils.h:44
@ iotm_spacedt
Definition: timer_utils.h:44
@ iotm_trace
Definition: timer_utils.h:44
@ iotm_chkpt_intv
Definition: timer_utils.h:44
@ iotm_state_var
Definition: timer_utils.h:44
sf_vec * get_data(datavec_t d)
Retrieve a petsc data vector from the data registry.
Definition: sim_utils.cc:925
void output_parameter_file(const char *fname, int argc, char **argv)
Definition: sim_utils.cc:1613
void initialize_physics()
Initialize all physics in the registry.
Definition: sim_utils.cc:115
bool setup_IO(int argc, char **argv)
Definition: sim_utils.cc:437
void retag_elements(sf_mesh &mesh, TagRegion *tagRegs, int ntr)
Definition: sim_utils.cc:1007
void parse_params_cpy(int argc, char **argv)
Initialize input parameters on a copy of the real command line parameters.
Definition: sim_utils.cc:50
sf_mesh & get_mesh(const mesh_t gt)
Get a mesh by specifying the gridID.
Definition: sf_interface.cc:33
int set_ignore_flags(int mode)
Definition: sim_utils.cc:192
SF::scattering * get_permutation(const int mesh_id, const int perm_id, const int dpn)
Get the PETSC to canonical permutation scattering for a given mesh and number of dpn.
SF::meshdata< mesh_int_t, mesh_real_t > sf_mesh
Definition: sf_interface.h:48
datavec_t
Enum used to adress the different data vectors stored in the data registry.
Definition: physics_types.h:99
int set_dir(IO_t dest)
Definition: sim_utils.cc:495
void cleanup_and_exit()
Definition: sim_utils.cc:1312
void register_physics()
Register physics to the physics registry.
Definition: sim_utils.cc:93
void post_process()
do postprocessing
Definition: sim_utils.cc:893
void check_and_convert_params()
Here we want to put all parameter checks, conversions and modifications that have been littered throu...
Definition: sim_utils.cc:242
int get_rank(MPI_Comm comm=PETSC_COMM_WORLD)
Definition: basics.h:292
short get_mesh_dim(mesh_t id)
get (lowest) dimension of the mesh used in the experiment
Definition: sim_utils.cc:1351
T get_global(T in, MPI_Op OP, MPI_Comm comm=PETSC_COMM_WORLD)
Do a global reduction on a variable.
Definition: basics.h:233
@ GND_ex
Definition: stimulate.h:78
bool is_potential(stim_t type)
uses current for stimulation
Definition: stimulate.cc:66
bool phys_defined(int physreg)
function to check if certain physics are defined
void update_console_output(const timer_manager &tm, prog_stats &p)
Definition: sim_utils.cc:782
void show_build_info()
show the build info, exit if -buildinfo was provided. This code runs before MPI_Init().
Definition: sim_utils.cc:234
FILE_SPEC f_open(const char *fname, const char *mode)
Open a FILE_SPEC.
Definition: basics.cc:138
void savequit()
save state and quit simulator
Definition: sim_utils.cc:1698
bool have_permutation(const int mesh_id, const int perm_id, const int dpn)
void set_io_dirs(char *sim_ID, char *pp_ID, IO_t init)
Definition: sim_utils.cc:366
void register_data(sf_vec *dat, datavec_t d)
Register a data vector in the global registry.
Definition: sim_utils.cc:935
void basic_timer_setup()
Here we set up the timers that we always want to have, independent of physics.
Definition: sim_utils.cc:507
char * get_file_dir(const char *file)
Definition: sim_utils.cc:1327
IO_t
The different output (directory) types.
Definition: sim_utils.h:55
@ POSTPROC
Definition: sim_utils.h:55
@ CURDIR
Definition: sim_utils.h:55
@ OUTPUT
Definition: sim_utils.h:55
void get_protocol_column_widths(std::vector< int > &col_width, std::vector< int > &used_timer_ids)
Definition: sim_utils.cc:549
int get_phys_index(int physreg)
get index in param_globals::phys_region array for a certain phys region
void check_nullspace_ok()
Definition: sim_utils.cc:214
int postproc_recover_phie()
Definition: electrics.cc:2028
char * dupstr(const char *old_str)
Definition: basics.cc:44
int plot_protocols(const char *fname)
plot simulation protocols (I/O timers, stimuli, boundary conditions, etc)
Definition: sim_utils.cc:592
void indices_from_geom_shape(SF::vector< mesh_int_t > &idx, const sf_mesh &mesh, const geom_shape shape, const bool nodal)
Populate vertex data with the vertices inside a defined box shape.
Definition: fem_utils.cc:184
void log_msg(FILE_SPEC out, int level, unsigned char flag, const char *fmt,...)
Definition: basics.cc:72
mesh_t
The enum identifying the different meshes we might want to load.
Definition: sf_interface.h:59
@ reference_msh
Definition: sf_interface.h:69
@ extra_elec_msh
Definition: sf_interface.h:61
@ intra_elec_msh
Definition: sf_interface.h:60
void setup_meshes(bool require_fibers=true)
Read in the reference mesh and use its data to populate all meshes registered in the mesh registry.
Definition: sim_utils.cc:1080
void get_time(double &tm)
Definition: basics.h:452
bool mesh_is_registered(const mesh_t gt)
check wheter a SF mesh is set
Definition: sf_interface.cc:63
void output_meshes()
Definition: sim_utils.cc:1260
int get_size(MPI_Comm comm=PETSC_COMM_WORLD)
Definition: basics.h:306
Basic_physic * get_physics(physic_t p, bool error_if_missing)
Convinience function to get a physics.
Definition: sim_utils.cc:909
size_t renormalise_fibres(SF::vector< mesh_real_t > &fib, size_t l_numelem)
Definition: sim_utils.cc:1056
void destroy_physics()
Destroy all physics in the registry.
Definition: sim_utils.cc:139
void ignore_extracellular_stim(Stimulus *st, int ns, int ignore)
Definition: sim_utils.cc:166
void init_console_output(const timer_manager &tm, prog_stats &p)
Definition: sim_utils.cc:757
tagreg_t
tag regions types. must be in line with carp.prm
Definition: sim_utils.h:58
@ tagreg_list
Definition: sim_utils.h:58
V timing(V &t2, const V &t1)
Definition: basics.h:464
void read_indices(SF::vector< T > &idx, const std::string filename, const hashmap::unordered_map< mesh_int_t, mesh_int_t > &dd_map, MPI_Comm comm)
Read indices from a file.
Definition: fem_utils.h:120
std::string get_basename(const std::string &path)
Definition: basics.cc:61
void update_cwd()
save the current working directory to curdir so that we can switch back to it if needed.
Definition: sim_utils.cc:490
void f_close(FILE_SPEC &f)
Close a FILE_SPEC.
Definition: basics.cc:165
void setup_petsc_err_log()
set up error logs for PETSc, so that it doesnt print errors to stderr.
Definition: sim_utils.cc:1336
void simulate()
Main simulate loop.
Definition: sim_utils.cc:810
void parse_mesh_types()
Parse the phys_type CLI parameters and set up (empty) SF::meshdata meshes.
Definition: sim_utils.cc:945
#define IGB_VEC9_f
Definition: IGBheader.h:76
#define IGB_VEC3_f
Definition: IGBheader.h:71
#define IGB_FLOAT
Definition: IGBheader.h:60
#define IGB_VEC4_f
Definition: IGBheader.h:73
Top-level header of physics module.
#define PHYSREG_INTRA_ELEC
Definition: sf_interface.h:84
#define PHYSREG_LAPLACE
Definition: sf_interface.h:89
#define PETSC_TO_CANONICAL
Permute algebraic data from PETSC to canonical ordering.
Definition: sf_interface.h:79
#define PHYSREG_EIKONAL
Definition: sf_interface.h:86
#define PHYSREG_EMI
Definition: sf_interface.h:90
#define ELEM_PETSC_TO_CANONICAL
Permute algebraic element data from PETSC to canonical ordering.
Definition: sf_interface.h:81
#define PHYSREG_EXTRA_ELEC
Definition: sf_interface.h:85
Simulator-level utility execution control functions.
#define BIDOMAIN
Definition: sim_utils.h:155
#define RECOVER_PHIE
Definition: sim_utils.h:160
#define MONODOMAIN
Definition: sim_utils.h:154
#define EXP_POSTPROCESS
Definition: sim_utils.h:172
#define PSEUDO_BIDM
Definition: sim_utils.h:156
#define EXP_LAPLACE
Definition: sim_utils.h:170
#define Extracellular_I
Definition: stimulate.h:38
#define Extracellular_V
Definition: stimulate.h:39
#define STM_IGNORE_PSEUDO_BIDM
Definition: stimulate.h:62
#define NO_EXTRA_GND
Definition: stimulate.h:56
#define NO_EXTRA_I
Definition: stimulate.h:58
#define Intracellular_I
Definition: stimulate.h:41
#define Ignore_Stim
Definition: stimulate.h:49
#define STM_IGNORE_MONODOMAIN
Definition: stimulate.h:61
#define IsExtraV(A)
Definition: stimulate.h:51
#define IGNORE_NONE
Definition: stimulate.h:55
#define STM_IGNORE_BIDOMAIN
Definition: stimulate.h:60
#define Extracellular_Ground
Definition: stimulate.h:40
#define Extracellular_V_OL
Definition: stimulate.h:42
#define Transmembrane_I
Definition: stimulate.h:37
#define NO_EXTRA_V
Definition: stimulate.h:57
const SF::vector< mesh_int_t > * restr_idx
when using asyncIO, here we store the different IDs associated to the vectors we output
Definition: sim_utils.h:301
SF::vector< mesh_int_t > restr_petsc_idx
pointer to index vector with nodal indices we restrict to.
Definition: sim_utils.h:302
int IO_id
pointer to data registered for output
Definition: sim_utils.h:300
long d_trigger_dur
discrete duration
Definition: timer_utils.h:58
const char * name
timer name
Definition: timer_utils.h:53
bool triggered
flag indicating trigger at current time step
Definition: timer_utils.h:54
File descriptor struct.
Definition: basics.h:133
for display execution progress and statistical data of electrical solve
Definition: sim_utils.h:283
double curr
current output wallclock time
Definition: sim_utils.h:287
double start
output start wallclock time
Definition: sim_utils.h:285
double last
last output wallclock time
Definition: sim_utils.h:286
bool elem_flag
igb header we use for output
Definition: sim_utils.h:294
IGBheader igb
pointer to index vector used for restricting output.
Definition: sim_utils.h:293
const SF::vector< mesh_int_t > * restr_idx
pointer to data registered for output
Definition: sim_utils.h:292
SF::mixed_tuple< mesh_t, int > spec
flag whether the data is elements-wise
Definition: sim_utils.h:295