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 "mpi_utils.h"
60 #include <stdio.h>
61 #ifdef HAS_CUDA_MODEL
62 #include <cuda_runtime.h>
63 #endif
64 #if defined HAS_ROCM_MODEL && defined __HIP__
65 #include <hip/hip_runtime.h>
66 #endif
67 
68 #include "SF_init.h" // for SF::init_xxx()
69 #include "petsc_utils.h" // TODO for EXIT
70 
71 namespace limpet {
72 
73 using ::opencarp::base_timer;
85 using ::opencarp::Salt_list;
90 #ifdef USE_FMEM_WRAPPER
92 #endif
93 
94 /*
95  PROTOTYPES
96  */
97 void initialize_params_MIIF(MULTI_IF *pMIIF);
98 void initialize_ionic_IF(MULTI_IF *pMIIF);
99 void CreateIIFLocalNodeLsts(MULTI_IF *pMIIF);
101 void alloc_MIIF(MULTI_IF *pMIIF);
102 void initializeIMPData(MULTI_IF *pMIIF);
103 void freeIMPData(MULTI_IF *pMIIF);
104 void allocate_shared_data(MULTI_IF *);
105 node_index_t getGlobalNodalIndex(IonIfBase& pIF, node_index_t relIdx);
107 bool isIMPdata(const char *);
108 
109 static MULTI_IF *gMIIF_Error_Recovery;
110 static int current_IIF_index;
111 static float current_time = 0;
112 static float start_time = 0;
113 
114 static void prepare_error_recovery(MULTI_IF *MIIF, int IIF_index, float time) {
115  gMIIF_Error_Recovery = MIIF;
116  current_IIF_index = IIF_index;
117  current_time = start_time+time;
118 }
119 
121  return gMIIF_Error_Recovery->NodeLists[current_IIF_index][local_node];
122 }
123 
125  return current_time;
126 }
127 
128 static int g_print_bounds_exceeded = 1;
129 
131  return g_print_bounds_exceeded;
132 }
133 
135  int oldval = g_print_bounds_exceeded;
136 
137  g_print_bounds_exceeded = newval;
138  return oldval;
139 }
140 
144  CreateIIFNodeLsts_(pMIIF->N_IIF, pMIIF->IIFmask, &pMIIF->N_Nodes,
145  &pMIIF->NodeLists, pMIIF->numNode);
146 }
147 
159 void CreateIIFNodeLsts_(int N_IIF, IIF_Mask_t *IIF_Mask, node_count_t **N_Nodes,
160  node_index_t ***NodeLists, node_count_t numNode)
161 {
162  /* create node lists for each IIF
163  NodeNum: store number of nodes for each IIF
164  NodeLst: store node indices of each IIF
165  */
166  node_count_t *NodeNum = static_cast<node_count_t *>(calloc(N_IIF, sizeof(node_count_t)));
167  node_index_t **NodeLst = static_cast<node_index_t **>(calloc(N_IIF, sizeof(node_index_t *)));
168 
169  // determine number of nodes of each IIF
170  for (node_index_t i = 0; i < numNode; i++)
171  NodeNum[static_cast<int>(IIF_Mask[i])]++;
172 
173  /* now we know the number of nodes per IIF and store the indices of
174  nodes belonging to a particular IIF into a list.
175  */
176  for (int i = 0; i < N_IIF; i++) {
177  if (NodeNum[i] > 0)
178  NodeLst[i] = static_cast<node_index_t *>(malloc(sizeof(node_index_t) * NodeNum[i]));
179  else
180  NodeLst[i] = NULL;
181  }
182 
183  // just store indices relative to low
184  for (int j = 0; j < N_IIF; j++) {
185  node_count_t hcount = 0;
186  for (node_index_t i = 0; i < numNode; i++)
187  if (IIF_Mask[i] == j)
188  NodeLst[j][hcount++] = i;
189  }
190 
191  for(int j=0; j<N_IIF; j++)
192  std::sort(NodeLst[j], NodeLst[j]+NodeNum[j]);
193 
194  *N_Nodes = NodeNum;
195  *NodeLists = NodeLst;
196 
197 } // CreateIIFNodeLsts_
198 
199 
208  // get pointer to parent MIIF
209  MULTI_IF *pMIIF = (MULTI_IF *) pIF.parent(); // TODO check?!?!?!?!?!
210 
211  if (pIF.get_type().is_plugin())
212  pMIIF = (MULTI_IF *) pIF.parent()->parent();
213 
214  return pMIIF->NodeLists[pIF.miifIdx][rIdx];
215 }
216 
223 void alloc_MIIF(MULTI_IF *pMIIF) {
224  int N_IIF = pMIIF->N_IIF;
225 
226  pMIIF->contiguous = static_cast<bool *>(calloc(N_IIF, sizeof(bool)));
227 #if 0
228  pMIIF->ldata = (GlobalData_t***) build_matrix_ns<GlobalData_t>(N_IIF, NUM_IMP_DATA_TYPES, sizeof(GlobalData_t **), Target::CPU);
229 #else
230  pMIIF->ldata = allocate_on_target<GlobalData_t**>(Target::CPU, N_IIF);
231  for (std::size_t i = 0; i < pMIIF->N_IIF; i++) {
232  pMIIF->ldata[i] = allocate_on_target<GlobalData_t*>(pMIIF->iontypes[i].get().select_target(pMIIF->targets[i]), NUM_IMP_DATA_TYPES);
233  }
234 #endif
235 
236  for (int i = 0; i < pMIIF->N_IIF; i++) {
237  // Create an IonIf object on the chosen target
238  pMIIF->IIF.push_back(pMIIF->iontypes[i].get().make_ion_if(pMIIF->targets[i], pMIIF->N_Nodes[i], pMIIF->plugtypes[i]));
239  // this is questionable. A IIF does not have a parent, parent should be NULL
240  // It might make sense in some situtation to be able to refer to the MIIF
241  // structure, but pIF->parent is of type IonIf and not MULTI_IF!!!!
242  pMIIF->IIF[i]->set_parent((IonIfBase *)pMIIF);
243 
244  // set unique miifIdx for imp and plugins to enable refering back
245  // to global entities such as global node index from within imp
246  pMIIF->IIF[i]->for_each([&](IonIfBase& IF) { IF.miifIdx = i; });
247  }
248 } // alloc_MIIF
249 
255  for (auto& imp : pMIIF->IIF) {
256  imp->initialize_params();
257  }
258 }
259 
264  pMIIF->getRealData(); // needed for initializing Vm, etc.
265  for (int i = 0; i < pMIIF->IIF.size(); i++)
266  pMIIF->IIF[i]->initialize(pMIIF->dt, pMIIF->ldata[i]);
267 
268  pMIIF->releaseRealDataDuringInit();
269 }
270 
277  // we need to copy all global data which has been set, even if it is not
278  // listed as moddat. This is the case with Vm which may not be modified by
279  // LIMPET but is initialized by LIMPET
280  unsigned int *moddat = static_cast<unsigned int *>(calloc(this->N_IIF, sizeof(unsigned int)));
281 
282  for (int i = 0; i < this->N_IIF; i++) {
283  moddat[i] = this->IIF[i]->get_moddat();
284  this->IIF[i]->set_moddat(moddat[i] | this->IIF[i]->get_reqdat()); // add in the reqdat
285  }
286  this->releaseRealData();
287  for (int i = 0; i < this->N_IIF; i++) {
288  this->IIF[i]->set_moddat(moddat[i]); // remove the reqdat
289  }
290  free(moddat);
291 }
292 
298 {
300  alloc_MIIF(this);
302 
303  memset(&svd, 0, sizeof(SV_DUMP) );
304  svd.active = 0;
305  svd.intv = 1.0; // 1. ms by default
306 }
307 
308 #define FILENAME_BUF 1024
309 
311 int node_index_cmp(const void *a, const void *b) {
312  const node_index_t lhs = *static_cast<const node_index_t *>(a);
313  const node_index_t rhs = *static_cast<const node_index_t *>(b);
314  return (lhs > rhs) - (lhs < rhs);
315 }
316 
344 void open_trace(MULTI_IF *MIIF, int n_traceNodes, int *traceNodes, int *label, opencarp::sf_mesh* imesh)
345 {
346  if (!n_traceNodes) return;
347 
348  if (n_traceNodes > 1000)
349  log_msg(0, 4, 0, "%s warning: %d trace nodes may impact performance", __func__, n_traceNodes);
350 
351  MIIF->trace_info = (Trace_Info *)calloc(n_traceNodes+1, sizeof(Trace_Info));
352  MIIF->trace_info[n_traceNodes].region = -1; // end of list marker
353 
354  // bijective index mapping between set A (local petsc indexing) and set B (local nodal indexing)
356  if(imesh)
357  SF::local_petsc_to_nodal_mapping(*imesh, petsc2nod);
358 
359  for (int iTrace = 0; iTrace < n_traceNodes; iTrace++) {
360  mesh_int_t lnode = imesh ? imesh->pl.localize(traceNodes[iTrace]) : traceNodes[iTrace];
361 
362  // here we check if lnode is in set B (i.e. local nodal indexing)
363  if(imesh) {
364  // here we check if lnode is in set B (i.e. local nodal indexing)
365  if(petsc2nod.in_b(lnode) == false) continue;
366 
367  // lnode is local nodal, we map it to local petsc
368  lnode = petsc2nod.backward_map(lnode);
369  }
370 
371  for (int iRegion = 0; iRegion < MIIF->N_IIF; iRegion++)
372  {
373  node_index_t local_node = static_cast<node_index_t>(lnode);
374  auto target = static_cast<node_index_t *>(bsearch(&local_node, MIIF->NodeLists[iRegion],
375  MIIF->N_Nodes[iRegion], sizeof(node_index_t), node_index_cmp));
376 
377  if (target != NULL ) {
378  node_index_t idx = target - MIIF->NodeLists[iRegion];
379  Trace_Info *trace_info = MIIF->trace_info+iTrace;
380  trace_info->found = true;
381  trace_info->region = iRegion;
382  trace_info->node_idx = idx;
383  }
384  }
385  }
386 
387  for (int iTrace = 0; iTrace < n_traceNodes; iTrace++) {
388  if(get_global(int(MIIF->trace_info[iTrace].found), MPI_SUM) == 0) {
389  MIIF->trace_info[iTrace].ignored = true;
390  log_msg(0,4,0, "trace node %d not found", traceNodes[iTrace]);
391  continue;
392  }
393 
394  char traceName[FILENAME_BUF];
395  snprintf(traceName, sizeof traceName, "Trace_%d.dat", label ? label[iTrace] : traceNodes[iTrace]);
396  MIIF->trace_info[iTrace].file = f_open(traceName, "w");
397  }
398 } // open_trace
399 
414 void dump_trace(MULTI_IF *MIIF, limpet::Real time) {
415  if (MIIF->trace_info == NULL)
416  return;
417 
418 // struct exception_type e;
419 
420 #define MAX_TRACE_LINE_LEN 8196
421 
422  std::vector<IonIfBase*>& IIF = MIIF->IIF;
423  FILE *fs;
424 
425  for (int iTrace = 0; MIIF->trace_info[iTrace].region >= 0; iTrace++) {
426  Trace_Info *ctrace = MIIF->trace_info+iTrace;
427 
428  if (ctrace->ignored)
429  continue;
430 
431  if (ctrace->found) {
432  // The buffer is printed with %s, so it has to be zeroed for every line: the
433  // BSD fmemopen_ fallback does not append a terminating null byte on close.
434  char trace_buf[MAX_TRACE_LINE_LEN] = {0};
435 
436  fs = fmemopen_(trace_buf, MAX_TRACE_LINE_LEN, "w");
437  fprintf(fs, "%4.10f\t", time);
438  if (IIF[ctrace->region]->get_type().has_trace()) {
439  IIF[ctrace->region]->get_type().trace(*IIF[ctrace->region], ctrace->node_idx,
440  fs, MIIF->ldata[ctrace->region]);
441  }
442  for (auto& plugin : IIF[ctrace->region]->plugins()) {
443  if (plugin->get_type().has_trace()) {
444  fprintf(fs, "\t");
445  plugin->get_type().trace(
446  *plugin, ctrace->node_idx,
447  fs, MIIF->ldata[ctrace->region]);
448  }
449  }
450  fprintf(fs, "\n");
451  fclose(fs);
452 
453  fprintf(ctrace->file->fd, "%s", trace_buf);
454  }
455  fflush(ctrace->file->fd);
456  }
457 } // dump_trace
458 
459 void close_trace(MULTI_IF *MIIF) {
460  if (!MIIF->trace_info) return;
461 
462  for (int iTrace = 0; MIIF->trace_info[iTrace].region >= 0; iTrace++)
463  f_close(MIIF->trace_info[iTrace].file);
464 
465  free(MIIF->trace_info);
466  MIIF->trace_info = NULL;
467 }
468 
475 void MULTI_IF::initialize_currents(double idt, int subDt) {
476  numSubDt = subDt;
477  dt = idt/this->numSubDt;
478 
479  allocate_shared_data(this);
480  initializeIMPData(this);
481  initialize_ionic_IF(this);
482 }
483 
490 void MULTI_IF::dump_luts_MIIF(bool zipped) {
491  // if one of the IFs in MIIF does not live in partion 0
492  // we won't get a LUT dumped.
493  if (get_rank()) return;
494 
495  std::vector<IonIfBase*>& pIF = this->IIF;
496  for (auto& IF : pIF) {
497  int ndmps = IF->dump_luts(zipped);
498  if (ndmps < IF->tables().size()) {
499  log_msg(logger, 4, 0, "LUT dump error %s: only %d out of %d LUTs dumped.\n",
500  IF->get_type().get_name().c_str(), ndmps, IF->tables().size());
501  }
502  for (auto& plugin : IF->plugins()) {
503  ndmps = plugin->dump_luts(zipped);
504  if (ndmps < plugin->tables().size()) {
505  log_msg(logger, 4, 0, "LUT dump error %s: only %d out of %d LUTs dumped.\n",
506  plugin->get_type().get_name().c_str(), ndmps, IF->tables().size());
507  }
508  }
509  }
510 }
511 
517  if (get_rank()) return;
518 
519  // close files
520  for (int i = 0; i < svd.n; i++)
521 #ifndef USE_HDF5
522  if (svd.hdls[i] != NULL)
523 #endif // ifndef USE_HDF5
524  f_close(svd.hdls[i]);
525 }
526 
537 char *get_sv(void *tab, int offset, node_count_t n, int svSize, int size, int dlo_vector_size) {
538  char *buf = static_cast<char *>(malloc(static_cast<size_t>(n)*size));
539  char *bp = buf;
540  char *p = static_cast<char *>(tab) + offset;
541 
542  for (node_index_t i = 0; i < n; i += dlo_vector_size) {
543  node_count_t dlo_array_size = min(static_cast<node_count_t>(dlo_vector_size), n - i);
544  memcpy(bp, p, size * dlo_array_size);
545  bp += size * dlo_array_size;
546  p += svSize;
547  }
548  return buf;
549 }
550 
558 size_t MULTI_IF::dump_svs(base_timer *iot) {
559  size_t nwr = 0;
560 
561  if (iot->triggered) {
562  for (int i = 0; i < svd.n; i++) {
563  nwr = 0;
564 
565  FILE* fd = svd.hdls[i] ? svd.hdls[i]->fd : NULL;
566  char *buf = get_sv(svd.svtab[i], svd.offset[i], svd.num[i], svd.svsize[i], svd.size[i], svd.dlo_vs[i]);
567  nwr += SF::root_write<char>(fd, (char*) buf,
568  static_cast<size_t>(svd.size[i]) * static_cast<size_t>(svd.num[i]),
569  PETSC_COMM_WORLD);
570  free(buf);
571  }
572  svd.nwr += static_cast<long>(nwr);
573  svd.n_dumps++;
574  }
575 
576  return nwr;
577 }
578 
584 #ifdef HAS_GPU_MODEL
585 __global__
586 void update_vm(node_index_t start, node_index_t end, double *vm, double *ion, double dt)
587 {
588  node_index_t i = blockIdx.x*blockDim.x + threadIdx.x;
589  if (i < end)
590  vm[i] = vm[i] + (ion[i] * (-dt));
591 }
592 #endif
593 
594 // * compute what has to be computed, current or otherwise
595 void MULTI_IF::compute_ionic_current(bool flag_send, bool flag_receive)
596 {
597  gdata[Iion]->set(0.0);
598  if (flag_send == 1) {
599  this->getRealData();
600  }
601 
602  for (int j = 0; j < this->numSubDt; j++)
603  {
604  for (int i = 0; i < this->N_IIF; i++)
605  {
606  IonIfBase* pIF = this->IIF[i];
607  if (!this->N_Nodes[i]) continue;
608 
609  prepare_error_recovery(this, i, pIF->get_tstp().cnt * this->dt);
610  update_ts(&pIF->get_tstp());
611 
612  node_index_t current = 0;
613  do {
614  try {
615  pIF->compute(current, this->N_Nodes[i], this->ldata[i]);
616  current = this->N_Nodes[i];
617  }
618  catch(int e) { //FIXME
619  if(e==-1) {
620  // CHECK this->NodeLists[i][e.node], e.node);
621  fprintf(stderr, "LIMPET compute fail in %s at node %jd (local %jd)! Aborting!\n",
622  this->name.c_str(), printable_int(this->NodeLists[i][j]), printable_int(j));
623  exit(1);
624  }
625  current++;
626  }
627  } while (current < this->N_Nodes[i]);
628 
629  for (auto& plugin : pIF->plugins()) {
630  current = 0;
631  update_ts(&plugin->get_tstp());
632 
633  do {
634  try {
635  plugin->compute(current, this->N_Nodes[i], this->ldata[i]);
636  current = this->N_Nodes[i];
637 
638  if (plugin == nullptr)
639  throw -1;
640  }
641  catch(int e) { //FIXME
642  if(e==-1) {
643  // CHECK this->NodeLists[i][e.node], e.node);
644  fprintf(stderr, "LIMPET plugin fail in %s at node %jd (local %jd)! Aborting!\n",
645  this->name.c_str(), printable_int(this->NodeLists[i][j]), printable_int(j));
646  exit(1);
647  }
648  current++;
649  }
650  } while (current < this->N_Nodes[i]);
651  }
652  }
653 
654  #ifdef HAS_GPU_MODEL
655  //TODO: extUpdateVm can be true outside of the bench executable ! But this should
656  // only run in bench! This should be done for each model according to its target
657  if(!extUpdateVm && is_gpu(this->targets[0])) {
658 
659  #if defined __CUDA__ || defined __HIP__
660  update_vm<<<(this->N_Nodes[0]/64)+1,64>>>(0, this->N_Nodes[0], this->ldata[0][Vm], this->ldata[0][Iion], this->dt);
661  #ifdef __CUDA__
662  cudaDeviceSynchronize();
663  #elif defined __HIP__
664  hipDeviceSynchronize();
665 #endif
666  #else
667  fprintf(stderr, "GPU/CUDA not found");
668  #endif
669  }
670  #endif
671 
672  if (flag_receive == 1) {
673  this->releaseRealData();
674  }
675 
676  // TODO: Target should be checked for each region
677  if(!extUpdateVm && !is_gpu(this->targets[0]))
678  gdata[Vm]->add_scaled(*gdata[Iion], SF_real(-dt));
679  }
680 }
681 
683  assert(!this->doppel);
684 
685  // Free data first since this function needs to access memory in the IIFs
686  freeIMPData(this);
687  for (auto& pIF : this->IIF) {
688  pIF->get_type().destroy_ion_if(pIF);
689  }
690 }
691 
699  for (int i = 0; i < pMIIF->N_IIF; i++) {
700  if (!pMIIF->N_Nodes[i])
701  continue;
702 
703  // if memory contiguous, we don't need to copy data, merely pass pointers
704  pMIIF->contiguous[i] = pMIIF->N_Nodes[i]-1 ==
705  pMIIF->NodeLists[i][pMIIF->N_Nodes[i]-1]-pMIIF->NodeLists[i][0];
706 
707  for (int j = 0; j < NUM_IMP_DATA_TYPES; j++) {
708  // check if it is used by the IMPs
709  if (!USED_DAT(pMIIF->IIF[i], imp_data_flag[j]) )
710  continue;
711 
712  // allocate mem buffer only if not contiguous or if data is on GPU
713  if (!pMIIF->contiguous[i] || is_gpu(pMIIF->IIF[i]->get_target()))
714  pMIIF->ldata[i][j] =
715  allocate_on_target<GlobalData_t>(pMIIF->IIF[i]->get_target(),
716  pMIIF->N_Nodes[i]);
717  // check if data supplied
718  if (pMIIF->gdata[j] == NULL) {
719  log_msg(pMIIF->logger, 5, LOCAL, "IMP data type %s not supplied for region %d", imp_data_names[j], i);
720  exit(1);
721  }
722  }
723  }
724 } // initializeIMPData
725 
735  // set rdata in the parent vector to point to the
736  // local data to be computed on the local processor
737  for (int j = 0; j < NUM_IMP_DATA_TYPES; j++) {
738  SF_real* rdata;
739  if (this->gdata[j] != NULL) {
740  rdata = this->gdata[j]->ptr();
741  this->procdata[j] = rdata;
742  }
743  else continue;
744 
745  // now map local data so that each ionic model can use it
746  // if noncontiguous in the parent vector, copy to contiguous local vector
747  for (int i = 0; i < this->N_IIF; i++) {
748  if (!this->N_Nodes[i] || !USED_DAT(this->IIF[i], imp_data_flag[j]) )
749  continue;
750 
751  if (this->contiguous[i] && !is_gpu(this->IIF[i]->get_target()))
752  this->ldata[i][j] = static_cast<GlobalData_t *>(rdata) + this->NodeLists[i][0];
753  else {
754  const node_index_t* ip = this->NodeLists[i];
755  for (node_index_t k = 0; k < this->N_Nodes[i]; k++)
756  this->ldata[i][j][k] = rdata[ip[k]];
757  }
758  }
759  }
760 } // getRealData
761 
771 
772  for (int i = 0; i < this->N_IIF; i++) {
773  if (!this->N_Nodes[i]) continue;
774 
775  // Every external variable used by the model must be copied on GPU
776  if (is_gpu(this->IIF[i]->get_target())) {
777  for (int j = 0; j < NUM_IMP_DATA_TYPES; j++) {
778  if (!USED_DAT(this->IIF[i], imp_data_flag[j]) )
779  continue;
780  if (this->contiguous[i])
781  memcpy(&this->procdata[j][this->NodeLists[i][0]], this->ldata[i][j], sizeof(this->ldata[i][j][0])*this->N_Nodes[i]);
782  else {
783  for (node_index_t 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  else {
789  for (int j = 0; j < NUM_IMP_DATA_TYPES; j++) {
790  if (this->IIF[i]->get_moddat() & imp_data_flag[j]) {
791  if (!this->contiguous[i]) {
792  for (node_index_t k = 0; k < this->N_Nodes[i]; k++)
793  this->procdata[j][this->NodeLists[i][k]] = this->ldata[i][j][k];
794  }
795  }
796  }
797  }
798  }
799 
800  for (int j = 0; j < NUM_IMP_DATA_TYPES; j++)
801  if (this->gdata[j] != NULL)
802  this->gdata[j]->release_ptr(this->procdata[j]);
803 }
804 
805 // * free the local storage vectors
806 void freeIMPData(MULTI_IF *pMIIF) {
807  for (int j = 0; j < NUM_IMP_DATA_TYPES; j++) {
808  if (pMIIF->gdata[j] != NULL) {
809  for (int i = 0; i < pMIIF->N_IIF; i++)
810  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) {
811  deallocate_on_target<GlobalData_t>(pMIIF->IIF[i]->get_target(),
812  pMIIF->ldata[i][j]);
813  }
814  delete pMIIF->gdata[j];
815  }
816  }
817  free(pMIIF->contiguous);
819 }
820 
830 int get_plug_flag(char *plgstr, int *out_num_plugins, IonTypeList& out_plugins) {
831  if (plgstr == NULL) {
832  *out_num_plugins = 0;
833  out_plugins.clear();
834  return 1;
835  }
836 
837  // maximum number of plugins is 200 apparently
838  char *plugspec = dupstr(plgstr);
839  char *saveptr;
840  char *token = tokstr_r(plugspec, ":", &saveptr);
841 
842  *out_num_plugins = 0;
843  while (token) {
844  IonType* type = get_ion_type(std::string(token));
845  if (type == NULL) {
846  free(plugspec);
847  return 0;
848  } else {
849  out_plugins.push_back(*type);
850  }
851  token = tokstr_r(NULL, ":", &saveptr);
852  }
853 
854  *out_num_plugins = out_plugins.size();
855 
856  free(plugspec);
857  return 1;
858 } // get_plug_flag
859 
878 int MULTI_IF::adjust_MIIF_variables(const char* variable,
879  const SF::vector<SF_int> & indices,
880  const SF::vector<SF_real> & values)
881 {
882  int num_changed = 0;
883 
884  // determine if we're dealing with an external variable or a state variable.
885  if (index(variable, '.') == NULL) {
886  // external variable, the easy case.
887  // which external variable?
888  int data_id = -1;
889  for (int ii = 0; ii < NUM_IMP_DATA_TYPES; ii++) {
890  if (strcmp(variable, imp_data_names[ii]) == 0) {
891  data_id = ii;
892  break;
893  }
894  }
895  if (data_id == -1) {
896  log_msg(logger, 5, FLUSH, "Error! No external variable named %s in this build of openCARP.", variable);
897  exit(EXIT_FAILURE);
898  }
899 
900  // make sure that external variable is being used.
901  if (this->gdata[data_id] == NULL) {
902  log_msg(logger, 4, 0,
903  "External variable %s is not being used this processor.\n"
904  "Is this really what you meant to do?\n"
905  "Perhaps you don't have the correct Ionic models selected.",
906  imp_data_names[data_id]);
907  }
908  else {
909  // fill in the external variable with the data from the file.
910  SF_real *raw_data = this->gdata[data_id]->ptr();
911 
912  for (size_t i = 0; i < indices.size(); i++) {
913  raw_data[indices[i]] = values[i];
914  num_changed++;
915  }
916 
917  this->gdata[data_id]->release_ptr(raw_data);
918  }
919  }
920  else {
921  // state variable.
922  // extract the IMP name and state variable name from the string.
923  char *saveptr;
924  char *my_variable = dupstr(variable);
925  char *IIF_name = tokstr_r(my_variable, ".", &saveptr);
926  char *sv_name = tokstr_r(NULL, ".", &saveptr);
927 
928  IonType* type = get_ion_type(std::string(IIF_name));
929  if (type == NULL) {
930  log_msg(logger, 5, 0, "%s error: %s is not a valid IMP name.", __func__, IIF_name);
931  exit(EXIT_FAILURE);
932  }
933 
934  int sv_offset;
935  int sv_size;
936  SVgetfcn sv_get = type->get_sv_offset(sv_name, &sv_offset, &sv_size);
937  if (sv_get == NULL) {
938  log_msg(logger, 5, 0, "%s error: %s is not a valid state variable for the %s model.",
939  __func__, sv_name, IIF_name);
940  exit(EXIT_FAILURE);
941  }
942  SVputfcn sv_put = getPutSV(sv_get);
943 
944  // Ok, go through the ionic models
945  for (int i_iif = 0; i_iif < this->N_IIF; i_iif++) {
946  IonIfBase *lIIF = NULL;
947  if (this->IIF[i_iif]->get_type() == *type) {
948  lIIF = this->IIF[i_iif];
949  }
950  else {
951  // Go through the plugins
952  for (auto& plugin : this->IIF[i_iif]->plugins()) {
953  if (plugin->get_type() == *type) {
954  lIIF = plugin;
955  break;
956  }
957  }
958  }
959 
960  if (lIIF == NULL) {
961  continue;
962  }
963 
964  for (size_t ii = 0; ii < indices.size(); ii++) {
965  GlobalData_t file_value = values[ii];
966 
967  node_index_t local_node = static_cast<node_index_t>(indices[ii]);
968  node_index_t *target = static_cast<node_index_t*>(bsearch(&local_node, this->NodeLists[i_iif],
969  this->N_Nodes[i_iif], sizeof(node_index_t), node_index_cmp));
970  if (target) {
971  // We found a point to adjust! Do the adjustment.
972  num_changed++;
973  sv_put(*lIIF, target - this->NodeLists[i_iif], sv_offset, file_value);
974  }
975  }
976 
977  }
978  free(my_variable);
979  }
980 
981  MPI_Allreduce(MPI_IN_PLACE, &num_changed, 1, MPI_INT, MPI_SUM, PETSC_COMM_WORLD);
982 
983  return num_changed;
984 } // adjust_MIIF_variables
985 
1003 int determine_write_ranges(int N, size_t *offset, size_t bufsize, int **ranges) {
1004  int nitems;
1005 
1006  if (!get_rank() ) {
1007  Salt_list r; STRUCT_ZERO(r); r.chunk = 10;
1008  int temp = 0;
1009  SLIST_APPEND(&r, temp);
1010  long loff = offset[0];
1011  for (int i = 0; i < N; i++)
1012  if (offset[i]-loff > bufsize) {
1013  SLIST_APPEND(&r, i);
1014  loff = offset[i];
1015  }
1016 
1017  nitems = r.nitems;
1018  SLIST_APPEND(&r, N);
1019  *ranges = (int *)r.data;
1020  }
1021 
1022  MPI_Bcast(&nitems, 1, MPI_INT, 0, PETSC_COMM_WORLD);
1023  if (get_rank() )
1024  *ranges = static_cast<int *>(malloc( (nitems+1)*sizeof(int) ));
1025  MPI_Bcast(*ranges, nitems+1, MPI_INT, 0, PETSC_COMM_WORLD);
1026  return nitems;
1027 } // determine_write_ranges
1028 
1067 void MULTI_IF::dump_state(char *fname, float simtime, mesh_t gid,
1068  bool append, unsigned int revision)
1069 {
1070  float t0, t1;
1071  t0 = get_time();
1072 
1073  FILE_SPEC out = NULL;
1074  int rank = get_rank();
1075  global_node_index_t miif_node_gsize = get_global(static_cast<global_node_index_t>(this->numNode), MPI_SUM);
1076  int error = 0;
1077 
1078  // EMI ionic state lives on mesh elements (membrane faces), electrics on nodes.
1079  // Pick the matching canonical permutation and numbering from the data layout.
1080  const bool elemwise = this->gdata[Vm] && this->gdata[Vm]->layout == sf_vec::elemwise;
1081  const int canon_perm = elemwise ? ELEM_PETSC_TO_CANONICAL : PETSC_TO_CANONICAL;
1082 
1083  log_msg(logger, 0, 0, "Saving state at time %f in file: %s", simtime, fname);
1084 
1085  if (rank == 0) {
1086  out = f_open(fname, append ? "a" : "w");
1087 
1088  if(out) {
1089  fseek(out->fd, 0, SEEK_END); // NOP call to sync disk data for switch to write mode
1090  write_bin_string(out, Magic_MIIF_ID);
1091 
1092  fwrite(&MIIF_Format, sizeof(unsigned int), 1, out->fd);
1093  fwrite(&revision, sizeof(unsigned int), 1, out->fd);
1094  time_t tm = time(NULL);
1095  fwrite(&tm, sizeof(time_t), 1, out->fd);
1096  fwrite(&simtime, sizeof(float), 1, out->fd);
1097  fwrite(&miif_node_gsize, sizeof(global_node_index_t), 1, out->fd);
1098 
1099  int num_gdata = 0;
1100  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++)
1101  if (this->gdata[i]) num_gdata++;
1102 
1103  fwrite(&num_gdata, sizeof(int), 1, out->fd);
1104  }
1105  else
1106  error++;
1107  }
1108 
1109 // #define CHATTY
1110 
1111  if(get_global(error, MPI_SUM))
1112  EXIT(EXIT_FAILURE);
1113 
1114  FILE* fd = rank == 0 ? out->fd : NULL;
1115  sf_vec* outVec;
1116 
1117  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++) {
1118  if (this->gdata[i]) {
1119  if(rank == 0) {
1120  write_bin_string(out, imp_data_names[i]);
1121 #ifdef CHATTY
1122  log_msg(NULL, 0, 0, "\tDumping %s at %d", imp_data_names[i], ftell(fd) );
1123 #endif
1124  }
1125 
1126  // If we have a proper set up parallel context, we pass a gid != unset_msh. Then
1127  // we can query petsc-to-canonical scattering. If we are in a simpler context
1128  // like bench, we assume that no scattering is needed and just shallow-copy the vector.
1129  if(gid != unset_msh) {
1130  SF::init_vector(&outVec, this->gdata[Vm]);
1131  SF::scattering* sc = get_permutation(gid, canon_perm, 1);
1132  assert(sc != NULL);
1133 
1134  sc->forward(*this->gdata[i], *outVec);
1135  outVec->write_binary<SF_real>(fd);
1136  delete outVec;
1137  }
1138  else {
1139  this->gdata[i]->write_binary<SF_real>(fd);
1140  }
1141  }
1142  }
1143 
1144  // output IMP region info
1145  // #regions and the IM and plugins for each region
1146  // determine memory requirements for each region
1147  int* imp_mem = new int[this->N_IIF];
1148  size_t *offset = NULL;
1149  long filepos = 0.;
1150 
1151  if (rank == 0) {
1152 #ifdef CHATTY
1153  log_msg(NULL, 0, 0, "\tDumping IMP sizes at %d", ftell(fd) );
1154 #endif
1155 
1156  int max = 1;
1157  fwrite(&this->N_IIF, sizeof(int), 1, fd); // #IIF's
1158 
1159  for (int i = 0; i < this->N_IIF; i++) {
1160  IonIfBase *imp = this->IIF[i];
1161  write_bin_string(out, imp->get_type().get_name().c_str());
1162 
1163  // record the de-interleaved per-node size (format 3), not the whole SoA block
1164  int sv_size = (int) imp->get_sv_per_node_size();
1165  fwrite(&sv_size, sizeof(int), 1, fd);
1166  // fingerprint of the SV layout, so a restart against a changed model revision
1167  // is detected instead of silently misread field-for-field
1168  uint64_t sv_fp = imp->sv_fingerprint();
1169  fwrite(&sv_fp, sizeof(uint64_t), 1, fd);
1170  unsigned long n_plugins = (unsigned long) imp->plugins().size();
1171  fwrite(&n_plugins, sizeof(int), 1, fd);
1172  imp_mem[i] = sv_size;
1173 
1174  for (auto& plug : imp->plugins()) {
1175  write_bin_string(out, plug->get_type().get_name().c_str());
1176 
1177  int plug_size = (int) plug->get_sv_per_node_size();
1178  fwrite(&plug_size, sizeof(int), 1, fd);
1179  uint64_t plug_fp = plug->sv_fingerprint();
1180  fwrite(&plug_fp, sizeof(uint64_t), 1, fd);
1181  imp_mem[i] += plug_size;
1182  }
1183  }
1184  filepos = ftell(fd);
1185  }
1186 
1187  MPI_Bcast(imp_mem, this->N_IIF, MPI_INT, 0, PETSC_COMM_WORLD);
1188 
1189  // If we have a proper set up parallel context, we pass a gid != unset_msh. Then
1190  // we can query a mesh and its parallel numbering. If we are in a simpler context
1191  // like bench, we build the numbering ourselves. Then, we assume that no
1192  // renumbering has taken place.
1194  if(gid != unset_msh) {
1195  const sf_mesh & mesh = get_mesh(gid);
1196  if(elemwise) {
1197  const SF::vector<mesh_int_t> & canon_nbr = mesh.get_numbering(SF::NBR_ELEM_SUBMESH);
1198  loc2canon.resize(canon_nbr.size());
1199 
1200  for(size_t i=0; i<canon_nbr.size(); i++) loc2canon[i] = canon_nbr[i];
1201  }
1202  else {
1203  const SF::vector<mesh_int_t> & canon_nbr = mesh.get_numbering(SF::NBR_SUBMESH);
1204  const SF::vector<mesh_int_t> & alg_idx = mesh.pl.algebraic_nodes();
1205  loc2canon.resize(alg_idx.size());
1206 
1207  for(size_t i=0; i<alg_idx.size(); i++) loc2canon[i] = canon_nbr[alg_idx[i]];
1208  }
1209  }
1210  else {
1211  int rank = get_rank();
1213  SF::layout_from_count<global_node_index_t>(static_cast<global_node_index_t>(this->numNode),
1214  layout, PETSC_COMM_WORLD);
1215  loc2canon.resize(this->numNode);
1216 
1217  for (node_index_t i = 0; i < this->numNode; i++) loc2canon[i] = layout[rank] + i;
1218  }
1219 
1220  // loc2canon must hold one canonical index per local node; a shorter array
1221  // would read out of bounds below and write a corrupt checkpoint
1222  assert(loc2canon.size() == static_cast<size_t>(this->numNode));
1223 
1224  // write out IIF_Mask canonically ordered
1225  global_node_index_t *canord = new global_node_index_t[this->numNode];
1226  for (node_index_t i = 0; i < this->numNode; i++)
1227  canord[i] = loc2canon[i];
1228 
1229 #ifdef CHATTY
1230  log_msg(NULL, 0, 0, "\tDumping IMP masks at %d", filepos);
1231 #endif // ifdef CHATTY
1232 
1233  SF::root_write_ordered<global_node_index_t, IIF_Mask_t>(fd, canord, IIFmask, numNode, PETSC_COMM_WORLD);
1234 
1235  // for every local node, we compute the size of its ionic model
1236  // and plugins, and the associated displacments between the nodes
1237  SF::vector<global_node_index_t> impsize(numNode), impdsp;
1238  for(node_index_t i=0; i<numNode; i++)
1239  impsize[i] = imp_mem[(int)IIFmask[i]];
1240  SF::dsp_from_cnt(impsize, impdsp);
1241 
1242  // this also gives us the size of the total buffer
1243  size_t num_entr = static_cast<size_t>(SF::sum(impsize));
1244  SF::vector<char> impdata(num_entr);
1245 
1246  // De-interleave each node's state into a contiguous per-node record (format 3).
1247  // The state lives as data-layout-optimized SoA blocks: node imp_nod_idx is lane
1248  // (imp_nod_idx % vec_size) of block (imp_nod_idx / vec_size). We gather that lane
1249  // of every state-variable field, keyed by the node's own canonical id (via loc).
1250  // This makes the dump independent of the partitioning. Without DLO vec_size is 1
1251  // and this reduces to copying each node's contiguous state.
1252  for (int imp_idx = 0; imp_idx < this->N_IIF; imp_idx++)
1253  {
1254  IonIfBase* iif = this->IIF[imp_idx];
1255 
1256  // field layouts are model properties: gather them once, not per node
1257  std::vector<std::pair<int, int>> main_fields;
1258  iif->get_sv_layout(main_fields);
1259 
1260  std::vector<std::vector<std::pair<int, int>>> plug_fields;
1261  for (auto& plug : iif->plugins()) {
1262  plug_fields.emplace_back();
1263  plug->get_sv_layout(plug_fields.back());
1264  }
1265 
1266  for (node_index_t imp_nod_idx = 0; imp_nod_idx < this->N_Nodes[imp_idx]; imp_nod_idx++) {
1267  node_index_t loc = this->NodeLists[imp_idx][imp_nod_idx];
1268 
1269  char* write = impdata.data() + impdsp[loc];
1270  auto gather = [&](char* src, std::size_t sz) { memcpy(write, src, sz); write += sz; };
1271 
1272  iif->for_each_sv_field(imp_nod_idx, main_fields, gather);
1273  for (size_t p = 0; p < plug_fields.size(); p++)
1274  iif->plugins()[p]->for_each_sv_field(imp_nod_idx, plug_fields[p], gather);
1275  }
1276  }
1277 
1278  // finally we can write out the IonIf data to disk
1279  SF::root_write_ordered<global_node_index_t, char>(fd, canord, impsize.data(), impdata.data(),
1280  numNode, num_entr, PETSC_COMM_WORLD);
1281  delete [] canord;
1282  delete [] imp_mem;
1283 
1284  if(fd) fclose(fd);
1285 
1286  double dump_time = timing(t1, t0);
1287  log_msg(logger, 0, 0, " in %.3f seconds.\n", dump_time);
1288 }
1289 
1312 float MULTI_IF::restore_state(const char *fname, mesh_t gid, bool close)
1313 {
1314  static FILE_SPEC in = NULL;
1315  static char *last_fn = NULL;
1316  double t0, t1;
1317 
1318  t0 = get_time();
1319  int error = 0;
1320  int my_rank = get_rank();
1321  int mpi_size = get_size();
1322 
1323  // EMI ionic state lives on mesh elements (membrane faces), electrics on nodes.
1324  // Pick the matching canonical permutation and numbering from the data layout.
1325  const bool elemwise = this->gdata[Vm] && this->gdata[Vm]->layout == sf_vec::elemwise;
1326  const int canon_perm = elemwise ? ELEM_PETSC_TO_CANONICAL : PETSC_TO_CANONICAL;
1327 
1328  if (my_rank == 0) {
1329  if (fname) in = f_open(fname, "r");
1330 
1331  if (!in) {
1332  if (fname) log_msg(logger, 5, 0, "Error: cannot open file: %s\n", fname);
1333  else log_msg(logger, 5, 0, "Error: file stream not open yet");
1334  error++;
1335  }
1336  else {
1337  if (strcmp(read_bin_string(in), Magic_MIIF_ID) ) {
1338  log_msg(logger, 5, 0, "%s is not a recognized MIIF dump file", fname);
1339  error++;
1340  }
1341  }
1342  }
1343 
1344  if (get_global(error, MPI_SUM))
1345  EXIT(1);
1346 
1347  if (fname) {
1348  free(last_fn);
1349  last_fn = strdup(fname);
1350  }
1351  else {
1352  fname = last_fn;
1353  }
1354 
1355  unsigned int format, version;
1356  float time = 0.0f;
1357  time_t save_date;
1358  f_read_par(&format, sizeof(unsigned int), 1, in);
1359  f_read_par(&version, sizeof(unsigned int), 1, in);
1360  f_read_par(&save_date, sizeof(time_t), 1, in);
1361  f_read_par(&time, sizeof(float), 1, in);
1362  log_msg(logger, 0, 0, "Restoring time %f from %s (format v%d) generated\n\tby calling "
1363  "program r%d on %s", time, fname, format, version, ctime(&save_date) );
1364 
1365  global_node_index_t savedNum = 0;
1366  global_node_index_t glob_numNode = get_global(static_cast<global_node_index_t>(this->numNode),
1367  MPI_SUM, PETSC_COMM_WORLD);
1368 
1369  if (format >= 2) {
1370  f_read_par(&savedNum, sizeof(global_node_index_t), 1, in);
1371  } else {
1372  int savedNum32 = 0;
1373  f_read_par(&savedNum32, sizeof(int), 1, in);
1374  savedNum = savedNum32;
1375  }
1376  if (savedNum != glob_numNode) {
1377  log_msg(logger, 5, 0, "expecting %jd nodes but read %jd nodes",
1378  printable_int(glob_numNode), printable_int(savedNum));
1379  EXIT(1);
1380  }
1381 
1382  int num_gdata;
1383  f_read_par(&num_gdata, sizeof(int), 1, in);
1384  SF::scattering & petsc2canon = *get_permutation(gid, canon_perm, 1);
1385 
1386  for (int g = 0; g < num_gdata; g++) {
1387  char *datatype = read_bin_string_par(in);
1388  int i;
1389  for (i = 0; i < NUM_IMP_DATA_TYPES; i++) {
1390  if (!strcmp(datatype, imp_data_names[i]) ) {
1391  if (this->gdata[i]) {
1392  log_msg(logger, 0, 0, "\tRestoring global data %s", imp_data_names[i], datatype);
1393 
1394  FILE* fd = my_rank == 0 ? in->fd : NULL;
1395  this->gdata[i]->read_binary<SF_real>(fd);
1396  bool fwd = false;
1397  petsc2canon(*this->gdata[i], fwd);
1398  } else {
1399  log_msg(logger, 0, 0, "\tGlobal data %s not used", datatype);
1400  }
1401  break;
1402  }
1403  }
1404  if (i == NUM_IMP_DATA_TYPES)
1405  log_msg(logger, 3, 0, "\tSaved global data %s not recognized", datatype);
1406 
1407  // skip the saved block for a global-data type we cannot restore. The skip
1408  // length must come from the saved size (one SF_real per global node), not
1409  // from this->gdata[i], which is exactly what is NULL / out of range here.
1410  if ( (my_rank == 0) && ((i == NUM_IMP_DATA_TYPES) || !this->gdata[i]) )
1411  fseek(in->fd, static_cast<long>(glob_numNode) * sizeof(SF_real), SEEK_CUR);
1412 
1413  free(datatype);
1414  }
1415 
1416  int N_IIF;
1417  f_read_par(&N_IIF, sizeof(int), 1, in);
1418 
1419  // read in the IMPs for each region
1420  IMPinfo *imps = static_cast<IMPinfo *>(calloc(N_IIF, sizeof(IMPinfo)));
1421  int *IMPsz = static_cast<int *>(malloc(N_IIF * sizeof(int)));
1422 
1423  for (int i = 0; i < N_IIF; i++) {
1424  imps[i].name = read_bin_string_par(in);
1425  f_read_par(&imps[i].sz, sizeof(int), 1, in);
1426  if (format >= 3) f_read_par(&imps[i].fingerprint, sizeof(uint64_t), 1, in);
1427  f_read_par(&imps[i].nplug, sizeof(int), 1, in);
1428 
1429  imps[i].offset = 0;
1430  IMPsz[i] = imps[i].sz;
1431  imps[i].plug = static_cast<IMPinfo *>(calloc(sizeof(IMPinfo), imps[i].nplug));
1432 
1433  for (int j = 0; j < imps[i].nplug; j++) {
1434  imps[i].plug[j].offset = IMPsz[i];
1435  imps[i].plug[j].name = read_bin_string_par(in);
1436  f_read_par(&imps[i].plug[j].sz, sizeof(int), 1, in);
1437  if (format >= 3) f_read_par(&imps[i].plug[j].fingerprint, sizeof(uint64_t), 1, in);
1438  IMPsz[i] += imps[i].plug[j].sz;
1439  }
1440  }
1441 
1442  // compare new and old regions to determine which can be copied
1443  for (int i = 0; i < N_IIF; i++) {
1444  if (i >= this->N_IIF) {
1445  log_msg(NULL, 3, 0, "Saved IMP region %d out of range", i);
1446  continue;
1447  }
1448  if (strcmp(imps[i].name, this->IIF[i]->get_type().get_name().c_str()) ) {
1449  log_msg(NULL, 3, 0, "Saved IMP region %d ionic model does not match that of IMP region %d", i, i);
1450  continue;
1451  }
1452  // format >= 3 stores the de-interleaved per-node size; format 2 the whole SoA block
1453  const bool per_node_fmt = format >= 3;
1454  const int exp_sz = per_node_fmt ? (int) this->IIF[i]->get_sv_per_node_size()
1455  : (int) this->IIF[i]->get_sv_size();
1456  if (imps[i].sz != exp_sz) {
1457  log_msg(NULL, 3, 0, "Saved IMP region %d size does not match current IMP size", i);
1458  continue;
1459  }
1460  // a matching size but different SV-layout fingerprint means the model's state
1461  // variables were reordered/retyped since the checkpoint: skip rather than misread
1462  if (per_node_fmt && imps[i].fingerprint != this->IIF[i]->sv_fingerprint()) {
1463  log_msg(NULL, 3, 0, "Saved IMP region %d state-variable layout does not match current model", i);
1464  continue;
1465  }
1466 
1467  // the IM is good to go
1468  imps[i].compatible = true;
1469  for (int j = 0; j < imps[i].nplug; j++)
1470  for (int k = 0; k < this->IIF[i]->plugins().size(); k++) {
1471  const int exp_plug_sz = per_node_fmt ? (int) this->IIF[i]->plugins()[k]->get_sv_per_node_size()
1472  : (int) this->IIF[i]->plugins()[k]->get_sv_size();
1473  const bool plug_fp_ok = !per_node_fmt ||
1474  imps[i].plug[j].fingerprint == this->IIF[i]->plugins()[k]->sv_fingerprint();
1475  if (!strcmp(imps[i].plug[j].name, this->IIF[i]->plugins()[k]->get_type().get_name().c_str()) &&
1476  (imps[i].plug[j].sz == exp_plug_sz) && plug_fp_ok) {
1477  log_msg(NULL, 3, 0, "Saved IMP region %d plugin %s compatible", i, imps[i].plug[j].name);
1478  imps[i].plug[j].map = k;
1479  imps[i].plug[j].compatible = true;
1480  break;
1481  }
1482  }
1483  }
1484 
1485  // read in region mask
1486  const size_t global_node_count = static_cast<size_t>(glob_numNode);
1487  IIF_Mask_t *canMask = static_cast<char *>(malloc(global_node_count*sizeof(this->IIFmask[0]) ));
1488  f_read_par(canMask, sizeof(IIF_Mask_t), global_node_count, in);
1489 
1490  // determine relative IMP data offsets based on canonical ordering
1491  size_t *offset = static_cast<size_t *>(calloc(global_node_count+1, sizeof(size_t)));
1492  for (size_t i = 1; i <= global_node_count; i++)
1493  offset[i] = offset[i-1] + IMPsz[static_cast<int>(canMask[i-1])];
1494 
1495  // read in the state variable data
1496  // TODO: Aurel: Ideally rank0 would read in the data and communicate it, but right now I dont have
1497  // time to code this up. Thus, at least the ranks will read their data one by one so
1498  // we dont saturate the file system.
1499  long SVstart;
1500  if (my_rank == 0) {
1501  SVstart = ftell(in->fd);
1502  f_close(in);
1503  }
1504 
1505  MPI_Bcast(&SVstart, sizeof(long), MPI_BYTE, 0, PETSC_COMM_WORLD);
1506  global_node_index_t mismatch = 0;
1507 
1508  // we need to map from a local algebraic index to a global canonical index
1509  const sf_mesh & mesh = get_mesh(gid);
1511  if(elemwise) {
1512  const SF::vector<mesh_int_t> & canon_nbr = mesh.get_numbering(SF::NBR_ELEM_SUBMESH);
1513  loc2canon.resize(canon_nbr.size());
1514 
1515  for(size_t i=0; i<canon_nbr.size(); i++) loc2canon[i] = canon_nbr[i];
1516  }
1517  else {
1518  const SF::vector<mesh_int_t> & canon_nbr = mesh.get_numbering(SF::NBR_SUBMESH);
1519  const SF::vector<mesh_int_t> & alg_idx = mesh.pl.algebraic_nodes();
1520  loc2canon.resize(alg_idx.size());
1521 
1522  for(size_t i=0; i<alg_idx.size(); i++) loc2canon[i] = canon_nbr[alg_idx[i]];
1523  }
1524 
1525  // loc2canon must hold one canonical index per local node; a shorter array
1526  // would be indexed out of bounds by the per-node restore below
1527  assert(loc2canon.size() == static_cast<size_t>(this->numNode));
1528 
1529  // the ranks read the file one-by-one
1530  for (int pid = 0; pid < mpi_size; pid++) {
1531  if (my_rank == pid) {
1532  if(in) delete in;
1533  in = f_open(fname, "r");
1534 
1535  for (int i = 0; i < N_IIF; i++) {
1536  fseek(in->fd, SVstart, SEEK_SET);
1537  if (format >= 3)
1538  mismatch += this->IIF[i]->restore_per_node(in, this->N_Nodes[i], this->NodeLists[i],
1539  canMask, offset, imps+i, loc2canon.data());
1540  else
1541  mismatch += this->IIF[i]->restore(in, this->N_Nodes[i], this->NodeLists[i],
1542  canMask, offset, imps+i, loc2canon.data());
1543  }
1544 
1545  f_close(in);
1546  }
1547  MPI_Barrier(PETSC_COMM_WORLD);
1548  }
1549  free(canMask);
1550  free(IMPsz);
1551 
1552  MPI_Reduce(my_rank ? &mismatch : MPI_IN_PLACE, &mismatch, 1,
1553  opencarp::mpi_datatype<global_node_index_t>(), MPI_SUM, 0, PETSC_COMM_WORLD);
1554  if ( (my_rank == 0) && mismatch)
1555  log_msg(NULL, 3, 0, "Number of nonmatching nodes: %jd", printable_int(mismatch));
1556 
1557  if ( (my_rank == 0) && (!close) ) {
1558  if(in) delete in;
1559  in = f_open(fname, "r");
1560 
1561  // position at end of current MIIF data
1562  fseek(in->fd, SVstart+offset[global_node_count], SEEK_SET);
1563  }
1564 
1565  free(offset);
1566  for (int i = 0; i < N_IIF; i++)
1567  free(imps[i].plug);
1568  free(imps);
1569 
1570  double restore_time = timing(t1, t0);
1571  log_msg(logger, 0, 0, "State restored from file %s in %.3f seconds.\n", fname, restore_time);
1572 
1573  return time;
1574 }
1575 
1587 void MULTI_IF::sv_dump_add_by_name_list( int region, char *imp_name,
1588  char *reg_name, char *sv_lst, char *plg_lst,
1589  char *plg_sv_lst, double t, double dump_dt) {
1590  char file[8000], svs[1024], plgs[1024], plgsvs[1024];
1591  char *e, *l, *p, *svnames, *plgnames, *plgsvnames;
1592 
1593  strcpy(svs, sv_lst);
1594  strcpy(plgs, plg_lst);
1595  strcpy(plgsvs, plg_sv_lst);
1596  svnames = &svs[0];
1597  plgnames = &plgs[0];
1598  plgsvnames = &plgsvs[0];
1599 
1600  // override default setting for dump interval
1601  this->svd.intv = dump_dt;
1602 
1603  // override default start time for dumping (in case of a restart)
1604  this->svd.t_dump = t;
1605 
1606  // parse ionic model sv list
1607  while ( (e = get_next_list(svnames, ',') ) ) {
1608  snprintf(file, sizeof file, "%s.%s.bin", reg_name, svnames);
1609  this->sv_dump_add_by_name(region, imp_name, svnames, reg_name, file);
1610  svnames = e;
1611  }
1612 
1613  // Information is provided as follows:
1614  // region[X].plugin = "PLG_A:PLG_B"
1615  // region[X].plug_sv_dumps = "m,h,n:x,y"
1616 
1617  // parse plugin list
1618  while ( (p = get_next_list(plgnames, ':') ) ) {
1619  // get sv list corresponding to current plugin
1620  l = get_next_list(plgsvnames, ':');
1621  while ( (e = get_next_list(plgsvnames, ',') ) ) {
1622  snprintf(file, sizeof file, "%s_%s.%s.bin", reg_name, plgnames, plgsvnames);
1623  this->sv_dump_add_by_name(region, plgnames, plgsvnames, reg_name, file);
1624  plgsvnames = e;
1625  }
1626  plgnames = p;
1627  plgsvnames = l;
1628  }
1629 } // sv_dump_add_by_name_list
1630 
1646 void MULTI_IF::sv_dump_add(int region, const IonType& type, int offset, int size, int dtype,
1647  const char *filename, const char *regname) {
1648  IonIfBase *IF = this->IIF[region];
1649  int n = this->svd.n;
1650 
1651  // find the proper IMP
1652  if (IF->get_type() != type) {
1653  int i;
1654  for (i = 0; i < IF->plugins().size(); i++)
1655  if (IF->plugins()[i]->get_type() == type)
1656  break;
1657  if (i == IF->plugins().size()) {
1658  log_msg(logger, 2, 0, "Warning: IMP %s not found in Region %s\n",
1659  type.get_name().c_str(), regname);
1660  return;
1661  } else {
1662  IF = IF->plugins()[i];
1663  }
1664  }
1665 
1666  this->svd.active = 1;
1667  this->svd.n++;
1668  this->svd.hdls = static_cast<FILE_SPEC *>(realloc(this->svd.hdls, this->svd.n*sizeof(this->svd.hdls[0])));
1669  this->svd.fn = static_cast<char **>(realloc(this->svd.fn, this->svd.n*sizeof(char *)));
1670  this->svd.reg = static_cast<int *>(realloc(this->svd.reg, this->svd.n*sizeof(int)));
1671  this->svd.svnames = static_cast<char **>(realloc(this->svd.svnames, this->svd.n*sizeof(char *)));
1672  this->svd.n_dumps = 0;
1673  this->svd.nwr = 0;
1674  this->svd.offset = static_cast<int *>(realloc(this->svd.offset, this->svd.n*sizeof(int)));
1675  this->svd.size = static_cast<int *>(realloc(this->svd.size, this->svd.n*sizeof(int)));
1676  this->svd.dlo_vs = static_cast<int *>(realloc(this->svd.dlo_vs, this->svd.n*sizeof(int)));
1677  this->svd.dtype = static_cast<int *>(realloc(this->svd.dtype, this->svd.n*sizeof(int)));
1678  this->svd.svtab = static_cast<void **>(realloc(this->svd.svtab, this->svd.n*sizeof(void *)));
1679  this->svd.svsize = static_cast<size_t *>(realloc(this->svd.svsize, this->svd.n*sizeof(size_t)));
1680  this->svd.num = static_cast<node_count_t *>(realloc(this->svd.num, this->svd.n*sizeof(node_count_t)));
1681  this->svd.reg = static_cast<int *>(realloc(this->svd.reg, this->svd.n*sizeof(int)));
1682 
1683  if (!get_rank() ) {
1684 #ifdef HDF5
1685  assert(0);
1686 #else // ifdef HDF5
1687  this->svd.hdls[n] = f_open(filename, "w+");
1688 #endif // ifdef HDF5
1689  } else {
1690  this->svd.hdls[n] = NULL;
1691  }
1692 
1693  this->svd.fn[n] = dupstr(filename);
1694  this->svd.reg[n] = region;
1695  this->svd.svnames[n] = NULL; // has to be filled in in calling routine
1696  this->svd.offset[n] = offset;
1697  this->svd.size[n] = size;
1698  this->svd.dtype[n] = dtype;
1699  this->svd.svtab[n] = IF->get_sv_address();
1700  this->svd.svsize[n] = IF->get_sv_size();
1701  this->svd.num[n] = IF->get_num_node();
1702  this->svd.dlo_vs[n] = IF->get_type().dlo_vector_size();
1703 } // sv_dump_add
1704 
1715 int MULTI_IF::sv_dump_add_by_name(int region, char *impname,
1716  char *svname, char *regname, char *filename) {
1717  int offset, size, dtype, added = 0;
1718  char *svtypename = NULL;
1719  char *filename_bin;
1720 
1721  IonType* type = get_ion_type(std::string(impname));
1722  filename_bin = strcat(filename, ".bin");
1723 
1724  assert(type != NULL);
1725  if (type->get_sv_offset(svname, &offset, &size) ) {
1726  type->get_sv_type(svname, &dtype, &svtypename);
1727  this->sv_dump_add(region, *type, offset, size, dtype, filename_bin, regname);
1728  this->svd.svnames[this->svd.n-1] = dupstr(svname);
1729  added = 1;
1730  } else {
1731  log_msg(NULL, 1, 0, "No state variable added to dump list");
1732  }
1733 
1734  return added;
1735 }
1736 
1749 {
1750  assert(miif->gdata[Vm] != NULL);
1751 
1752  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++) {
1753  for (int n = 0; n < miif->N_IIF; n++)
1754  if (USED_DAT(miif->IIF[n], imp_data_flag[i]) && (miif->gdata[i] == NULL)) {
1755  SF::init_vector(&miif->gdata[i], miif->gdata[Vm]);
1756  break;
1757  }
1758  }
1759 }
1760 
1769  int num_mech_data = 0;
1770 
1771  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++) {
1772  if ( (Lambda_DATA_FLAG == i) || (delLambda_DATA_FLAG == i) ||
1773  (Tension_DATA_FLAG == i) || (tension_component_DATA_FLAG == i) ) {
1774  for (int n = 0; n < this->N_IIF; n++)
1775  if (USED_DAT(this->IIF[n], imp_data_flag[i]) )
1776  num_mech_data++;
1777  }
1778  }
1779  return static_cast<bool>(num_mech_data);
1780 }
1781 
1799 void MULTI_IF::transmem_stim_species(float charge, const char *species,
1800  float beta, int *node, int numnode) {
1801  float z;
1802  if (strstr(species, "Ca") != NULL) {
1803  charge *= 1000; // Ca uses micromolar instead of millimolar
1804  z = 2;
1805  } else if (strstr(species, "Na") != NULL) {
1806  z = 1;
1807  } else if (strstr(species, "Cl") != NULL) {
1808  z = -1;
1809  } else if (strstr(species, "K") != NULL) {
1810  z = 1;
1811  } else {
1812  log_msg(logger, 5, 0, "Unimplemented ion species: %s\n", species);
1813  exit(1);
1814  }
1815  int offset, sz;
1816  SVgetfcn ion_get;
1817  SVputfcn ion_put;
1818  static int warned = 0;
1819 
1820  int rank = get_rank();
1821  SF::vector<int> layout;
1822  layout_from_count(numnode, layout, PETSC_COMM_WORLD);
1823  int my_low_idx = layout[rank], my_high_idx = layout[rank+1];
1824 
1825  for (int i = 0; i < numnode; i++) {
1826  if ((node[i] < my_low_idx) || (node[i] >= my_high_idx)) continue; // not on processor
1827 
1828  for (int j = 0; j < this->N_IIF; j++) {
1829  if (this->NodeLists[j] == NULL) continue; // no stim nodes on processor
1830 
1831  if ((node[i] < this->NodeLists[j][0]) ||
1832  (node[i] > this->NodeLists[j][this->N_Nodes[j]-1])) {
1833  continue; // not in this IMP
1834  } else {
1835  ion_get = this->IIF[j]->get_type().get_sv_offset(species, &offset, &sz);
1836  if (ion_get == NULL) {
1837  if (!warned) {
1838  warned = 1;
1839  log_msg(logger, 2, 0, "Ion species not present in ionic model: %s\n",
1840  species);
1841  }
1842  return;
1843  }
1844  ion_put = getPutSV(ion_get);
1845  }
1846 
1847  // does this model provide a correct conversion factor?
1848  double delta_conc;
1849  cell_geom g = this->IIF[j]->cgeom();
1850  if (g.sl_i2c != NDEF) {
1851  delta_conc = charge*g.sl_i2c/z;
1852  } else {
1853  // conversion with generic factor
1854  delta_conc = 10*charge*beta/(FARADAY*z); // convert to millimolar/L
1855  }
1856 
1857  for (node_index_t k = 0; k < this->N_Nodes[j]; k++)
1858  if (node[i] == this->NodeLists[j][k])
1859  ion_put(*this->IIF[j], k, offset, ion_get(*this->IIF[j], k, offset)-delta_conc);
1860  }
1861  }
1862 } // transmem_stim_species
1863 
1870 void MULTI_IF::MIIF_change_dt( double Dt) {
1871  this->dt = Dt;
1872  for (int i = 0; i < this->N_IIF; i++) {
1873  this->IIF[i]->destroy_luts();
1874  this->IIF[i]->set_dt((float) Dt);
1875  this->iontypes[i].get().construct_tables(*this->IIF[i]);
1876 
1877  for (int j = 0; j < this->IIF[i]->plugins().size(); j++) {
1878  auto& plugin = this->IIF[i]->plugins()[j];
1879  plugin->destroy_luts();
1880  plugin->set_dt((float) Dt);
1881  this->plugtypes[i][j].get().construct_tables(*plugin);
1882  }
1883  }
1884 }
1885 
1886 #define MEMFREE(A) free(A)
1887 
1895  assert(m->doppel);
1896 
1897  for (int i = 0; i < m->N_IIF; i++) {
1898  m->IIF[i]->get_type().destroy_ion_if(m->IIF[i]);
1899  }
1900 
1901  for (int j = 0; j < NUM_IMP_DATA_TYPES; j++) {
1902  if (m->gdata[j])
1903  delete m->gdata[j];
1904  }
1905 
1906  m->zero_data();
1907 }
1908 
1909 #undef MEMFREE
1910 
1918 void doppel_update(MULTI_IF *orig, MULTI_IF *miif_doppel) {
1919  // removed assertion, need to be able to update bidirectionally
1920  // assert(doppel->doppel);
1921 
1922  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++)
1923  if (orig->gdata[i])
1924  SF::init_vector(&miif_doppel->gdata[i], orig->gdata[i]);
1925 
1926  for (int i = 0; i < orig->N_IIF; i++) {
1927  miif_doppel->IIF[i]->copy_SVs_from(*orig->IIF[i], false);
1928 
1929  for (int j = 0; j < orig->IIF[i]->plugins().size(); j++)
1930  miif_doppel->IIF[i]->plugins()[j]->copy_SVs_from(*orig->IIF[i]->plugins()[j], false);
1931  }
1932 
1933  miif_doppel->getRealData();
1934 }
1935 
1946 void doppel_MIIF(MULTI_IF *orig, MULTI_IF *miif_doppel) {
1947  *miif_doppel = *orig;
1948  miif_doppel->doppel = true;
1949 
1950  // deal with the local portion of the global data
1951  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++)
1952  if (orig->gdata[i])
1953  SF::init_vector(&miif_doppel->gdata[i], orig->gdata[i]);
1954 
1955  // copy the state variables
1956  miif_doppel->IIF = {};
1957 
1958  for (int i = 0; i < orig->N_IIF; i++) {
1959  // Make a new IonIf for this clone and copy state variables and plugins
1960  miif_doppel->IIF.push_back(orig->IIF[i]->get_type().make_ion_if(orig->IIF[i]->get_target(),
1961  orig->IIF[i]->get_num_node(), orig->plugtypes[i]));
1962  miif_doppel->IIF[i]->copy_SVs_from(*orig->IIF[i], true);
1963  miif_doppel->IIF[i]->copy_plugins_from(*orig->IIF[i]);
1964  }
1965 }
1966 
1974 bool isIMPdata(const char *sv) {
1975  return IMPdataLabel2Index(sv) != -1;
1976 }
1977 
1985 int IMPdataLabel2Index(const char *sv) {
1986  int imp_data_id = -1;
1987 
1988  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++) {
1989  if (strcmp(sv, imp_data_names[i]) == 0) {
1990  imp_data_id = i;
1991  break;
1992  }
1993  }
1994  return imp_data_id;
1995 }
1996 
1997 /* copy all of the IMP data from one node to another
1998  *
1999  * \param IF ionic model
2000  * \param from local IMP number
2001  * \param to local IMP number
2002  */
2004  for (int i = 0; i < NUM_IMP_DATA_TYPES; i++)
2005  if (localdata && localdata[i])
2006  localdata[i][to] = localdata[i][from];
2007 
2008  IF.for_each([&](IonIfBase& imp) {
2009  char **sv_list;
2010  int sv_list_size = imp.get_type().get_sv_list(&sv_list);
2011 
2012  for (int i = 0; i < sv_list_size; i++) {
2013  char *sv_name = sv_list[i];
2014  int sv_offset;
2015  int sv_size;
2016  SVgetfcn sv_get = imp.get_type().get_sv_offset(sv_name, &sv_offset, &sv_size);
2017  if (sv_get == NULL) {
2018  throw std::runtime_error(std::string(__func__) + " error: " + sv_name + " is not a valid state variable for the " + imp.get_type().get_name() + " model.");
2019  }
2020  SVputfcn sv_put = getPutSV(sv_get);
2021 
2022  GlobalData_t sv_val = sv_get(imp, from, sv_offset);
2023  sv_put(imp, to, sv_offset, sv_val);
2024  }
2025  free(sv_list);
2026  });
2027 }
2028 
2029 } // namespace limpet
#define NDEF
definition of cell geometry
Definition: ION_IF.h:115
#define FILENAME_BUF
#define MAX_TRACE_LINE_LEN
Define multiple ionic models to be used in different regions.
#define FARADAY
Faraday's constant.
Definition: MULTI_ION_IF.h:147
#define USED_DAT(I, F)
Definition: MULTI_ION_IF.h:312
opencarp::local_index_t mesh_int_t
Definition: SF_container.h:46
opencarp::real_t SF_real
Global scalar type.
Definition: SF_globals.h:33
#define SLIST_APPEND(S, P)
Definition: basics.h:54
#define FLUSH
Definition: basics.h:319
#define LOCAL
Definition: basics.h:317
#define fmemopen_
Definition: basics.h:441
#define STRUCT_ZERO(S)
Definition: basics.h:53
virtual S * ptr()=0
virtual void release_ptr(S *&p)=0
size_t read_binary(FILE *fd)
ltype layout
used vector layout (nodal, algebraic, unset)
virtual void add_scaled(const abstract_vector< T, S > &vec, S k)=0
size_t write_binary(FILE *fd)
Write a vector to HD in binary. File descriptor is already set up.
virtual void set(const vector< T > &idx, const vector< S > &vals, const bool additive=false, const bool local=false)=0
bool in_b(const T idx)
return whether idx is in set B
Definition: SF_container.h:366
T backward_map(T idx) const
Map one index from b to a.
Definition: SF_container.h:274
overlapping_layout< T > pl
nodal parallel layout
Definition: SF_container.h:429
Container for a PETSc VecScatter.
void forward(abstract_vector< T, S > &in, abstract_vector< T, S > &out, bool add=false)
Forward scattering.
size_t size() const
The current size of the vector.
Definition: SF_vector.h:104
void resize(size_t n)
Resize a vector.
Definition: SF_vector.h:209
T * data()
Pointer to the vector's start.
Definition: SF_vector.h:91
Represents the ionic model and plug-in (IMP) data structure.
Definition: ION_IF.h:142
const IonType & get_type() const
Gets this IMP's model type.
Definition: ION_IF.cc:145
void for_each_sv_field(node_index_t node, const std::vector< std::pair< int, int >> &fields, Fn &&fn)
Visits each state-variable field of one node, in storage order.
Definition: ION_IF.h:469
std::vector< IonIfBase * > & plugins()
Returns a vector containing the plugins of this IMP.
Definition: ION_IF.cc:185
void get_sv_layout(std::vector< std::pair< int, int >> &fields) const
Describes the per-node memory layout of this IMP's state variables.
Definition: ION_IF.cc:342
void compute(node_index_t start, node_index_t end, GlobalData_t **data)
Perform ionic model computation for 1 time step.
Definition: ION_IF.cc:271
size_t get_sv_per_node_size() const
Size in bytes of one node's de-interleaved state-variable record.
Definition: ION_IF.cc:350
uint64_t sv_fingerprint() const
Fingerprint of this IMP's state-variable layout.
Definition: ION_IF.cc:357
ts & get_tstp()
Gets the time stepper.
Definition: ION_IF.cc:209
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:517
IonIfBase * parent() const
Gets the parent IMP.
Definition: ION_IF.cc:177
int miifIdx
imp index within miif
Definition: ION_IF.h:154
virtual std::size_t get_sv_size() const =0
Gets the size of the structure this IMP uses for state variables.
virtual void * get_sv_address()=0
Gets the raw address of the state variables for this IMP.
node_count_t get_num_node() const
Gets the number of nodes handled by this IMP.
Definition: ION_IF.cc:149
Abstract class representing an ionic model type.
Definition: ion_type.h:59
bool is_plugin() const
Returns whether this model is a plugin or not.
Definition: ion_type.cc:27
const std::string & get_name() const
Gets the model name.
Definition: ion_type.cc:23
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.
virtual int get_sv_type(const char *svname, int *type, char **type_name) const =0
Determines the type of a SV.
virtual size_t dlo_vector_size() const =0
Gets the vector size when using data layout optimization (DLO).
bool extUpdateVm
flag indicating update function for Vm
Definition: MULTI_ION_IF.h:219
std::vector< IonIfBase * > IIF
array of IIF's
Definition: MULTI_ION_IF.h:213
opencarp::sf_vec * gdata[NUM_IMP_DATA_TYPES]
data used by all IMPs
Definition: MULTI_ION_IF.h:227
void sv_dump_add_by_name_list(int, char *, char *, char *, char *, char *, double, double)
node_count_t numNode
local number of nodes
Definition: MULTI_ION_IF.h:221
size_t dump_svs(opencarp::base_timer *)
std::vector< Target > targets
target for each region
Definition: MULTI_ION_IF.h:224
std::vector< IonTypeList > plugtypes
plugins types for each region
Definition: MULTI_ION_IF.h:226
IonTypeList iontypes
type for each region
Definition: MULTI_ION_IF.h:223
void dump_state(char *, float, opencarp::mesh_t gid, bool, unsigned int)
SV_DUMP svd
state variable dump
Definition: MULTI_ION_IF.h:214
void sv_dump_add(int, const IonType &, int, int, int, const char *, const char *)
void transmem_stim_species(float, const char *, float, int *, int)
void initialize_currents(double, int)
GlobalData_t *** ldata
data local to each IMP
Definition: MULTI_ION_IF.h:216
float restore_state(const char *, opencarp::mesh_t gid, bool)
int N_IIF
how many different IIF's
Definition: MULTI_ION_IF.h:222
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...
int sv_dump_add_by_name(int, char *, char *, char *, char *)
Trace_Info * trace_info
Information about traces.
Definition: MULTI_ION_IF.h:218
double dt
time step (ms)
Definition: MULTI_ION_IF.h:230
node_count_t * N_Nodes
#nodes for each IMP
Definition: MULTI_ION_IF.h:211
GlobalData_t * procdata[NUM_IMP_DATA_TYPES]
data for this processor
Definition: MULTI_ION_IF.h:215
void dump_luts_MIIF(bool)
bool * contiguous
whether a region is contiguously numbered
Definition: MULTI_ION_IF.h:217
opencarp::FILE_SPEC logger
Definition: MULTI_ION_IF.h:228
int numSubDt
number of sub-dt time steps
Definition: MULTI_ION_IF.h:231
node_index_t ** NodeLists
local partitioned node lists for each IMP stored
Definition: MULTI_ION_IF.h:212
void MIIF_change_dt(double)
IIF_Mask_t * IIFmask
region for each node
Definition: MULTI_ION_IF.h:225
int adjust_MIIF_variables(const char *variable, const SF::vector< SF_int > &indices, const SF::vector< SF_real > &values)
std::string name
name for MIIF region
Definition: MULTI_ION_IF.h:209
bool doppel
is this a shallow clone?
Definition: MULTI_ION_IF.h:210
void releaseRealDataDuringInit()
#define log_msg(F, L, O,...)
Definition: filament.h:8
void dsp_from_cnt(const vector< T > &cnt, vector< T > &dsp)
Compute displacements from counts.
Definition: SF_vector.h:310
T sum(const vector< T > &vec)
Compute sum of a vector's entries.
Definition: SF_vector.h:340
void local_petsc_to_nodal_mapping(const meshdata< T, S > &mesh, index_mapping< T > &petsc_to_nodal)
void layout_from_count(const T count, vector< T > &layout, MPI_Comm comm)
Definition: SF_network.h:201
void init_vector(SF::abstract_vector< T, S > **vec)
Definition: SF_init.h:107
@ NBR_SUBMESH
Submesh nodal numbering: The globally ascending sorted reference indices are reindexed.
Definition: SF_container.h:202
@ NBR_ELEM_SUBMESH
Submesh element numbering: The globally ascending sorted reference indices are reindexed.
Definition: SF_container.h:205
void(* SVputfcn)(IonIfBase &, node_index_t, int, GlobalData_t)
Definition: ion_type.h:49
void CreateIIFNodeLsts_(int, IIF_Mask_t *, node_count_t **, node_index_t ***, node_count_t)
void doppel_MIIF(MULTI_IF *orig, MULTI_IF *miif_doppel)
void initializeIMPData(MULTI_IF *pMIIF)
int determine_write_ranges(int N, size_t *offset, size_t bufsize, int **ranges)
int get_plug_flag(char *plgstr, int *out_num_plugins, IonTypeList &out_plugins)
SVputfcn getPutSV(SVgetfcn)
void doppel_update(MULTI_IF *orig, MULTI_IF *miif_doppel)
double Real
Definition: MULTI_ION_IF.h:151
node_index_t getGlobalNodalIndex(IonIfBase &pIF, node_index_t relIdx)
int should_print_bounds_exceeded_messages()
@ CPU
baseline CPU model generated with the original opencarp code generator
Definition: target.h:48
IonType * get_ion_type(const std::string &name)
int IMPdataLabel2Index(const char *sv)
void CreateIIFGlobalNodeLsts(MULTI_IF *pMIIF)
SF_real GlobalData_t
Definition: limpet_types.h:27
void allocate_shared_data(MULTI_IF *)
bool isIMPdata(const char *)
bool is_gpu(Target const target)
Checks if this is a GPU target.
Definition: target.cc:67
void initialize_params_MIIF(MULTI_IF *pMIIF)
constexpr T min(T a, T b)
Definition: ion_type.h:33
void alloc_MIIF(MULTI_IF *pMIIF)
void freeIMPData(MULTI_IF *pMIIF)
GlobalData_t(* SVgetfcn)(IonIfBase &, node_index_t, int)
Definition: ion_type.h:48
std::vector< std::reference_wrapper< IonType > > IonTypeList
Definition: ion_type.h:291
void dup_IMP_node_state(IonIfBase &IF, node_index_t from, node_index_t to, GlobalData_t **localdata)
constexpr T max(T a, T b)
Definition: ion_type.h:31
char * get_sv(void *tab, int offset, node_count_t n, int svSize, int size, int dlo_vector_size)
void deallocate_on_target(Target target, T *ptr)
Utility function for deallocating memory on a target. See TargetAllocator.
Definition: target.h:318
void CreateIIFLocalNodeLsts(MULTI_IF *pMIIF)
void initialize_ionic_IF(MULTI_IF *pMIIF)
void update_ts(ts *ptstp)
Definition: ION_IF.cc:566
float current_global_time()
opencarp::local_index_t node_count_t
Definition: limpet_types.h:29
void close_trace(MULTI_IF *MIIF)
int node_index_cmp(const void *a, const void *b)
void dump_trace(MULTI_IF *MIIF, limpet::Real time)
char * get_next_list(char *lst, char delimiter)
Definition: ION_IF.cc:612
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.
char IIF_Mask_t
Definition: ion_type.h:50
void free_doppel(MULTI_IF *m)
opencarp::global_index_t global_node_index_t
Definition: limpet_types.h:30
node_index_t current_global_node(node_index_t local_node)
opencarp::local_index_t node_index_t
Definition: limpet_types.h:28
char * tokstr_r(char *s1, const char *s2, char **lasts)
Definition: ION_IF.cc:89
int set_print_bounds_exceeded_messages(int newval)
std::intmax_t printable_int(T value)
Definition: mpi_utils.h:130
sf_mesh & get_mesh(const mesh_t gt)
Get a mesh by specifying the gridID.
Definition: sf_interface.cc:33
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.
SF::meshdata< mesh_int_t, mesh_real_t > sf_mesh
Definition: sf_interface.h:48
char * read_bin_string(FILE_SPEC in)
Definition: basics.cc:231
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:174
int get_rank(MPI_Comm comm=PETSC_COMM_WORLD)
Definition: basics.h:284
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
void write_bin_string(FILE_SPEC out, const char *s)
Definition: basics.cc:222
FILE_SPEC f_open(const char *fname, const char *mode)
Open a FILE_SPEC.
Definition: basics.cc:138
char * read_bin_string_par(FILE_SPEC in)
Definition: basics.cc:242
char * dupstr(const char *old_str)
Definition: basics.cc:44
mesh_t
The enum identifying the different meshes we might want to load.
Definition: sf_interface.h:59
void get_time(double &tm)
Definition: basics.h:444
SF::abstract_vector< SF_int, SF_real > sf_vec
Definition: sf_interface.h:50
int get_size(MPI_Comm comm=PETSC_COMM_WORLD)
Definition: basics.h:298
V timing(V &t2, const V &t1)
Definition: basics.h:456
void f_close(FILE_SPEC &f)
Close a FILE_SPEC.
Definition: basics.cc:165
file_desc * FILE_SPEC
Definition: basics.h:140
#define PETSC_TO_CANONICAL
Permute algebraic data from PETSC to canonical ordering.
Definition: sf_interface.h:79
#define ELEM_PETSC_TO_CANONICAL
Permute algebraic element data from PETSC to canonical ordering.
Definition: sf_interface.h:81
int offset
offset into node data
Definition: ION_IF.h:135
int sz
storage required
Definition: ION_IF.h:130
char * name
IMP name.
Definition: ION_IF.h:129
int map
which plugin does this IMO match
Definition: ION_IF.h:134
IMPinfo * plug
plugins
Definition: ION_IF.h:132
bool compatible
does IM match stored IM
Definition: ION_IF.h:133
int nplug
number of plugins
Definition: ION_IF.h:131
uint64_t fingerprint
saved SV-layout fingerprint (format >= 3; 0 otherwise)
Definition: ION_IF.h:136
data structure to manage state variable file dumps
Definition: MULTI_ION_IF.h:171
size_t * svsize
state variable sizes
Definition: MULTI_ION_IF.h:187
char ** fn
array to store file names
Definition: MULTI_ION_IF.h:175
double intv
time interval for sv dumps
Definition: MULTI_ION_IF.h:178
int * dtype
data type
Definition: MULTI_ION_IF.h:184
int n_dumps
keep track of number of dumped time slices
Definition: MULTI_ION_IF.h:180
node_count_t * num
number of nodes
Definition: MULTI_ION_IF.h:185
long nwr
keep track of number of written tokens
Definition: MULTI_ION_IF.h:181
int * size
sizes of SV to dump
Definition: MULTI_ION_IF.h:183
void ** svtab
state variable tables
Definition: MULTI_ION_IF.h:186
int * reg
array to store region ids
Definition: MULTI_ION_IF.h:176
double t_dump
next instant for sv dump
Definition: MULTI_ION_IF.h:179
char ** svnames
array to store sv names
Definition: MULTI_ION_IF.h:177
int n
#state variables we want to dump
Definition: MULTI_ION_IF.h:173
int * offset
offsets into structure for SV
Definition: MULTI_ION_IF.h:182
opencarp::FILE_SPEC * hdls
array of file handles to sv output files
Definition: MULTI_ION_IF.h:174
data structure to manage trace dumps. Should eventually be combined with the state variable dumps,...
Definition: MULTI_ION_IF.h:195
bool ignored
globally not found
Definition: MULTI_ION_IF.h:197
node_index_t node_idx
local node number
Definition: MULTI_ION_IF.h:198
bool found
found on this node
Definition: MULTI_ION_IF.h:196
opencarp::FILE_SPEC file
Definition: MULTI_ION_IF.h:200
float sl_i2c
convert sl-currents in uA/cm^2 to mM/L without valence
Definition: ION_IF.h:121
int cnt
Definition: ION_IF.h:89