openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
MULTI_ION_IF.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 
54 // TODO remove the "get_sv_address()" and "get_sv_size()" calls by moving
55 // the functions that use them to the IonIf class
56 
57 #include "MULTI_ION_IF.h"
58 #include "limpet_types.h"
59 #include <stdio.h>
60 #ifdef HAS_CUDA_MODEL
61 #include <cuda_runtime.h>
62 #endif
63 #if defined HAS_ROCM_MODEL && defined __HIP__
64 #include <hip/hip_runtime.h>
65 #endif
66 
67 #include "SF_init.h" // for SF::init_xxx()
68 #include "petsc_utils.h" // TODO for EXIT
69 
70 namespace limpet {
71 
72 using ::opencarp::base_timer;
83 using ::opencarp::Salt_list;
88 #ifdef USE_FMEM_WRAPPER
90 #endif
91 
92 /*
93  PROTOTYPES
94  */
95 void initialize_params_MIIF(MULTI_IF *pMIIF);
96 void initialize_ionic_IF(MULTI_IF *pMIIF);
97 void CreateIIFLocalNodeLsts(MULTI_IF *pMIIF);
98 void CreateIIFNodeLsts_(int, char *, int **, int ***, int);
99 void alloc_MIIF(MULTI_IF *pMIIF);
100 void initializeIMPData(MULTI_IF *pMIIF);
101 void freeIMPData(MULTI_IF *pMIIF);
102 void allocate_shared_data(MULTI_IF *);
103 int getGlobalNodalIndex(IonIfBase& pIF, int relIdx);
104 void CreateIIFGlobalNodeLsts(MULTI_IF *pMIIF);
105 bool isIMPdata(const char *);
106 
107 static MULTI_IF *gMIIF_Error_Recovery;
108 static int current_IIF_index;
109 static float current_time = 0;
110 static float start_time = 0;
111 
112 static void prepare_error_recovery(MULTI_IF *MIIF, int IIF_index, float time) {
113  gMIIF_Error_Recovery = MIIF;
114  current_IIF_index = IIF_index;
115  current_time = start_time+time;
116 }
117 
118 int current_global_node(int local_node) {
119  return gMIIF_Error_Recovery->NodeLists[current_IIF_index][local_node];
120 }
121 
123  return current_time;
124 }
125 
126 static int g_print_bounds_exceeded = 1;
127 
129  return g_print_bounds_exceeded;
130 }
131 
133  int oldval = g_print_bounds_exceeded;
134 
135  g_print_bounds_exceeded = newval;
136  return oldval;
137 }
138 
142  CreateIIFNodeLsts_(pMIIF->N_IIF, pMIIF->IIFmask, &pMIIF->N_Nodes,
143  &pMIIF->NodeLists, pMIIF->numNode);
144 }
145 
157 void CreateIIFNodeLsts_(int N_IIF, IIF_Mask_t *IIF_Mask, int **N_Nodes,
158  int ***NodeLists, int numNode)
159 {
160  /* create node lists for each IIF
161  NodeNum: store number of nodes for each IIF
162  NodeLst: store node indices of each IIF
163  */
164  int *NodeNum = static_cast<int *>(calloc(N_IIF, sizeof(int)));
165  int **NodeLst = static_cast<int **>(calloc(N_IIF, sizeof(int *)));
166 
167  // determine number of nodes of each IIF
168  for (int i = 0; i < numNode; i++)
169  NodeNum[static_cast<int>(IIF_Mask[i])]++;
170 
171  /* now we know the number of nodes per IIF and store the indices of
172  nodes belonging to a particular IIF into a list.
173  */
174  for (int i = 0; i < N_IIF; i++) {
175  if (NodeNum[i] > 0)
176  NodeLst[i] = static_cast<int *>(malloc(sizeof(int *) * NodeNum[i]));
177  else
178  NodeLst[i] = NULL;
179  }
180 
181  // just store indices relative to low
182  for (int j = 0; j < N_IIF; j++) {
183  int hcount = 0;
184  for (int i = 0; i < numNode; i++)
185  if (IIF_Mask[i] == j)
186  NodeLst[j][hcount++] = i;
187  }
188 
189  for(int j=0; j<N_IIF; j++)
190  std::sort(NodeLst[j], NodeLst[j]+NodeNum[j]);
191 
192  *N_Nodes = NodeNum;
193  *NodeLists = NodeLst;
194 
195 } // CreateIIFNodeLsts_
196 
197 
205 int getGlobalNodalIndex(IonIfBase& pIF, int rIdx) {
206  // get pointer to parent MIIF
207  MULTI_IF *pMIIF = (MULTI_IF *) pIF.parent(); // TODO check?!?!?!?!?!
208 
209  if (pIF.get_type().is_plugin())
210  pMIIF = (MULTI_IF *) pIF.parent()->parent();
211 
212  return pMIIF->NodeLists[pIF.miifIdx][rIdx];
213 }
214 
221 void alloc_MIIF(MULTI_IF *pMIIF) {
222  int N_IIF = pMIIF->N_IIF;
223 
224  pMIIF->contiguous = static_cast<bool *>(calloc(N_IIF, sizeof(bool)));
225 #if 0
226  pMIIF->ldata = (GlobalData_t***) build_matrix_ns<GlobalData_t>(N_IIF, NUM_IMP_DATA_TYPES, sizeof(GlobalData_t **), Target::CPU);
227 #else
228  pMIIF->ldata = allocate_on_target<GlobalData_t**>(Target::CPU, N_IIF);
229  for (std::size_t i = 0; i < pMIIF->N_IIF; i++) {
230  pMIIF->ldata[i] = allocate_on_target<GlobalData_t*>(pMIIF->iontypes[i].get().select_target(pMIIF->targets[i]), NUM_IMP_DATA_TYPES);
231  }
232 #endif
233 
234  for (int i = 0; i < pMIIF->N_IIF; i++) {
235  // Create an IonIf object on the chosen target
236  pMIIF->IIF.push_back(pMIIF->iontypes[i].get().make_ion_if(pMIIF->targets[i], pMIIF->N_Nodes[i], pMIIF->plugtypes[i]));
237  // this is questionable. A IIF does not have a parent, parent should be NULL
238  // It might make sense in some situtation to be able to refer to the MIIF
239  // structure, but pIF->parent is of type IonIf and not MULTI_IF!!!!
240  pMIIF->IIF[i]->set_parent((IonIfBase *)pMIIF);
241 
242  // set unique miifIdx for imp and plugins to enable refering back
243  // to global entities such as global node index from within imp
244  pMIIF->IIF[i]->for_each([&](IonIfBase& IF) { IF.miifIdx = i; });
245  }
246 } // alloc_MIIF
247 
253  for (auto& imp : pMIIF->IIF) {
254  imp->initialize_params();
255  }
256 }
257 
262  pMIIF->getRealData(); // needed for initializing Vm, etc.
263  for (int i = 0; i < pMIIF->IIF.size(); i++)
264  pMIIF->IIF[i]->initialize(pMIIF->dt, pMIIF->ldata[i]);
265 
266  pMIIF->releaseRealDataDuringInit();
267 }
268 
275  // we need to copy all global data which has been set, even if it is not
276  // listed as moddat. This is the case with Vm which may not be modified by
277  // LIMPET but is initialized by LIMPET
278  unsigned int *moddat = static_cast<unsigned int *>(calloc(this->N_IIF, sizeof(unsigned int)));
279 
280  for (int i = 0; i < this->N_IIF; i++) {
281  moddat[i] = this->IIF[i]->get_moddat();
282  this->IIF[i]->set_moddat(moddat[i] | this->IIF[i]->get_reqdat()); // add in the reqdat
283  }
284  this->releaseRealData();
285  for (int i = 0; i < this->N_IIF; i++) {
286  this->IIF[i]->set_moddat(moddat[i]); // remove the reqdat
287  }
288  free(moddat);
289 }
290 
296 {
298  alloc_MIIF(this);
300 
301  memset(&svd, 0, sizeof(SV_DUMP) );
302  svd.active = 0;
303  svd.intv = 1.0; // 1. ms by default
304 }
305 
306 #define FILENAME_BUF 1024
307 
309 int int_cmp(const void *a, const void *b) {
310  return *(int *)a - *(int *)b;
311 }
312 
340 void open_trace(MULTI_IF *MIIF, int n_traceNodes, int *traceNodes, int *label, opencarp::sf_mesh* imesh)
341 {
342  if (!n_traceNodes) return;
343 
344  if (n_traceNodes > 1000)
345  log_msg(0, 4, 0, "%s warning: %d trace nodes may impact performance", __func__, n_traceNodes);
346 
347  MIIF->trace_info = (Trace_Info *)calloc(n_traceNodes+1, sizeof(Trace_Info));
348  MIIF->trace_info[n_traceNodes].region = -1; // end of list marker
349 
350  // bijective index mapping between set A (local petsc indexing) and set B (local nodal indexing)
352  if(imesh)
353  SF::local_petsc_to_nodal_mapping(*imesh, petsc2nod);
354 
355  for (int iTrace = 0; iTrace < n_traceNodes; iTrace++) {
356  mesh_int_t lnode = imesh ? imesh->pl.localize(traceNodes[iTrace]) : traceNodes[iTrace];
357 
358  // here we check if lnode is in set B (i.e. local nodal indexing)
359  if(imesh) {
360  // here we check if lnode is in set B (i.e. local nodal indexing)
361  if(petsc2nod.in_b(lnode) == false) continue;
362 
363  // lnode is local nodal, we map it to local petsc
364  lnode = petsc2nod.backward_map(lnode);
365  }
366 
367  for (int iRegion = 0; iRegion < MIIF->N_IIF; iRegion++)
368  {
369  auto target = static_cast<int *>(bsearch(&lnode, MIIF->NodeLists[iRegion],
370  MIIF->N_Nodes[iRegion], sizeof(int), int_cmp));
371 
372  if (target != NULL ) {
373  int idx = target - MIIF->NodeLists[iRegion];
374  Trace_Info *trace_info = MIIF->trace_info+iTrace;
375  trace_info->found = true;
376  trace_info->region = iRegion;
377  trace_info->node_idx = idx;
378  }
379  }
380  }
381 
382  for (int iTrace = 0; iTrace < n_traceNodes; iTrace++) {
383  if(get_global(int(MIIF->trace_info[iTrace].found), MPI_SUM) == 0) {
384  MIIF->trace_info[iTrace].ignored = true;
385  log_msg(0,4,0, "trace node %d not found", traceNodes[iTrace]);
386  continue;
387  }
388 
389  char traceName[FILENAME_BUF];
390  snprintf(traceName, sizeof traceName, "Trace_%d.dat", label ? label[iTrace] : traceNodes[iTrace]);
391  MIIF->trace_info[iTrace].file = f_open(traceName, "w");
392  }
393 } // open_trace
394 
409 void dump_trace(MULTI_IF *MIIF, limpet::Real time) {
410  if (MIIF->trace_info == NULL)
411  return;
412 
413 // struct exception_type e;
414 
415 #define MAX_TRACE_LINE_LEN 8196
416 
417  char trace_buf[MAX_TRACE_LINE_LEN] = {0};
418 
419  std::vector<IonIfBase*>& IIF = MIIF->IIF;
420  FILE *fs;
421 
422  for (int iTrace = 0; MIIF->trace_info[iTrace].region >= 0; iTrace++) {
423  Trace_Info *ctrace = MIIF->trace_info+iTrace;
424 
425  if (ctrace->ignored)
426  continue;
427 
428  if (ctrace->found) {
429  fs = fmemopen_(trace_buf, MAX_TRACE_LINE_LEN, "w");
430  fprintf(fs, "%4.10f\t", time);
431  if (IIF[ctrace->region]->get_type().has_trace()) {
432  IIF[ctrace->region]->get_type().trace(*IIF[ctrace->region], ctrace->node_idx,
433  fs, MIIF->ldata[ctrace->region]);
434  }
435  for (auto& plugin : IIF[ctrace->region]->plugins()) {
436  if (plugin->get_type().has_trace()) {
437  fprintf(fs, "\t");
438  plugin->get_type().trace(
439  *plugin, ctrace->node_idx,
440  fs, MIIF->ldata[ctrace->region]);
441  }
442  }
443  fprintf(fs, "\n");
444  fclose(fs);
445 
446  fprintf(ctrace->file->fd, "%s", trace_buf);
447  }
448  fflush(ctrace->file->fd);
449  }
450 } // dump_trace
451 
452 void close_trace(MULTI_IF *MIIF) {
453  if (!MIIF->trace_info) return;
454 
455  for (int iTrace = 0; MIIF->trace_info[iTrace].region >= 0; iTrace++)
456  f_close(MIIF->trace_info[iTrace].file);
457 
458  free(MIIF->trace_info);
459  MIIF->trace_info = NULL;
460 }
461 
468 void MULTI_IF::initialize_currents(double idt, int subDt) {
469  numSubDt = subDt;
470  dt = idt/this->numSubDt;
471 
472  allocate_shared_data(this);
473  initializeIMPData(this);
474  initialize_ionic_IF(this);
475 }
476 
483 void MULTI_IF::dump_luts_MIIF(bool zipped) {
484  // if one of the IFs in MIIF does not live in partion 0
485  // we won't get a LUT dumped.
486  if (get_rank()) return;
487 
488  std::vector<IonIfBase*>& pIF = this->IIF;
489  for (auto& IF : pIF) {
490  int ndmps = IF->dump_luts(zipped);
491  if (ndmps < IF->tables().size()) {
492  log_msg(logger, 4, 0, "LUT dump error %s: only %d out of %d LUTs dumped.\n",
493  IF->get_type().get_name().c_str(), ndmps, IF->tables().size());
494  }
495  for (auto& plugin : IF->plugins()) {
496  ndmps = plugin->dump_luts(zipped);
497  if (ndmps < plugin->tables().size()) {
498  log_msg(logger, 4, 0, "LUT dump error %s: only %d out of %d LUTs dumped.\n",
499  plugin->get_type().get_name().c_str(), ndmps, IF->tables().size());
500  }
501  }
502  }
503 }
504 
510  if (get_rank()) return;
511 
512  // close files
513  for (int i = 0; i < svd.n; i++)
514 #ifndef USE_HDF5
515  if (svd.hdls[i] != NULL)
516 #endif // ifndef USE_HDF5
517  f_close(svd.hdls[i]);
518 }
519 
530 char *get_sv(void *tab, int offset, int n, int svSize, int size, int dlo_vector_size) {
531  char *buf = static_cast<char *>(malloc(n*size));
532  char *bp = buf;
533  char *p = static_cast<char *>(tab) + offset;
534 
535  for (int i = 0; i < n; i += dlo_vector_size) {
536  int dlo_array_size = min(dlo_vector_size, n - i);
537  memcpy(bp, p, size * dlo_array_size);
538  bp += size * dlo_array_size;
539  p += svSize;
540  }
541  return buf;
542 }
543 
551 int MULTI_IF::dump_svs(base_timer *iot) {
552  int nwr = 0;
553 
554  if (iot->triggered) {
555  for (int i = 0; i < svd.n; i++) {
556  nwr = 0;
557 
558  FILE* fd = svd.hdls[i] ? svd.hdls[i]->fd : NULL;
559  char *buf = get_sv(svd.svtab[i], svd.offset[i], svd.num[i], svd.svsize[i], svd.size[i], svd.dlo_vs[i]);
560  nwr += SF::root_write<char>(fd, (char*) buf, svd.size[i]*svd.num[i], PETSC_COMM_WORLD);
561  free(buf);
562  }
563  svd.nwr += nwr;
564  svd.n_dumps++;
565  }
566 
567  return nwr;
568 }
569 
575 #ifdef HAS_GPU_MODEL
576 __global__
577 void update_vm(int start, int end, double *vm, double *ion, double dt)
578 {
579  int i = blockIdx.x*blockDim.x + threadIdx.x;
580  if (i < end)
581  vm[i] = vm[i] + (ion[i] * (-dt));
582 }
583 #endif
584 
585 // * compute what has to be computed, current or otherwise
586 void MULTI_IF::compute_ionic_current(bool flag_send, bool flag_receive)
587 {
588  gdata[Iion]->set(0.0);
589  if (flag_send == 1) {
590  this->getRealData();
591  }
592 
593  for (int j = 0; j < this->numSubDt; j++)
594  {
595  for (int i = 0; i < this->N_IIF; i++)
596  {
597  IonIfBase* pIF = this->IIF[i];
598  if (!this->N_Nodes[i]) continue;
599 
600  prepare_error_recovery(this, i, pIF->get_tstp().cnt * this->dt);
601  update_ts(&pIF->get_tstp());
602 
603  int current = 0;
604  do {
605  try {
606  pIF->compute(current, this->N_Nodes[i], this->ldata[i]);
607  current = this->N_Nodes[i];
608  }
609  catch(int e) { //FIXME
610  if(e==-1) {
611  // CHECK this->NodeLists[i][e.node], e.node);
612  fprintf(stderr, "LIMPET compute fail in %s at node %d (local %d)! Aborting!\n",
613  this->name.c_str(), this->NodeLists[i][j], j);
614  exit(1);
615  }
616  current++;
617  }
618  } while (current < this->N_Nodes[i]);
619 
620  for (auto& plugin : pIF->plugins()) {
621  current = 0;
622  update_ts(&plugin->get_tstp());
623 
624  do {
625  try {
626  plugin->compute(current, this->N_Nodes[i], this->ldata[i]);
627  current = this->N_Nodes[i];
628 
629  if (plugin == nullptr)
630  throw -1;
631  }
632  catch(int e) { //FIXME
633  if(e==-1) {
634  // CHECK this->NodeLists[i][e.node], e.node);
635  fprintf(stderr, "LIMPET plugin fail in %s at node %d (local %d)! Aborting!\n",
636  this->name.c_str(), this->NodeLists[i][j], j);
637  exit(1);
638  }
639  current++;
640  }
641  } while (current < this->N_Nodes[i]);
642  }
643  }
644 
645  #ifdef HAS_GPU_MODEL
646  //TODO: extUpdateVm can be true outside of the bench executable ! But this should
647  // only run in bench! This should be done for each model according to its target
648  if(!extUpdateVm && is_gpu(this->targets[0])) {
649 
650  #if defined __CUDA__ || defined __HIP__
651  update_vm<<<(this->N_Nodes[0]/64)+1,64>>>(0, this->N_Nodes[0], this->ldata[0][Vm], this->ldata[0][Iion], this->dt);
652  #ifdef __CUDA__
653  cudaDeviceSynchronize();
654  #elif defined __HIP__
655  hipDeviceSynchronize();
656 #endif
657  #else
658  fprintf(stderr, "GPU/CUDA not found");
659  #endif
660  }
661  #endif
662 
663  if (flag_receive == 1) {
664  this->releaseRealData();
665  }
666 
667  // TODO: Target should be checked for each region
668  if(!extUpdateVm && !is_gpu(this->targets[0]))
669  gdata[Vm]->add_scaled(*gdata[Iion], SF_real(-dt));
670  }
671 }
672 
674  assert(!this->doppel);
675 
676  // Free data first since this function needs to access memory in the IIFs
677  freeIMPData(this);
678  for (auto& pIF : this->IIF) {
679  pIF->get_type().destroy_ion_if(pIF);
680  }
681 }
682 
690  for (int i = 0; i < pMIIF->N_IIF; i++) {
691  if (!pMIIF->N_Nodes[i])
692  continue;
693 
694  // if memory contiguous, we don't need to copy data, merely pass pointers
695  pMIIF->contiguous[i] = pMIIF->N_Nodes[i]-1 ==
696  pMIIF->NodeLists[i][pMIIF->N_Nodes[i]-1]-pMIIF->NodeLists[i][0];
697 
698  for (int j = 0; j < NUM_IMP_DATA_TYPES; j++) {
699  // check if it is used by the IMPs
700  if (!USED_DAT(pMIIF->IIF[i], imp_data_flag[j]) )
701  continue;
702 
703  // allocate mem buffer only if not contiguous or if data is on GPU
704  if (!pMIIF->contiguous[i] || is_gpu(pMIIF->IIF[i]->get_target()))
705  pMIIF->ldata[i][j] =
706  allocate_on_target<GlobalData_t>(pMIIF->IIF[i]->get_target(),
707  pMIIF->N_Nodes[i]);
708  // check if data supplied
709  if (pMIIF->gdata[j] == NULL) {
710  log_msg(pMIIF->logger, 5, LOCAL, "IMP data type %s not supplied for region %d", imp_data_names[j], i);
711  exit(1);
712  }
713  }
714  }
715 } // initializeIMPData
716 
726  // set rdata in the parent vector to point to the
727  // local data to be computed on the local processor
728  for (int j = 0; j < NUM_IMP_DATA_TYPES; j++) {
729  SF_real* rdata;
730  if (this->gdata[j] != NULL) {
731  rdata = this->gdata[j]->ptr();
732  this->procdata[j] = rdata;
733  }
734  else continue;
735 
736  // now map local data so that each ionic model can use it
737  // if noncontiguous in the parent vector, copy to contiguous local vector
738  for (int i = 0; i < this->N_IIF; i++) {
739  if (!this->N_Nodes[i] || !USED_DAT(this->IIF[i], imp_data_flag[j]) )
740  continue;
741 
742  if (this->contiguous[i] && !is_gpu(this->IIF[i]->get_target()))
743  this->ldata[i][j] = static_cast<GlobalData_t *>(rdata) + this->NodeLists[i][0];
744  else {
745  const int* ip = this->NodeLists[i];
746  for (int k = 0; k < this->N_Nodes[i]; k++)
747  this->ldata[i][j][k] = rdata[ip[k]];
748  }
749  }
750  }
751 } // getRealData
752 
762 
763  for (int i = 0; i < this->N_IIF; i++) {
764  if (!this->N_Nodes[i]) continue;
765 
766  // Every external variable used by the model must be copied on GPU
767  if (is_gpu(this->IIF[i]->get_target())) {
768  for (int j = 0; j < NUM_IMP_DATA_TYPES; j++) {
769  if (!USED_DAT(this->IIF[i], imp_data_flag[j]) )
770  continue;
771  if (this->contiguous[i])
772  memcpy(&this->procdata[j][this->NodeLists[i][0]], this->ldata[i][j], sizeof(this->ldata[i][j][0])*this->N_Nodes[i]);
773  else {
774  for (int k = 0; k < this->N_Nodes[i]; k++)
775  this->procdata[j][this->NodeLists[i][k]] = this->ldata[i][j][k];
776  }
777  }
778  }
779  else {
780  for (int j = 0; j < NUM_IMP_DATA_TYPES; j++) {
781  if (this->IIF[i]->get_moddat() & imp_data_flag[j]) {
782  if (!this->contiguous[i]) {
783  for (int k = 0; k < this->N_Nodes[i]; k++)
784  this->procdata[j][this->NodeLists[i][k]] = this->ldata[i][j][k];
785  }
786  }
787  }
788  }
789  }
790 
791  for (int j = 0; j < NUM_IMP_DATA_TYPES; j++)
792  if (this->gdata[j] != NULL)
793  this->gdata[j]->release_ptr(this->procdata[j]);
794 }
795 
796 // * free the local storage vectors
797 void freeIMPData(MULTI_IF *pMIIF) {
798  for (int j = 0; j < NUM_IMP_DATA_TYPES; j++) {
799  if (pMIIF->gdata[j] != NULL) {
800  for (int i = 0; i < pMIIF->N_IIF; i++)
801  if ((!pMIIF->contiguous[i] || is_gpu(pMIIF->IIF[i]->get_target())) && USED_DAT(pMIIF->IIF[i], imp_data_flag[j]) && pMIIF->N_Nodes[i] > 0) {
802  deallocate_on_target<GlobalData_t>(pMIIF->IIF[i]->get_target(),
803  pMIIF->ldata[i][j]);
804  }
805  delete pMIIF->gdata[j];
806  }
807  }
808  free(pMIIF->contiguous);
810 }
811 
821 int get_plug_flag(char *plgstr, int *out_num_plugins, IonTypeList& out_plugins) {
822  if (plgstr == NULL) {
823  *out_num_plugins = 0;
824  out_plugins.clear();
825  return 1;
826  }
827 
828  // maximum number of plugins is 200 apparently
829  char *plugspec = dupstr(plgstr);
830  char *saveptr;
831  char *token = tokstr_r(plugspec, ":", &saveptr);
832 
833  *out_num_plugins = 0;
834  while (token) {
835  IonType* type = get_ion_type(std::string(token));
836  if (type == NULL) {
837  free(plugspec);
838  return 0;
839  } else {
840  out_plugins.push_back(*type);
841  }
842  token = tokstr_r(NULL, ":", &saveptr);
843  }
844 
845  *out_num_plugins = out_plugins.size();
846 
847  free(plugspec);
848  return 1;
849 } // get_plug_flag
850 
869 int MULTI_IF::adjust_MIIF_variables(const char* variable,
870  const SF::vector<SF_int> & indices,
871  const SF::vector<SF_real> & values)
872 {
873  int num_changed = 0;
874 
875  // determine if we're dealing with an external variable or a state variable.
876  if (index(variable, '.') == NULL) {
877  // external variable, the easy case.
878  // which external variable?
879  int data_id = -1;
880  for (int ii = 0; ii < NUM_IMP_DATA_TYPES; ii++) {
881  if (strcmp(variable, imp_data_names[ii]) == 0) {
882  data_id = ii;
883  break;
884  }
885  }
886  if (data_id == -1) {
887  log_msg(logger, 5, FLUSH, "Error! No external variable named %s in this build of openCARP.", variable);
888  exit(EXIT_FAILURE);
889  }
890 
891  // make sure that external variable is being used.
892  if (this->gdata[data_id] == NULL) {
893  log_msg(logger, 4, 0,
894  "External variable %s is not being used this processor.\n"
895  "Is this really what you meant to do?\n"
896  "Perhaps you don't have the correct Ionic models selected.",
897  imp_data_names[data_id]);
898  }
899  else {
900  // fill in the external variable with the data from the file.
901  SF_real *raw_data = this->gdata[data_id]->ptr();
902 
903  for (size_t i = 0; i < indices.size(); i++) {
904  raw_data[indices[i]] = values[i];
905  num_changed++;
906  }
907 
908  this->gdata[data_id]->release_ptr(raw_data);
909  }
910  }
911  else {
912  // state variable.
913  // extract the IMP name and state variable name from the string.
914  char *saveptr;
915  char *my_variable = dupstr(variable);
916  char *IIF_name = tokstr_r(my_variable, ".", &saveptr);
917  char *sv_name = tokstr_r(NULL, ".", &saveptr);
918 
919  IonType* type = get_ion_type(std::string(IIF_name));
920  if (type == NULL) {
921  log_msg(logger, 5, 0, "%s error: %s is not a valid IMP name.", __func__, IIF_name);
922  exit(EXIT_FAILURE);
923  }
924 
925  int sv_offset;
926  int sv_size;
927  SVgetfcn sv_get = type->get_sv_offset(sv_name, &sv_offset, &sv_size);
928  if (sv_get == NULL) {
929  log_msg(logger, 5, 0, "%s error: %s is not a valid state variable for the %s model.",
930  __func__, sv_name, IIF_name);
931  exit(EXIT_FAILURE);
932  }
933  SVputfcn sv_put = getPutSV(sv_get);
934 
935  // Ok, go through the ionic models
936  for (int i_iif = 0; i_iif < this->N_IIF; i_iif++) {
937  IonIfBase *lIIF = NULL;
938  if (this->IIF[i_iif]->get_type() == *type) {
939  lIIF = this->IIF[i_iif];
940  }
941  else {
942  // Go through the plugins
943  for (auto& plugin : this->IIF[i_iif]->plugins()) {
944  if (plugin->get_type() == *type) {
945  lIIF = plugin;
946  break;
947  }
948  }
949  }
950 
951  if (lIIF == NULL) {
952  continue;
953  }
954 
955  for (size_t ii = 0; ii < indices.size(); ii++) {
956  GlobalData_t file_value = values[ii];
957 
958  int *target = static_cast<int*>(bsearch(&indices[ii], this->NodeLists[i_iif],
959  this->N_Nodes[i_iif], sizeof(int), int_cmp));
960  if (target) {
961  // We found a point to adjust! Do the adjustment.
962  num_changed++;
963  sv_put(*lIIF, (int)(target - this->NodeLists[i_iif]), sv_offset, file_value);
964  }
965  }
966 
967  }
968  free(my_variable);
969  }
970 
971  MPI_Allreduce(MPI_IN_PLACE, &num_changed, 1, MPI_INT, MPI_SUM, PETSC_COMM_WORLD);
972 
973  return num_changed;
974 } // adjust_MIIF_variables
975 
993 int determine_write_ranges(int N, size_t *offset, size_t bufsize, int **ranges) {
994  int nitems;
995 
996  if (!get_rank() ) {
997  Salt_list r; STRUCT_ZERO(r); r.chunk = 10;
998  int temp = 0;
999  SLIST_APPEND(&r, temp);
1000  long loff = offset[0];
1001  for (int i = 0; i < N; i++)
1002  if (offset[i]-loff > bufsize) {
1003  SLIST_APPEND(&r, i);
1004  loff = offset[i];
1005  }
1006 
1007  nitems = r.nitems;
1008  SLIST_APPEND(&r, N);
1009  *ranges = (int *)r.data;
1010  }
1011 
1012  MPI_Bcast(&nitems, 1, MPI_INT, 0, PETSC_COMM_WORLD);
1013  if (get_rank() )
1014  *ranges = static_cast<int *>(malloc( (nitems+1)*sizeof(int) ));
1015  MPI_Bcast(*ranges, nitems+1, MPI_INT, 0, PETSC_COMM_WORLD);
1016  return nitems;
1017 } // determine_write_ranges
1018 
1057 void MULTI_IF::dump_state(char *fname, float simtime, mesh_t gid,
1058  bool append, unsigned int revision)
1059 {
1060  float t0, t1;
1061  t0 = get_time();
1062 
1063  FILE_SPEC out = NULL;
1064  int rank = get_rank();
1065  int miif_node_gsize = get_global(this->numNode, MPI_SUM);
1066  int error = 0;
1067 
1068  log_msg(logger, 0, 0, "Saving state at time %f in file: %s", simtime, fname);
1069 
1070  if (rank == 0) {
1071  out = f_open(fname, append ? "a" : "w");
1072 
1073  if(out) {
1074  fseek(out->fd, 0, SEEK_END); // NOP call to sync disk data for switch to write mode
1075  write_bin_string(out, Magic_MIIF_ID);
1076 
1077  fwrite(&MIIF_Format, sizeof(unsigned int), 1, out->fd);
1078  fwrite(&revision, sizeof(unsigned int), 1, out->fd);
1079  time_t tm = time(NULL);
1080  fwrite(&tm, sizeof(time_t), 1, out->fd);
1081  fwrite(&simtime, sizeof(float), 1, out->fd);
1082  fwrite(&miif_node_gsize, sizeof(int), 1, out->fd);
1083 
1084  int num_gdata = 0;
1085  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++)
1086  if (this->gdata[i]) num_gdata++;
1087 
1088  fwrite(&num_gdata, sizeof(int), 1, out->fd);
1089  }
1090  else
1091  error++;
1092  }
1093 
1094 // #define CHATTY
1095 
1096  if(get_global(error, MPI_SUM))
1097  EXIT(EXIT_FAILURE);
1098 
1099  FILE* fd = rank == 0 ? out->fd : NULL;
1100  sf_vec* outVec;
1101 
1102  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++) {
1103  if (this->gdata[i]) {
1104  if(rank == 0) {
1105  write_bin_string(out, imp_data_names[i]);
1106 #ifdef CHATTY
1107  log_msg(NULL, 0, 0, "\tDumping %s at %d", imp_data_names[i], ftell(fd) );
1108 #endif
1109  }
1110 
1111  // If we have a proper set up parallel context, we pass a gid != unset_msh. Then
1112  // we can query petsc-to-canonical scattering. If we are in a simpler context
1113  // like bench, we assume that no scattering is needed and just shallow-copy the vector.
1114  if(gid != unset_msh) {
1115  SF::init_vector(&outVec, this->gdata[Vm]);
1117  assert(sc != NULL);
1118 
1119  sc->forward(*this->gdata[i], *outVec);
1120  outVec->write_binary<SF_real>(fd);
1121  delete outVec;
1122  }
1123  else {
1124  this->gdata[i]->write_binary<SF_real>(fd);
1125  }
1126  }
1127  }
1128 
1129  // output IMP region info
1130  // #regions and the IM and plugins for each region
1131  // determine memory requirements for each region
1132  int* imp_mem = new int[this->N_IIF];
1133  size_t *offset = NULL;
1134  long filepos = 0.;
1135 
1136  if (rank == 0) {
1137 #ifdef CHATTY
1138  log_msg(NULL, 0, 0, "\tDumping IMP sizes at %d", ftell(fd) );
1139 #endif
1140 
1141  int max = 1;
1142  fwrite(&this->N_IIF, sizeof(int), 1, fd); // #IIF's
1143 
1144  for (int i = 0; i < this->N_IIF; i++) {
1145  IonIfBase *imp = this->IIF[i];
1146  write_bin_string(out, imp->get_type().get_name().c_str());
1147 
1148  std::size_t sv_size = imp->get_sv_size();
1149  fwrite(&sv_size, sizeof(int), 1, fd);
1150  unsigned long n_plugins = (unsigned long) imp->plugins().size();
1151  fwrite(&n_plugins, sizeof(int), 1, fd);
1152  imp_mem[i] = imp->get_sv_size();
1153 
1154  for (auto& plug : imp->plugins()) {
1155  write_bin_string(out, plug->get_type().get_name().c_str());
1156 
1157  fwrite(&sv_size, sizeof(int), 1, fd);
1158  imp_mem[i] += plug->get_sv_size();
1159  }
1160  }
1161  filepos = ftell(fd);
1162  }
1163 
1164  MPI_Bcast(imp_mem, this->N_IIF, MPI_INT, 0, PETSC_COMM_WORLD);
1165 
1166  // If we have a proper set up parallel context, we pass a gid != unset_msh. Then
1167  // we can query a mesh and its parallel numbering. If we are in a simpler context
1168  // like bench, we build the numbering ourselves. Then, we assume that no
1169  // renumbering has taken place.
1170  SF::vector<int> loc2canon;
1171  if(gid != unset_msh) {
1172  const sf_mesh & mesh = get_mesh(gid);
1173  const SF::vector<mesh_int_t> & canon_nbr = mesh.get_numbering(SF::NBR_SUBMESH);
1174  const SF::vector<mesh_int_t> & alg_idx = mesh.pl.algebraic_nodes();
1175  loc2canon.resize(alg_idx.size());
1176 
1177  for(size_t i=0; i<alg_idx.size(); i++) loc2canon[i] = canon_nbr[alg_idx[i]];
1178  }
1179  else {
1180  int rank = get_rank();
1181  SF::vector<int> layout;
1182  layout_from_count(this->numNode, layout, PETSC_COMM_WORLD);
1183  loc2canon.resize(this->numNode);
1184 
1185  for (int i = 0; i < this->numNode; i++) loc2canon[i] = layout[rank] + i;
1186  }
1187 
1188  // write out IIF_Mask canonically ordered
1189  int *canord = new int[this->numNode];
1190  for (int i = 0; i < this->numNode; i++)
1191  canord[i] = loc2canon[i];
1192 
1193 #ifdef CHATTY
1194  log_msg(NULL, 0, 0, "\tDumping IMP masks at %d", filepos);
1195 #endif // ifdef CHATTY
1196 
1197  SF::root_write_ordered<int, IIF_Mask_t>(fd, canord, IIFmask, numNode, PETSC_COMM_WORLD);
1198 
1199  // for every local node, we compute the size of its ionic model
1200  // and plugins, and the associated displacments between the nodes
1201  SF::vector<int> impsize(numNode), impdsp;
1202  for(int i=0; i<numNode; i++)
1203  impsize[i] = imp_mem[(int)IIFmask[i]];
1204  SF::dsp_from_cnt(impsize, impdsp);
1205 
1206  // this also gives us the size of the total buffer
1207  size_t num_entr = SF::sum(impsize);
1208  SF::vector<char> impdata(num_entr);
1209 
1210  // now we can memcpy the ionic models from the IonIfBase arrays into our buffer
1211  for (int imp_idx = 0; imp_idx < this->N_IIF; imp_idx++)
1212  {
1213  IonIfBase* iif = this->IIF[imp_idx];
1214  // When using data layout optimization, checkpointing indices have to be
1215  // change a bit. Without DLO, this value is simply 1
1216  std::size_t vec_size = iif->get_type().dlo_vector_size();
1217  for (int imp_nod_idx = 0; imp_nod_idx < this->N_Nodes[imp_idx]; imp_nod_idx+=vec_size) {
1218  int index = imp_nod_idx / vec_size;
1219  int loc = this->NodeLists[imp_idx][imp_nod_idx];
1220  int svSize = iif->get_sv_size();
1221 
1222  char* write = impdata.data() + impdsp[loc];
1223  char* read = (char*) iif->get_sv_address() + index * svSize;
1224  memcpy(write, read, svSize);
1225 
1226  int shift = svSize;
1227  for (auto& plug : iif->plugins()) {
1228  int plugsize = plug->get_sv_size();
1229 
1230  write = impdata.data() + impdsp[loc] + shift;
1231  read = (char*) plug->get_sv_address() + index * plugsize;
1232  memcpy(write, read, plugsize);
1233  shift += plugsize;
1234  }
1235  }
1236  }
1237 
1238  // finally we can write out the IonIf data to disk
1239  SF::root_write_ordered<int, char>(fd, canord, impsize.data(), impdata.data(),
1240  numNode, num_entr, PETSC_COMM_WORLD);
1241  delete [] canord;
1242  delete [] imp_mem;
1243 
1244  if(fd) fclose(fd);
1245 
1246  double dump_time = timing(t1, t0);
1247  log_msg(logger, 0, 0, " in %.3f seconds.\n", dump_time);
1248 }
1249 
1272 float MULTI_IF::restore_state(const char *fname, mesh_t gid, bool close)
1273 {
1274  static FILE_SPEC in = NULL;
1275  static char *last_fn = NULL;
1276  double t0, t1;
1277 
1278  t0 = get_time();
1279  int error = 0;
1280  int my_rank = get_rank();
1281  int mpi_size = get_size();
1282 
1283  if (my_rank == 0) {
1284  if (fname) in = f_open(fname, "r");
1285 
1286  if (!in) {
1287  if (fname) log_msg(logger, 5, 0, "Error: cannot open file: %s\n", fname);
1288  else log_msg(logger, 5, 0, "Error: file stream not open yet");
1289  error++;
1290  }
1291  else {
1292  if (strcmp(read_bin_string(in), Magic_MIIF_ID) ) {
1293  log_msg(logger, 5, 0, "%s is not a recognized MIIF dump file", fname);
1294  error++;
1295  }
1296  }
1297  }
1298 
1299  if (get_global(error, MPI_SUM))
1300  EXIT(1);
1301 
1302  if (fname) {
1303  free(last_fn);
1304  last_fn = strdup(fname);
1305  }
1306  else {
1307  fname = last_fn;
1308  }
1309 
1310  unsigned int format, version;
1311  float time = 0.0f;
1312  time_t save_date;
1313  f_read_par(&format, sizeof(unsigned int), 1, in);
1314  f_read_par(&version, sizeof(unsigned int), 1, in);
1315  f_read_par(&save_date, sizeof(time_t), 1, in);
1316  f_read_par(&time, sizeof(float), 1, in);
1317  log_msg(logger, 0, 0, "Restoring time %f from %s (format v%d) generated\n\tby calling "
1318  "program r%d on %s", time, fname, format, version, ctime(&save_date) );
1319 
1320  int savedNum, glob_numNode;
1321  MPI_Allreduce(&this->numNode, &glob_numNode, 1, MPI_INT, MPI_SUM, PETSC_COMM_WORLD);
1322 
1323  f_read_par(&savedNum, sizeof(int), 1, in);
1324  if (savedNum != glob_numNode) {
1325  log_msg(logger, 5, 0, "expecting %d nodes but read %d nodes", glob_numNode, savedNum);
1326  EXIT(1);
1327  }
1328 
1329  int num_gdata;
1330  f_read_par(&num_gdata, sizeof(int), 1, in);
1331  SF::scattering & petsc2canon = *get_permutation(gid, PETSC_TO_CANONICAL, 1);
1332 
1333  for (int g = 0; g < num_gdata; g++) {
1334  char *datatype = read_bin_string_par(in);
1335  int i;
1336  for (i = 0; i < NUM_IMP_DATA_TYPES; i++) {
1337  if (!strcmp(datatype, imp_data_names[i]) ) {
1338  if (this->gdata[i]) {
1339  log_msg(logger, 0, 0, "\tRestoring global data %s", imp_data_names[i], datatype);
1340 
1341  FILE* fd = my_rank == 0 ? in->fd : NULL;
1342  this->gdata[i]->read_binary<SF_real>(fd);
1343  bool fwd = false;
1344  petsc2canon(*this->gdata[i], fwd);
1345  } else {
1346  log_msg(logger, 0, 0, "\tGlobal data %s not used", datatype);
1347  }
1348  break;
1349  }
1350  }
1351  if (i == NUM_IMP_DATA_TYPES)
1352  log_msg(logger, 3, 0, "\tSaved global data %s not recognized", datatype);
1353 
1354  if ( (my_rank == 0) && ((i == NUM_IMP_DATA_TYPES) || !this->gdata[i]) )
1355  fseek(in->fd, this->gdata[i]->gsize() * sizeof(SF_real), SEEK_CUR);
1356 
1357  free(datatype);
1358  }
1359 
1360  int N_IIF;
1361  f_read_par(&N_IIF, sizeof(int), 1, in);
1362 
1363  // read in the IMPs for each region
1364  IMPinfo *imps = static_cast<IMPinfo *>(calloc(N_IIF, sizeof(IMPinfo)));
1365  int *IMPsz = static_cast<int *>(malloc(N_IIF * sizeof(int)));
1366 
1367  for (int i = 0; i < N_IIF; i++) {
1368  imps[i].name = read_bin_string_par(in);
1369  f_read_par(&imps[i].sz, sizeof(int), 1, in);
1370  f_read_par(&imps[i].nplug, sizeof(int), 1, in);
1371 
1372  imps[i].offset = 0;
1373  IMPsz[i] = imps[i].sz;
1374  imps[i].plug = static_cast<IMPinfo *>(calloc(sizeof(IMPinfo), imps[i].nplug));
1375 
1376  for (int j = 0; j < imps[i].nplug; j++) {
1377  imps[i].plug[j].offset = IMPsz[i];
1378  imps[i].plug[j].name = read_bin_string_par(in);
1379  f_read_par(&imps[i].plug[j].sz, sizeof(int), 1, in);
1380  IMPsz[i] += imps[i].plug[j].sz;
1381  }
1382  }
1383 
1384  // compare new and old regions to determine which can be copied
1385  for (int i = 0; i < N_IIF; i++) {
1386  if (i >= this->N_IIF) {
1387  log_msg(NULL, 3, 0, "Saved IMP region %d out of range", i);
1388  continue;
1389  }
1390  if (strcmp(imps[i].name, this->IIF[i]->get_type().get_name().c_str()) ) {
1391  log_msg(NULL, 3, 0, "Saved IMP region %d ionic model does not match that of IMP region %d", i, i);
1392  continue;
1393  }
1394  if (imps[i].sz != this->IIF[i]->get_sv_size()) {
1395  log_msg(NULL, 3, 0, "Saved IMP region %d size does not match current IMP size", i);
1396  continue;
1397  }
1398 
1399  // the IM is good to go
1400  imps[i].compatible = true;
1401  for (int j = 0; j < imps[i].nplug; j++)
1402  for (int k = 0; k < this->IIF[i]->plugins().size(); k++)
1403  if (!strcmp(imps[i].plug[j].name, this->IIF[i]->plugins()[k]->get_type().get_name().c_str()) &&
1404  (imps[i].plug[j].sz == this->IIF[i]->plugins()[k]->get_sv_size()) ) {
1405  log_msg(NULL, 3, 0, "Saved IMP region %d plugin %s compatible", i, imps[i].plug[j].name);
1406  imps[i].plug[j].map = k;
1407  imps[i].plug[j].compatible = true;
1408  break;
1409  }
1410 
1411  }
1412 
1413  // read in region mask
1414  IIF_Mask_t *canMask = static_cast<char *>(malloc(glob_numNode*sizeof(this->IIFmask[0]) ));
1415  f_read_par(canMask, sizeof(IIF_Mask_t), glob_numNode, in);
1416 
1417  // determine relative IMP data offsets based on canonical ordering
1418  size_t *offset = static_cast<size_t *>(calloc(glob_numNode+1, sizeof(size_t)));
1419  for (int i = 1; i <= glob_numNode; i++)
1420  offset[i] = offset[i-1] + IMPsz[static_cast<int>(canMask[i-1])];
1421 
1422  // read in the state variable data
1423  // TODO: Aurel: Ideally rank0 would read in the data and communicate it, but right now I dont have
1424  // time to code this up. Thus, at least the ranks will read their data one by one so
1425  // we dont saturate the file system.
1426  long SVstart;
1427  if (my_rank == 0) {
1428  SVstart = ftell(in->fd);
1429  f_close(in);
1430  }
1431 
1432  MPI_Bcast(&SVstart, sizeof(long), MPI_BYTE, 0, PETSC_COMM_WORLD);
1433  int mismatch = 0;
1434 
1435  // we need to map from a local algebraic index to a global canonical index
1436  const sf_mesh & mesh = get_mesh(gid);
1437  const SF::vector<mesh_int_t> & canon_nbr = mesh.get_numbering(SF::NBR_SUBMESH);
1438  const SF::vector<mesh_int_t> & alg_idx = mesh.pl.algebraic_nodes();
1439  SF::vector<mesh_int_t> loc2canon(alg_idx.size());
1440 
1441  for(size_t i=0; i<alg_idx.size(); i++) loc2canon[i] = canon_nbr[alg_idx[i]];
1442 
1443  // the ranks read the file one-by-one
1444  for (int pid = 0; pid < mpi_size; pid++) {
1445  if (my_rank == pid) {
1446  if(in) delete in;
1447  in = f_open(fname, "r");
1448 
1449  for (int i = 0; i < N_IIF; i++) {
1450  fseek(in->fd, SVstart, SEEK_SET);
1451  mismatch += this->IIF[i]->restore(in, this->N_Nodes[i], this->NodeLists[i],
1452  canMask, offset, imps+i, loc2canon.data());
1453  }
1454 
1455  f_close(in);
1456  }
1457  MPI_Barrier(PETSC_COMM_WORLD);
1458  }
1459  free(canMask);
1460  free(IMPsz);
1461 
1462  MPI_Reduce(my_rank ? &mismatch : MPI_IN_PLACE, &mismatch, 1, MPI_INT, MPI_SUM, 0, PETSC_COMM_WORLD);
1463  if ( (my_rank == 0) && mismatch)
1464  log_msg(NULL, 3, 0, "Number of nonmatching nodes: %d", mismatch);
1465 
1466  if ( (my_rank == 0) && (!close) ) {
1467  if(in) delete in;
1468  in = f_open(fname, "r");
1469 
1470  // position at end of current MIIF data
1471  fseek(in->fd, SVstart+offset[glob_numNode], SEEK_SET);
1472  }
1473 
1474  free(offset);
1475  for (int i = 0; i < N_IIF; i++)
1476  free(imps[i].plug);
1477  free(imps);
1478 
1479  double restore_time = timing(t1, t0);
1480  log_msg(logger, 0, 0, "State restored from file %s in %.3f seconds.\n", fname, restore_time);
1481 
1482  return time;
1483 }
1484 
1496 void MULTI_IF::sv_dump_add_by_name_list( int region, char *imp_name,
1497  char *reg_name, char *sv_lst, char *plg_lst,
1498  char *plg_sv_lst, double t, double dump_dt) {
1499  char file[8000], svs[1024], plgs[1024], plgsvs[1024];
1500  char *e, *l, *p, *svnames, *plgnames, *plgsvnames;
1501 
1502  strcpy(svs, sv_lst);
1503  strcpy(plgs, plg_lst);
1504  strcpy(plgsvs, plg_sv_lst);
1505  svnames = &svs[0];
1506  plgnames = &plgs[0];
1507  plgsvnames = &plgsvs[0];
1508 
1509  // override default setting for dump interval
1510  this->svd.intv = dump_dt;
1511 
1512  // override default start time for dumping (in case of a restart)
1513  this->svd.t_dump = t;
1514 
1515  // parse ionic model sv list
1516  while ( (e = get_next_list(svnames, ',') ) ) {
1517  snprintf(file, sizeof file, "%s.%s.bin", reg_name, svnames);
1518  this->sv_dump_add_by_name(region, imp_name, svnames, reg_name, file);
1519  svnames = e;
1520  }
1521 
1522  // Information is provided as follows:
1523  // region[X].plugin = "PLG_A:PLG_B"
1524  // region[X].plug_sv_dumps = "m,h,n:x,y"
1525 
1526  // parse plugin list
1527  while ( (p = get_next_list(plgnames, ':') ) ) {
1528  // get sv list corresponding to current plugin
1529  l = get_next_list(plgsvnames, ':');
1530  while ( (e = get_next_list(plgsvnames, ',') ) ) {
1531  snprintf(file, sizeof file, "%s_%s.%s.bin", reg_name, plgnames, plgsvnames);
1532  this->sv_dump_add_by_name(region, plgnames, plgsvnames, reg_name, file);
1533  plgsvnames = e;
1534  }
1535  plgnames = p;
1536  plgsvnames = l;
1537  }
1538 } // sv_dump_add_by_name_list
1539 
1555 void MULTI_IF::sv_dump_add(int region, const IonType& type, int offset, int size, int dtype,
1556  const char *filename, const char *regname) {
1557  IonIfBase *IF = this->IIF[region];
1558  int n = this->svd.n;
1559 
1560  // find the proper IMP
1561  if (IF->get_type() != type) {
1562  int i;
1563  for (i = 0; i < IF->plugins().size(); i++)
1564  if (IF->plugins()[i]->get_type() == type)
1565  break;
1566  if (i == IF->plugins().size()) {
1567  log_msg(logger, 2, 0, "Warning: IMP %s not found in Region %s\n",
1568  type.get_name().c_str(), regname);
1569  return;
1570  } else {
1571  IF = IF->plugins()[i];
1572  }
1573  }
1574 
1575  this->svd.active = 1;
1576  this->svd.n++;
1577  this->svd.hdls = static_cast<FILE_SPEC *>(realloc(this->svd.hdls, this->svd.n*sizeof(this->svd.hdls[0])));
1578  this->svd.fn = static_cast<char **>(realloc(this->svd.fn, this->svd.n*sizeof(char *)));
1579  this->svd.reg = static_cast<int *>(realloc(this->svd.reg, this->svd.n*sizeof(int)));
1580  this->svd.svnames = static_cast<char **>(realloc(this->svd.svnames, this->svd.n*sizeof(char *)));
1581  this->svd.n_dumps = 0;
1582  this->svd.nwr = 0;
1583  this->svd.offset = static_cast<int *>(realloc(this->svd.offset, this->svd.n*sizeof(int)));
1584  this->svd.size = static_cast<int *>(realloc(this->svd.size, this->svd.n*sizeof(int)));
1585  this->svd.dlo_vs = static_cast<int *>(realloc(this->svd.dlo_vs, this->svd.n*sizeof(int)));
1586  this->svd.dtype = static_cast<int *>(realloc(this->svd.dtype, this->svd.n*sizeof(int)));
1587  this->svd.svtab = static_cast<void **>(realloc(this->svd.svtab, this->svd.n*sizeof(void *)));
1588  this->svd.svsize = static_cast<size_t *>(realloc(this->svd.svsize, this->svd.n*sizeof(size_t)));
1589  this->svd.num = static_cast<int *>(realloc(this->svd.num, this->svd.n*sizeof(int)));
1590  this->svd.reg = static_cast<int *>(realloc(this->svd.reg, this->svd.n*sizeof(int)));
1591 
1592  if (!get_rank() ) {
1593 #ifdef HDF5
1594  assert(0);
1595 #else // ifdef HDF5
1596  this->svd.hdls[n] = f_open(filename, "w+");
1597 #endif // ifdef HDF5
1598  } else {
1599  this->svd.hdls[n] = NULL;
1600  }
1601 
1602  this->svd.fn[n] = dupstr(filename);
1603  this->svd.reg[n] = region;
1604  this->svd.svnames[n] = NULL; // has to be filled in in calling routine
1605  this->svd.offset[n] = offset;
1606  this->svd.size[n] = size;
1607  this->svd.dtype[n] = dtype;
1608  this->svd.svtab[n] = IF->get_sv_address();
1609  this->svd.svsize[n] = IF->get_sv_size();
1610  this->svd.num[n] = IF->get_num_node();
1611  this->svd.dlo_vs[n] = IF->get_type().dlo_vector_size();
1612 } // sv_dump_add
1613 
1624 int MULTI_IF::sv_dump_add_by_name(int region, char *impname,
1625  char *svname, char *regname, char *filename) {
1626  int offset, size, dtype, added = 0;
1627  char *svtypename = NULL;
1628  char *filename_bin;
1629 
1630  IonType* type = get_ion_type(std::string(impname));
1631  filename_bin = strcat(filename, ".bin");
1632 
1633  assert(type != NULL);
1634  if (type->get_sv_offset(svname, &offset, &size) ) {
1635  type->get_sv_type(svname, &dtype, &svtypename);
1636  this->sv_dump_add(region, *type, offset, size, dtype, filename_bin, regname);
1637  this->svd.svnames[this->svd.n-1] = dupstr(svname);
1638  added = 1;
1639  } else {
1640  log_msg(NULL, 1, 0, "No state variable added to dump list");
1641  }
1642 
1643  return added;
1644 }
1645 
1658 {
1659  assert(miif->gdata[Vm] != NULL);
1660 
1661  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++) {
1662  for (int n = 0; n < miif->N_IIF; n++)
1663  if (USED_DAT(miif->IIF[n], imp_data_flag[i]) && (miif->gdata[i] == NULL)) {
1664  SF::init_vector(&miif->gdata[i], miif->gdata[Vm]);
1665  break;
1666  }
1667  }
1668 }
1669 
1678  int num_mech_data = 0;
1679 
1680  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++) {
1681  if ( (Lambda_DATA_FLAG == i) || (delLambda_DATA_FLAG == i) ||
1682  (Tension_DATA_FLAG == i) || (tension_component_DATA_FLAG == i) ) {
1683  for (int n = 0; n < this->N_IIF; n++)
1684  if (USED_DAT(this->IIF[n], imp_data_flag[i]) )
1685  num_mech_data++;
1686  }
1687  }
1688  return static_cast<bool>(num_mech_data);
1689 }
1690 
1708 void MULTI_IF::transmem_stim_species(float charge, const char *species,
1709  float beta, int *node, int numnode) {
1710  float z;
1711  if (strstr(species, "Ca") != NULL) {
1712  charge *= 1000; // Ca uses micromolar instead of millimolar
1713  z = 2;
1714  } else if (strstr(species, "Na") != NULL) {
1715  z = 1;
1716  } else if (strstr(species, "Cl") != NULL) {
1717  z = -1;
1718  } else if (strstr(species, "K") != NULL) {
1719  z = 1;
1720  } else {
1721  log_msg(logger, 5, 0, "Unimplemented ion species: %s\n", species);
1722  exit(1);
1723  }
1724  int offset, sz;
1725  SVgetfcn ion_get;
1726  SVputfcn ion_put;
1727  static int warned = 0;
1728 
1729  int rank = get_rank();
1730  SF::vector<int> layout;
1731  layout_from_count(numnode, layout, PETSC_COMM_WORLD);
1732  int my_low_idx = layout[rank], my_high_idx = layout[rank+1];
1733 
1734  for (int i = 0; i < numnode; i++) {
1735  if ((node[i] < my_low_idx) || (node[i] >= my_high_idx)) continue; // not on processor
1736 
1737  for (int j = 0; j < this->N_IIF; j++) {
1738  if (this->NodeLists[j] == NULL) continue; // no stim nodes on processor
1739 
1740  if ((node[i] < this->NodeLists[j][0]) ||
1741  (node[i] > this->NodeLists[j][this->N_Nodes[j]-1]) ) {
1742  continue; // not in this IMP
1743  } else {
1744  ion_get = this->IIF[j]->get_type().get_sv_offset(species, &offset, &sz);
1745  if (ion_get == NULL) {
1746  if (!warned) {
1747  warned = 1;
1748  log_msg(logger, 2, 0, "Ion species not present in ionic model: %s\n",
1749  species);
1750  }
1751  return;
1752  }
1753  ion_put = getPutSV(ion_get);
1754  }
1755 
1756  // does this model provide a correct conversion factor?
1757  double delta_conc;
1758  cell_geom g = this->IIF[j]->cgeom();
1759  if (g.sl_i2c != NDEF) {
1760  delta_conc = charge*g.sl_i2c/z;
1761  } else {
1762  // conversion with generic factor
1763  delta_conc = charge*beta/FARADAY/z*1.e-1; // convert to millimolar/L
1764  }
1765 
1766  for (int k = 0; k < this->N_Nodes[j]; k++)
1767  if (node[i] == this->NodeLists[j][k])
1768  ion_put(*this->IIF[j], k, offset, ion_get(*this->IIF[j], k, offset)-delta_conc);
1769  }
1770  }
1771 } // transmem_stim_species
1772 
1779 void MULTI_IF::MIIF_change_dt( double Dt) {
1780  this->dt = Dt;
1781  for (int i = 0; i < this->N_IIF; i++) {
1782  this->IIF[i]->destroy_luts();
1783  this->IIF[i]->set_dt((float) Dt);
1784  this->iontypes[i].get().construct_tables(*this->IIF[i]);
1785 
1786  for (int j = 0; j < this->IIF[i]->plugins().size(); j++) {
1787  auto& plugin = this->IIF[i]->plugins()[j];
1788  plugin->destroy_luts();
1789  plugin->set_dt((float) Dt);
1790  this->plugtypes[i][j].get().construct_tables(*plugin);
1791  }
1792  }
1793 }
1794 
1795 #define MEMFREE(A) free(A)
1796 
1804  assert(m->doppel);
1805 
1806  for (int i = 0; i < m->N_IIF; i++) {
1807  m->IIF[i]->get_type().destroy_ion_if(m->IIF[i]);
1808  }
1809 
1810  for (int j = 0; j < NUM_IMP_DATA_TYPES; j++) {
1811  if (m->gdata[j])
1812  delete m->gdata[j];
1813  }
1814 
1815  m->zero_data();
1816 }
1817 
1818 #undef MEMFREE
1819 
1827 void doppel_update(MULTI_IF *orig, MULTI_IF *miif_doppel) {
1828  // removed assertion, need to be able to update bidirectionally
1829  // assert(doppel->doppel);
1830 
1831  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++)
1832  if (orig->gdata[i])
1833  SF::init_vector(&miif_doppel->gdata[i], orig->gdata[i]);
1834 
1835  for (int i = 0; i < orig->N_IIF; i++) {
1836  miif_doppel->IIF[i]->copy_SVs_from(*orig->IIF[i], false);
1837 
1838  for (int j = 0; j < orig->IIF[i]->plugins().size(); j++)
1839  miif_doppel->IIF[i]->plugins()[j]->copy_SVs_from(*orig->IIF[i]->plugins()[j], false);
1840  }
1841 
1842  miif_doppel->getRealData();
1843 }
1844 
1855 void doppel_MIIF(MULTI_IF *orig, MULTI_IF *miif_doppel) {
1856  *miif_doppel = *orig;
1857  miif_doppel->doppel = true;
1858 
1859  // deal with the local portion of the global data
1860  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++)
1861  if (orig->gdata[i])
1862  SF::init_vector(&miif_doppel->gdata[i], orig->gdata[i]);
1863 
1864  // copy the state variables
1865  miif_doppel->IIF = {};
1866 
1867  for (int i = 0; i < orig->N_IIF; i++) {
1868  // Make a new IonIf for this clone and copy state variables and plugins
1869  miif_doppel->IIF.push_back(orig->IIF[i]->get_type().make_ion_if(orig->IIF[i]->get_target(),
1870  orig->IIF[i]->get_num_node(), orig->plugtypes[i]));
1871  miif_doppel->IIF[i]->copy_SVs_from(*orig->IIF[i], true);
1872  miif_doppel->IIF[i]->copy_plugins_from(*orig->IIF[i]);
1873  }
1874 }
1875 
1883 bool isIMPdata(const char *sv) {
1884  return IMPdataLabel2Index(sv) != -1;
1885 }
1886 
1894 int IMPdataLabel2Index(const char *sv) {
1895  int imp_data_id = -1;
1896 
1897  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++) {
1898  if (strcmp(sv, imp_data_names[i]) == 0) {
1899  imp_data_id = i;
1900  break;
1901  }
1902  }
1903  return imp_data_id;
1904 }
1905 
1906 /* copy all of the IMP data from one node to another
1907  *
1908  * \param IF ionic model
1909  * \param from local IMP number
1910  * \param to local IMP number
1911  */
1912 void dup_IMP_node_state(IonIfBase& IF, int from, int to, GlobalData_t **localdata) {
1913  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++)
1914  if (localdata && localdata[i])
1915  localdata[i][to] = localdata[i][from];
1916 
1917  IF.for_each([&](IonIfBase& imp) {
1918  char **sv_list;
1919  int sv_list_size = imp.get_type().get_sv_list(&sv_list);
1920 
1921  for (int i = 0; i < sv_list_size; i++) {
1922  char *sv_name = sv_list[i];
1923  int sv_offset;
1924  int sv_size;
1925  SVgetfcn sv_get = imp.get_type().get_sv_offset(sv_name, &sv_offset, &sv_size);
1926  if (sv_get == NULL) {
1927  throw std::runtime_error(std::string(__func__) + " error: " + sv_name + " is not a valid state variable for the " + imp.get_type().get_name() + " model.");
1928  }
1929  SVputfcn sv_put = getPutSV(sv_get);
1930 
1931  GlobalData_t sv_val = sv_get(imp, from, sv_offset);
1932  sv_put(imp, to, sv_offset, sv_val);
1933  }
1934  free(sv_list);
1935  });
1936 }
1937 
1938 } // namespace limpet
constexpr T min(T a, T b)
Definition: ion_type.h:33
GlobalData_t(* SVgetfcn)(IonIfBase &, int, int)
Definition: ion_type.h:48
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.
int ** NodeLists
local partitioned node lists for each IMP stored
Definition: MULTI_ION_IF.h:201
int node_idx
local node number
Definition: MULTI_ION_IF.h:187
opencarp::FILE_SPEC file
Definition: MULTI_ION_IF.h:189
double Real
Definition: MULTI_ION_IF.h:151
const std::string & get_name() const
Gets the model name.
Definition: ion_type.cc:23
The mesh storage class. It contains both element and vertex data.
Definition: SF_container.h:383
bool compatible
does IM match stored IM
Definition: ION_IF.h:130
virtual void release_ptr(S *&p)=0
#define FLUSH
Definition: basics.h:311
void releaseRealDataDuringInit()
char * dupstr(const char *old_str)
Definition: basics.cc:44
Trace_Info * trace_info
Information about traces.
Definition: MULTI_ION_IF.h:207
virtual void add_scaled(const abstract_vector< T, S > &vec, S k)=0
void close_trace(MULTI_IF *MIIF)
int * offset
offsets into structure for SV
Definition: MULTI_ION_IF.h:171
int n_dumps
keep track of number of dumped time slices
Definition: MULTI_ION_IF.h:169
void local_petsc_to_nodal_mapping(const meshdata< T, S > &mesh, index_mapping< T > &petsc_to_nodal)
#define NDEF
definition of cell geometry
Definition: ION_IF.h:112
SV_DUMP svd
state variable dump
Definition: MULTI_ION_IF.h:203
void layout_from_count(const T count, vector< T > &layout, MPI_Comm comm)
Definition: SF_network.h:201
GlobalData_t * procdata[NUM_IMP_DATA_TYPES]
data for this processor
Definition: MULTI_ION_IF.h:204
std::vector< std::reference_wrapper< IonType > > IonTypeList
Definition: ion_type.h:289
int nplug
number of plugins
Definition: ION_IF.h:128
constexpr T max(T a, T b)
Definition: ion_type.h:31
void sv_dump_add_by_name_list(int, char *, char *, char *, char *, char *, double, double)
#define USED_DAT(I, F)
Definition: MULTI_ION_IF.h:301
#define PETSC_TO_CANONICAL
Permute algebraic data from PETSC to canonical ordering.
Definition: sf_interface.h:74
void CreateIIFNodeLsts_(int, char *, int **, int ***, int)
const IonType & get_type() const
Gets this IMP&#39;s model type.
Definition: ION_IF.cc:144
IonIfBase * parent() const
Gets the parent IMP.
Definition: ION_IF.cc:176
void compute_ionic_current(bool flag_send=1, bool flag_receive=1)
GPU kernel to emulate the add_scaled call made to adjust the Vm values when the update to Vm is not m...
void doppel_MIIF(MULTI_IF *orig, MULTI_IF *miif_doppel)
T backward_map(T idx) const
Map one index from b to a.
Definition: SF_container.h:262
overlapping_layout< T > pl
nodal parallel layout
Definition: SF_container.h:417
bool found
found on this node
Definition: MULTI_ION_IF.h:185
IIF_Mask_t * IIFmask
region for each node
Definition: MULTI_ION_IF.h:214
void dump_luts_MIIF(bool)
#define STRUCT_ZERO(S)
Definition: basics.h:51
void f_read_par(void *ptr, size_t size, size_t nmemb, FILE_SPEC stream, MPI_Comm comm)
Parallel fread. Root reads, then broadcasts.
Definition: basics.cc:171
void(* SVputfcn)(IonIfBase &, int, int, GlobalData_t)
Definition: ion_type.h:49
int sv_dump_add_by_name(int, char *, char *, char *, char *)
bool doppel
is this a shallow clone?
Definition: MULTI_ION_IF.h:199
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
std::string name
name for MIIF region
Definition: MULTI_ION_IF.h:198
virtual int get_sv_type(const char *svname, int *type, char **type_name) const =0
Determines the type of a SV.
float restore_state(const char *, opencarp::mesh_t gid, bool)
void allocate_shared_data(MULTI_IF *)
void sv_dump_add(int, const IonType &, int, int, int, const char *, const char *)
IMPinfo * plug
plugins
Definition: ION_IF.h:129
data structure to manage trace dumps. Should eventually be combined with the state variable dumps...
Definition: MULTI_ION_IF.h:184
SVputfcn getPutSV(SVgetfcn)
int * reg
array to store region ids
Definition: MULTI_ION_IF.h:165
IonTypeList iontypes
type for each region
Definition: MULTI_ION_IF.h:212
virtual std::size_t get_sv_size() const =0
Gets the size of the structure this IMP uses for state variables.
T * data()
Pointer to the vector&#39;s start.
Definition: SF_vector.h:91
float current_global_time()
int map
which plugin does this IMO match
Definition: ION_IF.h:131
mesh_t
The enum identifying the different meshes we might want to load.
Definition: sf_interface.h:58
int * size
sizes of SV to dump
Definition: MULTI_ION_IF.h:172
int get_plug_flag(char *plgstr, int *out_num_plugins, IonTypeList &out_plugins)
std::vector< IonTypeList > plugtypes
plugins types for each region
Definition: MULTI_ION_IF.h:215
int N_IIF
how many different IIF&#39;s
Definition: MULTI_ION_IF.h:211
void initialize_currents(double, int)
bool is_gpu(Target const target)
Checks if this is a GPU target.
Definition: target.cc:64
void write_bin_string(FILE_SPEC out, const char *s)
Definition: basics.cc:219
int sz
storage required
Definition: ION_IF.h:127
int should_print_bounds_exceeded_messages()
int * num
number of nodes
Definition: MULTI_ION_IF.h:174
#define SLIST_APPEND(S, P)
Definition: basics.h:52
void init_vector(SF::abstract_vector< T, S > **vec)
Definition: SF_init.h:99
bool * contiguous
whether a region is contiguously numbered
Definition: MULTI_ION_IF.h:206
std::vector< IonIfBase * > IIF
array of IIF&#39;s
Definition: MULTI_ION_IF.h:202
int set_print_bounds_exceeded_messages(int newval)
void alloc_MIIF(MULTI_IF *pMIIF)
char * read_bin_string_par(FILE_SPEC in)
Definition: basics.cc:239
void ** svtab
state variable tables
Definition: MULTI_ION_IF.h:175
void MIIF_change_dt(double)
long nwr
keep track of number of written tokens
Definition: MULTI_ION_IF.h:170
int numSubDt
number of sub-dt time steps
Definition: MULTI_ION_IF.h:220
void doppel_update(MULTI_IF *orig, MULTI_IF *miif_doppel)
virtual S * ptr()=0
void initialize_ionic_IF(MULTI_IF *pMIIF)
void dump_state(char *, float, opencarp::mesh_t gid, bool, unsigned int)
#define FARADAY
Faraday&#39;s constant.
Definition: MULTI_ION_IF.h:147
int mesh_int_t
Definition: SF_container.h:46
Define multiple ionic models to be used in different regions.
char ** fn
array to store file names
Definition: MULTI_ION_IF.h:164
Submesh nodal numbering: The globally ascending sorted reference indices are reindexed.
Definition: SF_container.h:190
Represents the ionic model and plug-in (IMP) data structure.
Definition: ION_IF.h:138
IonType * get_ion_type(const std::string &name)
size_t * svsize
state variable sizes
Definition: MULTI_ION_IF.h:176
int current_global_node(int local_node)
GlobalData_t *** ldata
data local to each IMP
Definition: MULTI_ION_IF.h:205
void forward(abstract_vector< T, S > &in, abstract_vector< T, S > &out, bool add=false)
Forward scattering.
float sl_i2c
convert sl-currents in uA/cm^2 to mM/L without valence
Definition: ION_IF.h:118
virtual void set(const vector< T > &idx, const vector< S > &vals, const bool additive=false)=0
void free_doppel(MULTI_IF *m)
virtual int get_sv_list(char ***list) const =0
Returns a list of SVs.
void compute(int start, int end, GlobalData_t **data)
Perform ionic model computation for 1 time step.
Definition: ION_IF.cc:270
sf_mesh & get_mesh(const mesh_t gt)
Get a mesh by specifying the gridID.
Definition: sf_interface.cc:33
virtual size_t dlo_vector_size() const =0
Gets the vector size when using data layout optimization (DLO).
char IIF_Mask_t
Definition: ion_type.h:50
size_t write_binary(FILE *fd)
Write a vector to HD in binary. File descriptor is already set up.
size_t read_binary(FILE *fd)
char * read_bin_string(FILE_SPEC in)
Definition: basics.cc:228
char * get_next_list(char *lst, char delimiter)
Definition: ION_IF.cc:512
int IMPdataLabel2Index(const char *sv)
int int_cmp(const void *a, const void *b)
bool in_b(const T idx)
return whether idx is in set B
Definition: SF_container.h:354
SF::abstract_vector< SF_int, SF_real > sf_vec
Definition: sf_interface.h:49
void CreateIIFLocalNodeLsts(MULTI_IF *pMIIF)
void get_time(double &tm)
Definition: basics.h:436
ts & get_tstp()
Gets the time stepper.
Definition: ION_IF.cc:208
int getGlobalNodalIndex(IonIfBase &pIF, int relIdx)
T sum(const vector< T > &vec)
Compute sum of a vector&#39;s entries.
Definition: SF_vector.h:340
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
void open_trace(MULTI_IF *MIIF, int n_traceNodes, int *traceNodes, int *label, opencarp::sf_mesh *imesh)
Set up ionic model traces at some global node numbers.
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
int * N_Nodes
#nodes for each IMP
Definition: MULTI_ION_IF.h:200
void freeIMPData(MULTI_IF *pMIIF)
V timing(V &t2, const V &t1)
Definition: basics.h:448
int dump_svs(opencarp::base_timer *)
void transmem_stim_species(float, const char *, float, int *, int)
int determine_write_ranges(int N, size_t *offset, size_t bufsize, int **ranges)
opencarp::sf_vec * gdata[NUM_IMP_DATA_TYPES]
data used by all IMPs
Definition: MULTI_ION_IF.h:216
opencarp::FILE_SPEC * hdls
array of file handles to sv output files
Definition: MULTI_ION_IF.h:163
int offset
offset into node data
Definition: ION_IF.h:132
void dup_IMP_node_state(IonIfBase &IF, int from, int to, GlobalData_t **localdata)
void for_each(const std::function< void(IonIfBase &)> &consumer)
Executes the consumer functions on this IMP and each of its plugins.
Definition: ION_IF.cc:417
size_t size() const
The current size of the vector.
Definition: SF_vector.h:104
void initializeIMPData(MULTI_IF *pMIIF)
void initialize_params_MIIF(MULTI_IF *pMIIF)
baseline CPU model generated with the original opencarp code generator
Definition: target.h:48
void log_msg(FILE_SPEC out, int level, unsigned char flag, const char *fmt,...)
Definition: basics.cc:72
#define FILENAME_BUF
double intv
time interval for sv dumps
Definition: MULTI_ION_IF.h:167
int get_num_node() const
Gets the number of nodes handled by this IMP.
Definition: ION_IF.cc:148
int cnt
Definition: ION_IF.h:86
std::vector< IonIfBase * > & plugins()
Returns a vector containing the plugins of this IMP.
Definition: ION_IF.cc:184
void update_ts(ts *ptstp)
Definition: ION_IF.cc:466
virtual void * get_sv_address()=0
Gets the raw address of the state variables for this IMP.
char * name
IMP name.
Definition: ION_IF.h:126
void dump_trace(MULTI_IF *MIIF, limpet::Real time)
#define fmemopen_
Definition: basics.h:433
std::vector< Target > targets
target for each region
Definition: MULTI_ION_IF.h:213
int numNode
local number of nodes
Definition: MULTI_ION_IF.h:210
void f_close(FILE_SPEC &f)
Close a FILE_SPEC.
Definition: basics.cc:162
bool isIMPdata(const char *)
#define LOCAL
Definition: basics.h:309
bool ignored
globally not found
Definition: MULTI_ION_IF.h:186
opencarp::FILE_SPEC logger
Definition: MULTI_ION_IF.h:217
double dt
time step (ms)
Definition: MULTI_ION_IF.h:219
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
int miifIdx
imp index within miif
Definition: ION_IF.h:150
bool extUpdateVm
flag indicating update function for Vm
Definition: MULTI_ION_IF.h:208
void CreateIIFGlobalNodeLsts(MULTI_IF *pMIIF)
char ** svnames
array to store sv names
Definition: MULTI_ION_IF.h:166
int * dtype
data type
Definition: MULTI_ION_IF.h:173
file_desc * FILE_SPEC
Definition: basics.h:138
void resize(size_t n)
Resize a vector.
Definition: SF_vector.h:209
int adjust_MIIF_variables(const char *variable, const SF::vector< SF_int > &indices, const SF::vector< SF_real > &values)
int get_size(MPI_Comm comm=PETSC_COMM_WORLD)
Definition: basics.h:290
bool is_plugin() const
Returns whether this model is a plugin or not.
Definition: ion_type.cc:27
#define MAX_TRACE_LINE_LEN
virtual SVgetfcn get_sv_offset(const char *svname, int *off, int *sz) const =0
Get the offset and size of a state variable of the model, as well as an access function.
void deallocate_on_target(Target target, T *ptr)
Utility function for deallocating memory on a target. See TargetAllocator.
Definition: target.h:318
char * get_sv(void *tab, int offset, int n, int svSize, int size, int dlo_vector_size)
void dsp_from_cnt(const vector< T > &cnt, vector< T > &dsp)
Compute displacements from counts.
Definition: SF_vector.h:310
SF::meshdata< mesh_int_t, mesh_real_t > sf_mesh
Definition: sf_interface.h:47
int get_rank(MPI_Comm comm=PETSC_COMM_WORLD)
Definition: basics.h:276
SF_real GlobalData_t
Definition: limpet_types.h:27
FILE_SPEC f_open(const char *fname, const char *mode)
Open a FILE_SPEC.
Definition: basics.cc:135
Container for a PETSc VecScatter.