26 #include "petsc_utils.h"
36 #include "caliper/cali.h"
46 MaterialType *m = mtype;
49 m->regions.resize(param_globals::num_gregions);
51 const char* grid_name =
"emi_grid_domain";
52 log_msg(logger, 0, 0,
"Setting up %s tissue poperties for %d regions ..", grid_name,
53 param_globals::num_gregions);
56 RegionSpecs* reg = m->regions.data();
62 for (
size_t i=0; i<m->regions.size(); i++)
64 for (
int j=0;j<param_globals::gregion[i].num_IDs;j++)
66 int tag = param_globals::gregion[i].ID[j];
69 if(extra_tags_default.
find(tag) != extra_tags_default.
end())
70 extra_tags_default.
erase(tag);
72 if(intra_tags_default.
find(tag) != intra_tags_default.
end())
73 intra_tags_default.
erase(tag);
77 for (
size_t i=0; i<m->regions.size(); i++, reg++)
79 if(!strcmp(param_globals::gregion[i].name,
"")) {
80 snprintf(buf,
sizeof buf,
", gregion_%d",
int(i));
81 param_globals::gregion[i].name =
dupstr(buf);
85 reg->regname = strdup(param_globals::gregion[i].name);
89 reg->nsubregs = extra_tags_default.
size();
91 reg->nsubregs = intra_tags_default.
size();
93 reg->nsubregs = param_globals::gregion[i].num_IDs;
96 reg->subregtags = NULL;
99 reg->subregtags =
new int[reg->nsubregs];
103 for (
int tag : extra_tags_default) {
104 reg->subregtags[j] = tag;
110 for (
int tag : intra_tags_default) {
111 reg->subregtags[j] = tag;
116 for (
int j=0;j<reg->nsubregs;j++)
117 reg->subregtags[j] = param_globals::gregion[i].ID[j];
122 elecMaterial *emat =
new elecMaterial();
126 emat->InVal[0] = param_globals::gregion[i].g_bath;
127 emat->InVal[1] = param_globals::gregion[i].g_bath;
128 emat->InVal[2] = param_globals::gregion[i].g_bath;
130 emat->ExVal[0] = param_globals::gregion[i].g_bath;
131 emat->ExVal[1] = param_globals::gregion[i].g_bath;
132 emat->ExVal[2] = param_globals::gregion[i].g_bath;
134 emat->BathVal[0] = param_globals::gregion[i].g_bath;
135 emat->BathVal[1] = param_globals::gregion[i].g_bath;
136 emat->BathVal[2] = param_globals::gregion[i].g_bath;
139 for (
int j=0; j<3; j++) {
140 emat->InVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
141 emat->ExVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
142 emat->BathVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
144 reg->material = emat;
147 if(strlen(param_globals::gi_scale_vec))
151 const char* file = param_globals::gi_scale_vec;
155 if(num_file_entries != mesh.g_numelem)
156 log_msg(0,4,0,
"%s warning: number of %s conductivity scaling entries does not match number of elements!",
162 escale->read_ascii(param_globals::gi_scale_vec);
175 m->el_scale.assign(p, p + escale->lsize());
176 escale->release_ptr(p);
180 void parabolic_solver_emi::init()
185 stats.init_logger(
"par_stats.dat");
204 MPI_Comm_rank(emi_surfmesh_w_counter_face.comm, &rank);
220 SF::init_vector(&vb_both_face, emi_surfmesh_w_counter_face, dpn, alg_surface_type);
221 SF::init_vector(&vb_unique_face, emi_surfmesh_unique_face, dpn, alg_surface_type);
222 SF::init_vector(&Ib_both_face, emi_surfmesh_w_counter_face, dpn, alg_surface_type);
223 SF::init_vector(&Ib_unique_face, emi_surfmesh_unique_face, dpn, alg_surface_type);
233 lhs_emi->init(emi_mesh, dpn, dpn, max_row_entries_emi*3);
234 stiffness_emi->init(emi_mesh, dpn, dpn, max_row_entries_emi*2);
235 mass_emi->init(emi_mesh, dpn, dpn, max_row_entries_emi*3);
236 mass_surf_emi->init(emi_mesh, dpn, dpn, max_row_entries_emi*3);
240 mesh_int_t M = emi_surfmesh_w_counter_face.g_numelem;
242 mesh_int_t m = emi_surfmesh_w_counter_face.l_numelem;
243 mesh_int_t m_one_side = emi_surfmesh_one_side.l_numelem;
244 mesh_int_t M_one_side = emi_surfmesh_one_side.g_numelem;
245 mesh_int_t m_unique_face = emi_surfmesh_unique_face.l_numelem;
246 mesh_int_t M_unique_face = emi_surfmesh_unique_face.g_numelem;
249 log_msg(NULL, 0, 0,
"\n**********************************");
250 log_msg(NULL, 0, 0,
"#elements of emi surfmesh unique face: %zu", emi_surfmesh_unique_face.g_numelem);
251 log_msg(NULL, 0, 0,
"#elements of emi surfmesh one side: %zu", emi_surfmesh_one_side.g_numelem);
252 log_msg(NULL, 0, 0,
"#elements of emi surfmesh: %zu", emi_surfmesh_w_counter_face.g_numelem);
253 log_msg(NULL, 0, 0,
"#elements of emi mesh: %zu", emi_mesh.g_numelem);
254 log_msg(NULL, 0, 0,
"#dofs for emi_mesh: %zu", emi_mesh.g_numpts);
255 log_msg(NULL, 0, 0,
"#max_row_entries_emi: %zu", max_row_entries_emi);
256 log_msg(NULL, 0, 0,
"**********************************\n");
259 SF::layout_from_count<long int>(emi_surfmesh_w_counter_face.l_numelem, layout, emi_surfmesh_w_counter_face.comm);
261 mesh_int_t n_l = emi_mesh.pl.algebraic_layout()[rank];
265 SF::layout_from_count<long int>(emi_surfmesh_one_side.l_numelem, layout_one_side, emi_surfmesh_one_side.comm);
266 mesh_int_t m_one_side_l = layout_one_side[rank];
269 SF::layout_from_count<long int>(emi_surfmesh_unique_face.l_numelem, layout_unique_face, emi_surfmesh_unique_face.comm);
270 mesh_int_t m_unique_face_l = layout_unique_face[rank];
277 B->init(M, N, m, n, m_l, max_row_entries_emi*2);
280 Bi->init(M, N, m, n, m_l, max_row_entries_emi*2);
283 BsM->init(N, M, n, m, n_l, max_row_entries_emi*2);
286 Vm_myocyte_emi->init(N, N, n, n, n_l, max_row_entries_emi*2);
287 Vm_myocyte_emi->zero();
296 map_elem_uniqueFace_to_elem_bothface.clear();
300 MPI_Comm_size(emi_surfmesh_w_counter_face.comm, &comm_size);
303 std::vector<int> both_counts(comm_size, 0), both_displs(comm_size, 0);
304 int local_both_count = (int)vec_both_to_one_face.size();
305 MPI_Allgather(&local_both_count, 1, MPI_INT, both_counts.data(), 1, MPI_INT,
306 emi_surfmesh_w_counter_face.comm);
308 int total_both_count = 0;
309 for (
int i = 0; i < comm_size; i++) {
310 both_displs[i] = total_both_count;
311 total_both_count += both_counts[i];
314 std::vector<mesh_int_t> all_both_to_one(total_both_count);
315 MPI_Allgatherv(vec_both_to_one_face.data(), local_both_count, MPI_INT,
316 all_both_to_one.data(), both_counts.data(), both_displs.data(),
317 MPI_INT, emi_surfmesh_w_counter_face.comm);
320 std::vector<std::vector<mesh_int_t>> one_to_both_first(comm_size);
321 std::vector<std::vector<mesh_int_t>> one_to_both_second(comm_size);
322 for (
int r = 0; r < comm_size; r++) {
325 for (
int i = 0; i < both_counts[r]; i++) {
326 mesh_int_t one_idx = all_both_to_one[both_displs[r] + i];
327 if (one_idx > max_one) max_one = one_idx;
329 if (max_one < 0)
continue;
330 one_to_both_first[r].assign(max_one + 1, -1);
331 one_to_both_second[r].assign(max_one + 1, -1);
332 for (
int i = 0; i < both_counts[r]; i++) {
333 mesh_int_t one_idx = all_both_to_one[both_displs[r] + i];
334 if (one_idx < 0)
continue;
335 if (one_to_both_first[r][one_idx] < 0) {
336 one_to_both_first[r][one_idx] = i;
338 one_to_both_second[r][one_idx] = i;
344 for (
const auto& [unique_idx, one_face_pair] : map_elem_uniqueFace_to_elem_oneface) {
345 const auto& first_oneface = one_face_pair.first;
346 const auto& second_oneface = one_face_pair.second;
348 SF::emi_index_rank<mesh_int_t> first_both, second_both;
349 first_both.index = -1; first_both.rank = -1;
350 second_both.index = -1; second_both.rank = -1;
352 auto map_one = [&](
const SF::emi_index_rank<mesh_int_t>& one,
bool use_second) {
353 SF::emi_index_rank<mesh_int_t> out;
354 out.index = -1; out.rank = -1;
355 if (one.rank >= 0 && one.rank < comm_size && one.index >= 0) {
356 if (one.index < (
int)one_to_both_first[one.rank].size()) {
357 mesh_int_t idx = one_to_both_first[one.rank][one.index];
358 if (use_second && one.index < (
int)one_to_both_second[one.rank].size() &&
359 one_to_both_second[one.rank][one.index] >= 0) {
360 idx = one_to_both_second[one.rank][one.index];
371 const bool same_oneface =
372 (first_oneface.index >= 0 && second_oneface.index >= 0 &&
373 first_oneface.index == second_oneface.index &&
374 first_oneface.rank == second_oneface.rank);
376 first_both = map_one(first_oneface,
false);
377 second_both = map_one(second_oneface, same_oneface);
379 map_elem_uniqueFace_to_elem_bothface[unique_idx] = std::make_pair(first_both, second_both);
389 operator_unique_to_both_faces->init(M, M_unique_face, m, m_unique_face, m_l, max_row_entries_emi*10);
390 operator_unique_to_both_faces->zero();
394 const int M_unique_global = (int)emi_surfmesh_unique_face.g_numelem;
395 std::vector<int> first_rank(M_unique_global, -1), first_idx(M_unique_global, -1);
396 std::vector<int> second_rank(M_unique_global, -1), second_idx(M_unique_global, -1);
398 for (
const auto& [local_unique_idx, both_pair] : map_elem_uniqueFace_to_elem_bothface) {
399 int global_unique = (int)(layout_unique_face[rank] + local_unique_idx);
400 if (global_unique < 0 || global_unique >= M_unique_global)
continue;
401 first_rank[global_unique] = (int)both_pair.first.rank;
402 first_idx[global_unique] = (
int)both_pair.first.index;
403 second_rank[global_unique] = (int)both_pair.second.rank;
404 second_idx[global_unique] = (
int)both_pair.second.index;
408 MPI_Allreduce(MPI_IN_PLACE, first_rank.data(), M_unique_global, MPI_INT, MPI_MAX, emi_surfmesh_w_counter_face.comm);
409 MPI_Allreduce(MPI_IN_PLACE, first_idx.data(), M_unique_global, MPI_INT, MPI_MAX, emi_surfmesh_w_counter_face.comm);
410 MPI_Allreduce(MPI_IN_PLACE, second_rank.data(), M_unique_global, MPI_INT, MPI_MAX, emi_surfmesh_w_counter_face.comm);
411 MPI_Allreduce(MPI_IN_PLACE, second_idx.data(), M_unique_global, MPI_INT, MPI_MAX, emi_surfmesh_w_counter_face.comm);
416 ebuff.assign(1, 1, 1.0);
418 for (
int gid = 0; gid < M_unique_global; gid++) {
420 if (first_rank[gid] == rank && first_idx[gid] >= 0) {
421 row_idx[0] = (
SF_int)(layout[rank] + first_idx[gid]);
422 operator_unique_to_both_faces->set_values(row_idx, col_idx, ebuff.data(),
false);
424 if (second_rank[gid] == rank && second_idx[gid] >= 0) {
425 row_idx[0] = (
SF_int)(layout[rank] + second_idx[gid]);
426 operator_unique_to_both_faces->set_values(row_idx, col_idx, ebuff.data(),
false);
429 operator_unique_to_both_faces->finish_assembly();
434 operator_both_to_unique_face->init(M_unique_face, M, m_unique_face, m, m_unique_face_l, max_row_entries_emi*10);
435 operator_both_to_unique_face->zero();
436 SF::assemble_map_both_to_unique(*operator_both_to_unique_face,
437 map_elem_uniqueFace_to_elem_bothface,
438 emi_surfmesh_unique_face,
439 emi_surfmesh_w_counter_face);
442 #ifdef EMI_DEBUG_MESH
445 mesh_int_t local_valid_first = 0, local_valid_second = 0;
448 SF::layout_from_count<long int>(emi_surfmesh_w_counter_face.l_numelem, layout_both_dbg, emi_surfmesh_w_counter_face.comm);
450 SF::layout_from_count<long int>(emi_surfmesh_unique_face.l_numelem, layout_unique_dbg, emi_surfmesh_unique_face.comm);
452 for (
const auto& [uidx, both_pair] : map_elem_uniqueFace_to_elem_bothface) {
453 if (both_pair.first.index >= 0) local_valid_first++;
454 if (both_pair.second.index >= 0) local_valid_second++;
456 if (both_pair.first.index >= 0 && both_pair.second.index >= 0 &&
457 both_pair.first.rank >= 0 && both_pair.second.rank >= 0) {
458 mesh_int_t global_first = layout_both_dbg[both_pair.first.rank] + both_pair.first.index;
459 mesh_int_t global_second = layout_both_dbg[both_pair.second.rank] + both_pair.second.index;
460 if (global_first == global_second) local_same_column++;
463 mesh_int_t global_valid_first = 0, global_valid_second = 0;
465 MPI_Allreduce(&local_valid_first, &global_valid_first, 1, MPI_INT, MPI_SUM, emi_surfmesh_w_counter_face.comm);
466 MPI_Allreduce(&local_valid_second, &global_valid_second, 1, MPI_INT, MPI_SUM, emi_surfmesh_w_counter_face.comm);
467 MPI_Allreduce(&local_same_column, &global_same_column, 1, MPI_INT, MPI_SUM, emi_surfmesh_w_counter_face.comm);
470 MPI_Comm_rank(emi_surfmesh_w_counter_face.comm, &dbg_rank);
472 log_msg(NULL, 0, 0,
"DEBUG map_unique_to_both: valid_first=%d valid_second=%d (global M=%zu, M_unique=%zu)",
473 (
int)global_valid_first, (
int)global_valid_second,
474 (
size_t)emi_surfmesh_w_counter_face.g_numelem,
475 (
size_t)emi_surfmesh_unique_face.g_numelem);
476 log_msg(NULL, 0, 0,
"DEBUG map_unique_to_both: same_column=%d", (
int)global_same_column);
481 std::vector<char> present(emi_surfmesh_unique_face.l_numelem, 0);
482 for (
const auto& [uidx, both_pair] : map_elem_uniqueFace_to_elem_bothface) {
483 if (uidx >= 0 && uidx < (
mesh_int_t)present.size()) present[uidx] = 1;
485 std::vector<int> missing;
487 if (!present[i]) missing.push_back((
int)i);
489 int local_missing = (int)missing.size();
490 int comm_size_dbg = 0;
491 MPI_Comm_size(emi_surfmesh_w_counter_face.comm, &comm_size_dbg);
492 std::vector<int> counts(comm_size_dbg, 0), displs(comm_size_dbg, 0);
493 MPI_Gather(&local_missing, 1, MPI_INT,
494 dbg_rank == 0 ? counts.data() :
nullptr, 1, MPI_INT,
495 0, emi_surfmesh_w_counter_face.comm);
498 for (
int r = 0; r < comm_size_dbg; r++) {
502 std::vector<int> all_missing(total, -1);
503 MPI_Gatherv(missing.data(), local_missing, MPI_INT,
504 all_missing.data(), counts.data(), displs.data(), MPI_INT,
505 0, emi_surfmesh_w_counter_face.comm);
508 for (
int r = 0; r < comm_size_dbg; r++) {
509 if (counts[r] == 0) { offset += counts[r];
continue; }
511 log_msg(NULL, 0, 0,
"DEBUG unique missing: rank=%d count=%d", r, counts[r]);
512 int to_print = counts[r] < 10 ? counts[r] : 10;
513 for (
int i = 0; i < to_print; i++) {
514 log_msg(NULL, 0, 0,
"DEBUG unique missing: rank=%d local_unique=%d", r, all_missing[offset + i]);
518 if (!any)
log_msg(NULL, 0, 0,
"DEBUG unique missing: none");
520 MPI_Gatherv(missing.data(), local_missing, MPI_INT,
521 nullptr,
nullptr,
nullptr, MPI_INT,
522 0, emi_surfmesh_w_counter_face.comm);
527 sf_vec* dbg_unique =
nullptr;
528 sf_vec* dbg_both =
nullptr;
529 sf_vec* dbg_back =
nullptr;
530 SF::init_vector(&dbg_unique, emi_surfmesh_unique_face, dpn, alg_surface_type);
531 SF::init_vector(&dbg_both, emi_surfmesh_w_counter_face, dpn, alg_surface_type);
532 SF::init_vector(&dbg_back, emi_surfmesh_unique_face, dpn, alg_surface_type);
533 dbg_unique->set(1.0);
534 operator_unique_to_both_faces->mult(*dbg_unique, *dbg_both);
535 operator_both_to_unique_face->mult(*dbg_both, *dbg_back);
541 for (
mesh_int_t i = 0; i < dbg_back->lsize(); i++) {
542 SF_real err = std::abs(p[i] - 1.0);
543 if (err > local_max_err) local_max_err = err;
544 if (std::abs(p[i] - 0.5) < 1e-12) local_half_count++;
546 dbg_back->release_ptr(p);
549 MPI_Allreduce(&local_max_err, &global_max_err, 1, MPI_DOUBLE, MPI_MAX, emi_surfmesh_w_counter_face.comm);
550 MPI_Allreduce(&local_half_count, &global_half_count, 1, MPI_INT, MPI_SUM, emi_surfmesh_w_counter_face.comm);
552 log_msg(NULL, 0, 0,
"DEBUG map_unique_to_both consistency: max_err=%.6e half_count=%d",
553 (
double)global_max_err, (
int)global_half_count);
559 const int max_print = 5;
560 const int fields = 9;
561 std::vector<int> local_buf(max_print * fields, -1);
564 for (
const auto& [local_unique_idx, both_pair] : map_elem_uniqueFace_to_elem_bothface) {
565 if (filled >= max_print)
break;
566 const auto& first_both = both_pair.first;
567 const auto& second_both = both_pair.second;
569 const bool first_valid = (first_both.index >= 0 && first_both.rank >= 0);
570 const bool second_valid = (second_both.index >= 0 && second_both.rank >= 0);
571 int count = (first_valid ? 1 : 0) + (second_valid ? 1 : 0);
572 if (
count != 1)
continue;
574 mesh_int_t global_unique = layout_unique_dbg[dbg_rank] + local_unique_idx;
575 mesh_int_t global_first = first_valid ? (layout_both_dbg[first_both.rank] + first_both.index) : -1;
576 mesh_int_t global_second = second_valid ? (layout_both_dbg[second_both.rank] + second_both.index) : -1;
578 int base = filled * fields;
579 local_buf[base + 0] = dbg_rank;
580 local_buf[base + 1] = (int)local_unique_idx;
581 local_buf[base + 2] = (int)global_unique;
582 local_buf[base + 3] = (int)first_both.rank;
583 local_buf[base + 4] = (
int)first_both.index;
584 local_buf[base + 5] = (int)global_first;
585 local_buf[base + 6] = (int)second_both.rank;
586 local_buf[base + 7] = (
int)second_both.index;
587 local_buf[base + 8] = (int)global_second;
592 MPI_Comm_size(emi_surfmesh_w_counter_face.comm, &comm_size);
593 std::vector<int> all_buf;
595 all_buf.resize(comm_size * max_print * fields, -1);
598 MPI_Gather(local_buf.data(), max_print * fields, MPI_INT,
599 dbg_rank == 0 ? all_buf.data() :
nullptr, max_print * fields, MPI_INT,
600 0, emi_surfmesh_w_counter_face.comm);
603 for (
int r = 0; r < comm_size; r++) {
604 for (
int i = 0; i < max_print; i++) {
605 int base = (r * max_print + i) * fields;
606 if (all_buf[base + 1] < 0)
continue;
608 "DEBUG map_unique_to_both bad: rank=%d local_unique=%d global_unique=%d "
609 "first(rank=%d idx=%d glob=%d) second(rank=%d idx=%d glob=%d)",
610 all_buf[base + 0], all_buf[base + 1], all_buf[base + 2],
611 all_buf[base + 3], all_buf[base + 4], all_buf[base + 5],
612 all_buf[base + 6], all_buf[base + 7], all_buf[base + 8]);
626 SF::assemble_restrict_operator(*B, *Bi, *BsM, elemTag_surface_w_counter_mesh, map_vertex_tag_to_dof_petsc, line_face, tri_face, quad_face, emi_surfmesh_w_counter_face, emi_mesh,
UM2_to_CM2);
629 #ifdef EMI_DEBUG_MESH
632 MPI_Comm_rank(emi_mesh.comm, &local_rank);
634 fprintf(stderr,
"RANK %d MESH SIZES: one_side=%zu, counter=%zu, unique=%zu\n",
635 local_rank, emi_surfmesh_one_side.l_numelem,
636 emi_surfmesh_w_counter_face.l_numelem, emi_surfmesh_unique_face.l_numelem);
638 fprintf(stderr,
"RANK %d MAP SIZES: uniqueFace_to_oneface=%zu\n",
639 local_rank, map_elem_uniqueFace_to_elem_oneface.size());
651 if(!(vb_ptr != NULL && Ib_ptr != NULL)) {
652 log_msg(0,5,0,
"%s error: global Vb and Ib vectors not properly set up! Ionics seem invalid! Aborting!",
658 vb->shallow_copy(*vb_ptr);
660 Ib->shallow_copy(*Ib_ptr);
662 parab_tech =
static_cast<parabolic_solver_emi::parabolic_t
>(param_globals::parab_solve_emi);
670 double start, end, period;
673 mass_integrator mass_integ;
674 mass_integrator mass_integ_emi;
677 int log_flag = param_globals::output_level > 1 ?
ECHO : 0;
678 MaterialType & mt = mtype[0];
694 log_msg(NULL, 0, 0,
"assemble stiffness matrix");
696 elec_stiffness_integrator stfn_integ_emi(mt);
697 stiffness_emi->zero();
700 log_msg(logger,0,log_flag,
"Computed parabolic stiffness matrix in %.5f seconds.",
float(dur));
702 log_msg(NULL, 0, 0,
"assemble mass matrix on the volumetric mesh");
704 mass_integrator mass_integ;
708 log_msg(logger,0,log_flag,
"Computed volumetric mass matrix in %.5f seconds.",
float(dur));
710 log_msg(NULL, 0, 0,
"assemble LHS matrix and mass matrix on the surface mesh");
714 SF::assemble_lhs_emi(*lhs_emi, *mass_surf_emi, mesh, emi_surfmesh_w_counter_face, map_vertex_tag_to_dof_petsc, line_face, tri_face, quad_face, stfn_integ_emi, mass_integ_emi, -1.,
UM2_to_CM2 / Dt);
716 log_msg(logger,0,log_flag,
"Computed parabolic mass matrix in %.5f seconds.",
float(dur));
720 bool same_nonzero =
false;
724 log_msg(logger,0,log_flag,
"lhs matrix enforcing Dirichlet boundaries.");
728 dbc =
new dbc_manager(*lhs_emi, stimuli);
730 dbc->recompute_dbcs();
732 dbc->enforce_dbc_lhs();
735 log_msg(logger,0,log_flag,
"lhs matrix Dirichlet enforcing done in %.5f seconds.",
float(dur));
738 log_msg(logger,0,
ECHO,
"without enforcing Dirichlet boundaries on the lhs matrix!");
740 phie_mat_has_nullspace =
true;
745 setup_linear_solver(logger);
747 log_msg(logger,0,log_flag,
"Initializing parabolic solver in %.5f seconds.",
float(dur));
750 period =
timing(end, start);
753 void parabolic_solver_emi::setup_linear_solver(
FILE_SPEC logger)
755 tol = param_globals::cg_tol_parab;
756 max_it = param_globals::cg_maxit_parab;
758 std::string default_opts;
759 std::string solver_file;
760 solver_file = param_globals::parab_options_file;
761 if (param_globals::flavor == std::string(
"ginkgo")) {
762 default_opts = std::string(
765 "type": "solver::Cg",
767 "type": "solver::Multigrid",
768 "min_coarse_rows": 8,
770 "default_initial_guess": "zero",
773 "type": "multigrid::Pgm",
774 "deterministic": false
778 "type": "preconditioner::Schwarz",
780 "type": "preconditioner::Jacobi"
796 "type": "ResidualNorm",
797 "reduction_factor": 1e-4
802 } else if (param_globals::flavor == std::string(
"petsc")) {
803 default_opts = std::string(
"-ksp_type cg -pc_type gamg -options_left");
805 lin_solver->setup_solver(*lhs_emi, tol, max_it * 100, param_globals::cg_norm_parab,
806 "parabolic PDE", phie_mat_has_nullspace, logger, solver_file.c_str(),
807 default_opts.c_str());
810 void parabolic_solver_emi::solve()
812 switch (parab_tech) {
813 case SEMI_IMPLICIT: solve_semiImplicit();
break;
817 void parabolic_solver_emi::solve_semiImplicit()
824 dbc->enforce_dbc_rhs(*ui);
832 stiffness_emi->mult(*ui, *Iij_temp);
839 if (Iij_stim->mag() > 0.0) {
841 mass_emi->mult(*Iij_stim, *Iij_temp);
853 (*lin_solver)(*dui, *Irhs);
857 if(lin_solver->reason < 0) {
858 log_msg(0, 5, 0,
"%s solver diverged. Reason: %s.", lin_solver->name.c_str(),
859 petsc_get_converged_reason_str(lin_solver->reason));
865 ui_pre->add_scaled(*dui, 1.0);
867 ui->add_scaled(*ui_pre, 1.0);
874 dbc->enforce_dbc_rhs(*ui);
879 stats.slvtime +=
timing(t1, t0);
880 stats.update_iter(lin_solver->niter);
893 hashmap::unordered_map<std::pair<mesh_int_t,mesh_int_t>, std::pair<mesh_int_t,mesh_int_t>> & map_vertex_tag_to_dof_petsc,
894 std::vector<std::string> & tags_data)
903 for(
size_t i=0; i<rnod.
size(); i++){
907 for(
size_t eidx=0; eidx<emi_surfmesh_w_counter_face.l_numelem; eidx++)
909 std::vector<int> elem_nodes;
910 mesh_int_t tag = emi_surfmesh_w_counter_face.tag[eidx];
911 for (
int n = emi_surfmesh_w_counter_face.dsp[eidx]; n < emi_surfmesh_w_counter_face.dsp[eidx+1];n++)
913 mesh_int_t l_idx = emi_surfmesh_w_counter_face.con[n];
915 std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
916 Index_tag_old = std::make_pair(l2g[l_idx],tags[eidx]);
917 mesh_int_t dof = map_vertex_tag_to_dof[Index_tag_old];
918 elem_nodes.push_back(dof);
923 std::string result_first;
924 std::string result_second;
925 std::sort(elem_nodes.begin(),elem_nodes.end());
928 if(elem_nodes.size()==2){
931 key.
v1 = elem_nodes[0];
932 key.
v2 = elem_nodes[1];
933 std::pair<SF::emi_face<mesh_int_t,SF::tuple<mesh_int_t>>,
934 SF::emi_face<mesh_int_t,SF::tuple<mesh_int_t>>> value = line_face[key];
936 tag_first = value.first.tag;
937 tag_second = value.second.tag;
938 result_first = std::to_string(tag_first) +
":" + std::to_string(tag_second);
939 result_second = std::to_string(tag_second) +
":" + std::to_string(tag_first);
941 else if(elem_nodes.size()==3){
943 key.
v1 = elem_nodes[0];
944 key.
v2 = elem_nodes[1];
945 key.
v3 = elem_nodes[2];
946 std::pair<SF::emi_face<mesh_int_t,SF::triple<mesh_int_t>>,
947 SF::emi_face<mesh_int_t,SF::triple<mesh_int_t>>> value = tri_face[key];
949 tag_first = value.first.tag;
950 tag_second = value.second.tag;
951 result_first = std::to_string(tag_first) +
":" + std::to_string(tag_second);
952 result_second = std::to_string(tag_second) +
":" + std::to_string(tag_first);
954 else if(elem_nodes.size()==4){
956 key.
v1 = elem_nodes[0];
957 key.
v2 = elem_nodes[1];
958 key.
v3 = elem_nodes[2];
959 key.
v4 = elem_nodes[3];
960 std::pair<SF::emi_face<mesh_int_t,SF::quadruple<mesh_int_t>>,
961 SF::emi_face<mesh_int_t,SF::quadruple<mesh_int_t>>> value = quad_face[key];
963 tag_first = value.first.tag;
964 tag_second = value.second.tag;
965 result_first = std::to_string(tag_first) +
":" + std::to_string(tag_second);
966 result_second = std::to_string(tag_second) +
":" + std::to_string(tag_first);
969 tags_data.push_back(result_first);
970 tags_data.push_back(result_second);
974 void EMI::initialize()
982 logger =
f_open(
"emi.log", param_globals::experiment != 4 ?
"w" :
"r");
983 const int verb = param_globals::output_level;
998 ion.set_surface_mesh_data(parab_solver.line_face,
999 parab_solver.tri_face,
1000 parab_solver.quad_face,
1001 parab_solver.map_vertex_tag_to_dof);
1004 std::vector<std::string> tags_data;
1005 tags_onFace(parab_solver.line_face,
1006 parab_solver.tri_face,
1007 parab_solver.quad_face,
1008 parab_solver.map_vertex_tag_to_dof,
1009 parab_solver.map_vertex_tag_to_dof_petsc,
1013 ion.set_tags_onFace(tags_data);
1015 ion.set_default_interface_pairs(parab_solver.membraneFace_default, parab_solver.gapjunctionFace_default, parab_solver.map_elem_uniqueFace_to_tags);
1019 set_elec_tissue_properties_emi_volume(mtype_vol, parab_solver.extra_tags, parab_solver.intra_tags, logger);
1020 region_mask(
emi_msh, mtype_vol[0].regions, mtype_vol[0].regionIDs,
true,
"gregion_vol");
1025 param_globals::dt, 0,
"elec::ref_dt",
"TS");
1029 param_globals::operator_splitting = 0;
1040 balance_electrodes();
1042 scale_total_stimulus_current(stimuli, *parab_solver.mass_emi, *parab_solver.mass_surf_emi, logger);
1060 parab_solver.operator_unique_to_both_faces->mult(*parab_solver.vb, *parab_solver.vb_both_face);
1061 SF::assign_resting_potential_from_ionic_models_on_myocyte(*parab_solver.Vm_myocyte_emi,
1062 parab_solver.vb_both_face,
1063 parab_solver.elemTag_emi_mesh,
1064 parab_solver.map_vertex_tag_to_dof_petsc,
1065 parab_solver.line_face, parab_solver.tri_face, parab_solver.quad_face,
1066 emi_surfmesh_w_counter_face, emi_mesh);
1068 parab_solver.Vm_myocyte_emi->get_diagonal(*parab_solver.ui);
1069 *parab_solver.vb_unique_face = *parab_solver.vb;
1077 this->initialize_time +=
timing(t2, t1);
1080 void EMI::setup_mappings()
1088 log_msg(logger, 0, 0,
"%s: Setting up intracellular algebraic-to-nodal scattering.", __func__);
1093 log_msg(logger, 0, 0,
"%s: Setting up intracellular PETSc to canonical permutation.", __func__);
1098 void EMI::compute_step()
1107 const int verb = param_globals::output_level;
1114 apply_dbc_stimulus();
1123 apply_current_stimulus();
1127 parab_solver.operator_unique_to_both_faces->mult(*parab_solver.Ib, *parab_solver.Ib_both_face);
1128 parab_solver.BsM->mult(*parab_solver.Ib_both_face, *parab_solver.Irhs);
1132 parab_solver.solve();
1135 parab_solver.B->mult(*parab_solver.ui, *parab_solver.vb_both_face);
1137 parab_solver.operator_both_to_unique_face->mult(*parab_solver.vb_both_face, *parab_solver.vb_unique_face);
1138 *parab_solver.vb = *parab_solver.vb_unique_face;
1146 this->compute_time +=
timing(t2, t1);
1153 void EMI::output_step()
1158 output_manager.write_data();
1160 double curtime =
timing(t2, t1);
1161 this->output_time += curtime;
1164 IO_stats.tot_time += curtime;
1180 output_manager.close_files_and_cleanup();
1186 void EMI::setup_stimuli()
1191 stimuli.
resize(param_globals::num_stim);
1192 for (
int i = 0; i < param_globals::num_stim; i++) {
1194 stimulus & s = stimuli[i];
1200 s.associated_intra_mesh =
emi_msh, s.associated_extra_mesh =
emi_msh;
1204 if (s.phys.type ==
Illum) {
1222 }
else if (s.phys.type ==
I_tm) {
1224 SF::restrict_to_membrane(s.electrode.vertices, dof2ptsData, mesh);
1236 if (s.electrode.dump_vtx) {
1241 if(param_globals::stim[i].pulse.dumpTrace &&
get_rank() == 0) {
1243 s.pulse.wave.write_trace(s.name+
".trc");
1249 void EMI::apply_dbc_stimulus()
1251 parabolic_solver_emi& ps = parab_solver;
1254 bool dbcs_have_updated = ps.dbc !=
nullptr && ps.dbc->dbc_update();
1257 if (dbcs_have_updated && time_not_final) {
1258 parab_solver.rebuild_matrices(mtype_vol, *ion.miif, stimuli, logger);
1262 void EMI::apply_current_stimulus()
1264 parabolic_solver_emi& ps = parab_solver;
1265 ps.Iij_stim->set(0.0);
1268 for(stimulus & s : stimuli) {
1270 switch (s.phys.type) {
1273 ps.Bi->mult(*ps.Iij_temp, *ps.Ib_both_face);
1274 ps.operator_both_to_unique_face->mult(*ps.Ib_both_face, *ps.Ib_unique_face);
1275 ps.Ib->add_scaled(*ps.Ib_unique_face, -0.5);
1289 void EMI::balance_electrodes()
1291 for (
int i = 0; i < param_globals::num_stim; i++) {
1292 if (param_globals::stim[i].crct.balance != -1) {
1293 int from = param_globals::stim[i].crct.balance;
1296 log_msg(NULL, 0, 0,
"Balancing stimulus %d with %d %s-wise.", from, to,
1297 is_current(stimuli[from].phys.type) ?
"current" :
"voltage");
1299 stimulus& s_from = stimuli[from];
1300 stimulus& s_to = stimuli[to];
1302 s_to.pulse = s_from.pulse;
1303 s_to.ptcl = s_from.ptcl;
1304 s_to.phys = s_from.phys;
1305 s_to.pulse.strength *= -1.0;
1307 if (s_from.phys.type ==
I_ex || s_from.phys.type ==
I_in) {
1311 if (!s_from.phys.total_current) {
1312 sf_mat& mass = *parab_solver.mass_emi;
1316 s_to.pulse.strength *= fabs(vol0 / vol1);
1328 for (stimulus & s : stimuli){
1329 if(
is_current(s.phys.type) && s.phys.total_current){
1330 switch (s.phys.type) {
1341 float scale = 1.e12 / vol;
1343 s.pulse.strength *= scale;
1346 "%s [Stimulus %d]: current density scaled to %.4g uA/cm^3\n",
1347 s.name.c_str(), s.idx, s.pulse.strength);
1361 s.pulse.strength /= surf;
1363 "%s [Stimulus %d]: current density scaled to %.4g uA/cm^2\n",
1364 s.name.c_str(), s.idx, s.pulse.strength);
1375 static void assign_deterministic_elem_numbering(
sf_mesh & mesh)
1377 const int KEY_SIZE = 6;
1378 int rank = 0, size = 0;
1379 MPI_Comm_rank(mesh.comm, &rank);
1380 MPI_Comm_size(mesh.comm, &size);
1382 auto make_key = [&](
size_t i) {
1383 std::array<long long, KEY_SIZE> k;
1385 k[0] = (
long long)mesh.type[i];
1386 k[1] = (
long long)mesh.tag[i];
1389 if (mesh.type[i] ==
SF::Line) nn = 2;
1390 else if (mesh.type[i] ==
SF::Tri) nn = 3;
1391 else if (mesh.type[i] ==
SF::Quad) nn = 4;
1394 std::vector<long long> nodes;
1396 size_t off = mesh.dsp[i];
1397 for (
int j = 0; j < nn; j++) {
1398 nodes.push_back((
long long)mesh.con[off + j]);
1400 std::sort(nodes.begin(), nodes.end());
1401 for (
int j = 0; j < (int)nodes.size(); j++) {
1402 k[2 + j] = nodes[j];
1408 std::vector<long long> local_keys(mesh.l_numelem * KEY_SIZE, -1);
1409 for (
size_t i = 0; i < mesh.l_numelem; i++) {
1410 auto k = make_key(i);
1411 for (
int j = 0; j < KEY_SIZE; j++) local_keys[i * KEY_SIZE + j] = k[j];
1415 std::vector<int> counts(size, 0), displs(size, 0);
1416 int local_count = (int)local_keys.size();
1417 MPI_Allgather(&local_count, 1, MPI_INT, counts.data(), 1, MPI_INT, mesh.comm);
1419 for (
int r = 0; r < size; r++) {
1424 std::vector<long long> all_keys;
1425 if (rank == 0) all_keys.resize(total, -1);
1426 MPI_Gatherv(local_keys.data(), local_count, MPI_LONG_LONG,
1427 rank == 0 ? all_keys.data() :
nullptr, counts.data(), displs.data(), MPI_LONG_LONG,
1431 std::vector<std::array<long long, KEY_SIZE>> sorted_keys;
1433 const int nkeys = total / KEY_SIZE;
1434 sorted_keys.resize(nkeys);
1435 for (
int i = 0; i < nkeys; i++) {
1436 std::array<long long, KEY_SIZE> k;
1437 for (
int j = 0; j < KEY_SIZE; j++) k[j] = all_keys[i * KEY_SIZE + j];
1440 std::sort(sorted_keys.begin(), sorted_keys.end());
1445 if (rank == 0) nkeys = (int)sorted_keys.size();
1446 MPI_Bcast(&nkeys, 1, MPI_INT, 0, mesh.comm);
1447 std::vector<long long> flat_sorted(nkeys * KEY_SIZE, -1);
1449 for (
int i = 0; i < nkeys; i++) {
1450 for (
int j = 0; j < KEY_SIZE; j++) flat_sorted[i * KEY_SIZE + j] = sorted_keys[i][j];
1453 MPI_Bcast(flat_sorted.data(), (
int)flat_sorted.size(), MPI_LONG_LONG, 0, mesh.comm);
1457 sorted_keys.resize(nkeys);
1458 for (
int i = 0; i < nkeys; i++) {
1459 std::array<long long, KEY_SIZE> k;
1460 for (
int j = 0; j < KEY_SIZE; j++) k[j] = flat_sorted[i * KEY_SIZE + j];
1468 nbr_ref.
resize(mesh.l_numelem);
1469 nbr_sub.
resize(mesh.l_numelem);
1470 for (
size_t i = 0; i < mesh.l_numelem; i++) {
1471 auto k = make_key(i);
1472 auto it = std::lower_bound(sorted_keys.begin(), sorted_keys.end(), k);
1473 if (it == sorted_keys.end() || *it != k) {
1474 log_msg(0, 5, 0,
"deterministic numbering failed to find key (rank %d, elem %zu)", rank, i);
1483 void EMI::setup_output()
1485 bool write_binary =
false;
1486 std::string output_base =
get_basename(param_globals::meshname);
1491 std::string output_file = output_base +
"_e";
1492 log_msg(0, 0, 0,
"Writing \"%s\" mesh: %s", mesh.name.c_str(), output_file.c_str());
1495 output_manager.register_output(parab_solver.ui,
emi_msh, 1, param_globals::phiefile,
"mV", NULL);
1499 mesh_m.name =
"Membrane";
1500 output_file = output_base +
"_m";
1501 log_msg(0, 0, 0,
"Writing \"%s\" mesh: %s", mesh_m.name.c_str(), output_file.c_str());
1508 output_manager.register_output(parab_solver.vb_unique_face,
emi_surface_unique_face_msh, 1, param_globals::vofile,
"mV", NULL,
true);
1510 if(param_globals::num_trace) {
1512 open_trace(ion.miif, param_globals::num_trace, param_globals::trace_node, NULL, &imesh);
1516 IO_stats.init_logger(
"IO_stats.dat");
1519 void EMI::dump_matrices()
1521 std::string bsname = param_globals::dump_basename;
1526 fn = bsname +
"_lhs.bin";
1527 parab_solver.lhs_emi->write(fn.c_str());
1529 fn = bsname +
"_K.bin";
1530 parab_solver.stiffness_emi->write(fn.c_str());
1532 fn = bsname +
"_B.bin";
1533 parab_solver.B->write(fn.c_str());
1535 fn = bsname +
"_Bi.bin";
1536 parab_solver.Bi->write(fn.c_str());
1538 fn = bsname +
"_BsM.bin";
1539 parab_solver.BsM->write(fn.c_str());
1541 fn = bsname +
"_M.bin";
1542 parab_solver.mass_emi->write(fn.c_str());
1544 fn = bsname +
"_Ms.bin";
1545 parab_solver.mass_surf_emi->write(fn.c_str());
1551 double EMI::timer_val(
const int timer_id)
1557 stimuli[sidx].value(val);
1560 val = std::nan(
"NaN");
1567 std::string EMI::timer_unit(
const int timer_id)
1574 s_unit = stimuli[sidx].pulse.wave.f_unit;
1579 void EMI::setup_solvers()
1582 parab_solver.init();
1583 parab_solver.rebuild_matrices(mtype_vol, *ion.miif, stimuli, logger);
1585 if(param_globals::dump2MatLab)
1593 MPI_Comm_size(comm, &size);
1594 MPI_Comm_rank(comm, &rank);
1603 void make_global_pairs(std::set<std::pair<mesh_int_t,mesh_int_t>>&set_pair,
int size)
1605 std::vector<std::pair<mesh_int_t,mesh_int_t>> vector_pair(set_pair.begin(), set_pair.end());
1608 std::vector<int> local_flat_data;
1609 for (
const auto& p : vector_pair) {
1610 local_flat_data.push_back(p.first);
1611 local_flat_data.push_back(p.second);
1615 int local_size = local_flat_data.size();
1616 std::vector<int> all_sizes(size);
1619 MPI_Allgather(&local_size, 1, MPI_INT, all_sizes.data(), 1, MPI_INT, MPI_COMM_WORLD);
1622 std::vector<int> displacements(size, 0);
1624 for (
int i = 0; i < size; ++i) {
1625 displacements[i] = total_size;
1626 total_size += all_sizes[i];
1630 std::vector<int> global_flat_data(total_size);
1634 local_flat_data.data(), local_size, MPI_INT,
1635 global_flat_data.data(), all_sizes.data(), displacements.data(), MPI_INT,
1640 std::vector<std::pair<int, int>> global_data;
1641 for (
size_t i = 0; i < global_flat_data.size(); i += 2) {
1642 global_data.emplace_back(global_flat_data[i], global_flat_data[i + 1]);
1645 std::set<std::pair<mesh_int_t,mesh_int_t>> membraneFace_default_set(global_data.begin(), global_data.end());
1647 set_pair = membraneFace_default_set;
1654 MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
1655 divide(num_tags, size, num_tags_per_rank);
1658 void EMI::setup_EMI_mesh()
1660 log_msg(0,0,0,
"\n *** Processing EMI mesh ***\n");
1662 const std::string basename = param_globals::meshname;
1663 const int verb = param_globals::output_level;
1665 assert(mesh_registry.count(
emi_msh) == 1);
1674 MPI_Comm comm = emi_mesh.
comm;
1677 double t1, t2, s1, s2;
1678 MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
1683 if (emi_mesh.l_numelem > 0) {
1688 const char* type_name = (first_elem ==
SF::Line) ?
"1D (Line)" :
1689 (first_elem ==
SF::Tri) ?
"2D (Tri)" :
1692 log_msg(0, 5, 0,
"\n*** ERROR: EMI model requires a 3D volumetric mesh!");
1693 log_msg(0, 5, 0,
"*** Current mesh element type: %s", type_name);
1694 log_msg(0, 5, 0,
"*** EMI only supports 3D element types: Tetra, Pyramid, Prism, Hexa");
1695 log_msg(0, 5, 0,
"*** Please provide a 3D mesh with volume elements.\n");
1704 if(verb)
log_msg(NULL, 0, 0,
"\nReading tags for extra and intra regions from input files");
1710 if(verb)
log_msg(NULL, 0, 0,
"Read extracellular tags");
1713 parab_solver.extra_tags.insert(tag);
1716 if(verb)
log_msg(NULL, 0, 0,
"Read intracellular tags");
1719 parab_solver.intra_tags.insert(tag);
1722 int total_num_tags = parab_solver.extra_tags.size() + parab_solver.intra_tags.size();
1723 if(total_num_tags < size){
1724 log_msg(0,5,0,
"\nThe number of unique tags on EMI mesh is smaller than number of processors!");
1727 if(verb)
log_msg(NULL, 0, 0,
"\nextra_tags=%lu, intra_tags=%lu", parab_solver.extra_tags.size(), parab_solver.intra_tags.size());
1730 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1738 if(verb)
log_msg(NULL, 0, 0,
"\nReading points with data on each vertex");
1742 assert(ptsidx.
size()==ptsData.
size());
1744 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1746 std::list< sf_mesh* > meshlist;
1747 meshlist.push_back(&emi_mesh);
1752 if(verb)
log_msg(NULL, 0, 0,
"\nDistribute mesh based on tags");
1756 distribute_elements_based_tags(emi_mesh, unique_tags);
1758 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1764 if(verb)
log_msg(NULL, 0, 0,
"\nInserting points");
1768 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1773 if(verb)
log_msg(NULL, 0, 0,
"\nCompute location of all dofs on emi mesh(inner dofs, memebarne, gap junction) saved into ptsData from original mesh");
1775 compute_ptsdata_from_original_mesh( emi_mesh,
1778 parab_solver.extra_tags,
1779 parab_solver.intra_tags);
1781 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1789 if(verb)
log_msg(NULL, 0, 0,
"\nExtract EMI surface mesh");
1791 extract_face_based_tags(emi_mesh,
SF::NBR_REF, vertex2ptsdata,
1792 parab_solver.line_face,
1793 parab_solver.tri_face,
1794 parab_solver.quad_face,
1795 parab_solver.extra_tags,
1796 parab_solver.intra_tags,
1797 parab_solver.membraneFace_default,
1798 parab_solver.gapjunctionFace_default,
1799 emi_surfmesh_one_side, emi_surfmesh_w_counter_face, emi_surfmesh_unique_face,
1800 parab_solver.map_elem_uniqueFace_to_elem_oneface,
1801 unused_map_elem_oneface_to_elem_uniqueFace);
1802 meshlist.push_back(&emi_surfmesh_one_side);
1804 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1808 if(verb)
log_msg(NULL, 0, 0,
"\nset membraneFace_default:");
1809 make_global_pairs(parab_solver.membraneFace_default, size);
1810 if(verb)
log_msg(NULL, 0, 0,
"\nset gapjunctionFace_default:");
1811 make_global_pairs(parab_solver.gapjunctionFace_default, size);
1813 #ifdef EMI_DEBUG_MESH
1815 for (
const auto& p : parab_solver.membraneFace_default) {
1816 log_msg(NULL, 0, 0,
"[%d:%d]", p.first, p.second);
1820 if(verb)
log_msg(NULL, 0, 0,
"\nset gapjunctionFace_default:");
1821 make_global_pairs(parab_solver.gapjunctionFace_default, size);
1823 for (
const auto& p : parab_solver.gapjunctionFace_default) {
1824 log_msg(NULL, 0, 0,
"[%d:%d]", p.first, p.second);
1831 compute_surface_mesh_with_counter_face(emi_surfmesh_w_counter_face,
SF::NBR_REF,
1832 parab_solver.line_face,
1833 parab_solver.tri_face,
1834 parab_solver.quad_face);
1836 compute_surface_mesh_with_unique_face(emi_surfmesh_unique_face,
SF::NBR_REF,
1837 parab_solver.line_face,
1838 parab_solver.tri_face,
1839 parab_solver.quad_face,
1840 parab_solver.map_elem_uniqueFace_to_tags);
1845 SF::create_reverse_elem_mapping_between_surface_meshes(parab_solver.line_face,
1846 parab_solver.tri_face,
1847 parab_solver.quad_face,
1848 parab_solver.vec_both_to_one_face,
1851 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1856 if(verb)
log_msg(NULL, 0, 0,
"\ncompute global number of interface");
1857 size_t global_count_surf = 0;
1858 size_t numelem_surface = emi_surfmesh_one_side.l_numelem;
1859 MPI_Reduce(&numelem_surface, &global_count_surf, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
1860 if(verb && rank==0) fprintf(stdout,
"global number of interfaces = %zu\n", global_count_surf);
1867 sub_numbering(emi_mesh);
1868 emi_mesh.generate_par_layout();
1880 if(verb)
log_msg(NULL, 0, 0,
"\ndecouple emi interfaces");
1881 if(verb)
log_msg(NULL, 0, 0,
"\tcompute map oldIdx to dof");
1882 compute_map_vertex_to_dof(emi_mesh,
SF::NBR_REF, vertex2ptsdata, parab_solver.extra_tags, parab_solver.map_vertex_tag_to_dof);
1887 if(verb)
log_msg(NULL, 0, 0,
"\tcomplete map oldIdx to dof with counter interface");
1889 complete_map_vertex_to_dof_with_counter_face(parab_solver.line_face, parab_solver.tri_face, parab_solver.quad_face, parab_solver.map_vertex_tag_to_dof);
1894 if(verb)
log_msg(NULL, 0, 0,
"\tupdate mesh with dof");
1895 update_emi_mesh_with_dofs(emi_mesh,
SF::NBR_REF, parab_solver.map_vertex_tag_to_dof, parab_solver.dof2vertex);
1897 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1903 if(verb)
log_msg(NULL, 0, 0,
"\nInitialize petsc =0 for map<oldIdx,tag> -><dof, petsc>");
1905 for(
const auto & key_value : parab_solver.map_vertex_tag_to_dof)
1907 mesh_int_t gIndex_old = key_value.first.first;
1911 std::pair <mesh_int_t,mesh_int_t> dof_petsc = std::make_pair(dof,-1);
1912 parab_solver.map_vertex_tag_to_dof_petsc.insert({key_value.first,dof_petsc});
1915 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1921 if(verb)
log_msg(NULL, 0, 0,
"Inserting points and ptsData of dofs to emi_mesh");
1922 insert_points_ptsData_to_dof(tmesh_backup_old, emi_mesh,
SF::NBR_REF, parab_solver.dof2vertex, vertex2ptsdata, dof2ptsData);
1924 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1930 if(verb)
log_msg(NULL, 0, 0,
"Generating unique PETSc numberings");
1933 sub_numbering(emi_mesh);
1934 emi_mesh.generate_par_layout();
1937 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1943 if(verb)
log_msg(NULL, 0, 0,
"Generating unique PETSc numberings");
1946 petsc_numbering(emi_mesh);
1949 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1954 if(verb)
log_msg(NULL, 0, 0,
"Updating the map between indices to PETSc numberings");
1956 update_map_indices_to_petsc(emi_mesh,
SF::NBR_REF,
SF::NBR_PETSC, parab_solver.extra_tags, parab_solver.map_vertex_tag_to_dof_petsc, parab_solver.dof2vertex, parab_solver.elemTag_emi_mesh);
1958 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1964 if(verb)
log_msg(NULL, 0, 0,
"Updating surface mesh with dof");
1965 update_faces_on_surface_mesh_after_decoupling_with_dofs(emi_surfmesh_one_side, parab_solver.map_vertex_tag_to_dof, parab_solver.line_face, parab_solver.tri_face, parab_solver.quad_face);
1967 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1973 if(verb)
log_msg(NULL, 0, 0,
"Layout for element of EMI surfmesh");
1976 SF::layout_from_count<long int>(emi_surfmesh_one_side.l_numelem, layout, emi_surfmesh_one_side.comm);
1977 size_t count = layout[rank+1] - layout[rank];
1979 for (
int i = 0; i <
count; ++i){
1980 emi_surfmesh_elem[i] = layout[rank]+i;
1986 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1992 if(verb)
log_msg(NULL, 0, 0,
"Layout for element of EMI surfmesh w counter face");
1995 SF::layout_from_count<long int>(emi_surfmesh_w_counter_face.l_numelem, layout_counter, emi_surfmesh_w_counter_face.comm);
1996 size_t count_counter = layout_counter[rank+1] - layout_counter[rank];
1997 emi_surfmesh_counter_elem.
resize(count_counter);
1998 for (
int i = 0; i < count_counter; ++i){
1999 emi_surfmesh_counter_elem[i] = layout_counter[rank]+i;
2001 emi_surfmesh_w_counter_face.localize(
SF::NBR_REF);
2004 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2011 if(verb)
log_msg(NULL, 0, 0,
"Layout for element of EMI surfmesh w counter face");
2014 SF::layout_from_count<long int>(emi_surfmesh_unique_face.l_numelem, layout_unique, emi_surfmesh_unique_face.comm);
2015 size_t count_unique = layout_unique[rank+1] - layout_unique[rank];
2016 emi_surfmesh_unique_elem.
resize(count_unique);
2017 for (
int i = 0; i < count_unique; ++i){
2018 emi_surfmesh_unique_elem[i] = layout_unique[rank]+i;
2023 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2029 if(verb)
log_msg(NULL, 0, 0,
"Inserting points to EMI surfmesh");
2030 insert_points_to_surface_mesh(tmesh_backup_old, emi_surfmesh_one_side,
SF::NBR_REF, parab_solver.dof2vertex, parab_solver.extra_tags, parab_solver.elemTag_surface_mesh);
2031 insert_points_to_surface_mesh(tmesh_backup_old, emi_surfmesh_w_counter_face,
SF::NBR_REF, parab_solver.dof2vertex, parab_solver.extra_tags, parab_solver.elemTag_surface_w_counter_mesh);
2032 insert_points_to_surface_mesh(tmesh_backup_old, emi_surfmesh_unique_face,
SF::NBR_REF, parab_solver.dof2vertex);
2034 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2040 if(verb)
log_msg(NULL, 0, 0,
"Generating submesh_numbering and PETSc numberings for surface mesh");
2043 sub_numbering(emi_surfmesh_one_side);
2044 emi_surfmesh_one_side.generate_par_layout();
2047 petsc_numbering(emi_surfmesh_one_side);
2052 sub_numbering(emi_surfmesh_w_counter_face);
2053 emi_surfmesh_w_counter_face.generate_par_layout();
2056 petsc_numbering(emi_surfmesh_w_counter_face);
2061 sub_numbering(emi_surfmesh_unique_face);
2062 emi_surfmesh_unique_face.generate_par_layout();
2065 petsc_numbering(emi_surfmesh_unique_face);
2067 assign_deterministic_elem_numbering(emi_surfmesh_unique_face);
2070 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2076 if(verb)
log_msg(NULL, 0, 0,
"assign PETSc numbering for new faces");
2077 SF::assign_petsc_on_counter_face(parab_solver.map_vertex_tag_to_dof_petsc,comm);
2080 for (it = parab_solver.map_vertex_tag_to_dof_petsc.begin(); it != parab_solver.map_vertex_tag_to_dof_petsc.end(); it++)
2082 std::pair <mesh_int_t,mesh_int_t> Index_tag_old = it->first;
2083 std::pair <mesh_int_t,mesh_int_t> dof_petsc = it->second;
2084 parab_solver.dof2petsc[dof_petsc.first] = dof_petsc.second;
2085 parab_solver.petsc2dof[dof_petsc.second] = dof_petsc.first;
2090 #ifdef EMI_DEBUG_MESH
2092 int invalid_count = 0;
2093 for (
const auto& [key, val] : parab_solver.map_vertex_tag_to_dof_petsc) {
2094 if (val.second < 0) {
2096 if (invalid_count <= 3) {
2097 fprintf(stderr,
"RANK %d INVALID: vertex=%ld tag=%ld dof=%ld petsc=%ld\n",
2098 rank, (
long)key.first, (
long)key.second, (
long)val.first, (
long)val.second);
2102 fprintf(stderr,
"RANK %d: After Step 26: %d invalid PETSc indices out of %zu total\n",
2103 rank, invalid_count, parab_solver.map_vertex_tag_to_dof_petsc.size());
2109 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2114 added_counter_faces_to_map(parab_solver.line_face, parab_solver.tri_face, parab_solver.quad_face);
2116 log_msg(0,0,0,
"\n *** EMI mesh processing Done ***\n");
2121 MPI_Comm comm = mesh.
comm;
2123 double t1, t2, s1, s2;
2124 MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
2125 const int verb = param_globals::output_level;
2127 if(verb==10)
log_msg(NULL, 0, 0,
"\ncompute the number of unique tags");
2129 unique_tags = mesh.
tag;
2131 extract_unique_tag(unique_tags);
2133 size_t ntags = unique_tags.
size();
2134 if(verb==10)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2138 PetscPrintf(PETSC_COMM_WORLD,
"\nThe number of processors should be less than the number of tags, size = %d & ntags = %zu !!!\n", size, ntags);
2142 if(verb==10)
log_msg(NULL, 0, 0,
"\ncompute the number of tags which belongs to one rank");
2146 compute_tags_per_rank(ntags, ntags_per_rank);
2148 if(verb==10)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2154 partition_based_tags(ntags, mesh.
tag, unique_tags, ntags_per_rank, part_based_Tags);
2157 permute_mesh_locally_based_on_tag_elemIdx(mesh);
2159 if(verb==10)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2166 const int verb = param_globals::output_level;
2169 MPI_Comm_size(comm, &size);
2170 MPI_Comm_rank(comm, &rank);
2176 if (load_partitions_from_file(tags_to_rank_map, comm)) {
2177 if (tags_to_rank_map.
size() != unique_tags.
size()) {
2178 log_msg(0,5,0,
"\nerror: the number of tags in the .part file does not match the number of unique tags in the EMI mesh");
2183 map_tags_to_rank(size, unique_tags, num_tags_per_rank, tags_to_rank_map);
2186 for (
size_t i = 0; i < part.
size(); ++i) {
2187 if(tags_to_rank_map.
count(tag[i]))
2188 part[i] = tags_to_rank_map[tag[i]];
2196 for (
size_t r = 0; r < size; ++r) {
2197 for (
size_t count = 0;
count < num_tags_per_rank[r];) {
2201 tags_to_rank_map.
insert({tag, r});
2214 const std::string basename = param_globals::meshname;
2215 FILE* fd = fopen((basename +
".part").c_str(),
"r");
2221 if (parts.
size() == 0)
return false;
2223 if (parts.
size() % 2 != 0) {
2224 log_msg(0,5,0,
"\nThe part file should contain 2 lines per tag, one for the tag number and the next for its associated partition number.!");
2228 for(
int i = 0; i < parts.
size(); i+=2) {
2229 tags_to_rank_map.
insert({parts[i], parts[i + 1]});
#define SF_COMM
the default SlimFem MPI communicator
double SF_real
Use the general double as real type.
std::int32_t SF_int
Use the general std::int32_t as int type.
#define CALI_CXX_MARK_FUNCTION
#define CALI_MARK_BEGIN(_str)
#define CALI_MARK_END(_str)
void globalize(SF_nbr nbr_type)
Localize the connectivity data w.r.t. a given numbering.
size_t l_numelem
local number of elements
void localize(SF_nbr nbr_type)
Localize the connectivity data w.r.t. a given numbering.
MPI_Comm comm
the parallel mesh is defined on a MPI world
vector< T > & get_numbering(SF_nbr nbr_type)
Get the vector defining a certain numbering.
vector< T > tag
element tag
Functor class generating a numbering optimized for PETSc.
Container for a PETSc VecScatter.
Functor class applying a submesh renumbering.
A vector storing arbitrary data.
size_t size() const
The current size of the vector.
void resize(size_t n)
Resize a vector.
hm_int count(const K &key) const
Check if key exists.
void insert(InputIterator first, InputIterator last)
Insert Iterator range.
iterator find(const K &key)
hm_int erase(const K &key)
long d_time
current time instance index
double time_step
global reference time step
int add_eq_timer(double istart, double iend, int ntrig, double iintv, double idur, const char *iname, const char *poolname=nullptr)
Add a equidistant step timer to the array of timers.
long d_end
final index in multiples of dt
EMI model based on computed current on the faces, main EMI physics class.
void init_solver(SF::abstract_linear_solver< T, S > **sol)
void read_points(const std::string basename, const MPI_Comm comm, vector< S > &pts, vector< T > &ptsidx)
Read the points and insert them into a list of meshes.
void interval(vector< T > &vec, size_t start, size_t end)
Create an integer interval between start and end.
void make_global(const vector< T > &vec, vector< T > &out, MPI_Comm comm)
make a parallel vector global
void permute_mesh(const meshdata< T, S > &inmesh, meshdata< T, S > &outmesh, const vector< T > &perm)
Permute the element data of a mesh based on a given permutation.
void unique_resize(vector< T > &_P)
void divide(const size_t gsize, const size_t num_parts, vector< T > &loc_sizes)
divide gsize into num_parts local parts with even distribution of the remainder
void count(const vector< T > &data, vector< S > &cnt)
Count number of occurrences of indices.
void insert_points(const vector< S > &pts, const vector< T > &ptsidx, std::list< meshdata< T, S > * > &meshlist)
Insert the points from the read-in buffers into a list of distributed meshes.
void assemble_matrix(abstract_matrix< T, S > &mat, meshdata< mesh_int_t, mesh_real_t > &domain, matrix_integrator< mesh_int_t, mesh_real_t > &integrator)
Generalized matrix assembly.
int max_nodal_edgecount(const meshdata< T, S > &mesh)
Compute the maximum number of node-to-node edges for a mesh.
void redistribute_elements(meshdata< T, S > &mesh, meshdata< T, S > &sendbuff, vector< T > &part)
Redistribute the element data of a parallel mesh among the ranks based on a partitioning.
void restrict_to_set(vector< T > &v, const hashmap::unordered_set< T > &set)
size_t root_count_ascii_lines(std::string file, MPI_Comm comm)
count the lines in a ascii file
void init_vector(SF::abstract_vector< T, S > **vec)
void binary_sort(vector< T > &_V)
void init_matrix(SF::abstract_matrix< T, S > **mat)
void write_mesh_parallel(const meshdata< T, S > &mesh, bool binary, std::string basename)
Write a parallel mesh to harddisk without gathering it on one rank.
void binary_sort_sort_copy(vector< T > &_V, vector< T > &_W, vector< S > &_A)
@ NBR_PETSC
PETSc numbering of nodes.
@ NBR_ELEM_REF
The element numbering of the reference mesh (the one stored on HD).
@ 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.
@ NBR_ELEM_SUBMESH
Submesh element numbering: The globally ascending sorted reference indices are reindexed.
void dump_trace(MULTI_IF *MIIF, limpet::Real time)
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.
timer_manager * tm_manager
a manager for the various physics timers
bool using_legacy_stimuli
flag storing whether legacy stimuli are used
std::map< mesh_t, sf_mesh > mesh_reg
Registry for the different meshes used in a multi-physics simulation.
int stimidx_from_timeridx(const SF::vector< stimulus > &stimuli, const int timer_id)
determine link between timer and stimulus
sf_vec * get_data(datavec_t d)
Retrieve a petsc data vector from the data registry.
SF::scattering * get_scattering(const int from, const int to, const SF::SF_nbr nbr, const int dpn)
Get a scattering from the global scatter registry.
void set_cond_type(MaterialType &m, cond_t type)
SF_real get_volume_from_nodes(sf_mat &mass, SF::vector< mesh_int_t > &local_idx)
sf_mesh & get_mesh(const mesh_t gt)
Get a mesh by specifying the gridID.
SF::scattering * register_scattering(const int from, const int to, const SF::SF_nbr nbr, const int dpn)
Register a scattering between to grids, or between algebraic and nodal representation of data on the ...
cond_t
description of electrical tissue properties
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 apply_stim_to_vector(const stimulus &s, sf_vec &vec, bool add)
void read_indices_global(SF::vector< T > &idx, const std::string filename, MPI_Comm comm)
int get_rank(MPI_Comm comm=PETSC_COMM_WORLD)
void indices_from_region_tags(SF::vector< mesh_int_t > &idx, const sf_mesh &mesh, const hashmap::unordered_set< int > &tags)
Populate vertex data with the vertices of multiple tag regions.
const char * get_mesh_type_name(mesh_t t)
get a char* to the name of a mesh type
void region_mask(mesh_t meshspec, SF::vector< RegionSpecs > ®spec, SF::vector< int > ®ionIDs, bool mask_elem, const char *reglist)
classify elements/points as belonging to a region
void init_stim_info(void)
uses potential for stimulation
bool is_extra(stim_t type)
whether stimulus is on extra grid (or on intra)
FILE_SPEC f_open(const char *fname, const char *mode)
Open a FILE_SPEC.
bool have_dbc_stims(const SF::vector< stimulus > &stimuli)
return wheter any stimuli require dirichlet boundary conditions
SF::scattering * register_permutation(const int mesh_id, const int perm_id, const int dpn)
Register a permutation between two orderings for a mesh.
bool is_current(stim_t type)
uses current as stimulation
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
@ emi_surface_counter_msh
void get_time(double &tm)
bool mesh_is_registered(const mesh_t gt)
check wheter a SF mesh is set
SF::abstract_vector< SF_int, SF_real > sf_vec
int get_size(MPI_Comm comm=PETSC_COMM_WORLD)
SF::abstract_matrix< SF_int, SF_real > sf_mat
V timing(V &t2, const V &t1)
std::string get_basename(const std::string &path)
void f_close(FILE_SPEC &f)
Close a FILE_SPEC.
#define UM2_to_CM2
convert um^2 to cm^2
#define PETSC_TO_CANONICAL
Permute algebraic data from PETSC to canonical ordering.
#define ALG_TO_NODAL
Scatter algebraic to nodal.
#define ELEM_PETSC_TO_CANONICAL
Permute algebraic element data from PETSC to canonical ordering.
Electrical stimulation functions.