26 #include "petsc_utils.h"
36 #include "SF_petsc_matrix.h"
39 #include "caliper/cali.h"
46 void log_mesh_local_element_ranges(
const sf_mesh& emi_mesh,
47 const sf_mesh& emi_surfmesh_w_counter_face,
48 const sf_mesh& emi_surfmesh_unique_face)
52 MPI_Comm_rank(emi_surfmesh_w_counter_face.comm, &rank);
53 MPI_Comm_size(emi_surfmesh_w_counter_face.comm, &comm_size);
55 const unsigned long long local_emi_elems =
56 static_cast<unsigned long long>(emi_mesh.l_numelem);
57 const unsigned long long local_both_face_elems =
58 static_cast<unsigned long long>(emi_surfmesh_w_counter_face.l_numelem);
59 const unsigned long long local_unique_face_elems =
60 static_cast<unsigned long long>(emi_surfmesh_unique_face.l_numelem);
62 std::vector<unsigned long long> all_emi_elems;
63 std::vector<unsigned long long> all_both_face_elems;
64 std::vector<unsigned long long> all_unique_face_elems;
66 all_emi_elems.resize(comm_size, 0);
67 all_both_face_elems.resize(comm_size, 0);
68 all_unique_face_elems.resize(comm_size, 0);
71 MPI_Gather(&local_emi_elems, 1, MPI_UNSIGNED_LONG_LONG,
72 rank == 0 ? all_emi_elems.data() :
nullptr, 1, MPI_UNSIGNED_LONG_LONG,
73 0, emi_surfmesh_w_counter_face.comm);
74 MPI_Gather(&local_both_face_elems, 1, MPI_UNSIGNED_LONG_LONG,
75 rank == 0 ? all_both_face_elems.data() :
nullptr, 1, MPI_UNSIGNED_LONG_LONG,
76 0, emi_surfmesh_w_counter_face.comm);
77 MPI_Gather(&local_unique_face_elems, 1, MPI_UNSIGNED_LONG_LONG,
78 rank == 0 ? all_unique_face_elems.data() :
nullptr, 1, MPI_UNSIGNED_LONG_LONG,
79 0, emi_surfmesh_w_counter_face.comm);
81 if (rank != 0)
return;
83 const auto print_min_max = [](
const char* label,
const std::vector<unsigned long long>& counts) {
84 if (counts.empty())
return;
86 unsigned long long min_val = counts[0];
87 unsigned long long max_val = counts[0];
91 for (
int r = 1; r < static_cast<int>(counts.size()); ++r) {
92 if (counts[r] < min_val) {
96 if (counts[r] > max_val) {
102 log_msg(NULL, 0, 0,
" %s: \n\t\t min=%llu on rank=%d, \n\t\t max=%llu on rank=%d\n",
103 label, min_val, min_rank, max_val, max_rank);
105 log_msg(NULL, 0, 0,
"\n**********************************");
106 log_msg(NULL, 0, 0,
"min/max number of local-element ranges:");
107 print_min_max(
"emi_mesh", all_emi_elems);
108 print_min_max(
"emi_surfmesh_w_counter_face", all_both_face_elems);
109 print_min_max(
"emi_surfmesh_unique_face", all_unique_face_elems);
110 log_msg(NULL, 0, 0,
"**********************************");
113 #ifdef EMI_DEBUG_MESH
120 if (param_globals::flavor != std::string(
"petsc"))
return;
122 auto* petsc_mat =
dynamic_cast<SF::petsc_matrix*
>(mat);
123 if (petsc_mat ==
nullptr)
return;
125 Vec x = NULL, y = NULL;
126 MatCreateVecs(petsc_mat->data, &x, &y);
128 PetscRandom rnd = NULL;
129 PetscRandomCreate(PETSC_COMM_WORLD, &rnd);
130 PetscRandomSetFromOptions(rnd);
133 MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
135 PetscReal min_q = PETSC_MAX_REAL;
136 PetscReal max_q = -PETSC_MAX_REAL;
137 PetscInt nonpos_count = 0;
139 for (
int i = 0; i < num_trials; ++i) {
140 VecSetRandom(x, rnd);
141 MatMult(petsc_mat->data, x, y);
143 PetscScalar q_scalar = 0.0;
144 VecDot(x, y, &q_scalar);
146 const PetscReal q = PetscRealPart(q_scalar);
149 if (q <= 0.0) nonpos_count++;
153 PetscPrintf(PETSC_COMM_SELF,
154 "%s: SPD probe with %d random vectors: min(x^T A x)=%g, max(x^T A x)=%g, nonpositive=%d\n",
155 stage, num_trials,
double(min_q),
double(max_q),
int(nonpos_count));
158 PetscRandomDestroy(&rnd);
163 PetscScalar emi_probe_vector_entry(
const PetscInt gid,
const int probe_id)
165 const double x =
static_cast<double>(gid + 1);
169 return std::sin(1.0e-3 * x) + 0.5 * std::cos(3.0e-3 * x);
171 return std::cos(7.0e-4 * x) - 0.35 * std::sin(2.0e-3 * x);
173 return 0.75 * std::sin(1.3e-3 * x) + 0.25 * std::cos(4.0e-3 * x);
183 if (param_globals::flavor != std::string(
"petsc"))
return;
185 auto* petsc_mat =
dynamic_cast<SF::petsc_matrix*
>(mat);
186 if (petsc_mat ==
nullptr)
return;
188 Vec x = NULL, y = NULL;
189 MatCreateVecs(petsc_mat->data, &x, &y);
191 PetscInt i_start = 0, i_end = 0;
192 VecGetOwnershipRange(x, &i_start, &i_end);
194 PetscScalar* x_arr = NULL;
196 MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
198 for (
int probe_id = 0; probe_id < num_probes; ++probe_id) {
199 VecGetArray(x, &x_arr);
200 for (PetscInt i = i_start; i < i_end; ++i) {
201 x_arr[i - i_start] = emi_probe_vector_entry(i, probe_id);
203 VecRestoreArray(x, &x_arr);
205 MatMult(petsc_mat->data, x, y);
207 PetscReal y_norm2 = 0.0;
208 PetscReal y_norminf = 0.0;
209 PetscScalar y_sum = 0.0;
210 PetscScalar xAy = 0.0;
211 VecNorm(y, NORM_2, &y_norm2);
212 VecNorm(y, NORM_INFINITY, &y_norminf);
216 PetscScalar weighted_checksum_local = 0.0;
217 const PetscScalar* y_arr = NULL;
218 VecGetArrayRead(y, &y_arr);
219 for (PetscInt i = i_start; i < i_end; ++i) {
220 const PetscScalar weight =
static_cast<PetscScalar
>(i + 1);
221 weighted_checksum_local += weight * y_arr[i - i_start];
223 VecRestoreArrayRead(y, &y_arr);
225 PetscScalar weighted_checksum = 0.0;
226 MPI_Allreduce(&weighted_checksum_local, &weighted_checksum, 1, MPIU_SCALAR, MPI_SUM, PETSC_COMM_WORLD);
229 PetscPrintf(PETSC_COMM_SELF,
230 "%s: operator probe %d ||Ax||_2=%g, ||Ax||_inf=%g, sum(Ax)=%g, x^T A x=%g, weighted_checksum=%g\n",
231 stage, probe_id + 1,
double(y_norm2),
double(y_norminf),
double(PetscRealPart(y_sum)),
232 double(PetscRealPart(xAy)),
double(PetscRealPart(weighted_checksum)));
243 if (param_globals::flavor != std::string(
"petsc"))
return;
245 auto* petsc_vec =
dynamic_cast<SF::petsc_vector*
>(vec);
246 if (petsc_vec ==
nullptr)
return;
248 PetscReal norm2 = 0.0;
249 PetscReal norminf = 0.0;
250 PetscScalar
sum = 0.0;
251 VecNorm(petsc_vec->data, NORM_2, &norm2);
252 VecNorm(petsc_vec->data, NORM_INFINITY, &norminf);
253 VecSum(petsc_vec->data, &
sum);
255 PetscInt i_start = 0, i_end = 0;
256 VecGetOwnershipRange(petsc_vec->data, &i_start, &i_end);
258 PetscScalar weighted_checksum_local = 0.0;
259 const PetscScalar* arr = NULL;
260 VecGetArrayRead(petsc_vec->data, &arr);
261 for (PetscInt i = i_start; i < i_end; ++i) {
262 const PetscScalar weight =
static_cast<PetscScalar
>(i + 1);
263 weighted_checksum_local += weight * arr[i - i_start];
265 VecRestoreArrayRead(petsc_vec->data, &arr);
267 PetscScalar weighted_checksum = 0.0;
268 MPI_Allreduce(&weighted_checksum_local, &weighted_checksum, 1, MPIU_SCALAR, MPI_SUM, PETSC_COMM_WORLD);
271 MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
273 PetscPrintf(PETSC_COMM_SELF,
274 "%s: rhs probe ||b||_2=%g, ||b||_inf=%g, sum(b)=%g, weighted_checksum=%g\n",
275 stage,
double(norm2),
double(norminf),
double(PetscRealPart(
sum)),
276 double(PetscRealPart(weighted_checksum)));
285 if (param_globals::flavor != std::string(
"petsc"))
return;
287 auto* petsc_mat =
dynamic_cast<SF::petsc_matrix*
>(mat);
288 auto* petsc_vec =
dynamic_cast<SF::petsc_vector*
>(vec);
289 if (petsc_mat ==
nullptr || petsc_vec ==
nullptr)
return;
291 Vec x = NULL, ax = NULL, residual = NULL;
292 MatCreateVecs(petsc_mat->data, &x, &ax);
293 VecDuplicate(ax, &residual);
295 PetscInt i_start = 0, i_end = 0;
296 VecGetOwnershipRange(x, &i_start, &i_end);
299 MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
301 for (
int probe_id = 0; probe_id < num_probes; ++probe_id) {
302 PetscScalar* x_arr = NULL;
303 VecGetArray(x, &x_arr);
304 for (PetscInt i = i_start; i < i_end; ++i) {
305 x_arr[i - i_start] = emi_probe_vector_entry(i, probe_id);
307 VecRestoreArray(x, &x_arr);
309 MatMult(petsc_mat->data, x, ax);
310 VecWAXPY(residual, -1.0, petsc_vec->data, ax);
312 PetscReal residual_norm2 = 0.0;
313 PetscReal residual_norminf = 0.0;
314 PetscScalar residual_sum = 0.0;
315 PetscScalar xTResidual = 0.0;
316 VecNorm(residual, NORM_2, &residual_norm2);
317 VecNorm(residual, NORM_INFINITY, &residual_norminf);
318 VecSum(residual, &residual_sum);
319 VecDot(x, residual, &xTResidual);
321 PetscScalar weighted_checksum_local = 0.0;
322 const PetscScalar* residual_arr = NULL;
323 VecGetArrayRead(residual, &residual_arr);
324 for (PetscInt i = i_start; i < i_end; ++i) {
325 const PetscScalar weight =
static_cast<PetscScalar
>(i + 1);
326 weighted_checksum_local += weight * residual_arr[i - i_start];
328 VecRestoreArrayRead(residual, &residual_arr);
330 PetscScalar weighted_checksum = 0.0;
331 MPI_Allreduce(&weighted_checksum_local, &weighted_checksum, 1, MPIU_SCALAR, MPI_SUM, PETSC_COMM_WORLD);
334 PetscPrintf(PETSC_COMM_SELF,
335 "%s: linear-system probe %d ||Ax-b||_2=%g, ||Ax-b||_inf=%g, sum(Ax-b)=%g, x^T(Ax-b)=%g, weighted_checksum=%g\n",
336 stage, probe_id + 1,
double(residual_norm2),
double(residual_norminf),
337 double(PetscRealPart(residual_sum)),
double(PetscRealPart(xTResidual)),
338 double(PetscRealPart(weighted_checksum)));
344 VecDestroy(&residual);
351 MaterialType *m = mtype;
354 m->regions.resize(param_globals::num_gregions);
356 const char* grid_name =
"emi_grid_domain";
357 log_msg(logger, 0, 0,
"Setting up %s tissue poperties for %d regions ..", grid_name,
358 param_globals::num_gregions);
361 RegionSpecs* reg = m->regions.data();
367 for (
size_t i=0; i<m->regions.size(); i++)
369 for (
int j=0;j<param_globals::gregion[i].num_IDs;j++)
371 int tag = param_globals::gregion[i].ID[j];
374 if(extra_tags_default.
find(tag) != extra_tags_default.
end())
375 extra_tags_default.
erase(tag);
377 if(intra_tags_default.
find(tag) != intra_tags_default.
end())
378 intra_tags_default.
erase(tag);
382 for (
size_t i=0; i<m->regions.size(); i++, reg++)
384 if(!strcmp(param_globals::gregion[i].name,
"")) {
385 snprintf(buf,
sizeof buf,
", gregion_%d",
int(i));
386 param_globals::gregion[i].name =
dupstr(buf);
390 reg->regname = strdup(param_globals::gregion[i].name);
394 reg->nsubregs = extra_tags_default.
size();
396 reg->nsubregs = intra_tags_default.
size();
398 reg->nsubregs = param_globals::gregion[i].num_IDs;
401 reg->subregtags = NULL;
404 reg->subregtags =
new int[reg->nsubregs];
408 for (
int tag : extra_tags_default) {
409 reg->subregtags[j] = tag;
415 for (
int tag : intra_tags_default) {
416 reg->subregtags[j] = tag;
421 for (
int j=0;j<reg->nsubregs;j++)
422 reg->subregtags[j] = param_globals::gregion[i].ID[j];
427 elecMaterial *emat =
new elecMaterial();
431 emat->InVal[0] = param_globals::gregion[i].g_bath;
432 emat->InVal[1] = param_globals::gregion[i].g_bath;
433 emat->InVal[2] = param_globals::gregion[i].g_bath;
435 emat->ExVal[0] = param_globals::gregion[i].g_bath;
436 emat->ExVal[1] = param_globals::gregion[i].g_bath;
437 emat->ExVal[2] = param_globals::gregion[i].g_bath;
439 emat->BathVal[0] = param_globals::gregion[i].g_bath;
440 emat->BathVal[1] = param_globals::gregion[i].g_bath;
441 emat->BathVal[2] = param_globals::gregion[i].g_bath;
444 for (
int j=0; j<3; j++) {
445 emat->InVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
446 emat->ExVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
447 emat->BathVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
449 reg->material = emat;
452 if(strlen(param_globals::gi_scale_vec))
456 const char* file = param_globals::gi_scale_vec;
460 if(num_file_entries != mesh.g_numelem)
461 log_msg(0,4,0,
"%s warning: number of %s conductivity scaling entries does not match number of elements!",
467 escale->read_ascii(param_globals::gi_scale_vec);
480 m->el_scale.assign(p, p + escale->lsize());
481 escale->release_ptr(p);
485 void parabolic_solver_emi::init()
490 stats.init_logger(
"par_stats.dat");
509 MPI_Comm_rank(emi_surfmesh_w_counter_face.comm, &rank);
525 SF::init_vector(&vb_both_face, emi_surfmesh_w_counter_face, dpn, alg_surface_type);
526 SF::init_vector(&vb_unique_face, emi_surfmesh_unique_face, dpn, alg_surface_type);
527 SF::init_vector(&Ib_both_face, emi_surfmesh_w_counter_face, dpn, alg_surface_type);
528 SF::init_vector(&Ib_unique_face, emi_surfmesh_unique_face, dpn, alg_surface_type);
538 lhs_emi->init(emi_mesh, dpn, dpn, max_row_entries_emi*3);
539 stiffness_emi->init(emi_mesh, dpn, dpn, max_row_entries_emi*2);
540 mass_emi->init(emi_mesh, dpn, dpn, max_row_entries_emi*3);
541 mass_surf_emi->init(emi_mesh, dpn, dpn, max_row_entries_emi*3);
545 mesh_int_t M = emi_surfmesh_w_counter_face.g_numelem;
547 mesh_int_t m = emi_surfmesh_w_counter_face.l_numelem;
548 mesh_int_t m_one_side = emi_surfmesh_one_side.l_numelem;
549 mesh_int_t M_one_side = emi_surfmesh_one_side.g_numelem;
550 mesh_int_t m_unique_face = emi_surfmesh_unique_face.l_numelem;
551 mesh_int_t M_unique_face = emi_surfmesh_unique_face.g_numelem;
554 log_msg(NULL, 0, 0,
"\n**********************************");
555 log_msg(NULL, 0, 0,
"#elements of emi surfmesh unique face: %zu", emi_surfmesh_unique_face.g_numelem);
556 log_msg(NULL, 0, 0,
"#elements of emi surfmesh one side: %zu", emi_surfmesh_one_side.g_numelem);
557 log_msg(NULL, 0, 0,
"#elements of emi surfmesh: %zu", emi_surfmesh_w_counter_face.g_numelem);
558 log_msg(NULL, 0, 0,
"#elements of emi mesh: %zu", emi_mesh.g_numelem);
559 log_msg(NULL, 0, 0,
"#dofs for emi_mesh: %zu", emi_mesh.g_numpts);
560 log_msg(NULL, 0, 0,
"#max_row_entries_emi: %zu", max_row_entries_emi);
561 log_msg(NULL, 0, 0,
"**********************************\n");
562 log_mesh_local_element_ranges(emi_mesh, emi_surfmesh_w_counter_face, emi_surfmesh_unique_face);
565 SF::layout_from_count<long int>(emi_surfmesh_w_counter_face.l_numelem, layout, emi_surfmesh_w_counter_face.comm);
567 mesh_int_t n_l = emi_mesh.pl.algebraic_layout()[rank];
571 SF::layout_from_count<long int>(emi_surfmesh_one_side.l_numelem, layout_one_side, emi_surfmesh_one_side.comm);
572 mesh_int_t m_one_side_l = layout_one_side[rank];
575 SF::layout_from_count<long int>(emi_surfmesh_unique_face.l_numelem, layout_unique_face, emi_surfmesh_unique_face.comm);
576 mesh_int_t m_unique_face_l = layout_unique_face[rank];
583 B->init(M, N, m, n, m_l, max_row_entries_emi*2);
586 Bi->init(M, N, m, n, m_l, max_row_entries_emi*2);
589 BsM->init(N, M, n, m, n_l, max_row_entries_emi*2);
592 Vm_myocyte_emi->init(N, N, n, n, n_l, max_row_entries_emi*2);
593 Vm_myocyte_emi->zero();
596 SF::construct_direct_unique_both_operators(operator_unique_to_both_faces,
597 operator_both_to_unique_face,
598 map_elem_uniqueFace_to_elem_bothface,
599 map_elem_uniqueFace_to_elem_oneface,
600 vec_both_to_one_face,
601 emi_surfmesh_w_counter_face,
602 emi_surfmesh_unique_face,
608 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);
611 #ifdef EMI_DEBUG_MESH
614 MPI_Comm_rank(emi_mesh.comm, &local_rank);
616 fprintf(stderr,
"RANK %d MESH SIZES: one_side=%zu, counter=%zu, unique=%zu\n",
617 local_rank, emi_surfmesh_one_side.l_numelem,
618 emi_surfmesh_w_counter_face.l_numelem, emi_surfmesh_unique_face.l_numelem);
620 fprintf(stderr,
"RANK %d MAP SIZES: uniqueFace_to_oneface=%zu\n",
621 local_rank, map_elem_uniqueFace_to_elem_oneface.size());
633 if(!(vb_ptr != NULL && Ib_ptr != NULL)) {
634 log_msg(0,5,0,
"%s error: global Vb and Ib vectors not properly set up! Ionics seem invalid! Aborting!",
640 vb->shallow_copy(*vb_ptr);
642 Ib->shallow_copy(*Ib_ptr);
644 parab_tech =
static_cast<parabolic_solver_emi::parabolic_t
>(param_globals::parab_solve_emi);
652 double start, end, period;
655 mass_integrator mass_integ;
656 mass_integrator mass_integ_emi;
659 int log_flag = param_globals::output_level > 1 ?
ECHO : 0;
660 MaterialType & mt = mtype[0];
669 #ifdef EMI_DEBUG_MESH
671 const auto log_lhs_symmetry = [&](
const char* stage) {
672 if (param_globals::flavor != std::string(
"petsc"))
return;
674 auto* petsc_lhs =
dynamic_cast<SF::petsc_matrix*
>(lhs_emi);
675 if (petsc_lhs ==
nullptr)
return;
678 MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
680 for (PetscReal tol_sym : {PetscReal(1e-14), PetscReal(1e-12), PetscReal(1e-10), PetscReal(1e-8)}) {
681 PetscBool is_sym = PETSC_FALSE;
682 MatIsSymmetric(petsc_lhs->data, tol_sym, &is_sym);
684 PetscPrintf(PETSC_COMM_SELF,
"%s: MatIsSymmetric(%g) = %s\n",
685 stage,
double(tol_sym), is_sym ?
"TRUE" :
"FALSE");
698 log_msg(NULL, 0, 0,
"assemble stiffness matrix");
700 elec_stiffness_integrator stfn_integ_emi(mt);
701 stiffness_emi->zero();
704 log_msg(logger,0,log_flag,
"Computed parabolic stiffness matrix in %.5f seconds.",
float(dur));
706 log_msg(NULL, 0, 0,
"assemble mass matrix on the volumetric mesh");
708 mass_integrator mass_integ;
712 log_msg(logger,0,log_flag,
"Computed volumetric mass matrix in %.5f seconds.",
float(dur));
714 log_msg(NULL, 0, 0,
"assemble LHS matrix and mass matrix on the surface mesh");
718 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);
720 log_msg(logger,0,log_flag,
"Computed parabolic mass matrix in %.5f seconds.",
float(dur));
721 #ifdef EMI_DEBUG_MESH
722 log_lhs_symmetry(
"lhs_emi after assemble_lhs_emi");
723 log_lhs_positive_definite_probe(lhs_emi, logger,
"lhs_emi after assemble_lhs_emi");
724 log_lhs_operator_probe(lhs_emi, logger,
"lhs_emi after assemble_lhs_emi");
729 bool same_nonzero =
false;
733 log_msg(logger,0,log_flag,
"lhs matrix enforcing Dirichlet boundaries.");
737 dbc =
new dbc_manager(*lhs_emi, stimuli);
739 dbc->recompute_dbcs();
741 dbc->enforce_dbc_lhs();
742 #ifdef EMI_DEBUG_MESH
743 log_lhs_symmetry(
"lhs_emi after enforce_dbc_lhs");
744 log_lhs_positive_definite_probe(lhs_emi, logger,
"lhs_emi after enforce_dbc_lhs");
745 log_lhs_operator_probe(lhs_emi, logger,
"lhs_emi after enforce_dbc_lhs");
748 log_msg(logger,0,log_flag,
"lhs matrix Dirichlet enforcing done in %.5f seconds.",
float(dur));
751 log_msg(logger,0,
ECHO,
"without enforcing Dirichlet boundaries on the lhs matrix!");
753 phie_mat_has_nullspace =
true;
758 setup_linear_solver(logger);
760 log_msg(logger,0,log_flag,
"Initializing parabolic solver in %.5f seconds.",
float(dur));
763 period =
timing(end, start);
766 void parabolic_solver_emi::setup_linear_solver(
FILE_SPEC logger)
768 tol = param_globals::cg_tol_parab;
769 max_it = param_globals::cg_maxit_parab;
771 std::string default_opts;
772 std::string solver_file;
773 solver_file = param_globals::parab_options_file;
774 if (param_globals::flavor == std::string(
"ginkgo")) {
775 default_opts = std::string(
778 "type": "solver::Cg",
780 "type": "solver::Multigrid",
781 "min_coarse_rows": 8,
783 "default_initial_guess": "zero",
786 "type": "multigrid::Pgm",
787 "deterministic": false
791 "type": "preconditioner::Schwarz",
793 "type": "preconditioner::Jacobi"
809 "type": "ResidualNorm",
810 "reduction_factor": 1e-4
815 } else if (param_globals::flavor == std::string(
"petsc")) {
816 default_opts = std::string(
"-ksp_type cg -pc_type gamg -options_left");
818 lin_solver->setup_solver(*lhs_emi, tol, max_it * 100, param_globals::cg_norm_parab,
819 "parabolic PDE", phie_mat_has_nullspace, logger, solver_file.c_str(),
820 default_opts.c_str());
823 void parabolic_solver_emi::solve()
825 switch (parab_tech) {
826 case SEMI_IMPLICIT: solve_semiImplicit();
break;
830 void parabolic_solver_emi::solve_semiImplicit()
837 dbc->enforce_dbc_rhs(*ui);
845 stiffness_emi->mult(*ui, *Iij_temp);
852 if (Iij_stim->mag() > 0.0) {
854 mass_emi->mult(*Iij_stim, *Iij_temp);
864 #ifdef EMI_DEBUG_MESH
865 log_rhs_probe(Irhs,
"rhs before linear_solve");
866 log_linear_system_probe(lhs_emi, Irhs,
"rhs before linear_solve");
871 (*lin_solver)(*dui, *Irhs);
875 if(lin_solver->reason < 0) {
876 log_msg(0, 5, 0,
"%s solver diverged. Reason: %s.", lin_solver->name.c_str(),
877 petsc_get_converged_reason_str(lin_solver->reason));
883 ui_pre->add_scaled(*dui, 1.0);
885 ui->add_scaled(*ui_pre, 1.0);
892 dbc->enforce_dbc_rhs(*ui);
897 stats.slvtime +=
timing(t1, t0);
898 stats.update_iter(lin_solver->niter);
911 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,
912 std::vector<std::string> & tags_data)
921 for(
size_t i=0; i<rnod.
size(); i++){
925 for(
size_t eidx=0; eidx<emi_surfmesh_w_counter_face.l_numelem; eidx++)
927 std::vector<int> elem_nodes;
928 mesh_int_t tag = emi_surfmesh_w_counter_face.tag[eidx];
929 for (
int n = emi_surfmesh_w_counter_face.dsp[eidx]; n < emi_surfmesh_w_counter_face.dsp[eidx+1];n++)
931 mesh_int_t l_idx = emi_surfmesh_w_counter_face.con[n];
933 std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
934 Index_tag_old = std::make_pair(l2g[l_idx],tags[eidx]);
935 mesh_int_t dof = map_vertex_tag_to_dof[Index_tag_old];
936 elem_nodes.push_back(dof);
941 std::string result_first;
942 std::string result_second;
943 std::sort(elem_nodes.begin(),elem_nodes.end());
946 if(elem_nodes.size()==2){
949 key.
v1 = elem_nodes[0];
950 key.
v2 = elem_nodes[1];
951 std::pair<SF::emi_face<mesh_int_t,SF::tuple<mesh_int_t>>,
952 SF::emi_face<mesh_int_t,SF::tuple<mesh_int_t>>> value = line_face[key];
954 tag_first = value.first.tag;
955 tag_second = value.second.tag;
956 result_first = std::to_string(tag_first) +
":" + std::to_string(tag_second);
957 result_second = std::to_string(tag_second) +
":" + std::to_string(tag_first);
959 else if(elem_nodes.size()==3){
961 key.
v1 = elem_nodes[0];
962 key.
v2 = elem_nodes[1];
963 key.
v3 = elem_nodes[2];
964 std::pair<SF::emi_face<mesh_int_t,SF::triple<mesh_int_t>>,
965 SF::emi_face<mesh_int_t,SF::triple<mesh_int_t>>> value = tri_face[key];
967 tag_first = value.first.tag;
968 tag_second = value.second.tag;
969 result_first = std::to_string(tag_first) +
":" + std::to_string(tag_second);
970 result_second = std::to_string(tag_second) +
":" + std::to_string(tag_first);
972 else if(elem_nodes.size()==4){
974 key.
v1 = elem_nodes[0];
975 key.
v2 = elem_nodes[1];
976 key.
v3 = elem_nodes[2];
977 key.
v4 = elem_nodes[3];
978 std::pair<SF::emi_face<mesh_int_t,SF::quadruple<mesh_int_t>>,
979 SF::emi_face<mesh_int_t,SF::quadruple<mesh_int_t>>> value = quad_face[key];
981 tag_first = value.first.tag;
982 tag_second = value.second.tag;
983 result_first = std::to_string(tag_first) +
":" + std::to_string(tag_second);
984 result_second = std::to_string(tag_second) +
":" + std::to_string(tag_first);
987 tags_data.push_back(result_first);
988 tags_data.push_back(result_second);
992 void EMI::initialize()
1000 logger =
f_open(
"emi.log", param_globals::experiment != 4 ?
"w" :
"r");
1001 const int verb = param_globals::output_level;
1014 ion.logger = logger;
1016 ion.set_surface_mesh_data(parab_solver.line_face,
1017 parab_solver.tri_face,
1018 parab_solver.quad_face,
1019 parab_solver.map_vertex_tag_to_dof);
1022 std::vector<std::string> tags_data;
1023 tags_onFace(parab_solver.line_face,
1024 parab_solver.tri_face,
1025 parab_solver.quad_face,
1026 parab_solver.map_vertex_tag_to_dof,
1027 parab_solver.map_vertex_tag_to_dof_petsc,
1031 ion.set_tags_onFace(tags_data);
1033 ion.set_face_region_data(parab_solver.intra_tags, parab_solver.map_elem_uniqueFace_to_tags);
1037 set_elec_tissue_properties_emi_volume(mtype_vol, parab_solver.extra_tags, parab_solver.intra_tags, logger);
1038 region_mask(
emi_msh, mtype_vol[0].regions, mtype_vol[0].regionIDs,
true,
"gregion_vol");
1043 param_globals::dt, 0,
"elec::ref_dt",
"TS");
1047 param_globals::operator_splitting = 0;
1058 balance_electrodes();
1060 scale_total_stimulus_current(stimuli, *parab_solver.mass_emi, *parab_solver.mass_surf_emi, logger);
1078 parab_solver.operator_unique_to_both_faces->mult(*parab_solver.vb, *parab_solver.vb_both_face);
1079 SF::assign_resting_potential_from_ionic_models_on_myocyte(*parab_solver.Vm_myocyte_emi,
1080 parab_solver.vb_both_face,
1081 parab_solver.elemTag_emi_mesh,
1082 parab_solver.map_vertex_tag_to_dof_petsc,
1083 parab_solver.line_face, parab_solver.tri_face, parab_solver.quad_face,
1084 emi_surfmesh_w_counter_face, emi_mesh);
1086 parab_solver.Vm_myocyte_emi->get_diagonal(*parab_solver.ui);
1087 *parab_solver.vb_unique_face = *parab_solver.vb;
1095 this->initialize_time +=
timing(t2, t1);
1098 void EMI::setup_mappings()
1106 log_msg(logger, 0, 0,
"%s: Setting up intracellular algebraic-to-nodal scattering.", __func__);
1111 log_msg(logger, 0, 0,
"%s: Setting up intracellular PETSc to canonical permutation.", __func__);
1116 void EMI::compute_step()
1125 const int verb = param_globals::output_level;
1132 apply_dbc_stimulus();
1141 apply_current_stimulus();
1145 parab_solver.operator_unique_to_both_faces->mult(*parab_solver.Ib, *parab_solver.Ib_both_face);
1146 parab_solver.BsM->mult(*parab_solver.Ib_both_face, *parab_solver.Irhs);
1150 parab_solver.solve();
1153 parab_solver.B->mult(*parab_solver.ui, *parab_solver.vb_both_face);
1155 parab_solver.operator_both_to_unique_face->mult(*parab_solver.vb_both_face, *parab_solver.vb_unique_face);
1156 *parab_solver.vb = *parab_solver.vb_unique_face;
1164 this->compute_time +=
timing(t2, t1);
1171 void EMI::output_step()
1176 output_manager.write_data();
1178 double curtime =
timing(t2, t1);
1179 this->output_time += curtime;
1182 IO_stats.tot_time += curtime;
1198 output_manager.close_files_and_cleanup();
1204 void EMI::setup_stimuli()
1209 stimuli.
resize(param_globals::num_stim);
1210 for (
int i = 0; i < param_globals::num_stim; i++) {
1212 stimulus & s = stimuli[i];
1218 s.associated_intra_mesh =
emi_msh, s.associated_extra_mesh =
emi_msh;
1222 if (s.phys.type ==
Illum) {
1240 }
else if (s.phys.type ==
I_tm) {
1242 SF::restrict_to_membrane(s.electrode.vertices, dof2ptsData, mesh);
1254 if (s.electrode.dump_vtx) {
1259 if(param_globals::stim[i].pulse.dumpTrace &&
get_rank() == 0) {
1261 s.pulse.wave.write_trace(s.name+
".trc");
1267 void EMI::apply_dbc_stimulus()
1269 parabolic_solver_emi& ps = parab_solver;
1272 bool dbcs_have_updated = ps.dbc !=
nullptr && ps.dbc->dbc_update();
1275 if (dbcs_have_updated && time_not_final) {
1276 parab_solver.rebuild_matrices(mtype_vol, *ion.miif, stimuli, logger);
1280 void EMI::apply_current_stimulus()
1282 parabolic_solver_emi& ps = parab_solver;
1283 ps.Iij_stim->set(0.0);
1286 for(stimulus & s : stimuli) {
1288 switch (s.phys.type) {
1291 ps.Bi->mult(*ps.Iij_temp, *ps.Ib_both_face);
1292 ps.operator_both_to_unique_face->mult(*ps.Ib_both_face, *ps.Ib_unique_face);
1293 ps.Ib->add_scaled(*ps.Ib_unique_face, -0.5);
1307 void EMI::balance_electrodes()
1309 for (
int i = 0; i < param_globals::num_stim; i++) {
1310 if (param_globals::stim[i].crct.balance != -1) {
1311 int from = param_globals::stim[i].crct.balance;
1314 log_msg(NULL, 0, 0,
"Balancing stimulus %d with %d %s-wise.", from, to,
1315 is_current(stimuli[from].phys.type) ?
"current" :
"voltage");
1317 stimulus& s_from = stimuli[from];
1318 stimulus& s_to = stimuli[to];
1320 s_to.pulse = s_from.pulse;
1321 s_to.ptcl = s_from.ptcl;
1322 s_to.phys = s_from.phys;
1323 s_to.pulse.strength *= -1.0;
1325 if (s_from.phys.type ==
I_ex || s_from.phys.type ==
I_in) {
1329 if (!s_from.phys.total_current) {
1330 sf_mat& mass = *parab_solver.mass_emi;
1334 s_to.pulse.strength *= fabs(vol0 / vol1);
1346 for (stimulus & s : stimuli){
1347 if(
is_current(s.phys.type) && s.phys.total_current){
1348 switch (s.phys.type) {
1359 float scale = 1.e12 / vol;
1361 s.pulse.strength *= scale;
1364 "%s [Stimulus %d]: current density scaled to %.4g uA/cm^3\n",
1365 s.name.c_str(), s.idx, s.pulse.strength);
1379 s.pulse.strength /= surf;
1381 "%s [Stimulus %d]: current density scaled to %.4g uA/cm^2\n",
1382 s.name.c_str(), s.idx, s.pulse.strength);
1393 static void assign_deterministic_elem_numbering(
sf_mesh & mesh)
1395 const int KEY_SIZE = 6;
1396 int rank = 0, size = 0;
1397 MPI_Comm_rank(mesh.comm, &rank);
1398 MPI_Comm_size(mesh.comm, &size);
1400 auto make_key = [&](
size_t i) {
1401 std::array<long long, KEY_SIZE> k;
1403 k[0] = (
long long)mesh.type[i];
1404 k[1] = (
long long)mesh.tag[i];
1407 if (mesh.type[i] ==
SF::Line) nn = 2;
1408 else if (mesh.type[i] ==
SF::Tri) nn = 3;
1409 else if (mesh.type[i] ==
SF::Quad) nn = 4;
1412 std::vector<long long> nodes;
1414 size_t off = mesh.dsp[i];
1415 for (
int j = 0; j < nn; j++) {
1416 nodes.push_back((
long long)mesh.con[off + j]);
1418 std::sort(nodes.begin(), nodes.end());
1419 for (
int j = 0; j < (int)nodes.size(); j++) {
1420 k[2 + j] = nodes[j];
1426 std::vector<long long> local_keys(mesh.l_numelem * KEY_SIZE, -1);
1427 for (
size_t i = 0; i < mesh.l_numelem; i++) {
1428 auto k = make_key(i);
1429 for (
int j = 0; j < KEY_SIZE; j++) local_keys[i * KEY_SIZE + j] = k[j];
1433 std::vector<int> counts(size, 0), displs(size, 0);
1434 int local_count = (int)local_keys.size();
1435 MPI_Allgather(&local_count, 1, MPI_INT, counts.data(), 1, MPI_INT, mesh.comm);
1437 for (
int r = 0; r < size; r++) {
1442 std::vector<long long> all_keys;
1443 if (rank == 0) all_keys.resize(total, -1);
1444 MPI_Gatherv(local_keys.data(), local_count, MPI_LONG_LONG,
1445 rank == 0 ? all_keys.data() :
nullptr, counts.data(), displs.data(), MPI_LONG_LONG,
1449 std::vector<std::array<long long, KEY_SIZE>> sorted_keys;
1451 const int nkeys = total / KEY_SIZE;
1452 sorted_keys.resize(nkeys);
1453 for (
int i = 0; i < nkeys; i++) {
1454 std::array<long long, KEY_SIZE> k;
1455 for (
int j = 0; j < KEY_SIZE; j++) k[j] = all_keys[i * KEY_SIZE + j];
1458 std::sort(sorted_keys.begin(), sorted_keys.end());
1463 if (rank == 0) nkeys = (int)sorted_keys.size();
1464 MPI_Bcast(&nkeys, 1, MPI_INT, 0, mesh.comm);
1465 std::vector<long long> flat_sorted(nkeys * KEY_SIZE, -1);
1467 for (
int i = 0; i < nkeys; i++) {
1468 for (
int j = 0; j < KEY_SIZE; j++) flat_sorted[i * KEY_SIZE + j] = sorted_keys[i][j];
1471 MPI_Bcast(flat_sorted.data(), (
int)flat_sorted.size(), MPI_LONG_LONG, 0, mesh.comm);
1475 sorted_keys.resize(nkeys);
1476 for (
int i = 0; i < nkeys; i++) {
1477 std::array<long long, KEY_SIZE> k;
1478 for (
int j = 0; j < KEY_SIZE; j++) k[j] = flat_sorted[i * KEY_SIZE + j];
1486 nbr_ref.
resize(mesh.l_numelem);
1487 nbr_sub.
resize(mesh.l_numelem);
1488 for (
size_t i = 0; i < mesh.l_numelem; i++) {
1489 auto k = make_key(i);
1490 auto it = std::lower_bound(sorted_keys.begin(), sorted_keys.end(), k);
1491 if (it == sorted_keys.end() || *it != k) {
1492 log_msg(0, 5, 0,
"deterministic numbering failed to find key (rank %d, elem %zu)", rank, i);
1501 void EMI::setup_output()
1503 bool write_binary =
false;
1504 std::string output_base =
get_basename(param_globals::meshname);
1509 std::string output_file = output_base +
"_e";
1510 log_msg(0, 0, 0,
"Writing \"%s\" mesh: %s", mesh.name.c_str(), output_file.c_str());
1513 output_manager.register_output(parab_solver.ui,
emi_msh, 1, param_globals::phiefile,
"mV", NULL);
1517 mesh_m.name =
"Membrane";
1518 output_file = output_base +
"_m";
1519 log_msg(0, 0, 0,
"Writing \"%s\" mesh: %s", mesh_m.name.c_str(), output_file.c_str());
1526 output_manager.register_output(parab_solver.vb_unique_face,
emi_surface_unique_face_msh, 1, param_globals::vofile,
"mV", NULL,
true);
1528 if(param_globals::num_trace) {
1530 open_trace(ion.miif, param_globals::num_trace, param_globals::trace_node, NULL, &imesh);
1534 IO_stats.init_logger(
"IO_stats.dat");
1537 void EMI::dump_matrices()
1539 std::string bsname = param_globals::dump_basename;
1544 fn = bsname +
"_lhs.bin";
1545 parab_solver.lhs_emi->write(fn.c_str());
1547 fn = bsname +
"_K.bin";
1548 parab_solver.stiffness_emi->write(fn.c_str());
1550 fn = bsname +
"_B.bin";
1551 parab_solver.B->write(fn.c_str());
1553 fn = bsname +
"_Bi.bin";
1554 parab_solver.Bi->write(fn.c_str());
1556 fn = bsname +
"_BsM.bin";
1557 parab_solver.BsM->write(fn.c_str());
1559 fn = bsname +
"_M.bin";
1560 parab_solver.mass_emi->write(fn.c_str());
1562 fn = bsname +
"_Ms.bin";
1563 parab_solver.mass_surf_emi->write(fn.c_str());
1569 double EMI::timer_val(
const int timer_id)
1575 stimuli[sidx].value(val);
1578 val = std::nan(
"NaN");
1585 std::string EMI::timer_unit(
const int timer_id)
1592 s_unit = stimuli[sidx].pulse.wave.f_unit;
1597 void EMI::setup_solvers()
1600 parab_solver.init();
1601 parab_solver.rebuild_matrices(mtype_vol, *ion.miif, stimuli, logger);
1603 if(param_globals::dump2MatLab)
1611 MPI_Comm_size(comm, &size);
1612 MPI_Comm_rank(comm, &rank);
1625 MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
1626 divide(num_tags, size, num_tags_per_rank);
1629 void EMI::setup_EMI_mesh()
1631 log_msg(0,0,0,
"\n *** Processing EMI mesh ***\n");
1633 const std::string basename = param_globals::meshname;
1634 const int verb = param_globals::output_level;
1636 assert(mesh_registry.count(
emi_msh) == 1);
1645 MPI_Comm comm = emi_mesh.
comm;
1648 double t1, t2, s1, s2;
1649 const double total_setup_t0 = MPI_Wtime();
1650 MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
1655 if (emi_mesh.l_numelem > 0) {
1660 const char* type_name = (first_elem ==
SF::Line) ?
"1D (Line)" :
1661 (first_elem ==
SF::Tri) ?
"2D (Tri)" :
1664 log_msg(0, 5, 0,
"\n*** ERROR: EMI model requires a 3D volumetric mesh!");
1665 log_msg(0, 5, 0,
"*** Current mesh element type: %s", type_name);
1666 log_msg(0, 5, 0,
"*** EMI only supports 3D element types: Tetra, Pyramid, Prism, Hexa");
1667 log_msg(0, 5, 0,
"*** Please provide a 3D mesh with volume elements.\n");
1676 int total_num_tags = 0;
1677 if(verb)
log_msg(NULL, 0, 0,
"\nReading tags for extra and intra regions from input files");
1683 if(verb)
log_msg(NULL, 0, 0,
"Read extracellular tags");
1686 parab_solver.extra_tags.insert(tag);
1689 if(verb)
log_msg(NULL, 0, 0,
"Read intracellular tags");
1692 parab_solver.intra_tags.insert(tag);
1695 total_num_tags = parab_solver.extra_tags.size() + parab_solver.intra_tags.size();
1696 if(total_num_tags < size){
1697 log_msg(0,5,0,
"\nThe number of unique tags on EMI mesh is smaller than number of processors!");
1700 if(verb)
log_msg(NULL, 0, 0,
"\nextra_tags=%lu, intra_tags=%lu", parab_solver.extra_tags.size(), parab_solver.intra_tags.size());
1703 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1711 if(verb)
log_msg(NULL, 0, 0,
"\nReading points with data on each vertex");
1715 assert(ptsidx.
size()==ptsData.
size());
1717 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1719 std::list< sf_mesh* > meshlist;
1720 meshlist.push_back(&emi_mesh);
1725 if(verb)
log_msg(NULL, 0, 0,
"\nDistribute mesh based on tags");
1728 distribute_elements_based_tags(emi_mesh, total_num_tags);
1730 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1736 if(verb)
log_msg(NULL, 0, 0,
"\nInserting points");
1740 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1745 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");
1747 compute_ptsdata_from_original_mesh( emi_mesh,
1750 parab_solver.extra_tags,
1751 parab_solver.intra_tags);
1753 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1761 if(verb)
log_msg(NULL, 0, 0,
"\nExtract EMI surface mesh");
1763 extract_face_based_tags(emi_mesh,
SF::NBR_REF, vertex2ptsdata,
1764 parab_solver.line_face,
1765 parab_solver.tri_face,
1766 parab_solver.quad_face,
1767 parab_solver.extra_tags,
1768 parab_solver.intra_tags,
1769 emi_surfmesh_one_side, emi_surfmesh_w_counter_face, emi_surfmesh_unique_face,
1770 parab_solver.map_elem_uniqueFace_to_elem_oneface,
1771 unused_map_elem_oneface_to_elem_uniqueFace);
1772 meshlist.push_back(&emi_surfmesh_one_side);
1774 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1778 compute_surface_mesh_with_counter_face(emi_surfmesh_w_counter_face,
SF::NBR_REF,
1779 parab_solver.line_face,
1780 parab_solver.tri_face,
1781 parab_solver.quad_face);
1783 compute_surface_mesh_with_unique_face(emi_surfmesh_unique_face,
SF::NBR_REF,
1784 parab_solver.line_face,
1785 parab_solver.tri_face,
1786 parab_solver.quad_face,
1787 parab_solver.map_elem_uniqueFace_to_tags);
1792 SF::create_reverse_elem_mapping_between_surface_meshes(parab_solver.line_face,
1793 parab_solver.tri_face,
1794 parab_solver.quad_face,
1795 parab_solver.vec_both_to_one_face,
1798 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1803 if(verb)
log_msg(NULL, 0, 0,
"\ncompute global number of interface");
1804 size_t global_count_surf = 0;
1805 size_t numelem_surface = emi_surfmesh_one_side.l_numelem;
1806 unsigned long long local_count_surf =
static_cast<unsigned long long>(numelem_surface);
1807 unsigned long long global_count_surf_ull = 0;
1808 MPI_Reduce(&local_count_surf, &global_count_surf_ull, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, 0, MPI_COMM_WORLD);
1809 global_count_surf =
static_cast<size_t>(global_count_surf_ull);
1810 if(verb && rank==0) fprintf(stdout,
"global number of interfaces = %zu\n", global_count_surf);
1817 sub_numbering(emi_mesh);
1818 emi_mesh.generate_par_layout();
1830 if(verb)
log_msg(NULL, 0, 0,
"\ndecouple emi interfaces");
1831 if(verb)
log_msg(NULL, 0, 0,
"\tcompute map oldIdx to dof");
1832 compute_map_vertex_to_dof(emi_mesh,
SF::NBR_REF, vertex2ptsdata, parab_solver.extra_tags, parab_solver.map_vertex_tag_to_dof);
1837 if(verb)
log_msg(NULL, 0, 0,
"\tcomplete map oldIdx to dof with counter interface");
1839 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);
1844 if(verb)
log_msg(NULL, 0, 0,
"\tupdate mesh with dof");
1845 update_emi_mesh_with_dofs(emi_mesh,
SF::NBR_REF, parab_solver.map_vertex_tag_to_dof, parab_solver.dof2vertex);
1847 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1853 if(verb)
log_msg(NULL, 0, 0,
"\nInitialize petsc =0 for map<oldIdx,tag> -><dof, petsc>");
1855 for(
const auto & key_value : parab_solver.map_vertex_tag_to_dof)
1857 mesh_int_t gIndex_old = key_value.first.first;
1861 std::pair <mesh_int_t,mesh_int_t> dof_petsc = std::make_pair(dof,-1);
1862 parab_solver.map_vertex_tag_to_dof_petsc.insert({key_value.first,dof_petsc});
1865 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1871 if(verb)
log_msg(NULL, 0, 0,
"Inserting points and ptsData of dofs to emi_mesh");
1872 insert_points_ptsData_to_dof(tmesh_backup_old, emi_mesh,
SF::NBR_REF, parab_solver.dof2vertex, vertex2ptsdata, dof2ptsData);
1874 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1880 if(verb)
log_msg(NULL, 0, 0,
"Generating unique PETSc numberings");
1883 sub_numbering(emi_mesh);
1884 emi_mesh.generate_par_layout();
1887 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1893 if(verb)
log_msg(NULL, 0, 0,
"Generating unique PETSc numberings");
1896 petsc_numbering(emi_mesh);
1899 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1904 if(verb)
log_msg(NULL, 0, 0,
"Updating the map between indices to PETSc numberings");
1906 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);
1908 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1914 if(verb)
log_msg(NULL, 0, 0,
"Updating surface mesh with dof");
1915 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);
1917 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1923 if(verb)
log_msg(NULL, 0, 0,
"Layout for element of EMI surfmesh");
1926 SF::layout_from_count<long int>(emi_surfmesh_one_side.l_numelem, layout, emi_surfmesh_one_side.comm);
1927 size_t count = layout[rank+1] - layout[rank];
1929 for (
int i = 0; i <
count; ++i){
1930 emi_surfmesh_elem[i] = layout[rank]+i;
1936 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1942 if(verb)
log_msg(NULL, 0, 0,
"Layout for element of EMI surfmesh w counter face");
1945 SF::layout_from_count<long int>(emi_surfmesh_w_counter_face.l_numelem, layout_counter, emi_surfmesh_w_counter_face.comm);
1946 size_t count_counter = layout_counter[rank+1] - layout_counter[rank];
1947 emi_surfmesh_counter_elem.
resize(count_counter);
1948 for (
int i = 0; i < count_counter; ++i){
1949 emi_surfmesh_counter_elem[i] = layout_counter[rank]+i;
1951 emi_surfmesh_w_counter_face.localize(
SF::NBR_REF);
1954 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1961 if(verb)
log_msg(NULL, 0, 0,
"Layout for element of EMI surfmesh w counter face");
1964 SF::layout_from_count<long int>(emi_surfmesh_unique_face.l_numelem, layout_unique, emi_surfmesh_unique_face.comm);
1965 size_t count_unique = layout_unique[rank+1] - layout_unique[rank];
1966 emi_surfmesh_unique_elem.
resize(count_unique);
1967 for (
int i = 0; i < count_unique; ++i){
1968 emi_surfmesh_unique_elem[i] = layout_unique[rank]+i;
1973 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1979 if(verb)
log_msg(NULL, 0, 0,
"Inserting points to EMI surfmesh");
1980 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);
1981 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);
1982 insert_points_to_surface_mesh(tmesh_backup_old, emi_surfmesh_unique_face,
SF::NBR_REF, parab_solver.dof2vertex);
1984 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1990 if(verb)
log_msg(NULL, 0, 0,
"Generating submesh_numbering and PETSc numberings for surface mesh");
1993 sub_numbering(emi_surfmesh_one_side);
1994 emi_surfmesh_one_side.generate_par_layout();
1997 petsc_numbering(emi_surfmesh_one_side);
2002 sub_numbering(emi_surfmesh_w_counter_face);
2003 emi_surfmesh_w_counter_face.generate_par_layout();
2006 petsc_numbering(emi_surfmesh_w_counter_face);
2011 sub_numbering(emi_surfmesh_unique_face);
2012 emi_surfmesh_unique_face.generate_par_layout();
2015 petsc_numbering(emi_surfmesh_unique_face);
2017 assign_deterministic_elem_numbering(emi_surfmesh_unique_face);
2020 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2026 if(verb)
log_msg(NULL, 0, 0,
"assign PETSc numbering for new faces");
2027 SF::assign_petsc_on_counter_face(parab_solver.map_vertex_tag_to_dof_petsc,comm);
2030 for (it = parab_solver.map_vertex_tag_to_dof_petsc.begin(); it != parab_solver.map_vertex_tag_to_dof_petsc.end(); it++)
2032 std::pair <mesh_int_t,mesh_int_t> Index_tag_old = it->first;
2033 std::pair <mesh_int_t,mesh_int_t> dof_petsc = it->second;
2034 parab_solver.dof2petsc[dof_petsc.first] = dof_petsc.second;
2035 parab_solver.petsc2dof[dof_petsc.second] = dof_petsc.first;
2040 #ifdef EMI_DEBUG_MESH
2042 int invalid_count = 0;
2043 for (
const auto& [key, val] : parab_solver.map_vertex_tag_to_dof_petsc) {
2044 if (val.second < 0) {
2046 if (invalid_count <= 3) {
2047 fprintf(stderr,
"RANK %d INVALID: vertex=%ld tag=%ld dof=%ld petsc=%ld\n",
2048 rank, (
long)key.first, (
long)key.second, (
long)val.first, (
long)val.second);
2052 fprintf(stderr,
"RANK %d: After Step 26: %d invalid PETSc indices out of %zu total\n",
2053 rank, invalid_count, parab_solver.map_vertex_tag_to_dof_petsc.size());
2059 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2064 added_counter_faces_to_map(parab_solver.line_face, parab_solver.tri_face, parab_solver.quad_face);
2065 const double total_setup = MPI_Wtime() - total_setup_t0;
2066 log_msg(0,0,0,
"Total setup_EMI_mesh processing time: %.5f sec.",
float(total_setup));
2068 log_msg(0,0,0,
"\n *** EMI mesh processing Done ***\n");
2074 MPI_Comm comm = mesh.
comm;
2076 double t1, t2, s1, s2;
2077 MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
2078 const int verb = param_globals::output_level;
2080 if(total_num_tags < size)
2082 PetscPrintf(PETSC_COMM_WORLD,
"\nThe number of processors should be less than the number of tags, size = %d & ntags = %d !!!\n",
2083 size, total_num_tags);
2087 if(verb==10)
log_msg(NULL, 0, 0,
"\ncompute the number of tags which belongs to one rank");
2091 compute_tags_per_rank(total_num_tags, ntags_per_rank);
2093 if(verb==10)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2099 partition_based_tags(total_num_tags, mesh.
tag, ntags_per_rank, part_based_Tags);
2102 permute_mesh_locally_based_on_tag_elemIdx(mesh);
2104 if(verb==10)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2109 void partition_based_tags(
int num_tags,
2114 const int verb = param_globals::output_level;
2117 MPI_Comm_size(comm, &size);
2118 MPI_Comm_rank(comm, &rank);
2124 if (!load_partitions_from_file(tags_to_rank_map, num_tags, comm)) {
2125 if(verb==10)
log_msg(NULL, 0, 0,
"\ncompute the number of unique tags");
2127 double t1 = MPI_Wtime();
2128 extract_unique_tag(unique_tags);
2129 double t2 = MPI_Wtime();
2130 if(verb==10)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2132 if (unique_tags.
size() !=
static_cast<size_t>(num_tags)) {
2133 log_msg(0,5,0,
"\nerror: the number of tags in the EMI mesh does not match the total tags from *.extra and *.intra");
2136 map_tags_to_rank(size, unique_tags, num_tags_per_rank, tags_to_rank_map);
2139 for (
size_t i = 0; i < part.
size(); ++i) {
2140 if(tags_to_rank_map.
count(tag[i]))
2141 part[i] = tags_to_rank_map[tag[i]];
2149 for (
size_t r = 0; r < size; ++r) {
2150 for (
size_t count = 0;
count < num_tags_per_rank[r];) {
2154 tags_to_rank_map.
insert({tag, r});
2163 int expected_num_tags,
2169 const std::string basename = param_globals::meshname;
2170 FILE* fd = fopen((basename +
".part").c_str(),
"r");
2176 if (parts.
size() == 0)
return false;
2178 if (parts.
size() % 2 != 0) {
2179 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.!");
2183 for(
int i = 0; i < parts.
size(); i+=2) {
2184 tags_to_rank_map.
insert({parts[i], parts[i + 1]});
2187 if (tags_to_rank_map.
size() !=
static_cast<size_t>(expected_num_tags)) {
2188 log_msg(0,5,0,
"\nerror: the number of tags in the .part file does not match the total tags from *.extra and *.intra");
#define SF_COMM
the default SlimFem MPI communicator
double SF_real
Use the general double as real 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.
T sum(const vector< T > &vec)
Compute sum of a vector's entries.
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.
constexpr T min(T a, T b)
constexpr T max(T a, T b)
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.