openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
ionicsOnFace.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 
27 #if WITH_EMI_MODEL
28 #include "ionicsOnFace.h"
29 
30 #include "SF_init.h"
31 
32 namespace opencarp {
33 
34 void initialize_sv_dumps_onFace(limpet::MULTI_IF *pmiif, IMPregion_EMI* reg, int id, double t, double dump_dt);
35 
36 namespace {
37 
38 // Region 0 and 1 are default EMI face regions and are valid when an ionic
39 // model is assigned. User-defined regions 2+ must additionally provide at
40 // least one face-tag pair.
41 bool imp_region_emi_is_assigned(const IMPregion_EMI& region, int idx)
42 {
43  if (!(region.im && strlen(region.im) > 0)) return false;
44  if (idx < 2) return true;
45  return region.num_IDs > 0;
46 }
47 
48 // Ignore trailing unassigned imp_region_emi entries. This lets users keep the
49 // generated default parameter count while only assigning the regions they use.
50 int effective_num_imp_regions_emi()
51 {
52  const int configured_nreg = param_globals::num_imp_regions;
53  int nreg = configured_nreg;
54  while (nreg > 2 && !imp_region_emi_is_assigned(param_globals::imp_region_emi[nreg - 1], nreg - 1)) {
55  --nreg;
56  }
57 
58  if (nreg < configured_nreg) {
59  static bool warned_once = false;
60  int rank = 0;
61  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
62  if (!warned_once && rank == 0) {
63  log_msg(NULL, 1, ECHO,
64  "Warning: num_imp_regions=%d but only %d imp_region_emi entries are assigned; ignoring trailing unassigned regions.\n",
65  configured_nreg, nreg);
66  warned_once = true;
67  }
68  }
69 
70  return nreg;
71 }
72 
73 } // namespace
74 
75 void IonicsOnFace::compute_step()
76 {
77  double t1, t2;
78  get_time(t1);
79 
80  miif->compute_ionic_current();
81 
82  double comp_time = timing(t2, t1);
83  this->compute_time += comp_time;
84 
85  comp_stats.calls++;
86  comp_stats.tot_time += comp_time;
87 
89  comp_stats.log_stats(user_globals::tm_manager->time, false);
90 }
91 
92 void IonicsOnFace::destroy()
93 {
94  miif->free_MIIF();
95 }
96 
97 void IonicsOnFace::output_step()
98 {}
99 
100 void IonicsOnFace::initialize()
101 {
102  double t1, t2;
103  get_time(t1);
104 
105  set_dir(OUTPUT);
106 
107  // initialize generic logger for ODE timings per time_dt
108  comp_stats.init_logger("ODE_stats.dat");
109 
110  double tstart = 0.;
111  sf_mesh & mesh = get_mesh(ion_domain);
112  limpet::node_count_t loc_size = mesh.l_numelem;
113 
114  // create ionic current vector and register it
115  sf_vec *IIon;
116  sf_vec *Vmv;
117  SF::init_vector(&IIon, mesh, 1, sf_vec::elemwise);
118  SF::init_vector(&Vmv, mesh, 1, sf_vec::elemwise);
121 
122  // setup miif
123  miif = new limpet::MULTI_IF();
124  // hand down the physics log file, so that what MULTI_IF reports while setting up and
125  // restoring state is recorded rather than only echoed to the console
126  miif->logger = logger;
127  // store IIF_IDs and Plugins in arrays
128  miif->name = "myocardium";
129  miif->gdata[limpet::Vm] = Vmv;
130  miif->gdata[limpet::Iion] = IIon;
131 
132  const int num_imp_regions = effective_num_imp_regions_emi();
133  SF::vector<RegionSpecs_EMI> rs(num_imp_regions);
134 
135  if (rs.size() <2) {
136  log_msg(NULL, 5, ECHO, "\tError: num_imp_regions must be at least 2: the first region is reserved for the ionic model, and the second is designated for the gap junction.\n");
137  log_msg(NULL, 5, ECHO, "set a default ionic model and gapjuntion model in parameter file\\n");
138  exit(1);
139  }
140 
141  // Region 0 and 1 are implicit defaults:
142  // - region 0: default membrane faces
143  // - region 1: default gap-junction faces
144  rs[0].nsubregs = 0;
145  rs[0].subregtags = nullptr;
146  rs[1].nsubregs = 0;
147  rs[1].subregtags = nullptr;
148 
149  // imp_region_emi[2+] assign models to specific face tag pairs like "t1:t2"
150  for (size_t i = 2; i < rs.size(); i++ ) {
151  rs[i].nsubregs = param_globals::imp_region_emi[i].num_IDs;
152  rs[i].subregtags = new std::string[rs[i].nsubregs]; // Allocate memory for the string array
153  for (int j = 0; j < rs[i].nsubregs;j++) {
154  std::string t1t2 = param_globals::imp_region_emi[i].ID[j];
155  size_t colon_pos = t1t2.find(':');
156  if (colon_pos == std::string::npos) {
157  std::cerr << "Error: ':' the face tags are not defined properly in input file, it should be tag1:tag2 as a string" << std::endl;
158  exit(1);
159  }
160 
161  rs[i].subregtags[j] = param_globals::imp_region_emi[i].ID[j]; // Convert char* to std::string.
162  }
163  }
164 
165  SF::vector<int> reg_mask;
166  region_mask_onFace(ion_domain, tags_data, line_face, tri_face, quad_face,
167  map_vertex_tag_to_dof, map_elem_uniqueFace_to_tags, intra_tags,
168  rs, reg_mask, true, "imp_region_emi");
169 
170  for (size_t i = 0; i < rs.size(); i++ ) {
171  delete[] rs[i].subregtags;
172  rs[i].subregtags = nullptr;
173  }
174 
175  int purkfLen = 1;
176  tstart = setup_MIIF(loc_size, num_imp_regions, param_globals::imp_region_emi,
177  reg_mask.data(), param_globals::start_statef, param_globals::num_adjustments,
178  param_globals::adjustment, param_globals::dt, purkfLen > 0);
179 
180  miif->extUpdateVm = !param_globals::operator_splitting;
181 
182  // if we start at a non-zero time (i.e. we have restored a state), we notify the
183  // timer manager
184  if(tstart > 0.) {
185  log_msg(logger, 0, 0, "Changing simulation start time to %.2lf", tstart);
186  user_globals::tm_manager->setup(param_globals::dt, tstart, param_globals::tend);
188  }
189 
190  set_dir(INPUT);
191 
192  this->initialize_time += timing(t2, t1);
193 }
194 
195 double IonicsOnFace::setup_MIIF(limpet::node_count_t nnodes, int nreg, IMPregion_EMI* impreg, int* mask,
196  const char *start_fn, int numadjust, IMPVariableAdjustment *adjust,
197  double time_step, bool close)
198 {
199  double tstart = 0;
200 
201  miif->N_IIF = nreg;
202  miif->numNode = nnodes;
203  miif->iontypes = {};
204  miif->numplugs = (int*)calloc( miif->N_IIF, sizeof(int));
205  miif->plugtypes = std::vector<limpet::IonTypeList>(miif->N_IIF);
206  miif->targets = std::vector<limpet::Target>(miif->N_IIF, limpet::Target::AUTO);
207 
208  log_msg(logger,0,ECHO, "\nSetting up ionic models on EMI Face and plugins\n" \
209  "-----------------------------------\n\n" \
210  "Assigning IMPS to tagged regions:" );
211 
212  for (int i=0;i<miif->N_IIF;i++) {
213  auto pT = limpet::get_ion_type(std::string(impreg[i].im));
214  if (pT != NULL)
215  {
216  miif->iontypes.push_back(*pT);
217  log_msg(logger, 0, ECHO|NONL, "\tIonic model: %s to tag region(s)", impreg[i].im);
218 
219  if(impreg[i].num_IDs > 0) {
220  for(int j = 0; j < impreg[i].num_IDs; j++)
221  log_msg(logger,0,ECHO|NONL, " [%s],", impreg[i].ID[j]);
222  log_msg(logger,0,ECHO,"\b.");
223  }
224  else {
225  log_msg(logger,0,ECHO, " [0] (implicitely)");
226  }
227  }
228  else {
229  log_msg(NULL,5,ECHO, "Illegal IM specified: %s\n", impreg[i].im );
230  log_msg(NULL,5,ECHO, "Run bench --list-imps for a list of all available models.\n" );
231  EXIT(1);
232  }
233  if (limpet::get_plug_flag( impreg[i].plugins, &miif->numplugs[i], miif->plugtypes[i]))
234  {
235  if(impreg[i].plugins[0] != '\0') {
236  log_msg(logger,0, ECHO|NONL, "\tPlug-in(s) : %s to tag region(s)", impreg[i].plugins);
237 
238  for(int j = 0; j < impreg[i].num_IDs; j++)
239  log_msg(logger,0,ECHO|NONL, " [%d],", impreg[i].ID[j]);
240  log_msg(logger,0,ECHO,"\b.");
241  }
242  }
243  else {
244  log_msg(NULL,5,ECHO,"Illegal plugin specified: %s\n", impreg[i].plugins);
245  log_msg(NULL,5,ECHO, "Run bench --list-imps for a list of all available plugins.\n" );
246  EXIT(1);
247  }
248  }
249 
250  miif->IIFmask = (limpet::IIF_Mask_t*)calloc(miif->numNode, sizeof(limpet::IIF_Mask_t));
251 
252  // The mask is element-wise on the EMI unique-face mesh: one ionic/gap-junction
253  // region id per local unique-face element.
254  if (mask) {
255  for (limpet::node_index_t i=0; i<miif->numNode; i++)
256  miif->IIFmask[i] = (limpet::IIF_Mask_t) mask[i];
257  }
258 
259  miif->initialize_MIIF();
260 
261  for (int i=0;i<miif->N_IIF;i++) {
262  // the IMP tuning does not handle spaces well, thus we remove them here
263  remove_char(impreg[i].im_param, strlen(impreg[i].im_param), ' ');
264  miif->IIF[i]->tune(impreg[i].im_param, impreg[i].plugins, impreg[i].plug_param);
265  }
266 
267  set_dir(INPUT);
268  miif->initialize_currents(time_step, param_globals::ode_fac);
269 
270  // overriding initial values goes here
271  // read in single cell state vector and spread it out over the entire region
272  set_dir(INPUT);
273  for (int i=0;i<miif->N_IIF;i++) {
274  if (impreg[i].im_sv_init && strlen(impreg[i].im_sv_init) > 0)
275  if (read_sv(miif, i, impreg[i].im_sv_init)) {
276  log_msg(NULL, 5, ECHO|FLUSH, "State vector initialization failed for %s.\n", impreg[i].name);
277  EXIT(-1);
278  }
279  }
280 
281  if( !start_fn || strlen(start_fn)>0 )
282  tstart = (double) miif->restore_state(start_fn, ion_domain, close);
283 
284  for (int i=0; i<numadjust; i++)
285  {
286  set_dir(INPUT);
287 
288  SF::vector<SF_int> indices;
289  SF::vector<double> values;
290  bool restrict_to_algebraic = true;
291 
292  sf_mesh & imesh = get_mesh(ion_domain);
293  std::map<std::string,std::string> metadata;
294  read_metadata(adjust[i].file, metadata, PETSC_COMM_WORLD);
295 
296  SF::SF_nbr nbr = SF::NBR_REF;
297  if(metadata.count("grid") && metadata["grid"].compare("intra") == 0) {
298  nbr = SF::NBR_SUBMESH;
299  }
300  read_indices_with_data(indices, values, adjust[i].file, imesh, nbr, restrict_to_algebraic, 1, PETSC_COMM_WORLD);
301 
302  int rank = get_rank();
303 
304  for(size_t gi = 0; gi < indices.size(); gi++)
305  indices[gi] = SF::local_nodal_to_local_petsc<mesh_int_t, mesh_real_t>(imesh, rank, indices[gi]);
306 
307  // debug, output parameters on global intracellular vector
308  if(adjust[i].dump) {
309  sf_vec* adjPars;
310  SF::init_vector(&adjPars, imesh, 1, sf_vec::algebraic);
311  adjPars->set(indices, values, false, true);
312 
313  set_dir(OUTPUT);
314  char fname[2085];
315  snprintf(fname, sizeof fname, "adj_%s_perm.dat", adjust[i].variable);
316  adjPars->write_ascii(fname, false);
317 
318  // get the scattering to the canonical permutation
319  SF::scattering* sc = get_permutation(ion_domain, PETSC_TO_CANONICAL, 1);
320  if(sc == NULL) {
321  log_msg(0,3,0, "%s warning: PETSC_TO_CANONICAL permutation needed registering!", __func__);
322  sc = register_permutation(ion_domain, PETSC_TO_CANONICAL, 1);
323  }
324 
325  (*sc)(*adjPars, true);
326  snprintf(fname, sizeof fname, "adj_%s_canonical.dat", adjust[i].variable);
327  adjPars->write_ascii(fname, false);
328  }
329 
330  int nc = miif->adjust_MIIF_variables(adjust[i].variable, indices, values);
331  log_msg(logger, 0, 0, "Adjusted %d values for %s", nc, adjust[i].variable);
332  }
333 
334  set_dir(OUTPUT);
335 
336  for (int i=0;i<miif->N_IIF;i++)
337  initialize_sv_dumps_onFace(miif, impreg+i, i, tstart, param_globals::spacedt);
338 
339  return tstart;
340 }
341 
353 void initialize_sv_dumps_onFace(limpet::MULTI_IF *pmiif, IMPregion_EMI* reg, int id, double t, double dump_dt)
354 {
355  char svs[1024], plgs[1024], plgsvs[1024], fname[1024];
356 
357  strcpy(svs, reg->im_sv_dumps ? reg->im_sv_dumps : "");
358  strcpy(plgs, reg->plugins ? reg->plugins : "");
359  strcpy(plgsvs, reg->plug_sv_dumps ? reg->plug_sv_dumps : "");
360 
361  if( !(strlen(svs)+strlen(plgsvs) ) )
362  return;
363 
364  /* The string passed to the "reg_name" argument (#4) of the sv_dump_add
365  * function is supposed to be "region name". It's only purpose is to
366  * provide the base name for the SV dump file, eg: Purkinje.Ca_i.bin.
367  * Thus, we pass: [vofile].[reg name], Otherwise, dumping SVs in
368  * batched runs would be extremely tedious.
369  */
370  strcpy(fname, param_globals::vofile); // [vofile].igb
371 
372  if( !reg->name ) {
373  log_msg(NULL, 5, ECHO, "%s: a region name must be specified\n", __func__ );
374  exit(0);
375  }
376 
377  // We want to convert vofile.igb to vofile.regname
378  size_t fname_len = strlen(fname);
379  char* ext_start = fname + fname_len;
380  if(fname_len >= 4 && strcmp(ext_start - 4, ".igb") == 0) ext_start -= 3;
381  strcpy(ext_start, reg->name);
382 
383  pmiif->sv_dump_add_by_name_list(id, reg->im, fname, svs, plgs, plgsvs, t, dump_dt);
384 }
385 
396 bool check_tags_in_elems_onFace(std::vector<std::string> & tags_data, SF::vector<RegionSpecs_EMI> & regspec,
397  const char* gridname, const char* reglist)
398 {
399  bool AllTagsExist = true;
401 
402  tagset.insert(tags_data.begin(), tags_data.end());
403 
404  // cycle through all user-specified regions
405  for (size_t reg=0; reg<regspec.size(); reg++)
406  // cycle through all tags which belong to the region
407  for (int k=0; k<regspec[reg].nsubregs; k++) {
408  // check whether this tag exists in element list
409  int n = 0;
410  size_t colon_pos = regspec[reg].subregtags[k].find(':');
411  int tag1 = std::stoi(regspec[reg].subregtags[k].substr(0, colon_pos));
412  int tag2 = std::stoi(regspec[reg].subregtags[k].substr(colon_pos + 1));
413 
414  // considered the other combinations of tags
415  std::string inverse_pair;
416  inverse_pair = std::to_string(tag2) + ":" + std::to_string(tag1);
417  if(tagset.count(regspec[reg].subregtags[k]) || tagset.count(inverse_pair)) {
418  n++;
419  }
420 
421  // globalize n
422  int N = get_global(n, MPI_SUM);
423 
424  if (N==0) {
425  log_msg(NULL, 3, ECHO,
426  "on face Region tag %s in %s[%d] not found in element list for %s grid.\n",
427  regspec[reg].subregtags[k].c_str(), reglist, reg, gridname);
428  AllTagsExist = false;
429  }
430  }
431 
432  if (!AllTagsExist) {
433  log_msg(NULL, 4, ECHO,"Assigned wrong pair of tags on the face of EMI surface mesh!\n"
434  "Check region ID specs in input files!\n");
435  }
436 
437  return AllTagsExist;
438 }
439 
440 inline bool pair_is_gapjunction(const std::pair<mesh_int_t, mesh_int_t>& tags,
441  const hashmap::unordered_set<int>& intra_tags)
442 {
443  return intra_tags.find(tags.first) != intra_tags.end() &&
444  intra_tags.find(tags.second) != intra_tags.end();
445 }
446 
447 void region_mask_onFace(mesh_t meshspec,
448  std::vector<std::string> & tags_data,
450  std::pair<SF::emi_face<mesh_int_t,SF::tuple<mesh_int_t>>,
451  SF::emi_face<mesh_int_t,SF::tuple<mesh_int_t>>>> & line_face,
453  std::pair<SF::emi_face<mesh_int_t,SF::triple<mesh_int_t>>,
454  SF::emi_face<mesh_int_t,SF::triple<mesh_int_t>>>> & tri_face,
456  std::pair<SF::emi_face<mesh_int_t,SF::quadruple<mesh_int_t>>,
457  SF::emi_face<mesh_int_t,SF::quadruple<mesh_int_t>>>> & quad_face,
458  hashmap::unordered_map<std::pair<mesh_int_t,mesh_int_t>, mesh_int_t> & map_vertex_tag_to_dof,
459  hashmap::unordered_map<mesh_int_t, std::pair<mesh_int_t, mesh_int_t>> & map_elem_uniqueFace_to_tags,
460  const hashmap::unordered_set<int> & intra_tags,
461  SF::vector<RegionSpecs_EMI> & regspec,
462  SF::vector<int> & regionIDs, bool mask_elem, const char* reglist)
463 {
464  if(regspec.size() == 1) return;
465 
466  sf_mesh & mesh = get_mesh(meshspec);
468 
469  // Initialize the list with the default membrane region id, 0.
470  size_t rIDsize = mask_elem ? mesh.l_numelem : mesh.l_numpts;
471 
472  size_t nelem = mesh.l_numelem;
473 
474  // Check whether all specified face-tag pairs exist in the unique-face list.
475  check_tags_in_elems_onFace(tags_data, regspec, mesh.name.c_str(), reglist);
476 
477  regionIDs.assign(rIDsize, 0);
478 
479  int* rid = regionIDs.data();
481 
482  // Generate a map from face-tag pair strings to region IDs. This also lets us
483  // detect duplicate assignments and simplifies the regionIDs fill loop.
484  for (size_t reg=0; reg < regspec.size(); reg++) {
485  int err = 0;
486  for (int k=0; k < regspec[reg].nsubregs; k++) {
487  std::string curtag = regspec[reg].subregtags[k];
488  if(tag_to_reg.count(curtag)) err++;
489 
490  if(get_global(err, MPI_SUM))
491  log_msg(0,4,0, "%s warning: Tag idx %s is assigned to multiple regions!\n"
492  "Its final assignment will be to the highest assigned region ID!",
493  __func__, curtag.c_str());
494 
495  tag_to_reg[curtag] = reg;
496  }
497  }
498 
499  std::vector<int> mx_tag( rIDsize, -1 );
500 
501  // Cycle through unique-face elements and assign default or user-defined regions.
502  for(size_t eidx=0; eidx<nelem; eidx++)
503  {
504  std::vector<int> elem_nodes;
505  mesh_int_t tag = mesh.tag[eidx];
506  std::string result_pair_orginal;
507  std::string result_pair_reverse;
508  std::pair<mesh_int_t,mesh_int_t> value = map_elem_uniqueFace_to_tags[eidx];
509  result_pair_orginal = std::to_string(value.first) + ":" + std::to_string(value.second);
510  result_pair_reverse = std::to_string(value.second) + ":" + std::to_string(value.first);
511  rid[eidx] = pair_is_gapjunction(value, intra_tags) ? 1 : 0;
512 
513  if (tag_to_reg.count(result_pair_orginal)) {
514  rid[eidx] = tag_to_reg[result_pair_orginal];
515  } else if(tag_to_reg.count(result_pair_reverse)){
516  rid[eidx] = tag_to_reg[result_pair_reverse];
517  }
518  if(tag!=value.first){
519  log_msg(NULL, 5, ECHO, "error in tags on surface mesh with the unique faces!!!.\\n");
520  exit(1);
521  }
522  }
523 }
524 
527 double IonicsOnFace::timer_val(const int timer_id)
528 {
529  double val = std::nan("NaN");
530  return val;
531 }
532 
535 std::string IonicsOnFace::timer_unit(const int timer_id)
536 {
537  std::string s_unit;
538  return s_unit;
539 }
540 
541 void compute_IIF_OnFace(limpet::IonIfBase& pIF, limpet::GlobalData_t** impdata, limpet::node_index_t n)
542 {
543  if (impdata[limpet::Iion] != NULL)
544  impdata[limpet::Iion][n] = 0;
545 
546  pIF.for_each([&](limpet::IonIfBase& imp) {
547  update_ts(&imp.get_tstp());
548  imp.compute(n, n + 1, impdata);
549  });
550 }
551 
563 void* find_SV_in_IMP_onFace(limpet::MULTI_IF* miif, const int idx, const char *IMP, const char *SV,
564  int* offset, int* sz)
565 {
566  if(strcmp(IMP, miif->iontypes[idx].get().get_name().c_str()) == 0) {
567  return (void*) miif->iontypes[idx].get().get_sv_offset(SV, offset, sz);
568  }
569  else {
570  for( int k=0; k<miif->numplugs[idx]; k++ )
571  if(strcmp(IMP, miif->plugtypes[idx][k].get().get_name().c_str()) == 0) {
572  return (void*) miif->plugtypes[idx][k].get().get_sv_offset(SV, offset, sz);
573  }
574  }
575 
576  return NULL;
577 }
578 
579 
590 void alloc_gvec_data_onFace(const int nGVcs, const int nRegs, const int nEmiRegs,
591  GVecs *prmGVecs, gvec_data_OnFace &glob_vecs)
592 {
593  glob_vecs.nRegs = nRegs;
594 
595  if (nGVcs) {
596  glob_vecs.vecs.resize(nGVcs);
597 
598  for (size_t i = 0; i < glob_vecs.vecs.size(); i++) {
599  sv_data_onFace &gvec = glob_vecs.vecs[i];
600 
601  gvec.name = dupstr(prmGVecs[i].name);
602  gvec.units = dupstr(prmGVecs[i].units);
603  gvec.bogus = prmGVecs[i].bogus;
604 
605  gvec.imps = (char**) calloc(nRegs, sizeof(char *));
606  gvec.svNames = (char**) calloc(nRegs, sizeof(char *));
607  gvec.svSizes = (int*) calloc(nRegs, sizeof(int));
608  gvec.svOff = (int*) calloc(nRegs, sizeof(int));
609  gvec.getsv = (void**) calloc(nRegs, sizeof(limpet::SVgetfcn));
610 
611  for (int j = 0; j < nRegs; j++) {
612  if (strlen(prmGVecs[i].imp)) gvec.imps[j] = dupstr(prmGVecs[i].imp);
613 #ifdef WITH_PURK
614  else if (j < nEmiRegs)
615  gvec.imps[j] = dupstr(param_globals::imp_region_emi[j].im);
616  else
617  gvec.imps[j] = dupstr(param_globals::PurkIon[j - nEmiRegs].im);
618 #else
619  else if (j < nEmiRegs)
620  gvec.imps[j] = dupstr(param_globals::imp_region_emi[j].im);
621 #endif
622  gvec.svNames[j] = dupstr(prmGVecs[i].ID[j]);
623  }
624  }
625  }
626 }
627 
638 void init_sv_gvec_onFace(gvec_data_OnFace& GVs, limpet::MULTI_IF* miif, sf_vec & tmpl,
639  igb_output_manager & output_manager)
640 {
641  GVs.inclPS = false;
642  // int num_purk_regions = GVs->inclPS ? purk->ion.N_IIF : 0;
643  int num_purk_regions = 0;
644  int nEmiRegs = effective_num_imp_regions_emi();
645  int nRegs = nEmiRegs + num_purk_regions;
646 
647  alloc_gvec_data_onFace(param_globals::num_gvecs, nRegs, nEmiRegs, param_globals::gvec, GVs);
648 
649 #ifdef WITH_PURK
650  if (GVs->inclPS) sample_PS_ionSVs(purk);
651 #endif
652 
653  for (unsigned int i = 0; i < GVs.vecs.size(); i++) {
654  sv_data_onFace & gv = GVs.vecs[i];
655  int noSV = 0;
656 
657  for (int j = 0; j < miif->N_IIF; j++) {
658  gv.getsv[j] = find_SV_in_IMP_onFace(miif, j, gv.imps[j], gv.svNames[j],
659  gv.svOff + j, gv.svSizes + j);
660 
661  if (gv.getsv[j] == NULL) {
662  log_msg(NULL, 3, ECHO, "\tWarning: SV(%s) not found in region %d\n", gv.svNames[j], j);
663  noSV++;
664  }
665  }
666 
667  SF::init_vector(&gv.ordered, &tmpl);
668  output_manager.register_output(gv.ordered, intra_elec_msh, 1, gv.name, gv.units);
669 
670 #ifdef WITH_PURK
671  // same procedure for Purkinje
672  if (GVs->inclPS) {
673  IF_PURK_PROC(purk) {
674  MULTI_IF* pmiif = &purk->ion;
675  for (int j = miif->N_IIF; j < nRegs; j++) {
676  gv.getsv[j] = find_SV_in_IMP_onFace(pmiif, j - miif->N_IIF, gv.imps[j],
677  gv.svNames[j], gv.svOff + j, gv.svSizes + j);
678 
679  if (gv.getsv[j] == NULL) {
680  LOG_MSG(NULL, 3, ECHO, "\tWarning: state variable \"%s\" not found in region %d\n", gv.svNames[j], j);
681  noSV++;
682  }
683  }
684  RVector_dup(purk->vm_pt, &gv.orderedPS_PS);
685  MYO_COMM(purk);
686  }
687  RVector_dup(purk->vm_pt_over, &gv.orderedPS);
688  initialize_grid_output(grid, NULL, tmo, intra_elec_msh, GRID_WRITE, 0., 1., gv.units, gv.orderedPS,
689  1, gv.GVcName, -purk->npt, param_globals::output_level);
690  }
691 #endif
692 
693  if (noSV == nRegs) {
694  log_msg(NULL, 5, ECHO, "\tError: no state variables found for global vector %d\n", i);
695  log_msg(NULL, 5, ECHO, "Run bench --imp=YourModel --imp-info to get a list of all parameters.\\n");
696  exit(1);
697  }
698  }
699 }
700 
710 void assemble_sv_gvec_onFace(gvec_data_OnFace & gvecs, limpet::MULTI_IF *miif)
711 {
712  for(size_t i=0; i<gvecs.vecs.size(); i++ ) {
713  sv_data_onFace & gv = gvecs.vecs[i];
714 
715  // Set to the default value.
716  gv.ordered->set(gv.bogus);
717  SF_int start, stop;
718  gv.ordered->get_ownership_range(start, stop);
719 
720  for( int n = 0; n<miif->N_IIF; n++ ) {
721  if( !gv.getsv[n] ) continue;
722 
723  SF::vector<SF_real> data (miif->N_Nodes[n]);
724  SF::vector<SF_int> indices(miif->N_Nodes[n]);
725 
726  for( limpet::node_index_t j=0; j<miif->N_Nodes[n]; j++ ) {
727  indices[j] = miif->NodeLists[n][j] + start;
728  data[j] = ((limpet::SVgetfcn)(gv.getsv[n]))( *miif->IIF[n], j, gv.svOff[n]);
729  }
730 
731  bool add = false;
732  gv.ordered->set(indices, data, add);
733  }
734 
735 #ifdef WITH_PURK
736  // include purkinje in sv dump, if exists
737  if(gvecs->inclPS) {
738  RVector_set( gv.orderedPS, gv.bogus ); // set this to the default value
739 
740  if(IS_PURK_PROC(purk))
741  {
742  MULTI_IF *pmiif = &purk->ion;
743  Real *data = new Real[purk->gvec_cab.nitems];
744 
745  int ci=0;
746  for( int n = 0; n<pmiif->N_IIF; n++ ) {
747  int gvidx = n+miif->N_IIF;
748  if( !gv.getsv[gvidx] ) continue;
749  for( int j=0; j<purk->gvec_ion[n].nitems; j++ )
750  data[ci++] = ((SVgetfcn)(gv.getsv[gvidx]))( pmiif->IIF+n, ((int*)(purk->gvec_ion[n].data))[j],
751  gv.svOff[gvidx]);
752  }
753  RVector_setvals(gv.orderedPS, purk->gvec_cab.nitems, (int*)purk->gvec_cab.data, data, true);
754  delete [] data;
755  }
756  RVector_sync( gv.orderedPS );
757  }
758 #endif
759  }
760 }
761 
762 
763 } // namespace opencarp
764 #endif
double Real
Definition: DataTypes.h:13
opencarp::local_index_t mesh_int_t
Definition: SF_container.h:46
opencarp::global_index_t SF_int
Global algebraic index type.
Definition: SF_globals.h:32
#define FLUSH
Definition: basics.h:319
#define ECHO
Definition: basics.h:316
#define NONL
Definition: basics.h:320
Comfort class. Provides getter functions to access the mesh member variables more comfortably.
Definition: SF_fem_utils.h:704
Container for a PETSc VecScatter.
A vector storing arbitrary data.
Definition: SF_vector.h:43
size_t size() const
The current size of the vector.
Definition: SF_vector.h:104
void assign(InputIterator s, InputIterator e)
Assign a memory range.
Definition: SF_vector.h:161
T * data()
Pointer to the vector's start.
Definition: SF_vector.h:91
hm_int count(const K &key) const
Check if key exists.
Definition: hashmap.hpp:627
Custom unordered_set implementation.
Definition: hashmap.hpp:754
iterator find(const K &key)
Definition: hashmap.hpp:1096
hm_int count(const K &key) const
Definition: hashmap.hpp:1082
void insert(InputIterator first, InputIterator last)
Definition: hashmap.hpp:1052
Represents the ionic model and plug-in (IMP) data structure.
Definition: ION_IF.h:142
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
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:511
std::vector< IonIfBase * > IIF
array of IIF's
Definition: MULTI_ION_IF.h:213
void sv_dump_add_by_name_list(int, char *, char *, char *, char *, char *, double, double)
int * numplugs
number of plugins for each region
Definition: MULTI_ION_IF.h:220
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
int N_IIF
how many different IIF's
Definition: MULTI_ION_IF.h:222
node_count_t * N_Nodes
#nodes for each IMP
Definition: MULTI_ION_IF.h:211
node_index_t ** NodeLists
local partitioned node lists for each IMP stored
Definition: MULTI_ION_IF.h:212
void setup(double inp_dt, double inp_start, double inp_end)
Initialize the timer_manager.
Definition: timer_utils.cc:36
void reset_timers()
Reset time in timer_manager and then reset registered timers.
Definition: timer_utils.h:115
LIMPET ionics and gap-junction models on the EMI unique-face interface mesh.
void init_vector(SF::abstract_vector< T, S > **vec)
Definition: SF_init.h:107
SF_nbr
Enumeration encoding the different supported numberings.
Definition: SF_container.h:200
@ NBR_PETSC
PETSc numbering of nodes.
Definition: SF_container.h:203
@ NBR_REF
The nodal numbering of the reference mesh (the one stored on HD).
Definition: SF_container.h:201
@ NBR_SUBMESH
Submesh nodal numbering: The globally ascending sorted reference indices are reindexed.
Definition: SF_container.h:202
int get_plug_flag(char *plgstr, int *out_num_plugins, IonTypeList &out_plugins)
@ AUTO
Definition: target.h:46
IonType * get_ion_type(const std::string &name)
SF_real GlobalData_t
Definition: limpet_types.h:27
GlobalData_t(* SVgetfcn)(IonIfBase &, node_index_t, int)
Definition: ion_type.h:48
void update_ts(ts *ptstp)
Definition: ION_IF.cc:560
opencarp::local_index_t node_count_t
Definition: limpet_types.h:29
int read_sv(MULTI_IF *, int, const char *)
char IIF_Mask_t
Definition: ion_type.h:50
opencarp::local_index_t node_index_t
Definition: limpet_types.h:28
std::map< int, std::string > units
Definition: stimulate.cc:41
timer_manager * tm_manager
a manager for the various physics timers
Definition: main.cc:55
@ iotm_console
Definition: timer_utils.h:44
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
int set_dir(IO_t dest)
Definition: sim_utils.cc:1582
void read_metadata(const std::string filename, std::map< std::string, std::string > &metadata, MPI_Comm comm)
Read metadata from the header.
Definition: fem_utils.cc:72
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
SF::scattering * register_permutation(const int mesh_id, const int perm_id, const int dpn)
Register a permutation between two orderings for a mesh.
void register_data(sf_vec *dat, datavec_t d)
Register a data vector in the global registry.
Definition: sim_utils.cc:2056
@ OUTPUT
Definition: sim_utils.h:54
char * dupstr(const char *old_str)
Definition: basics.cc:44
void log_msg(FILE_SPEC out, int level, unsigned char flag, const char *fmt,...)
Definition: basics.cc:72
mesh_t
The enum identifying the different meshes we might want to load.
Definition: sf_interface.h:59
@ intra_elec_msh
Definition: sf_interface.h:60
void get_time(double &tm)
Definition: basics.h:444
SF::abstract_vector< SF_int, SF_real > sf_vec
Definition: sf_interface.h:50
void remove_char(char *buff, const int buffsize, const char c)
Definition: basics.h:364
void read_indices_with_data(SF::vector< T > &idx, SF::vector< S > &dat, const std::string filename, const hashmap::unordered_map< mesh_int_t, mesh_int_t > &dd_map, const int dpn, MPI_Comm comm)
like read_indices, but with associated data for each index
Definition: fem_utils.h:269
V timing(V &t2, const V &t1)
Definition: basics.h:456
#define PETSC_TO_CANONICAL
Permute algebraic data from PETSC to canonical ordering.
Definition: sf_interface.h:79