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