24 bool imp_region_emi_is_assigned(
const IMPregion_EMI& region,
int idx)
26 if (!(region.im && strlen(region.im) > 0))
return false;
27 if (idx < 2)
return true;
28 return region.num_IDs > 0;
33 int effective_num_imp_regions_emi()
35 const int configured_nreg = param_globals::num_imp_regions;
36 int nreg = configured_nreg;
37 while (nreg > 2 && !imp_region_emi_is_assigned(param_globals::imp_region_emi[nreg - 1], nreg - 1)) {
41 if (nreg < configured_nreg) {
42 static bool warned_once =
false;
45 if (!warned_once && rank == 0) {
47 "Warning: num_imp_regions=%d but only %d imp_region_emi entries are assigned; ignoring trailing unassigned regions.\n",
48 configured_nreg, nreg);
58 void IonicsOnFace::compute_step()
63 miif->compute_ionic_current();
65 double comp_time =
timing(t2, t1);
66 this->compute_time += comp_time;
69 comp_stats.tot_time += comp_time;
75 void IonicsOnFace::destroy()
80 void IonicsOnFace::output_step()
83 void IonicsOnFace::initialize()
91 comp_stats.init_logger(
"ODE_stats.dat");
109 miif->logger = logger;
111 miif->name =
"myocardium";
112 miif->gdata[limpet::Vm] = Vmv;
113 miif->gdata[limpet::Iion] = IIon;
115 const int num_imp_regions = effective_num_imp_regions_emi();
119 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");
120 log_msg(NULL, 5,
ECHO,
"set a default ionic model and gapjuntion model in parameter file\\n");
128 rs[0].subregtags =
nullptr;
130 rs[1].subregtags =
nullptr;
133 for (
size_t i = 2; i < rs.size(); i++ ) {
134 rs[i].nsubregs = param_globals::imp_region_emi[i].num_IDs;
135 rs[i].subregtags =
new std::string[rs[i].nsubregs];
136 for (
int j = 0; j < rs[i].nsubregs;j++) {
137 std::string t1t2 = param_globals::imp_region_emi[i].ID[j];
138 size_t colon_pos = t1t2.find(
':');
139 if (colon_pos == std::string::npos) {
140 std::cerr <<
"Error: ':' the face tags are not defined properly in input file, it should be tag1:tag2 as a string" << std::endl;
144 rs[i].subregtags[j] = param_globals::imp_region_emi[i].ID[j];
149 region_mask_onFace(ion_domain, tags_data, line_face, tri_face, quad_face,
150 map_vertex_tag_to_dof, map_elem_uniqueFace_to_tags, intra_tags,
151 rs, reg_mask,
true,
"imp_region_emi");
153 for (
size_t i = 0; i < rs.size(); i++ ) {
154 delete[] rs[i].subregtags;
155 rs[i].subregtags =
nullptr;
159 tstart = setup_MIIF(loc_size, num_imp_regions, param_globals::imp_region_emi,
160 reg_mask.
data(), param_globals::start_statef, param_globals::num_adjustments,
161 param_globals::adjustment, param_globals::dt, purkfLen > 0);
163 miif->extUpdateVm = !param_globals::operator_splitting;
168 log_msg(logger, 0, 0,
"Changing simulation start time to %.2lf", tstart);
175 this->initialize_time +=
timing(t2, t1);
178 double IonicsOnFace::setup_MIIF(
limpet::node_count_t nnodes,
int nreg, IMPregion_EMI* impreg,
int* mask,
179 const char *start_fn,
int numadjust, IMPVariableAdjustment *adjust,
180 double time_step,
bool close)
185 miif->numNode = nnodes;
187 miif->numplugs = (
int*)calloc( miif->N_IIF,
sizeof(
int));
188 miif->plugtypes = std::vector<limpet::IonTypeList>(miif->N_IIF);
191 log_msg(logger,0,
ECHO,
"\nSetting up ionic models on EMI Face and plugins\n" \
192 "-----------------------------------\n\n" \
193 "Assigning IMPS to tagged regions:" );
195 for (
int i=0;i<miif->N_IIF;i++) {
199 miif->iontypes.push_back(*pT);
200 log_msg(logger, 0,
ECHO|
NONL,
"\tIonic model: %s to tag region(s)", impreg[i].im);
202 if(impreg[i].num_IDs > 0) {
203 for(
int j = 0; j < impreg[i].num_IDs; j++)
212 log_msg(NULL,5,
ECHO,
"Illegal IM specified: %s\n", impreg[i].im );
213 log_msg(NULL,5,
ECHO,
"Run bench --list-imps for a list of all available models.\n" );
218 if(impreg[i].plugins[0] !=
'\0') {
219 log_msg(logger,0,
ECHO|
NONL,
"\tPlug-in(s) : %s to tag region(s)", impreg[i].plugins);
221 for(
int j = 0; j < impreg[i].num_IDs; j++)
227 log_msg(NULL,5,
ECHO,
"Illegal plugin specified: %s\n", impreg[i].plugins);
228 log_msg(NULL,5,
ECHO,
"Run bench --list-imps for a list of all available plugins.\n" );
242 miif->initialize_MIIF();
244 for (
int i=0;i<miif->N_IIF;i++) {
246 remove_char(impreg[i].im_param, strlen(impreg[i].im_param),
' ');
247 miif->IIF[i]->tune(impreg[i].im_param, impreg[i].plugins, impreg[i].plug_param);
251 miif->initialize_currents(time_step, param_globals::ode_fac);
256 for (
int i=0;i<miif->N_IIF;i++) {
257 if (impreg[i].im_sv_init && strlen(impreg[i].im_sv_init) > 0)
258 if (
read_sv(miif, i, impreg[i].im_sv_init)) {
259 log_msg(NULL, 5,
ECHO|
FLUSH,
"State vector initialization failed for %s.\n", impreg[i].name);
264 if( !start_fn || strlen(start_fn)>0 )
265 tstart = (double) miif->restore_state(start_fn, ion_domain, close);
267 for (
int i=0; i<numadjust; i++)
273 bool restrict_to_algebraic =
true;
276 std::map<std::string,std::string> metadata;
280 if(metadata.count(
"grid") && metadata[
"grid"].compare(
"intra") == 0) {
287 for(
size_t gi = 0; gi < indices.
size(); gi++)
288 indices[gi] = SF::local_nodal_to_local_petsc<mesh_int_t, mesh_real_t>(imesh, rank, indices[gi]);
294 adjPars->set(indices, values,
false,
true);
298 snprintf(fname,
sizeof fname,
"adj_%s_perm.dat", adjust[i].variable);
299 adjPars->write_ascii(fname,
false);
304 log_msg(0,3,0,
"%s warning: PETSC_TO_CANONICAL permutation needed registering!", __func__);
308 (*sc)(*adjPars,
true);
309 snprintf(fname,
sizeof fname,
"adj_%s_canonical.dat", adjust[i].variable);
310 adjPars->write_ascii(fname,
false);
313 int nc = miif->adjust_MIIF_variables(adjust[i].variable, indices, values);
314 log_msg(logger, 0, 0,
"Adjusted %d values for %s", nc, adjust[i].variable);
331 const char* gridname,
const char* reglist)
333 bool AllTagsExist =
true;
336 tagset.
insert(tags_data.begin(), tags_data.end());
339 for (
size_t reg=0; reg<regspec.
size(); reg++)
341 for (
int k=0; k<regspec[reg].nsubregs; k++) {
344 size_t colon_pos = regspec[reg].subregtags[k].find(
':');
345 int tag1 = std::stoi(regspec[reg].subregtags[k].substr(0, colon_pos));
346 int tag2 = std::stoi(regspec[reg].subregtags[k].substr(colon_pos + 1));
349 std::string inverse_pair;
350 inverse_pair = std::to_string(tag2) +
":" + std::to_string(tag1);
351 if(tagset.
count(regspec[reg].subregtags[k]) || tagset.
count(inverse_pair)) {
360 "on face Region tag %s in %s[%d] not found in element list for %s grid.\n",
361 regspec[reg].subregtags[k].c_str(), reglist, reg, gridname);
362 AllTagsExist =
false;
367 log_msg(NULL, 4,
ECHO,
"Assigned wrong pair of tags on the face of EMI surface mesh!\n"
368 "Check region ID specs in input files!\n");
374 inline bool pair_is_gapjunction(
const std::pair<mesh_int_t, mesh_int_t>& tags,
377 return intra_tags.
find(tags.first) != intra_tags.
end() &&
378 intra_tags.
find(tags.second) != intra_tags.
end();
381 void region_mask_onFace(
mesh_t meshspec,
382 std::vector<std::string> & tags_data,
398 if(regspec.
size() == 1)
return;
404 size_t rIDsize = mask_elem ? mesh.l_numelem : mesh.l_numpts;
406 size_t nelem = mesh.l_numelem;
409 check_tags_in_elems_onFace(tags_data, regspec, mesh.name.c_str(), reglist);
411 regionIDs.
assign(rIDsize, 0);
413 int* rid = regionIDs.
data();
418 for (
size_t reg=0; reg < regspec.
size(); reg++) {
420 for (
int k=0; k < regspec[reg].nsubregs; k++) {
421 std::string curtag = regspec[reg].subregtags[k];
422 if(tag_to_reg.
count(curtag)) err++;
425 log_msg(0,4,0,
"%s warning: Tag idx %s is assigned to multiple regions!\n"
426 "Its final assignment will be to the highest assigned region ID!",
427 __func__, curtag.c_str());
429 tag_to_reg[curtag] = reg;
433 std::vector<int> mx_tag( rIDsize, -1 );
436 for(
size_t eidx=0; eidx<nelem; eidx++)
438 std::vector<int> elem_nodes;
440 std::string result_pair_orginal;
441 std::string result_pair_reverse;
442 std::pair<mesh_int_t,mesh_int_t> value = map_elem_uniqueFace_to_tags[eidx];
443 result_pair_orginal = std::to_string(value.first) +
":" + std::to_string(value.second);
444 result_pair_reverse = std::to_string(value.second) +
":" + std::to_string(value.first);
445 rid[eidx] = pair_is_gapjunction(value, intra_tags) ? 1 : 0;
447 if (tag_to_reg.
count(result_pair_orginal)) {
448 rid[eidx] = tag_to_reg[result_pair_orginal];
449 }
else if(tag_to_reg.
count(result_pair_reverse)){
450 rid[eidx] = tag_to_reg[result_pair_reverse];
452 if(tag!=value.first){
453 log_msg(NULL, 5,
ECHO,
"error in tags on surface mesh with the unique faces!!!.\\n");
461 double IonicsOnFace::timer_val(
const int timer_id)
463 double val = std::nan(
"NaN");
469 std::string IonicsOnFace::timer_unit(
const int timer_id)
477 if (impdata[limpet::Iion] != NULL)
478 impdata[limpet::Iion][n] = 0;
482 imp.
compute(n, n + 1, impdata);
498 void* find_SV_in_IMP_onFace(
limpet::MULTI_IF* miif,
const int idx,
const char *IMP,
const char *SV,
499 int* offset,
int* sz,
int* plugin_idx)
503 if(strcmp(IMP, miif->
iontypes[idx].get().get_name().c_str()) == 0) {
504 return (
void*) miif->
iontypes[idx].get().get_sv_offset(SV, offset, sz);
507 for(
int k=0; k<miif->
numplugs[idx]; k++ )
508 if(strcmp(IMP, miif->
plugtypes[idx][k].get().get_name().c_str()) == 0) {
510 return (
void*) miif->
plugtypes[idx][k].get().get_sv_offset(SV, offset, sz);
528 void alloc_gvec_data_onFace(
const int nGVcs,
const int nRegs,
const int nEmiRegs,
529 GVecs *prmGVecs, gvec_data_OnFace &glob_vecs)
531 glob_vecs.nRegs = nRegs;
534 glob_vecs.vecs.resize(nGVcs);
536 for (
size_t i = 0; i < glob_vecs.vecs.size(); i++) {
537 sv_data_onFace &gvec = glob_vecs.vecs[i];
539 gvec.name =
dupstr(prmGVecs[i].name);
541 gvec.bogus = prmGVecs[i].bogus;
543 gvec.imps = (
char**) calloc(nRegs,
sizeof(
char *));
544 gvec.svNames = (
char**) calloc(nRegs,
sizeof(
char *));
545 gvec.svSizes = (
int*) calloc(nRegs,
sizeof(
int));
546 gvec.svOff = (
int*) calloc(nRegs,
sizeof(
int));
547 gvec.plugin_idx = (
int*) calloc(nRegs,
sizeof(
int));
550 for (
int j = 0; j < nRegs; j++) {
551 gvec.plugin_idx[j] = -1;
552 if (strlen(prmGVecs[i].imp)) gvec.imps[j] =
dupstr(prmGVecs[i].imp);
554 else if (j < nEmiRegs)
555 gvec.imps[j] =
dupstr(param_globals::imp_region_emi[j].im);
557 gvec.imps[j] =
dupstr(param_globals::PurkIon[j - nEmiRegs].im);
559 else if (j < nEmiRegs)
560 gvec.imps[j] =
dupstr(param_globals::imp_region_emi[j].im);
562 gvec.svNames[j] =
dupstr(prmGVecs[i].ID[j]);
581 igb_output_manager & output_manager,
586 int num_purk_regions = 0;
587 int nEmiRegs = effective_num_imp_regions_emi();
588 int nRegs = nEmiRegs + num_purk_regions;
590 alloc_gvec_data_onFace(param_globals::num_gvecs, nRegs, nEmiRegs, param_globals::gvec, GVs);
593 if (GVs->inclPS) sample_PS_ionSVs(purk);
596 for (
unsigned int i = 0; i < GVs.vecs.size(); i++) {
597 sv_data_onFace & gv = GVs.vecs[i];
600 for (
int j = 0; j < miif->
N_IIF; j++) {
601 gv.getsv[j] = find_SV_in_IMP_onFace(miif, j, gv.imps[j], gv.svNames[j],
602 gv.svOff + j, gv.svSizes + j, gv.plugin_idx + j);
604 if (gv.getsv[j] == NULL) {
605 log_msg(NULL, 3,
ECHO,
"\tWarning: SV(%s) not found in region %d\n", gv.svNames[j], j);
613 gv.name, gv.units, gvec_output_idx,
true);
619 MULTI_IF* pmiif = &purk->ion;
620 for (
int j = miif->
N_IIF; j < nRegs; j++) {
621 gv.getsv[j] = find_SV_in_IMP_onFace(pmiif, j - miif->
N_IIF, gv.imps[j],
622 gv.svNames[j], gv.svOff + j, gv.svSizes + j, gv.plugin_idx + j);
624 if (gv.getsv[j] == NULL) {
625 LOG_MSG(NULL, 3,
ECHO,
"\tWarning: state variable \"%s\" not found in region %d\n", gv.svNames[j], j);
629 RVector_dup(purk->vm_pt, &gv.orderedPS_PS);
632 RVector_dup(purk->vm_pt_over, &gv.orderedPS);
633 initialize_grid_output(grid, NULL, tmo,
intra_elec_msh, GRID_WRITE, 0., 1., gv.units, gv.orderedPS,
634 1, gv.GVcName, -purk->npt, param_globals::output_level);
639 log_msg(NULL, 5,
ECHO,
"\tError: no state variables found for global vector %d\n", i);
640 log_msg(NULL, 5,
ECHO,
"Run bench --imp=YourModel --imp-info to get a list of all parameters.\\n");
655 void assemble_sv_gvec_onFace(gvec_data_OnFace & gvecs,
limpet::MULTI_IF *miif)
657 for(
size_t i=0; i<gvecs.vecs.size(); i++ ) {
658 sv_data_onFace & gv = gvecs.vecs[i];
661 gv.ordered->set(gv.bogus);
663 gv.ordered->get_ownership_range(start, stop);
665 for(
int n = 0; n<miif->
N_IIF; n++ ) {
666 if( !gv.getsv[n] )
continue;
672 int plugin_idx = gv.plugin_idx[n];
676 indices[j] = miif->
NodeLists[n][j] + start;
681 gv.ordered->set(indices, data, add);
687 RVector_set( gv.orderedPS, gv.bogus );
689 if(IS_PURK_PROC(purk))
691 MULTI_IF *pmiif = &purk->ion;
692 Real *data =
new Real[purk->gvec_cab.nitems];
695 for(
int n = 0; n<pmiif->N_IIF; n++ ) {
696 int gvidx = n+miif->
N_IIF;
697 if( !gv.getsv[gvidx] )
continue;
698 for(
int j=0; j<purk->gvec_ion[n].nitems; j++ )
699 data[ci++] = ((
SVgetfcn)(gv.getsv[gvidx]))( pmiif->IIF+n, ((
int*)(purk->gvec_ion[n].data))[j],
702 RVector_setvals(gv.orderedPS, purk->gvec_cab.nitems, (
int*)purk->gvec_cab.data, data,
true);
705 RVector_sync( gv.orderedPS );
opencarp::local_index_t mesh_int_t
#define SF_COMM
the default SlimFem MPI communicator
opencarp::global_index_t SF_int
Global algebraic index type.
#define EXIT(A)
finalize the backend, if it runs, and exit
Comfort class. Provides getter functions to access the mesh member variables more comfortably.
Redistributes vector data from indexing "a" to indexing "b".
A vector storing arbitrary data.
size_t size() const
The current size of the vector.
void assign(InputIterator s, InputIterator e)
Assign a memory range.
T * data()
Pointer to the vector's start.
hm_int count(const K &key) const
Check if key exists.
Custom unordered_set implementation.
iterator find(const K &key)
hm_int count(const K &key) const
void insert(InputIterator first, InputIterator last)
Represents the ionic model and plug-in (IMP) data structure.
std::vector< IonIfBase * > & plugins()
Returns a vector containing the plugins of this IMP.
void compute(node_index_t start, node_index_t end, GlobalData_t **data)
Perform ionic model computation for 1 time step.
ts & get_tstp()
Gets the time stepper.
void for_each(const std::function< void(IonIfBase &)> &consumer)
Executes the consumer functions on this IMP and each of its plugins.
std::vector< IonIfBase * > IIF
array of IIF's
int * numplugs
number of plugins for each region
std::vector< IonTypeList > plugtypes
plugins types for each region
IonTypeList iontypes
type for each region
int N_IIF
how many different IIF's
node_count_t * N_Nodes
#nodes for each IMP
node_index_t ** NodeLists
local partitioned node lists for each IMP stored
void setup(double inp_dt, double inp_start, double inp_end)
Initialize the timer_manager.
void reset_timers()
Reset time in timer_manager and then reset registered timers.
LIMPET ionics and gap-junction models on the EMI unique-face interface mesh.
void init_vector(SF::abstract_vector< T, S > **vec)
SF_nbr
Enumeration encoding the different supported numberings.
@ NBR_PETSC
PETSc numbering of nodes.
@ NBR_REF
The nodal numbering of the reference mesh (the one stored on HD).
@ NBR_SUBMESH
Submesh nodal numbering: The globally ascending sorted reference indices are reindexed.
int get_plug_flag(char *plgstr, int *out_num_plugins, IonTypeList &out_plugins)
IonType * get_ion_type(const std::string &name)
GlobalData_t(* SVgetfcn)(IonIfBase &, node_index_t, int)
void update_ts(ts *ptstp)
opencarp::local_index_t node_count_t
int read_sv(MULTI_IF *, int, const char *)
opencarp::local_index_t node_index_t
std::map< int, std::string > units
timer_manager * tm_manager
a manager for the various physics timers
sf_mesh & get_mesh(const mesh_t gt)
Get a mesh by specifying the gridID.
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
void read_metadata(const std::string filename, std::map< std::string, std::string > &metadata, MPI_Comm comm)
Read metadata from the header.
T get_global(T in, MPI_Op OP, MPI_Comm comm=SF_COMM)
Do a global reduction on a variable.
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.
char * dupstr(const char *old_str)
void log_msg(FILE_SPEC out, int level, unsigned char flag, const char *fmt,...)
mesh_t
The enum identifying the different meshes we might want to load.
@ emi_surface_unique_face_msh
void get_time(double &tm)
SF::abstract_vector< SF_int, SF_real > sf_vec
int get_rank(MPI_Comm comm=SF_COMM)
void remove_char(char *buff, const int buffsize, const char c)
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
V timing(V &t2, const V &t1)
#define PETSC_TO_CANONICAL
Permute algebraic data from PETSC to canonical ordering.