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