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