openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
bench_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 
19 #include "bench_utils.h"
20 #include "petsc_compat.h"
21 
22 #include "libgen.h"
23 
24 namespace limpet {
25 
32 using ::opencarp::timer_manager;
33 
34 namespace {
35 
38 void write_global(sf_vec* v, GVEC_DUMP *gvd, FILE *out, MULTI_IF *pMIIF, IOCtrl *io, int numNode) {
39  char stdsep[] = " ";
40  char fsep[] = "\n";
41  static char sep[2];
42  SF_real *buf;
43 
44  if (io->first) {
45  // choose separator
46  if (io->wsplt)
47  strcpy(sep, fsep);
48  else
49  strcpy(sep, stdsep);
50 
51  io->first = 0;
52  }
53 
54  buf = v->ptr();
55 
56 #ifndef ENABLE_TESTING
57  if (!get_rank()) {
58  if (io->w2file) {
59  if (io->wbin) {
60  // fwrite(&val, 1, sizeof(float), out );
61  fwrite(buf, 1, data_type_sizes[gvd->dtype[0]], out);
62  } else {
63  fprintf(out, "%+.8e%s", buf[0], sep);
64  }
65  }
66  }
67 
68  // stdout only
69  if (io->w2stdout)
70  log_msg(NULL, 0, NONL, "%+.8e%s", buf[0], stdsep);
71 #else
72  for(int i = 0; i< 8; i++)
73  {
74  if (io->w2stdout) {
75  int cell_node = rand()%numNode;
76  log_msg(NULL, 0, NONL, "\n\t(Cell: %d) ", cell_node);
77  log_msg(NULL, 0, NONL, "%+.6e%s", buf[cell_node], stdsep);
78  }
79  }
80 #endif
81 
82  v->release_ptr(buf);
83 }
84 
86 int double_cmp(const void *a, const void *b) {
87  return *((double *)a)-*((double *)b);
88 }
89 
90 } // unnamed namespace
91 
101 void determine_stim_list(char *stl, TrgList *trg, bool DIAs) {
102  trg->n = 1;
103  for (size_t i = 1; i < strlen(stl)-1; i++)
104  trg->n += stl[i] == ',';
105  trg->lst = static_cast<double *>(malloc(trg->n * sizeof(double) ));
106 
107  char *sp, *token;
108  token = tokstr_r(stl, ",", &sp);
109  int i = 0;
110  while (token != NULL) {
111  char *endp;
112  trg->lst[i++] = strtod(token, &endp);
113  if (endp == token) {
114  fprintf(stderr, "Error in stimulus timing list\n");
115  exit(1);
116  }
117  if (DIAs && (i > 1)) trg->lst[i-1] += trg->lst[i-2];
118  token = tokstr_r(NULL, ",", &sp);
119  }
120  qsort(trg->lst, trg->n, sizeof(double), double_cmp);
121 }
122 
123 /* write a header file for state variable dumps
124  * This is to determine with postprocessing routines
125  * which files belong together to a particular experiment.
126  * Particularly, this will be used to gather validation output
127  * in a single hdf5 file.
128  *
129  * \param svd pointer to state variable dumping structure
130  * \param ExpID string to identify an experiment
131  *
132  * \return 0 if header written successfully, -1 otherwise
133  *
134  */
135 int write_dump_header(GVEC_DUMP *gvd, SV_DUMP *svd, const char *ExpID) {
136  int retval = 0;
137 
138  // did we dump some state variables
139  if (!svd->active)
140  return retval;
141 
142  if (!get_rank()) {
143  char hd_fname[512];
144  snprintf(hd_fname, sizeof hd_fname, "%s_header.txt", ExpID);
145  FILE *fh = fopen(hd_fname, "wt");
146  if (fh) {
147  fprintf(fh, "%d # is bigendian\n", is_big_endian() );
148 
149  // write global vectors first
150  for (int i = 0; i < NUM_IMP_DATA_TYPES+1; i++) {
151  if (gvd->hdls[i]) {
152  fprintf(fh, "%32s %10s %2d %10d\n", basename(gvd->fn[i]),
153  data_type_names[gvd->dtype[i]],
154  data_type_sizes[gvd->dtype[i]], gvd->n_dumps);
155  }
156  }
157 
158  // now state variables
159  for (int i = 0; i < svd->n; i++) {
160  fprintf(fh, "%32s %10s %2d %10d\n", basename(svd->fn[i]), data_type_names[svd->dtype[i]],
161  data_type_sizes[svd->dtype[i]], svd->n_dumps);
162  }
163  fclose(fh);
164  } else {
165  fprintf(stderr, "Could not open %s.\n", hd_fname);
166  retval = -1;
167  }
168  }
169 
170  PetscBarrier(COMPAT_PETSC_NULLPTR);
171  return retval;
172 } // write_dump_header
173 
176 void open_globalvec_dump(FILE **fhdls, GVEC_DUMP *gvd, MULTI_IF *pMIIF, char *base_name, IOCtrl *io) {
177  char nbuf[1024];
178  char outname[] = {"GVECS"};
179  char ext[5];
180  char wmode[3];
181  const char *nptr;
182 
183 
184  // initialize
185  io->first = 1;
186  memset(gvd, 0, sizeof(GVEC_DUMP) );
187  gvd->n_dumps = 0;
188 
189  // strategy
190  if (io->w2file) {
191  // writing global vector output to a file
192  if (io->wbin) {
193  io->wsplt = 1;
194  io->w2stdout = 0;
195  strcpy(ext, ".bin");
196  strcpy(wmode, "wb");
197  } else {
198  io->wsplt = 0;
199  io->w2stdout = 0;
200  strcpy(ext, ".txt");
201  strcpy(wmode, "wt");
202  }
203  } else {
204  // output to stdout only
205  io->wsplt = 0;
206  io->w2stdout = 1;
207  }
208 
209  // open file handles
210  if (io->w2file) {
211  int p;
212  for (p = 0; p < NUM_IMP_DATA_TYPES; p++) {
213  if (pMIIF->gdata[p] == NULL) {
214  fhdls[p] = NULL;
215  gvd->hdls[p] = NULL;
216  gvd->fn[p] = NULL;
217  } else {
218  if (io->wsplt) {
219  nptr = imp_data_names[p];
220  snprintf(nbuf, sizeof nbuf, "%s.%s%s", base_name, nptr, ext);
221  gvd->fn[p] = dupstr(nbuf);
222  gvd->dtype[p] = dtype_Real;
223  } else {
224  nptr = &outname[0];
225  snprintf(nbuf, sizeof nbuf, "%s%s", base_name, ext);
226  }
227 
228  if (io->wsplt || !p) {
229  fhdls[p] = fopen(nbuf, wmode);
230  gvd->hdls[p] = f_open(nbuf, wmode);
231  } else {
232  // not splitting, all valid hdls are the same
233  // everything ends up in the same file
234  // fhdls[0] is Vm which is always present
235  fhdls[p] = fhdls[0];
236  }
237  }
238  }
239 
240  // time is not a gdata vector -> repeat the same crap as above
241  if (io->wsplt) {
242  snprintf(nbuf, sizeof nbuf, "%s.t%s", base_name, ext);
243  gvd->fn[p] = dupstr(nbuf);
244  gvd->dtype[p] = dtype_Real;
245  fhdls[p] = fopen(nbuf, wmode);
246  gvd->hdls[p] = f_open(nbuf, wmode);
247  } else {
248  fhdls[p] = fhdls[0];
249  }
250  }
251 
252  if (!get_rank()) {
253  fprintf(stderr, "Outputting the following quantities at each time: \n");
254  fprintf(stderr, "%10s\t", "Time");
255  for (int p = 0; p < NUM_IMP_DATA_TYPES; p++)
256  if (pMIIF->gdata[p] != NULL)
257  fprintf(stderr, "%10s\t", imp_data_names[p]);
258  fprintf(stderr, "\n\n");
259  }
260 
261  PetscBarrier(COMPAT_PETSC_NULLPTR);
262 } // open_globalvec_dump
263 
272 void globalvec_dump(FILE **fhdls, GVEC_DUMP *gvd, MULTI_IF *pMIIF, timer_manager *tmo, IOCtrl *io, int numNode) {
273  int p;
274  float ft = tmo->time;
275  double t = tmo->time;
276 
277  if (!tmo->trigger(CON_TM_IDX))
278  return;
279  else
280  gvd->n_dumps++;
281 
282  if (io->w2stdout) {
283  // write time only if we are not writing to file
284  // this is Ed's preference
285  log_msg(NULL, 0, NONL, "%10.3f ", ft);
286  }
287 
288  if (!get_rank()) {
289  if (io->w2file) {
290  if (io->wbin)
291  fwrite(&tmo->time, 1, data_type_sizes[gvd->dtype[NUM_IMP_DATA_TYPES]], gvd->hdls[NUM_IMP_DATA_TYPES]->fd);
292  // fwrite(&tmo->tm, 1, data_type_sizes[gvd->dtype[NUM_IMP_DATA_TYPES]], fhdls[NUM_IMP_DATA_TYPES]);
293  else
294  fprintf(fhdls[NUM_IMP_DATA_TYPES], "%10.3f ", t);
295  }
296  } // globalvec_dump
297 
298  for (p = 0; p < NUM_IMP_DATA_TYPES; p++)
299  if (pMIIF->gdata[p])
300  write_global(pMIIF->gdata[p], gvd, fhdls[p], pMIIF, io, numNode);
301 
302  if (io->w2stdout)
303  log_msg(NULL, 0, 0, "");
304 
305 
306  if (!get_rank()) {
307  // we write everything into one file
308  if (!io->wsplt && io->w2file)
309  fprintf(fhdls[0], "\n");
310  }
311 }
312 
315 void close_globalvec_dump(FILE **fhdls, GVEC_DUMP *gvd, IOCtrl *io) {
316  int p;
317 
318  if (!io->w2file)
319  return;
320 
321  if (!io->wsplt) {
322  if (fhdls[0])
323  fclose(fhdls[0]);
324  } else {
325  for (p = 0; p < NUM_IMP_DATA_TYPES+1; p++) {
326  if (fhdls[p])
327  fclose(fhdls[p]);
328 
329  f_close(gvd->hdls[p]);
330  }
331  }
332 }
333 
334 
335 /* dump all state variables for IMP and all PLUGS
336  *
337  * \param MIIF
338  * \param reg region number
339  * \param imp name of IMP
340  * \param plugs plug-ins
341  * \param t start of dump time
342  * \param dt dump interval
343  */
344 void dump_all(MULTI_IF *MIIF, int reg, char *imp, char *plugs, double t, double ddt, char *fout) {
345  MIIF->svd.active = 1;
346  MIIF->svd.intv = ddt;
347  MIIF->svd.t_dump = t;
348 
349  char **sv;
350  char file[1024];
351  char IMPS[2048];
352 
353  snprintf(IMPS, sizeof IMPS, "%s:%s", imp, plugs); // make one string with all IMPS
354  char *plgs = dupstr(IMPS), *p;
355  while ( (p = get_next_list(plgs, ':')) != NULL) {
356  int nsv = get_ion_type(std::string(plgs))->get_sv_list(&sv);
357  for (int i = 0; i < nsv; i++) {
358  snprintf(file, sizeof file, "%s_%s.%s", fout, plgs, sv[i]);
359  MIIF->sv_dump_add_by_name(reg, plgs, sv[i], plgs, file);
360  }
361  free(sv);
362  plgs = p;
363  }
364 }
365 
367  for (int i = 0; i < N_TIMINGS; i++) {
368  t[i].mn = 10000000.;
369  t[i].mx = 0.;
370  t[i].avg = 0.;
371  t[i].tot = 0.;
372  t[i].count = 0;
373  }
374 }
375 
376 void update_timing(event_timing *t, double event_duration) {
377  if (event_duration < t->mn)
378  t->mn = event_duration;
379  if (event_duration > t->mx)
380  t->mx = event_duration;
381  t->tot += event_duration;
382  t->avg = t->tot/++t->count;
383 }
384 
385 /* Read value from global vector for a particular cell
386  *
387  * \param v global vector
388  * \param ind index of cell for which we read value
389  *
390  * \return value of cell ind in vector v
391  */
392 double getCellVal(sf_vec *v, int ind) {
393  return v->get(ind);
394 }
395 
396 /* initalize state variables
397  *
398  * The SVs string has the following format
399  *
400  * extern_var1=a:imp_sv0=b,imp_sv1=c::plug2_sv=d
401  *
402  * where extern_var is a global variable, eg. Vm, imp_sv0 and imp_sv1 are state variables in the IMP,
403  * and plug2_sv is a state variable in the second plugin. Any number of value pairs can be added,
404  * each separated by a ",".
405  *
406  * \param MIIF ionic interface
407  * \param SVs state variables to change
408  * \param imp IMP being used
409  * \param plgins colon spearated plug-in list
410  * \param num number of cells
411  */
412 void initial_SVs(MULTI_IF *miif, char *SVs, char *imp, char *plgins, int num) {
413  SF::vector<SF_int> ind (num);
414  SF::vector<SF_real> vals(num);
415 
416  char impsv[4096];
417  char *colptr;
418  int mode = 0;
419  char *last_col = SVs;
420 
421  do {
422  colptr = strchr(last_col, ':');
423  char *tmp;
424  if (colptr)
425  tmp = strndup(last_col, colptr-last_col);
426  else
427  tmp = strdup(last_col);
428  char *iptr = NULL;
429  char *pair = strtok_r(tmp, ",", &iptr);
430  while (pair) {
431  char sv[2048];
432  double value;
433  if (sscanf(pair, "%[^= ]=%lf", sv, &value) != 2) {
434  log_msg(_nc_logf, 5, 0, "bad SV initializer: %s", pair);
435  exit(1);
436  }
437 
438  for (int i = 0; i < num; i++) {
439  ind[i] = i;
440  vals[i] = value;
441  }
442 
443  if (!mode) {
444  strcpy(impsv, sv);
445  } else if (mode == 1) {
446  snprintf(impsv, sizeof impsv, "%s.%s", imp, sv);
447  } else if (mode > 1) {
448  char *ptr = plgins;
449  for (int j = 0; j < mode-2; j++)
450  ptr = strchr(ptr, ':') + 1;
451  char *ptr1 = strchr(ptr, ':');
452  snprintf(impsv, sizeof impsv, "%.*s.%s", (int)(ptr1 ? ptr1-ptr : strlen(ptr)), ptr, sv);
453  }
454 
455  miif->adjust_MIIF_variables(impsv, ind, vals);
456  pair = strtok_r(NULL, ",", &iptr);
457  }
458  free(tmp);
459  last_col = colptr+1;
460  mode++;
461  } while (colptr != NULL);
462 } // initial_SVs
463 
464 /* print out the parameter help
465  *
466  * \param
467  */
469  printf("Ionic model:\n");
470  printf("------------\n");
471  im->print_metadata();
472  im->print_params();
473  char **sv;
474  int n = im->get_sv_list(&sv);
475  printf("\tState variables:\n");
476  for (int i = 0; i < n; i++)
477  printf("\t\t%20s\n", sv[i]);
478 
479  if (!plugs.empty()) {
480  printf("\n\n");
481  printf("Plugins:\n");
482  printf("--------\n");
483  }
484  for (auto& plug : plugs) {
485  plug.get().print_metadata();
486  plug.get().print_params();
487  n = plug.get().get_sv_list(&sv);
488  printf("\tState variables:\n");
489  for (int j = 0; j < n; j++)
490  printf("\t\t%20s\n", sv[j]);
491  printf("\n");
492  }
493 }
494 
503 float determine_duration(struct gengetopt_args_info *p, TrgList *stim_lst) {
504  if (p->stim_times_given)
505  determine_stim_list(p->stim_times_arg, stim_lst, p->DIA_flag);
506 
507  if (p->duration_given)
508  return p->duration_arg;
509  else if (p->restitute_given)
510  return p->duration_arg;
511  else if (p->stim_times_given) // user specified list
512  return stim_lst->lst[stim_lst->n-1]+p->past_stim_arg;
513  else
514  return p->stim_start_arg+p->bcl_arg*(p->numstim_arg-1)+p->past_stim_arg;
515 }
516 
517 } // namespace limpet
void update_timing(event_timing *t, double event_duration)
Definition: bench_utils.cc:376
int dtype[NUM_IMP_DATA_TYPES+1]
data type
Definition: bench_utils.h:59
void open_globalvec_dump(FILE **fhdls, GVEC_DUMP *gvd, MULTI_IF *pMIIF, char *base_name, IOCtrl *io)
Definition: bench_utils.cc:176
char * dupstr(const char *old_str)
Definition: basics.cc:44
void dump_all(MULTI_IF *MIIF, int reg, char *imp, char *plugs, double t, double ddt, char *fout)
Definition: bench_utils.cc:344
int n_dumps
keep track of number of dumped time slices
Definition: MULTI_ION_IF.h:169
double getCellVal(sf_vec *v, int ind)
Definition: bench_utils.cc:392
char * fn[NUM_IMP_DATA_TYPES+1]
array to store file names
Definition: bench_utils.h:58
SV_DUMP svd
state variable dump
Definition: MULTI_ION_IF.h:203
virtual void print_params() const =0
Prints the parameters of this model.
std::vector< std::reference_wrapper< IonType > > IonTypeList
Definition: ion_type.h:289
bool is_big_endian()
Definition: basics.cc:290
void initialize_timings(event_timing *t)
Definition: bench_utils.cc:366
float determine_duration(struct gengetopt_args_info *p, TrgList *stim_lst)
determine time of last stimulus
Definition: bench_utils.cc:503
double avg
average duration of event
Definition: bench_utils.h:69
int sv_dump_add_by_name(int, char *, char *, char *, char *)
data structure to manage state variable file dumps
Definition: MULTI_ION_IF.h:160
int n
#state variables we want to dump
Definition: MULTI_ION_IF.h:162
FILE_SPEC _nc_logf
Definition: ION_IF.cc:84
void print_param_help(IonType *im, IonTypeList &plugs)
Definition: bench_utils.cc:468
char wbin
write to file in binary format
Definition: bench_utils.h:50
double mx
maximum duration of event
Definition: bench_utils.h:68
char first
first line of output
Definition: bench_utils.h:53
int n
number of pulses required for protocol
Definition: restitute.h:29
double tot
total duration of all events
Definition: bench_utils.h:70
char ** fn
array to store file names
Definition: MULTI_ION_IF.h:164
IonType * get_ion_type(const std::string &name)
double * lst
store instants of pulse delivery
Definition: restitute.h:30
int n_dumps
keep track of number of dumped time slices
Definition: bench_utils.h:60
virtual int get_sv_list(char ***list) const =0
Returns a list of SVs.
void determine_stim_list(char *stl, TrgList *trg, bool DIAs)
Definition: bench_utils.cc:101
char wsplt
split -> each vector goes into separate file
Definition: bench_utils.h:51
char w2file
write to file
Definition: bench_utils.h:49
void globalvec_dump(FILE **fhdls, GVEC_DUMP *gvd, MULTI_IF *pMIIF, timer_manager *tmo, IOCtrl *io, int numNode)
Definition: bench_utils.cc:272
char * get_next_list(char *lst, char delimiter)
Definition: ION_IF.cc:512
virtual void print_metadata() const =0
Prints the metadata of this model.
SF::abstract_vector< SF_int, SF_real > sf_vec
Definition: sf_interface.h:49
char * tokstr_r(char *s1, const char *s2, char **lasts)
Definition: ION_IF.cc:88
double t_dump
next instant for sv dump
Definition: MULTI_ION_IF.h:168
int count
number of events counted so far
Definition: bench_utils.h:71
number of benchmark timings we use
Definition: bench_utils.h:79
int write_dump_header(GVEC_DUMP *gvd, SV_DUMP *svd, const char *ExpID)
Definition: bench_utils.cc:135
opencarp::sf_vec * gdata[NUM_IMP_DATA_TYPES]
data used by all IMPs
Definition: MULTI_ION_IF.h:216
void log_msg(FILE_SPEC out, int level, unsigned char flag, const char *fmt,...)
Definition: basics.cc:72
#define NONL
Definition: basics.h:312
double intv
time interval for sv dumps
Definition: MULTI_ION_IF.h:167
void f_close(FILE_SPEC &f)
Close a FILE_SPEC.
Definition: basics.cc:162
void close_globalvec_dump(FILE **fhdls, GVEC_DUMP *gvd, IOCtrl *io)
Definition: bench_utils.cc:315
double mn
minimum duration of event
Definition: bench_utils.h:67
Abstract class representing an ionic model type.
Definition: ion_type.h:59
double SF_real
Use the general double as real type.
Definition: SF_globals.h:38
void initial_SVs(MULTI_IF *miif, char *SVs, char *imp, char *plgins, int num)
Definition: bench_utils.cc:412
int * dtype
data type
Definition: MULTI_ION_IF.h:173
int adjust_MIIF_variables(const char *variable, const SF::vector< SF_int > &indices, const SF::vector< SF_real > &values)
char w2stdout
turn on/off output to stdout
Definition: bench_utils.h:52
int get_rank(MPI_Comm comm=PETSC_COMM_WORLD)
Definition: basics.h:276
FILE_SPEC f_open(const char *fname, const char *mode)
Open a FILE_SPEC.
Definition: basics.cc:135
opencarp::FILE_SPEC hdls[NUM_IMP_DATA_TYPES+1]
array of file handles to gvec output files
Definition: bench_utils.h:57