26 #include "petsc_utils.h"
32 #include "runtime.hpp"
40 #include <initializer_list>
42 #include <sys/resource.h>
46 #include "caliper/cali.h"
55 template<
class Assemble>
56 void assemble_with_exact_preallocation(std::initializer_list<sf_mat*> matrices, Assemble assemble)
58 bool any_supported =
false;
59 bool all_supported =
true;
60 bool saw_matrix =
false;
62 for(
sf_mat* mat : matrices) {
63 if(mat ==
nullptr)
continue;
66 const bool supported = mat->begin_exact_preallocation();
67 any_supported = any_supported || supported;
68 all_supported = all_supported && supported;
71 if(!saw_matrix)
return;
76 assert(any_supported == all_supported);
82 for(
sf_mat* mat : matrices) {
83 if(mat !=
nullptr) mat->finalize_exact_preallocation();
92 void log_emi_petsc_matrix_preallocation_report(std::initializer_list<std::pair<const char*, sf_mat*>> matrices)
95 PetscBool enabled = PETSC_FALSE;
96 PetscOptionsHasName(NULL, NULL,
"-mat_view_info", &enabled);
105 MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
107 log_msg(NULL, 0, 0,
"\nEMI PETSc matrix preallocation report");
108 log_msg(NULL, 0, 0,
"matrix alloc/used used_est allocated_est overalloc_est");
111 constexpr
double bytes_per_nz = double(
sizeof(PetscScalar) +
sizeof(PetscInt));
112 constexpr
double gib = 1024.0 * 1024.0 * 1024.0;
113 double total_used_gib = 0.0;
114 double total_allocated_gib = 0.0;
116 for(
const auto& item : matrices) {
117 auto* petsc_mat =
dynamic_cast<SF::petsc_matrix*
>(item.second);
118 if(petsc_mat ==
nullptr || petsc_mat->data ==
nullptr)
continue;
121 MatGetInfo(petsc_mat->data, MAT_GLOBAL_SUM, &info);
123 const double used =
static_cast<double>(info.nz_used);
124 const double allocated =
static_cast<double>(info.nz_allocated);
125 const double ratio = used > 0.0 ? allocated / used : 0.0;
126 const double used_gib = used * bytes_per_nz / gib;
127 const double allocated_gib = allocated * bytes_per_nz / gib;
128 const double overallocated_gib = allocated_gib - used_gib;
129 total_used_gib += used_gib;
130 total_allocated_gib += allocated_gib;
134 "%-18s alloc/used=%5.2f used_est=%6.2fG allocated_est=%6.2fG overalloc_est=%6.2fG",
135 item.first, ratio, used_gib, allocated_gib, overallocated_gib);
140 const double total_ratio = total_used_gib > 0.0 ? total_allocated_gib / total_used_gib : 0.0;
142 "%-18s alloc/used=%5.2f used_est=%6.2fG allocated_est=%6.2fG overalloc_est=%6.2fG",
143 "TOTAL:", total_ratio, total_used_gib, total_allocated_gib,
144 total_allocated_gib - total_used_gib);
148 getrusage(RUSAGE_SELF, &usage);
150 const double local_peak_rss_gib = double(usage.ru_maxrss) / gib;
152 const double local_peak_rss_gib = double(usage.ru_maxrss) * 1024.0 / gib;
154 double summed_peak_rss_gib = 0.0;
155 double max_rank_peak_rss_gib = 0.0;
156 MPI_Reduce(&local_peak_rss_gib, &summed_peak_rss_gib, 1, MPI_DOUBLE, MPI_SUM, 0, PETSC_COMM_WORLD);
157 MPI_Reduce(&local_peak_rss_gib, &max_rank_peak_rss_gib, 1, MPI_DOUBLE, MPI_MAX, 0, PETSC_COMM_WORLD);
161 "process peak RSS estimate: summed ranks=%6.2fG max rank=%6.2fG",
162 summed_peak_rss_gib, max_rank_peak_rss_gib);
164 "external peak memory from mprof --include-children is still the recommended whole-run reference.\n");
171 bool parse_emi_output_tags(
const char* tag_list,
176 static const char* parameter_name =
"gridout_tags";
177 const std::string spec = tag_list ? tag_list :
"";
179 std::vector<int> tags;
181 if(!opencarp::paramschema::parse_idset_spec(spec, &tags, &error)) {
182 log_msg(0, 5,
ECHO,
"Could not parse %s: %s.", parameter_name, error.c_str());
186 if(tags.size() == 0)
return false;
189 output_tags.
insert(tags.begin(), tags.end());
192 for(
int tag_id : output_tags) {
193 if(extra_tags.
count(tag_id) == 0 && intra_tags.
count(tag_id) == 0) {
198 if(missing_tags.
size()) {
201 std::stringstream msg;
202 for(
size_t i = 0; i < missing_tags.
size(); i++) {
204 msg << missing_tags[i];
208 "Warning: ignoring %s tag(s) not listed in the EMI extra/intra tag sets: %s.",
209 parameter_name, msg.str().c_str());
211 for(
int tag_id : missing_tags)
212 output_tags.erase(tag_id);
214 if(output_tags.size() == 0) {
215 log_msg(0, 5,
ECHO,
"%s did not match any EMI extra/intra tag.", parameter_name);
220 log_msg(0, 0, 0,
"Restricting EMI output to %zu tag(s) from %s.",
221 output_tags.size(), parameter_name);
239 struct restricted_point_record {
244 std::string gather_rank_text_root(
const std::string& local_text, MPI_Comm comm)
246 int rank = 0, size = 0;
247 MPI_Comm_rank(comm, &rank);
248 MPI_Comm_size(comm, &size);
250 std::string all_text;
252 all_text = local_text;
254 for(
int pid = 1; pid < size; pid++) {
256 size_t len = local_text.size();
257 MPI_Send(&len,
sizeof(
size_t), MPI_BYTE, 0,
SF_MPITAG, comm);
259 MPI_Send(local_text.data(),
static_cast<int>(len), MPI_CHAR, 0,
SF_MPITAG, comm);
260 }
else if(rank == 0) {
263 MPI_Recv(&len,
sizeof(
size_t), MPI_BYTE, pid,
SF_MPITAG, comm, &stat);
265 size_t offset = all_text.size();
266 all_text.resize(offset + len);
267 MPI_Recv(all_text.data() + offset,
static_cast<int>(len), MPI_CHAR, pid,
SF_MPITAG, comm, &stat);
275 struct direct_element_record {
280 std::string elem_line;
281 std::string fib_line;
284 void write_direct_restricted_mesh_text_root(
const sf_mesh& mesh,
285 const std::string& output_file,
288 MPI_Comm comm = mesh.comm;
289 int rank = 0, size = 0;
290 MPI_Comm_rank(comm, &rank);
291 MPI_Comm_size(comm, &size);
295 const bool write_fibers = mesh.fib.
size() == mesh.l_numelem * 3;
298 std::ostringstream elem_records;
300 for(
size_t eidx = 0; eidx < mesh.l_numelem; eidx++) {
301 if(!keep_elem[eidx])
continue;
303 const char* type_name = elem_type_name(mesh.type[eidx]);
304 if(type_name[0] ==
'\0') {
305 log_msg(0, 5,
ECHO,
"Unsupported element type in restricted EMI output.");
309 elem_records << elem_ref[eidx] <<
'\t' << type_name <<
'\t' << mesh.tag[eidx] <<
'\t';
310 for(
mesh_int_t j = mesh.dsp[eidx]; j < mesh.dsp[eidx + 1]; j++) {
312 elem_records << node_ref[local_node] <<
' ';
314 restricted_point_record point;
315 point.idx = node_ref[local_node];
316 point.xyz[0] = mesh.xyz[local_node * 3 + 0];
317 point.xyz[1] = mesh.xyz[local_node * 3 + 1];
318 point.xyz[2] = mesh.xyz[local_node * 3 + 2];
322 elem_records <<
'\t';
324 elem_records << mesh.fib[eidx * 3 + 0] <<
' '
325 << mesh.fib[eidx * 3 + 1] <<
' '
326 << mesh.fib[eidx * 3 + 2];
327 if(mesh.she.size() == mesh.fib.size()) {
328 elem_records <<
' ' << mesh.she[eidx * 3 + 0] <<
' '
329 << mesh.she[eidx * 3 + 1] <<
' '
330 << mesh.she[eidx * 3 + 2];
333 elem_records <<
'\n';
336 std::sort(local_points.
begin(), local_points.
end(),
337 [](
const restricted_point_record& lhs,
const restricted_point_record& rhs) {
338 return lhs.idx < rhs.idx;
340 auto unique_end = std::unique(local_points.
begin(), local_points.
end(),
341 [](
const restricted_point_record& lhs,
342 const restricted_point_record& rhs) {
343 return lhs.idx == rhs.idx;
345 local_points.
resize(unique_end - local_points.
begin());
349 all_points = local_points;
351 for(
int pid = 1; pid < size; pid++) {
353 size_t len = local_points.
size();
354 MPI_Send(&len,
sizeof(
size_t), MPI_BYTE, 0,
SF_MPITAG, comm);
356 MPI_Send(local_points.
data(),
static_cast<int>(len *
sizeof(restricted_point_record)),
358 }
else if(rank == 0) {
361 MPI_Recv(&len,
sizeof(
size_t), MPI_BYTE, pid,
SF_MPITAG, comm, &stat);
363 size_t offset = all_points.
size();
364 all_points.
resize(offset + len);
365 MPI_Recv(all_points.
data() + offset,
366 static_cast<int>(len *
sizeof(restricted_point_record)),
372 const std::string all_elem_records = gather_rank_text_root(elem_records.str(), comm);
373 if(rank != 0)
return;
375 std::sort(all_points.
begin(), all_points.
end(),
376 [](
const restricted_point_record& lhs,
const restricted_point_record& rhs) {
377 return lhs.idx < rhs.idx;
379 unique_end = std::unique(all_points.
begin(), all_points.
end(),
380 [](
const restricted_point_record& lhs,
381 const restricted_point_record& rhs) {
382 return lhs.idx == rhs.idx;
384 all_points.
resize(unique_end - all_points.
begin());
388 for(
size_t i = 0; i < all_points.
size(); i++)
389 point_map[all_points[i].idx] =
static_cast<mesh_int_t>(i);
392 std::istringstream input(all_elem_records);
394 while(std::getline(input, line)) {
395 if(line.empty())
continue;
397 std::istringstream rec(line);
398 std::string type_name;
399 direct_element_record elem;
400 rec >> elem.idx >> type_name >> elem.tag;
405 std::getline(rec, nodes,
'\t');
406 std::istringstream node_input(nodes);
408 while(node_input >> node)
409 elem.node_ref.push_back(node);
411 std::getline(rec, elem.fib_line);
415 if(elements.
size() == 0) {
416 log_msg(0, 5,
ECHO,
"Restricted EMI output mesh \"%s\" is empty.", mesh.name.c_str());
420 for(direct_element_record& elem : elements) {
421 std::ostringstream elem_line;
422 elem_line << elem_type_name(elem.type);
424 auto it = point_map.
find(ref_node);
425 if(it == point_map.
end()) {
426 log_msg(0, 5,
ECHO,
"Restricted EMI output mesh element references an unknown point.");
429 elem_line <<
' ' << it->second;
431 elem_line <<
' ' << elem.tag;
432 elem.elem_line = elem_line.str();
435 std::sort(elements.begin(), elements.end(),
436 [](
const direct_element_record& lhs,
const direct_element_record& rhs) {
437 if(lhs.idx != rhs.idx) return lhs.idx < rhs.idx;
438 if(lhs.elem_line != rhs.elem_line) return lhs.elem_line < rhs.elem_line;
439 return lhs.fib_line < rhs.fib_line;
442 FILE* pts_fd = fopen((output_file +
".pts").c_str(),
"w");
443 if(pts_fd ==
nullptr) {
444 log_msg(0, 5,
ECHO,
"Could not open restricted EMI output file %s.pts.", output_file.c_str());
447 fprintf(pts_fd,
"%zu\n", all_points.
size());
448 for(
const restricted_point_record& point : all_points)
449 fprintf(pts_fd,
"%.16g %.16g %.16g\n",
450 static_cast<double>(point.xyz[0]),
451 static_cast<double>(point.xyz[1]),
452 static_cast<double>(point.xyz[2]));
455 FILE* elem_fd = fopen((output_file +
".elem").c_str(),
"w");
456 if(elem_fd ==
nullptr) {
457 log_msg(0, 5,
ECHO,
"Could not open restricted EMI output file %s.elem.", output_file.c_str());
460 fprintf(elem_fd,
"%zu\n", elements.size());
461 for(
const direct_element_record& elem : elements) {
462 fputs(elem.elem_line.c_str(), elem_fd);
463 fputc(
'\n', elem_fd);
468 FILE* lon_fd = fopen((output_file +
".lon").c_str(),
"w");
469 if(lon_fd ==
nullptr) {
470 log_msg(0, 5,
ECHO,
"Could not open restricted EMI output file %s.lon.", output_file.c_str());
473 for(
const direct_element_record& elem : elements) {
474 fputs(elem.fib_line.c_str(), lon_fd);
481 void build_emi_volume_output_restriction(
sf_mesh& mesh,
488 for(
size_t eidx = 0; eidx < mesh.l_numelem; eidx++) {
489 if(output_tags.
count(mesh.tag[eidx]) == 0)
continue;
491 for(
mesh_int_t j = mesh.dsp[eidx]; j < mesh.dsp[eidx + 1]; j++)
492 selected_nodes.
push_back(nbr[mesh.con[j]]);
500 void build_emi_surface_output_restriction(
sf_mesh& mesh,
511 for(
size_t eidx = 0; eidx < mesh.l_numelem; eidx++) {
512 bool keep = output_tags.
count(mesh.tag[eidx]) != 0;
513 auto it = face_tags.find(eidx);
514 if(it != face_tags.end()) {
516 output_tags.
count(
static_cast<int>(it->second.first)) != 0 ||
517 output_tags.
count(
static_cast<int>(it->second.second)) != 0;
520 if(keep && nbr[eidx] >= start && nbr[eidx] < stop)
521 vm_output_idx.
push_back(nbr[eidx] - start);
529 void write_emi_output_mesh(
const sf_mesh& mesh,
531 const std::string& output_file,
532 const char* full_mesh_name,
536 for(
size_t eidx = 0; eidx < mesh.l_numelem; eidx++)
537 keep_elem[eidx] = keep(eidx);
540 write_direct_restricted_mesh_text_root(mesh, output_file, keep_elem);
545 out_mesh.
name = mesh.name;
548 if(out_mesh.g_numelem == 0) {
549 log_msg(0, 5,
ECHO,
"Restricted EMI output mesh \"%s\" is empty.", full_mesh_name);
557 std::list<sf_mesh*> meshlist;
558 meshlist.push_back(&out_mesh);
563 out_mesh.generate_par_layout();
572 struct emifld_header {
579 static_assert(
sizeof(emifld_header) == 32,
"emifld_header must be tightly packed");
581 const char EMIFLD_MAGIC[8] = {
'E',
'M',
'I',
'F',
'L',
'D',
'\0',
'\0'};
582 const uint32_t EMIFLD_VERSION = 1;
585 bool fnv1a_file(
const char* path, uint64_t& out)
587 constexpr uint64_t kFNVOffsetBasis = 0xcbf29ce484222325ULL;
588 constexpr uint64_t kFNVPrime = 0x100000001b3ULL;
589 FILE* f = fopen(path,
"rb");
590 if (!f)
return false;
591 uint64_t h = kFNVOffsetBasis;
592 unsigned char buf[1 << 16];
594 while ((n = fread(buf, 1,
sizeof buf, f)) > 0)
595 for (
size_t i = 0; i < n; i++) { h ^= buf[i]; h *= kFNVPrime; }
596 const bool ok = !ferror(f);
604 void log_mesh_local_element_ranges(
const sf_mesh& emi_mesh,
605 const sf_mesh& emi_surfmesh_w_counter_face,
606 const sf_mesh& emi_surfmesh_unique_face)
610 MPI_Comm_rank(emi_surfmesh_w_counter_face.comm, &rank);
611 MPI_Comm_size(emi_surfmesh_w_counter_face.comm, &comm_size);
613 const size_t local_emi_elems = emi_mesh.l_numelem;
614 const size_t local_both_face_elems = emi_surfmesh_w_counter_face.l_numelem;
615 const size_t local_unique_face_elems = emi_surfmesh_unique_face.l_numelem;
617 std::vector<size_t> all_emi_elems;
618 std::vector<size_t> all_both_face_elems;
619 std::vector<size_t> all_unique_face_elems;
621 all_emi_elems.resize(comm_size, 0);
622 all_both_face_elems.resize(comm_size, 0);
623 all_unique_face_elems.resize(comm_size, 0);
626 const MPI_Datatype size_mpi_t = mpi_datatype<size_t>();
627 MPI_Gather(&local_emi_elems, 1, size_mpi_t,
628 rank == 0 ? all_emi_elems.data() :
nullptr, 1, size_mpi_t,
629 0, emi_surfmesh_w_counter_face.comm);
630 MPI_Gather(&local_both_face_elems, 1, size_mpi_t,
631 rank == 0 ? all_both_face_elems.data() :
nullptr, 1, size_mpi_t,
632 0, emi_surfmesh_w_counter_face.comm);
633 MPI_Gather(&local_unique_face_elems, 1, size_mpi_t,
634 rank == 0 ? all_unique_face_elems.data() :
nullptr, 1, size_mpi_t,
635 0, emi_surfmesh_w_counter_face.comm);
637 if (rank != 0)
return;
639 const auto print_min_max = [](
const char* label,
const std::vector<size_t>& counts) {
640 if (counts.empty())
return;
642 size_t min_val = counts[0];
643 size_t max_val = counts[0];
647 for (
int r = 1; r < static_cast<int>(counts.size()); ++r) {
648 if (counts[r] < min_val) {
652 if (counts[r] > max_val) {
658 log_msg(NULL, 0, 0,
" %s: \n\t\t min=%zu on rank=%d, \n\t\t max=%zu on rank=%d\n",
659 label, min_val, min_rank, max_val, max_rank);
661 log_msg(NULL, 0, 0,
"\n**********************************");
662 log_msg(NULL, 0, 0,
"min/max number of local-element ranges:");
663 print_min_max(
"emi_mesh", all_emi_elems);
664 print_min_max(
"emi_surfmesh_w_counter_face", all_both_face_elems);
665 print_min_max(
"emi_surfmesh_unique_face", all_unique_face_elems);
666 log_msg(NULL, 0, 0,
"**********************************");
669 #ifdef EMI_DEBUG_MESH
676 if (param_globals::flavor != std::string(
"petsc"))
return;
678 auto* petsc_mat =
dynamic_cast<SF::petsc_matrix*
>(mat);
679 if (petsc_mat ==
nullptr)
return;
681 Vec x = NULL, y = NULL;
682 MatCreateVecs(petsc_mat->data, &x, &y);
684 PetscRandom rnd = NULL;
685 PetscRandomCreate(PETSC_COMM_WORLD, &rnd);
686 PetscRandomSetFromOptions(rnd);
689 MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
691 PetscReal min_q = PETSC_MAX_REAL;
692 PetscReal max_q = -PETSC_MAX_REAL;
693 PetscInt nonpos_count = 0;
695 for (
int i = 0; i < num_trials; ++i) {
696 VecSetRandom(x, rnd);
697 MatMult(petsc_mat->data, x, y);
699 PetscScalar q_scalar = 0.0;
700 VecDot(x, y, &q_scalar);
702 const PetscReal q = PetscRealPart(q_scalar);
705 if (q <= 0.0) nonpos_count++;
709 PetscPrintf(PETSC_COMM_SELF,
710 "%s: SPD probe with %d random vectors: min(x^T A x)=%g, max(x^T A x)=%g, nonpositive=%d\n",
711 stage, num_trials,
double(min_q),
double(max_q),
int(nonpos_count));
714 PetscRandomDestroy(&rnd);
719 PetscScalar emi_probe_vector_entry(
const PetscInt gid,
const int probe_id)
721 const double x =
static_cast<double>(gid + 1);
725 return std::sin(1.0e-3 * x) + 0.5 * std::cos(3.0e-3 * x);
727 return std::cos(7.0e-4 * x) - 0.35 * std::sin(2.0e-3 * x);
729 return 0.75 * std::sin(1.3e-3 * x) + 0.25 * std::cos(4.0e-3 * x);
739 if (param_globals::flavor != std::string(
"petsc"))
return;
741 auto* petsc_mat =
dynamic_cast<SF::petsc_matrix*
>(mat);
742 if (petsc_mat ==
nullptr)
return;
744 Vec x = NULL, y = NULL;
745 MatCreateVecs(petsc_mat->data, &x, &y);
747 PetscInt i_start = 0, i_end = 0;
748 VecGetOwnershipRange(x, &i_start, &i_end);
750 PetscScalar* x_arr = NULL;
752 MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
754 for (
int probe_id = 0; probe_id < num_probes; ++probe_id) {
755 VecGetArray(x, &x_arr);
756 for (PetscInt i = i_start; i < i_end; ++i) {
757 x_arr[i - i_start] = emi_probe_vector_entry(i, probe_id);
759 VecRestoreArray(x, &x_arr);
761 MatMult(petsc_mat->data, x, y);
763 PetscReal y_norm2 = 0.0;
764 PetscReal y_norminf = 0.0;
765 PetscScalar y_sum = 0.0;
766 PetscScalar xAy = 0.0;
767 VecNorm(y, NORM_2, &y_norm2);
768 VecNorm(y, NORM_INFINITY, &y_norminf);
772 PetscScalar weighted_checksum_local = 0.0;
773 const PetscScalar* y_arr = NULL;
774 VecGetArrayRead(y, &y_arr);
775 for (PetscInt i = i_start; i < i_end; ++i) {
776 const PetscScalar weight =
static_cast<PetscScalar
>(i + 1);
777 weighted_checksum_local += weight * y_arr[i - i_start];
779 VecRestoreArrayRead(y, &y_arr);
781 PetscScalar weighted_checksum = 0.0;
782 MPI_Allreduce(&weighted_checksum_local, &weighted_checksum, 1, MPIU_SCALAR, MPI_SUM, PETSC_COMM_WORLD);
785 PetscPrintf(PETSC_COMM_SELF,
786 "%s: operator probe %d ||Ax||_2=%g, ||Ax||_inf=%g, sum(Ax)=%g, x^T A x=%g, weighted_checksum=%g\n",
787 stage, probe_id + 1,
double(y_norm2),
double(y_norminf),
double(PetscRealPart(y_sum)),
788 double(PetscRealPart(xAy)),
double(PetscRealPart(weighted_checksum)));
799 if (param_globals::flavor != std::string(
"petsc"))
return;
801 auto* petsc_vec =
dynamic_cast<SF::petsc_vector*
>(vec);
802 if (petsc_vec ==
nullptr)
return;
804 PetscReal norm2 = 0.0;
805 PetscReal norminf = 0.0;
806 PetscScalar
sum = 0.0;
807 VecNorm(petsc_vec->data, NORM_2, &norm2);
808 VecNorm(petsc_vec->data, NORM_INFINITY, &norminf);
809 VecSum(petsc_vec->data, &
sum);
811 PetscInt i_start = 0, i_end = 0;
812 VecGetOwnershipRange(petsc_vec->data, &i_start, &i_end);
814 PetscScalar weighted_checksum_local = 0.0;
815 const PetscScalar* arr = NULL;
816 VecGetArrayRead(petsc_vec->data, &arr);
817 for (PetscInt i = i_start; i < i_end; ++i) {
818 const PetscScalar weight =
static_cast<PetscScalar
>(i + 1);
819 weighted_checksum_local += weight * arr[i - i_start];
821 VecRestoreArrayRead(petsc_vec->data, &arr);
823 PetscScalar weighted_checksum = 0.0;
824 MPI_Allreduce(&weighted_checksum_local, &weighted_checksum, 1, MPIU_SCALAR, MPI_SUM, PETSC_COMM_WORLD);
827 MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
829 PetscPrintf(PETSC_COMM_SELF,
830 "%s: rhs probe ||b||_2=%g, ||b||_inf=%g, sum(b)=%g, weighted_checksum=%g\n",
831 stage,
double(norm2),
double(norminf),
double(PetscRealPart(
sum)),
832 double(PetscRealPart(weighted_checksum)));
841 if (param_globals::flavor != std::string(
"petsc"))
return;
843 auto* petsc_mat =
dynamic_cast<SF::petsc_matrix*
>(mat);
844 auto* petsc_vec =
dynamic_cast<SF::petsc_vector*
>(vec);
845 if (petsc_mat ==
nullptr || petsc_vec ==
nullptr)
return;
847 Vec x = NULL, ax = NULL, residual = NULL;
848 MatCreateVecs(petsc_mat->data, &x, &ax);
849 VecDuplicate(ax, &residual);
851 PetscInt i_start = 0, i_end = 0;
852 VecGetOwnershipRange(x, &i_start, &i_end);
855 MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
857 for (
int probe_id = 0; probe_id < num_probes; ++probe_id) {
858 PetscScalar* x_arr = NULL;
859 VecGetArray(x, &x_arr);
860 for (PetscInt i = i_start; i < i_end; ++i) {
861 x_arr[i - i_start] = emi_probe_vector_entry(i, probe_id);
863 VecRestoreArray(x, &x_arr);
865 MatMult(petsc_mat->data, x, ax);
866 VecWAXPY(residual, -1.0, petsc_vec->data, ax);
868 PetscReal residual_norm2 = 0.0;
869 PetscReal residual_norminf = 0.0;
870 PetscScalar residual_sum = 0.0;
871 PetscScalar xTResidual = 0.0;
872 VecNorm(residual, NORM_2, &residual_norm2);
873 VecNorm(residual, NORM_INFINITY, &residual_norminf);
874 VecSum(residual, &residual_sum);
875 VecDot(x, residual, &xTResidual);
877 PetscScalar weighted_checksum_local = 0.0;
878 const PetscScalar* residual_arr = NULL;
879 VecGetArrayRead(residual, &residual_arr);
880 for (PetscInt i = i_start; i < i_end; ++i) {
881 const PetscScalar weight =
static_cast<PetscScalar
>(i + 1);
882 weighted_checksum_local += weight * residual_arr[i - i_start];
884 VecRestoreArrayRead(residual, &residual_arr);
886 PetscScalar weighted_checksum = 0.0;
887 MPI_Allreduce(&weighted_checksum_local, &weighted_checksum, 1, MPIU_SCALAR, MPI_SUM, PETSC_COMM_WORLD);
890 PetscPrintf(PETSC_COMM_SELF,
891 "%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",
892 stage, probe_id + 1,
double(residual_norm2),
double(residual_norminf),
893 double(PetscRealPart(residual_sum)),
double(PetscRealPart(xTResidual)),
894 double(PetscRealPart(weighted_checksum)));
900 VecDestroy(&residual);
915 MaterialType *m = mtype;
918 m->regions.resize(param_globals::num_gregions);
920 const char* grid_name =
"emi_grid_domain";
921 log_msg(logger, 0, 0,
"Setting up %s tissue poperties for %d regions ..", grid_name,
922 param_globals::num_gregions);
925 RegionSpecs* reg = m->regions.data();
931 for (
size_t i=0; i<m->regions.size(); i++)
933 for (
int j=0;j<param_globals::gregion[i].num_IDs;j++)
935 int tag = param_globals::gregion[i].ID[j];
938 if(extra_tags_default.
find(tag) != extra_tags_default.
end())
939 extra_tags_default.
erase(tag);
941 if(intra_tags_default.
find(tag) != intra_tags_default.
end())
942 intra_tags_default.
erase(tag);
946 for (
size_t i=0; i<m->regions.size(); i++, reg++)
948 if(!strcmp(param_globals::gregion[i].name,
"")) {
949 snprintf(buf,
sizeof buf,
", gregion_%d",
int(i));
950 param_globals::gregion[i].name =
dupstr(buf);
954 reg->regname = strdup(param_globals::gregion[i].name);
958 reg->nsubregs = extra_tags_default.
size();
960 reg->nsubregs = intra_tags_default.
size();
962 reg->nsubregs = param_globals::gregion[i].num_IDs;
965 reg->subregtags = NULL;
968 reg->subregtags =
new int[reg->nsubregs];
972 for (
int tag : extra_tags_default) {
973 reg->subregtags[j] = tag;
979 for (
int tag : intra_tags_default) {
980 reg->subregtags[j] = tag;
985 for (
int j=0;j<reg->nsubregs;j++)
986 reg->subregtags[j] = param_globals::gregion[i].ID[j];
991 elecMaterial *emat =
new elecMaterial();
995 emat->InVal[0] = param_globals::gregion[i].g_bath;
996 emat->InVal[1] = param_globals::gregion[i].g_bath;
997 emat->InVal[2] = param_globals::gregion[i].g_bath;
999 emat->ExVal[0] = param_globals::gregion[i].g_bath;
1000 emat->ExVal[1] = param_globals::gregion[i].g_bath;
1001 emat->ExVal[2] = param_globals::gregion[i].g_bath;
1003 emat->BathVal[0] = param_globals::gregion[i].g_bath;
1004 emat->BathVal[1] = param_globals::gregion[i].g_bath;
1005 emat->BathVal[2] = param_globals::gregion[i].g_bath;
1008 for (
int j=0; j<3; j++) {
1009 emat->InVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
1010 emat->ExVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
1011 emat->BathVal[j] *= 1e-3 * param_globals::gregion[i].g_mult;
1013 reg->material = emat;
1016 if (strlen(param_globals::gi_scale_vec))
1020 void parabolic_solver_emi::init()
1025 const int log_flag = param_globals::output_level > 1 ?
ECHO : 0;
1026 const auto log_init_timing = [&](
const char* label,
double start) {
1027 log_msg(NULL, 0, log_flag,
"EMI solver init: %s in %.5f seconds.", label,
float(MPI_Wtime() - start));
1030 double phase_t = MPI_Wtime();
1031 stats.init_logger(
"par_stats.dat");
1035 log_init_timing(
"linear solver object", phase_t);
1046 phase_t = MPI_Wtime();
1048 log_init_timing(
"maximum nodal edge counts", phase_t);
1051 MPI_Comm_rank(emi_surfmesh_w_counter_face.comm, &rank);
1061 phase_t = MPI_Wtime();
1068 SF::init_vector(&vb_both_face, emi_surfmesh_w_counter_face, dpn, alg_surface_type);
1069 SF::init_vector(&vb_unique_face, emi_surfmesh_unique_face, dpn, alg_surface_type);
1070 SF::init_vector(&Ib_both_face, emi_surfmesh_w_counter_face, dpn, alg_surface_type);
1071 SF::init_vector(&Ib_unique_face, emi_surfmesh_unique_face, dpn, alg_surface_type);
1072 log_init_timing(
"vectors", phase_t);
1078 const bool use_petsc_exact_preallocation = param_globals::flavor == std::string(
"petsc");
1079 const int petsc_initial_prealloc = 1;
1083 mesh_int_t M = emi_surfmesh_w_counter_face.g_numelem;
1085 mesh_int_t m = emi_surfmesh_w_counter_face.l_numelem;
1086 mesh_int_t m_one_side = emi_surfmesh_one_side.l_numelem;
1087 mesh_int_t M_one_side = emi_surfmesh_one_side.g_numelem;
1088 mesh_int_t m_unique_face = emi_surfmesh_unique_face.l_numelem;
1089 mesh_int_t M_unique_face = emi_surfmesh_unique_face.g_numelem;
1092 if (param_globals::output_level > 1) {
1093 log_msg(NULL, 0, 0,
"\n**********************************");
1094 log_msg(NULL, 0, 0,
"#elements of emi surfmesh unique face: %zu", emi_surfmesh_unique_face.g_numelem);
1095 log_msg(NULL, 0, 0,
"#elements of emi surfmesh one side: %zu", emi_surfmesh_one_side.g_numelem);
1096 log_msg(NULL, 0, 0,
"#elements of emi surfmesh: %zu", emi_surfmesh_w_counter_face.g_numelem);
1097 log_msg(NULL, 0, 0,
"#elements of emi mesh: %zu", emi_mesh.g_numelem);
1098 log_msg(NULL, 0, 0,
"#dofs for emi_mesh: %zu", emi_mesh.g_numpts);
1099 log_msg(NULL, 0, 0,
"#max_row_entries_emi: %zu", max_row_entries_emi);
1100 log_msg(NULL, 0, 0,
"**********************************\n");
1101 log_mesh_local_element_ranges(emi_mesh, emi_surfmesh_w_counter_face, emi_surfmesh_unique_face);
1105 SF::layout_from_count<long int>(emi_surfmesh_w_counter_face.l_numelem, layout, emi_surfmesh_w_counter_face.comm);
1107 mesh_int_t n_l = emi_mesh.pl.algebraic_layout()[rank];
1111 SF::layout_from_count<long int>(emi_surfmesh_one_side.l_numelem, layout_one_side, emi_surfmesh_one_side.comm);
1112 mesh_int_t m_one_side_l = layout_one_side[rank];
1115 SF::layout_from_count<long int>(emi_surfmesh_unique_face.l_numelem, layout_unique_face, emi_surfmesh_unique_face.comm);
1116 mesh_int_t m_unique_face_l = layout_unique_face[rank];
1124 phase_t = MPI_Wtime();
1126 B->init(M, N, m, n, m_l, use_petsc_exact_preallocation ? petsc_initial_prealloc : max_row_entries_emi*2);
1129 Bi->init(M, N, m, n, m_l, use_petsc_exact_preallocation ? petsc_initial_prealloc : max_row_entries_emi*2);
1132 BsM->init(N, M, n, m, n_l, use_petsc_exact_preallocation ? petsc_initial_prealloc : max_row_entries_emi*2);
1134 log_init_timing(
"EMI coupling matrices", phase_t);
1137 phase_t = MPI_Wtime();
1138 SF::construct_direct_unique_both_operators(operator_unique_to_both_faces,
1139 operator_both_to_unique_face,
1140 map_elem_uniqueFace_to_elem_bothface,
1141 map_elem_uniqueFace_to_elem_oneface,
1142 vec_both_to_one_face,
1143 emi_surfmesh_w_counter_face,
1144 emi_surfmesh_unique_face,
1145 max_row_entries_emi,
1148 log_init_timing(
"unique/both face transfer operators", phase_t);
1151 phase_t = MPI_Wtime();
1152 assemble_with_exact_preallocation({B, Bi, BsM}, [&]() {
1153 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);
1155 log_init_timing(
"restriction operators", phase_t);
1160 phase_t = MPI_Wtime();
1168 lhs_emi->init(emi_mesh, dpn, dpn, use_petsc_exact_preallocation ? petsc_initial_prealloc : max_row_entries_emi*3);
1169 stiffness_emi->init(emi_mesh, dpn, dpn, use_petsc_exact_preallocation ? petsc_initial_prealloc : max_row_entries_emi*2);
1170 mass_emi->init(emi_mesh, dpn, dpn, use_petsc_exact_preallocation ? petsc_initial_prealloc : max_row_entries_emi*3);
1171 mass_surf_emi->init(emi_mesh, dpn, dpn, use_petsc_exact_preallocation ? petsc_initial_prealloc : max_row_entries_emi*3);
1172 log_init_timing(
"system matrices", phase_t);
1175 #ifdef EMI_DEBUG_MESH
1178 MPI_Comm_rank(emi_mesh.comm, &local_rank);
1180 fprintf(stderr,
"RANK %d MESH SIZES: one_side=%zu, counter=%zu, unique=%zu\n",
1181 local_rank, emi_surfmesh_one_side.l_numelem,
1182 emi_surfmesh_w_counter_face.l_numelem, emi_surfmesh_unique_face.l_numelem);
1184 fprintf(stderr,
"RANK %d MAP SIZES: uniqueFace_to_oneface=%zu\n",
1185 local_rank, map_elem_uniqueFace_to_elem_oneface.size());
1190 decltype(map_elem_uniqueFace_to_elem_oneface)().swap(map_elem_uniqueFace_to_elem_oneface);
1191 decltype(map_elem_uniqueFace_to_elem_bothface)().swap(map_elem_uniqueFace_to_elem_bothface);
1197 phase_t = MPI_Wtime();
1202 if(!(vb_ptr != NULL && Ib_ptr != NULL)) {
1203 log_msg(0,5,0,
"%s error: global Vb and Ib vectors not properly set up! Ionics seem invalid! Aborting!",
1209 vb->shallow_copy(*vb_ptr);
1211 Ib->shallow_copy(*Ib_ptr);
1213 parab_tech =
static_cast<parabolic_solver_emi::parabolic_t
>(param_globals::parab_solve_emi);
1214 log_init_timing(
"ionic face vectors", phase_t);
1217 log_msg(NULL, 0, log_flag,
"EMI solver init total in %.5f seconds.",
float(dur));
1223 double start, end, period;
1226 mass_integrator mass_integ;
1227 mass_integrator mass_integ_emi;
1230 int log_flag = param_globals::output_level > 1 ?
ECHO : 0;
1231 MaterialType & mt = mtype[0];
1233 const bool use_petsc_exact_preallocation = param_globals::flavor == std::string(
"petsc");
1234 const bool reuse_petsc_fem_preallocation = use_petsc_exact_preallocation && fem_matrices_exact_preallocated;
1249 log_msg(NULL, 0, 0,
"assemble stiffness matrix");
1251 elec_stiffness_integrator stfn_integ_emi(mt);
1252 auto assemble_stiffness_emi = [&]() {
1253 stiffness_emi->zero();
1256 if(reuse_petsc_fem_preallocation) {
1257 assemble_stiffness_emi();
1259 assemble_with_exact_preallocation({stiffness_emi}, assemble_stiffness_emi);
1262 log_msg(logger,0,log_flag,
"Computed parabolic stiffness matrix in %.5f seconds.",
float(dur));
1264 log_msg(NULL, 0, 0,
"assemble mass matrix on the volumetric mesh");
1266 mass_integrator mass_integ;
1267 auto assemble_mass_emi = [&]() {
1271 auto* petsc_mass_emi = use_petsc_exact_preallocation ?
dynamic_cast<SF::petsc_matrix*
>(mass_emi) :
nullptr;
1272 auto* petsc_stiffness_emi = use_petsc_exact_preallocation ?
dynamic_cast<SF::petsc_matrix*
>(stiffness_emi) :
nullptr;
1273 if(reuse_petsc_fem_preallocation) {
1274 assemble_mass_emi();
1275 }
else if(petsc_mass_emi !=
nullptr && petsc_stiffness_emi !=
nullptr) {
1277 petsc_mass_emi->duplicate_pattern(*petsc_stiffness_emi);
1278 assemble_mass_emi();
1280 assemble_with_exact_preallocation({mass_emi}, assemble_mass_emi);
1283 log_msg(logger,0,log_flag,
"Computed volumetric mass matrix in %.5f seconds.",
float(dur));
1285 log_msg(NULL, 0, 0,
"assemble LHS matrix and mass matrix on the surface mesh");
1287 auto assemble_lhs_and_surface_mass = [&]() {
1289 mass_surf_emi->zero();
1291 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);
1293 if(reuse_petsc_fem_preallocation) {
1294 assemble_lhs_and_surface_mass();
1296 assemble_with_exact_preallocation({lhs_emi, mass_surf_emi}, assemble_lhs_and_surface_mass);
1299 log_msg(logger,0,log_flag,
"Computed parabolic mass matrix in %.5f seconds.",
float(dur));
1303 bool same_nonzero =
false;
1307 log_msg(logger,0,log_flag,
"lhs matrix enforcing Dirichlet boundaries.");
1311 dbc =
new dbc_manager(*lhs_emi, stimuli);
1313 dbc->recompute_dbcs();
1315 dbc->enforce_dbc_lhs();
1317 log_msg(logger,0,log_flag,
"lhs matrix Dirichlet enforcing done in %.5f seconds.",
float(dur));
1320 log_msg(logger,0,
ECHO,
"without enforcing Dirichlet boundaries on the lhs matrix!");
1322 phie_mat_has_nullspace =
true;
1326 log_emi_petsc_matrix_preallocation_report({
1330 {
"unique_to_both:", operator_unique_to_both_faces},
1331 {
"both_to_unique:", operator_both_to_unique_face},
1332 {
"stiffness_emi:", stiffness_emi},
1333 {
"mass_emi:", mass_emi},
1334 {
"mass_surf_emi:", mass_surf_emi},
1335 {
"lhs_emi:", lhs_emi},
1337 if(use_petsc_exact_preallocation) fem_matrices_exact_preallocated =
true;
1339 setup_linear_solver(logger);
1341 log_msg(logger,0,log_flag,
"Initializing parabolic solver in %.5f seconds.",
float(dur));
1344 period =
timing(end, start);
1347 void parabolic_solver_emi::setup_linear_solver(
FILE_SPEC logger)
1349 tol = param_globals::cg_tol_parab;
1350 max_it = param_globals::cg_maxit_parab;
1352 std::string default_opts;
1353 std::string solver_file;
1354 solver_file = param_globals::parab_options_file;
1355 if (param_globals::flavor == std::string(
"ginkgo")) {
1356 default_opts = std::string(
1359 "type": "solver::Cg",
1361 "type": "solver::Multigrid",
1362 "min_coarse_rows": 8,
1364 "default_initial_guess": "zero",
1367 "type": "multigrid::Pgm",
1368 "deterministic": false
1371 "coarsest_solver": {
1372 "type": "preconditioner::Schwarz",
1374 "type": "preconditioner::Jacobi"
1379 "type": "Iteration",
1386 "type": "Iteration",
1390 "type": "ResidualNorm",
1391 "reduction_factor": 1e-4
1396 } else if (param_globals::flavor == std::string(
"petsc")) {
1397 default_opts = std::string(
"-ksp_type cg -pc_type gamg -options_left");
1399 lin_solver->setup_solver(*lhs_emi, tol, max_it * 100, param_globals::cg_norm_parab,
1400 "parabolic PDE", phie_mat_has_nullspace, logger, solver_file.c_str(),
1401 default_opts.c_str());
1404 void parabolic_solver_emi::solve()
1406 switch (parab_tech) {
1407 case SEMI_IMPLICIT: solve_semiImplicit();
break;
1411 void parabolic_solver_emi::solve_semiImplicit()
1418 dbc->enforce_dbc_rhs(*ui);
1426 stiffness_emi->mult(*ui, *Iij_temp);
1433 if (Iij_stim->mag() > 0.0) {
1435 mass_emi->mult(*Iij_stim, *Iij_temp);
1447 (*lin_solver)(*dui, *Irhs);
1451 if(lin_solver->reason < 0) {
1452 log_msg(0, 5, 0,
"%s solver diverged. Reason: %s.", lin_solver->name.c_str(),
1453 petsc_get_converged_reason_str(lin_solver->reason));
1459 ui_pre->add_scaled(*dui, 1.0);
1461 ui->add_scaled(*ui_pre, 1.0);
1468 dbc->enforce_dbc_rhs(*ui);
1473 stats.slvtime +=
timing(t1, t0);
1474 stats.update_iter(lin_solver->niter);
1487 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,
1488 std::vector<std::string> & tags_data)
1497 for(
size_t i=0; i<rnod.
size(); i++){
1501 for(
size_t eidx=0; eidx<emi_surfmesh_w_counter_face.l_numelem; eidx++)
1503 std::vector<mesh_int_t> elem_nodes;
1504 mesh_int_t tag = emi_surfmesh_w_counter_face.tag[eidx];
1505 for (
int n = emi_surfmesh_w_counter_face.dsp[eidx]; n < emi_surfmesh_w_counter_face.dsp[eidx+1];n++)
1507 mesh_int_t l_idx = emi_surfmesh_w_counter_face.con[n];
1509 std::pair <mesh_int_t,mesh_int_t> Index_tag_old;
1510 Index_tag_old = std::make_pair(l2g[l_idx],tags[eidx]);
1511 mesh_int_t dof = map_vertex_tag_to_dof[Index_tag_old];
1512 elem_nodes.push_back(dof);
1517 std::string result_first;
1518 std::string result_second;
1519 std::sort(elem_nodes.begin(),elem_nodes.end());
1522 if(elem_nodes.size()==2){
1525 key.
v1 = elem_nodes[0];
1526 key.
v2 = elem_nodes[1];
1527 std::pair<SF::emi_face<mesh_int_t,SF::tuple<mesh_int_t>>,
1528 SF::emi_face<mesh_int_t,SF::tuple<mesh_int_t>>> value = line_face[key];
1530 tag_first = value.first.tag;
1531 tag_second = value.second.tag;
1532 result_first = std::to_string(tag_first) +
":" + std::to_string(tag_second);
1533 result_second = std::to_string(tag_second) +
":" + std::to_string(tag_first);
1535 else if(elem_nodes.size()==3){
1537 key.
v1 = elem_nodes[0];
1538 key.
v2 = elem_nodes[1];
1539 key.
v3 = elem_nodes[2];
1540 std::pair<SF::emi_face<mesh_int_t,SF::triple<mesh_int_t>>,
1541 SF::emi_face<mesh_int_t,SF::triple<mesh_int_t>>> value = tri_face[key];
1543 tag_first = value.first.tag;
1544 tag_second = value.second.tag;
1545 result_first = std::to_string(tag_first) +
":" + std::to_string(tag_second);
1546 result_second = std::to_string(tag_second) +
":" + std::to_string(tag_first);
1548 else if(elem_nodes.size()==4){
1550 key.
v1 = elem_nodes[0];
1551 key.
v2 = elem_nodes[1];
1552 key.
v3 = elem_nodes[2];
1553 key.
v4 = elem_nodes[3];
1554 std::pair<SF::emi_face<mesh_int_t,SF::quadruple<mesh_int_t>>,
1555 SF::emi_face<mesh_int_t,SF::quadruple<mesh_int_t>>> value = quad_face[key];
1557 tag_first = value.first.tag;
1558 tag_second = value.second.tag;
1559 result_first = std::to_string(tag_first) +
":" + std::to_string(tag_second);
1560 result_second = std::to_string(tag_second) +
":" + std::to_string(tag_first);
1563 tags_data.push_back(result_first);
1564 tags_data.push_back(result_second);
1568 void EMI::initialize()
1576 logger =
f_open(
"emi.log", param_globals::experiment != 4 ?
"w" :
"r");
1577 const int verb = param_globals::output_level;
1578 const auto log_init_timing = [&](
const char* label,
double start) {
1580 log_msg(logger, 0,
ECHO,
"EMI init: %s in %.5f seconds.", label,
float(MPI_Wtime() - start));
1583 double phase_t = MPI_Wtime();
1589 log_init_timing(
"mesh setup", phase_t);
1593 phase_t = MPI_Wtime();
1595 log_init_timing(
"mesh mappings", phase_t);
1599 phase_t = MPI_Wtime();
1600 ion.logger = logger;
1602 ion.set_surface_mesh_data(parab_solver.line_face,
1603 parab_solver.tri_face,
1604 parab_solver.quad_face,
1605 parab_solver.map_vertex_tag_to_dof);
1608 std::vector<std::string> tags_data;
1609 tags_onFace(parab_solver.line_face,
1610 parab_solver.tri_face,
1611 parab_solver.quad_face,
1612 parab_solver.map_vertex_tag_to_dof,
1613 parab_solver.map_vertex_tag_to_dof_petsc,
1616 log_init_timing(
"ionic face metadata", phase_t);
1618 ion.set_tags_onFace(tags_data);
1620 ion.set_face_region_data(parab_solver.intra_tags, parab_solver.map_elem_uniqueFace_to_tags);
1621 phase_t = MPI_Wtime();
1623 log_init_timing(
"ionic model initialization", phase_t);
1625 phase_t = MPI_Wtime();
1627 set_elec_tissue_properties_emi_volume(mtype_vol, parab_solver.extra_tags, parab_solver.intra_tags, logger);
1631 region_mask(
emi_msh, mtype_vol[0].regions, mtype_vol[0].regionIDs,
true,
"gregion_vol",
false);
1636 param_globals::dt, 0,
"elec::ref_dt",
"TS");
1637 log_init_timing(
"tissue properties and timers", phase_t);
1641 phase_t = MPI_Wtime();
1642 param_globals::operator_splitting = 0;
1644 log_init_timing(
"stimuli", phase_t);
1650 phase_t = MPI_Wtime();
1652 log_init_timing(
"solver setup", phase_t);
1655 phase_t = MPI_Wtime();
1657 balance_electrodes();
1659 scale_total_stimulus_current(stimuli, *parab_solver.mass_emi, *parab_solver.mass_surf_emi, logger);
1660 log_init_timing(
"stimulus current scaling", phase_t);
1665 phase_t = MPI_Wtime();
1671 parab_solver.operator_unique_to_both_faces->mult(*parab_solver.vb, *parab_solver.vb_both_face);
1672 SF::assign_resting_potential_from_ionic_models_on_myocyte(*parab_solver.ui,
1673 parab_solver.vb_both_face,
1674 parab_solver.elemTag_emi_mesh,
1675 parab_solver.map_vertex_tag_to_dof_petsc,
1676 parab_solver.line_face, parab_solver.tri_face, parab_solver.quad_face,
1677 emi_surfmesh_w_counter_face, emi_mesh);
1682 if (strlen(param_globals::start_statef) > 0)
1683 restore_field_state(param_globals::start_statef);
1685 *parab_solver.vb_unique_face = *parab_solver.vb;
1686 log_init_timing(
"initial membrane state projection", phase_t);
1689 phase_t = MPI_Wtime();
1693 log_init_timing(
"output setup", phase_t);
1696 const double init_dur =
timing(t2, t1);
1697 this->initialize_time += init_dur;
1699 log_msg(logger, 0,
ECHO,
"EMI init total in %.5f seconds.",
float(init_dur));
1702 void EMI::setup_mappings()
1710 log_msg(logger, 0, 0,
"%s: Setting up intracellular algebraic-to-nodal scattering.", __func__);
1715 log_msg(logger, 0, 0,
"%s: Setting up intracellular PETSc to canonical permutation.", __func__);
1720 void EMI::checkpointing()
1726 char save_fnm[1024];
1729 snprintf(save_fnm,
sizeof save_fnm,
"%s.%s.roe", param_globals::write_statef, tsav_ext);
1731 ion.miif->dump_state(save_fnm, tm.time, ion.ion_domain,
false, GIT_COMMIT_COUNT);
1732 dump_field_state(save_fnm);
1737 char save_fnm[1024];
1738 snprintf(save_fnm,
sizeof save_fnm,
"checkpoint.%.1f.roe", tm.time);
1739 ion.miif->dump_state(save_fnm, tm.time, ion.ion_domain,
false, GIT_COMMIT_COUNT);
1740 dump_field_state(save_fnm);
1744 void EMI::dump_field_state(
const char* roe_fnm)
1747 std::string fnm = std::string(roe_fnm) +
".emifld";
1749 const uint64_t gsize = parab_solver.ui->gsize();
1759 memcpy(hdr.magic, EMIFLD_MAGIC,
sizeof hdr.magic);
1760 hdr.version = EMIFLD_VERSION;
1761 hdr.real_bytes =
sizeof(
SF_real);
1764 if (!fnv1a_file(roe_fnm, hdr.roe_hash)) {
1765 log_msg(logger, 5, 0,
"Cannot fingerprint ionic checkpoint %s for the EMI field file.", roe_fnm);
1767 }
else if (!(fd = fopen(fnm.c_str(),
"wb"))) {
1768 log_msg(logger, 5, 0,
"Cannot open EMI field checkpoint %s for writing.", fnm.c_str());
1772 if (
get_global(error, MPI_SUM)) EXIT(EXIT_FAILURE);
1774 log_msg(logger, 0, 0,
"Saving EMI bulk potential field in file: %s", fnm.c_str());
1780 if (rank == 0) fwrite(&hdr,
sizeof hdr, 1, fd);
1789 canon->write_binary<
SF_real>(fd);
1795 void EMI::restore_field_state(
const char* roe_fnm)
1798 std::string fnm = std::string(roe_fnm) +
".emifld";
1806 const uint64_t gsize = parab_solver.ui->gsize();
1810 fd = fopen(fnm.c_str(),
"rb");
1812 log_msg(logger, 5, 0,
"Cannot open EMI field checkpoint %s.", fnm.c_str());
1816 const long expected =
static_cast<long>(
sizeof hdr + gsize *
sizeof(
SF_real));
1817 fseek(fd, 0, SEEK_END);
1818 const long actual = ftell(fd);
1820 uint64_t roe_hash = 0;
1821 if (fread(&hdr,
sizeof hdr, 1, fd) != 1) {
1822 log_msg(logger, 5, 0,
"EMI field checkpoint %s is truncated.", fnm.c_str());
1824 }
else if (memcmp(hdr.magic, EMIFLD_MAGIC,
sizeof hdr.magic) != 0 || hdr.version != EMIFLD_VERSION) {
1825 log_msg(logger, 5, 0,
"%s is not a version-%u EMI field checkpoint.", fnm.c_str(), EMIFLD_VERSION);
1827 }
else if (hdr.real_bytes !=
sizeof(
SF_real) || hdr.gsize != gsize || actual != expected) {
1828 log_msg(logger, 5, 0,
"EMI field checkpoint %s does not match this run (wrong precision, mesh, or size).", fnm.c_str());
1830 }
else if (!fnv1a_file(roe_fnm, roe_hash) || hdr.roe_hash != roe_hash) {
1831 log_msg(logger, 5, 0,
"EMI field checkpoint %s does not belong to ionic checkpoint %s.", fnm.c_str(), roe_fnm);
1834 if (err) { fclose(fd); fd =
nullptr; }
1837 if (
get_global(err, MPI_SUM)) EXIT(EXIT_FAILURE);
1840 size_t nrd = parab_solver.ui->read_binary<
SF_real>(fd);
1842 if (nrd !=
static_cast<size_t>(gsize)) {
1843 log_msg(logger, 5, 0,
"Short read of EMI field checkpoint %s (%zu of %lu values).",
1844 fnm.c_str(), nrd,
static_cast<unsigned long>(gsize));
1850 log_msg(logger, 0, 0,
"Restored EMI bulk potential field from %s.", fnm.c_str());
1855 void EMI::compute_step()
1867 const int verb = param_globals::output_level;
1874 apply_dbc_stimulus();
1883 apply_current_stimulus();
1887 parab_solver.operator_unique_to_both_faces->mult(*parab_solver.Ib, *parab_solver.Ib_both_face);
1888 parab_solver.BsM->mult(*parab_solver.Ib_both_face, *parab_solver.Irhs);
1892 parab_solver.solve();
1895 parab_solver.B->mult(*parab_solver.ui, *parab_solver.vb_both_face);
1897 parab_solver.operator_both_to_unique_face->mult(*parab_solver.vb_both_face, *parab_solver.vb_unique_face);
1898 *parab_solver.vb = *parab_solver.vb_unique_face;
1906 this->compute_time +=
timing(t2, t1);
1913 void EMI::output_step()
1918 output_manager.write_data();
1920 double curtime =
timing(t2, t1);
1921 this->output_time += curtime;
1924 IO_stats.tot_time += curtime;
1940 output_manager.close_files_and_cleanup();
1946 void EMI::setup_stimuli()
1951 stimuli.
resize(param_globals::num_stim);
1952 for (
int i = 0; i < param_globals::num_stim; i++) {
1954 stimulus & s = stimuli[i];
1960 s.associated_intra_mesh =
emi_msh, s.associated_extra_mesh =
emi_msh;
1964 if (s.phys.type ==
Illum) {
1982 }
else if (s.phys.type ==
I_tm) {
1984 SF::restrict_to_membrane(s.electrode.vertices, dof2ptsData, mesh);
1996 if (s.electrode.dump_vtx) {
2001 if(param_globals::stim[i].pulse.dumpTrace &&
get_rank() == 0) {
2003 s.pulse.wave.write_trace(s.name+
".trc");
2009 void EMI::apply_dbc_stimulus()
2011 parabolic_solver_emi& ps = parab_solver;
2015 bool dbcs_have_updated = ps.dbc !=
nullptr && ps.dbc->dbc_update();
2018 if (dbcs_have_updated && time_not_final) {
2019 parab_solver.rebuild_matrices(mtype_vol, *ion.miif, stimuli, logger);
2023 void EMI::apply_current_stimulus()
2025 parabolic_solver_emi& ps = parab_solver;
2026 ps.Iij_stim->set(0.0);
2029 for(stimulus & s : stimuli) {
2031 switch (s.phys.type) {
2034 ps.Bi->mult(*ps.Iij_temp, *ps.Ib_both_face);
2035 ps.operator_both_to_unique_face->mult(*ps.Ib_both_face, *ps.Ib_unique_face);
2036 ps.Ib->add_scaled(*ps.Ib_unique_face, -0.5);
2050 void EMI::balance_electrodes()
2052 for (
int i = 0; i < param_globals::num_stim; i++) {
2053 if (param_globals::stim[i].crct.balance != -1) {
2054 int from = param_globals::stim[i].crct.balance;
2057 log_msg(NULL, 0, 0,
"Balancing stimulus %d with %d %s-wise.", from, to,
2058 is_current(stimuli[from].phys.type) ?
"current" :
"voltage");
2060 stimulus& s_from = stimuli[from];
2061 stimulus& s_to = stimuli[to];
2063 s_to.pulse = s_from.pulse;
2064 s_to.ptcl = s_from.ptcl;
2065 s_to.phys = s_from.phys;
2066 s_to.pulse.strength *= -1.0;
2068 if (s_from.phys.type ==
I_ex || s_from.phys.type ==
I_in) {
2072 if (!s_from.phys.total_current) {
2073 sf_mat& mass = *parab_solver.mass_emi;
2077 s_to.pulse.strength *= fabs(vol0 / vol1);
2089 for (stimulus & s : stimuli){
2090 if(
is_current(s.phys.type) && s.phys.total_current){
2091 switch (s.phys.type) {
2102 float scale = 1.e12 / vol;
2104 s.pulse.strength *= scale;
2107 "%s [Stimulus %d]: current density scaled to %.4g uA/cm^3\n",
2108 s.name.c_str(), s.idx, s.pulse.strength);
2122 s.pulse.strength /= surf;
2124 "%s [Stimulus %d]: current density scaled to %.4g uA/cm^2\n",
2125 s.name.c_str(), s.idx, s.pulse.strength);
2136 static void assign_deterministic_elem_numbering(
sf_mesh & mesh)
2138 const int KEY_SIZE = 6;
2139 int rank = 0, size = 0;
2140 MPI_Comm_rank(mesh.comm, &rank);
2141 MPI_Comm_size(mesh.comm, &size);
2143 auto make_key = [&](
size_t i) {
2144 std::array<mesh_int_t, KEY_SIZE> k;
2146 k[0] =
static_cast<mesh_int_t>(mesh.type[i]);
2150 if (mesh.type[i] ==
SF::Line) nn = 2;
2151 else if (mesh.type[i] ==
SF::Tri) nn = 3;
2152 else if (mesh.type[i] ==
SF::Quad) nn = 4;
2155 std::vector<mesh_int_t> nodes;
2157 size_t off = mesh.dsp[i];
2158 for (
int j = 0; j < nn; j++) {
2159 nodes.push_back(mesh.con[off + j]);
2161 std::sort(nodes.begin(), nodes.end());
2162 for (
int j = 0; j < (int)nodes.size(); j++) {
2163 k[2 + j] = nodes[j];
2169 std::vector<mesh_int_t> local_keys(mesh.l_numelem * KEY_SIZE, -1);
2170 for (
size_t i = 0; i < mesh.l_numelem; i++) {
2171 auto k = make_key(i);
2172 for (
int j = 0; j < KEY_SIZE; j++) local_keys[i * KEY_SIZE + j] = k[j];
2176 std::vector<int> counts(size, 0), displs(size, 0);
2177 int local_count = (int)local_keys.size();
2178 MPI_Allgather(&local_count, 1, MPI_INT, counts.data(), 1, MPI_INT, mesh.comm);
2180 for (
int r = 0; r < size; r++) {
2185 std::vector<mesh_int_t> all_keys;
2186 if (rank == 0) all_keys.resize(total, -1);
2187 const MPI_Datatype key_mpi_t = mpi_datatype<mesh_int_t>();
2188 MPI_Gatherv(local_keys.data(), local_count, key_mpi_t,
2189 rank == 0 ? all_keys.data() :
nullptr, counts.data(), displs.data(), key_mpi_t,
2193 std::vector<std::array<mesh_int_t, KEY_SIZE>> sorted_keys;
2195 const int nkeys = total / KEY_SIZE;
2196 sorted_keys.resize(nkeys);
2197 for (
int i = 0; i < nkeys; i++) {
2198 std::array<mesh_int_t, KEY_SIZE> k;
2199 for (
int j = 0; j < KEY_SIZE; j++) k[j] = all_keys[i * KEY_SIZE + j];
2202 std::sort(sorted_keys.begin(), sorted_keys.end());
2207 if (rank == 0) nkeys = (int)sorted_keys.size();
2208 MPI_Bcast(&nkeys, 1, MPI_INT, 0, mesh.comm);
2209 std::vector<mesh_int_t> flat_sorted(nkeys * KEY_SIZE, -1);
2211 for (
int i = 0; i < nkeys; i++) {
2212 for (
int j = 0; j < KEY_SIZE; j++) flat_sorted[i * KEY_SIZE + j] = sorted_keys[i][j];
2215 MPI_Bcast(flat_sorted.data(), (
int)flat_sorted.size(), key_mpi_t, 0, mesh.comm);
2219 sorted_keys.resize(nkeys);
2220 for (
int i = 0; i < nkeys; i++) {
2221 std::array<mesh_int_t, KEY_SIZE> k;
2222 for (
int j = 0; j < KEY_SIZE; j++) k[j] = flat_sorted[i * KEY_SIZE + j];
2230 nbr_ref.
resize(mesh.l_numelem);
2231 nbr_sub.
resize(mesh.l_numelem);
2232 for (
size_t i = 0; i < mesh.l_numelem; i++) {
2233 auto k = make_key(i);
2234 auto it = std::lower_bound(sorted_keys.begin(), sorted_keys.end(), k);
2235 if (it == sorted_keys.end() || *it != k) {
2236 log_msg(0, 5, 0,
"deterministic numbering failed to find key (rank %d, elem %zu)", rank, i);
2245 void EMI::setup_output()
2247 std::string output_base =
get_basename(param_globals::meshname);
2250 const bool write_binary =
2251 SF::fileExists(std::string(param_globals::meshname) +
".belem") ||
2253 const bool restrict_output =
2254 parse_emi_output_tags(param_globals::gridout_tags,
2255 parab_solver.extra_tags, parab_solver.intra_tags,
2260 const int gridout_emi = param_globals::gridout_emi;
2262 if(restrict_output && param_globals::num_io_nodes > 0) {
2263 log_msg(0, 5,
ECHO,
"Restricted EMI output with gridout_tags is not supported with async I/O.");
2269 if(restrict_output) {
2270 build_emi_volume_output_restriction(mesh, output_tags, phie_output_idx);
2271 if(
get_global(
static_cast<long int>(phie_output_idx.
size()), MPI_SUM, PETSC_COMM_WORLD) == 0) {
2272 log_msg(0, 5,
ECHO,
"Restricted EMI volume output is empty.");
2277 if(gridout_emi & 2) {
2278 std::string output_file = output_base +
"_e";
2279 log_msg(0, 0, 0,
"Writing \"%s\" mesh: %s (%s)", mesh.name.c_str(), output_file.c_str(), write_binary ?
"binary" :
"text");
2280 const double t0 = MPI_Wtime();
2281 if(restrict_output) {
2282 write_emi_output_mesh(mesh, write_binary, output_file, mesh.name.c_str(),
2283 [&](
size_t eidx) { return output_tags.count(mesh.tag[eidx]) != 0; });
2287 log_msg(0, 0, 0,
"Wrote \"%s\" mesh in %.5f seconds.", mesh.name.c_str(),
float(MPI_Wtime() - t0));
2289 else if(param_globals::output_level > 1) {
2290 log_msg(0, 0, 0,
"Skipping \"%s\" mesh output.", mesh.name.c_str());
2293 output_manager.register_output(parab_solver.ui,
emi_msh, 1, param_globals::phiefile,
"mV",
2294 restrict_output ? &phie_output_idx : NULL);
2297 mesh_m.name =
"Membrane";
2298 if(restrict_output) {
2299 build_emi_surface_output_restriction(mesh_m, output_tags, parab_solver.map_elem_uniqueFace_to_tags,
2301 if(
get_global(
static_cast<long int>(vm_output_idx.
size()), MPI_SUM, PETSC_COMM_WORLD) == 0) {
2302 log_msg(0, 5,
ECHO,
"Restricted EMI membrane output is empty.");
2307 if(gridout_emi & 1) {
2308 std::string output_file = output_base +
"_m";
2309 log_msg(0, 0, 0,
"Writing \"%s\" mesh: %s (%s)", mesh_m.name.c_str(), output_file.c_str(), write_binary ?
"binary" :
"text");
2310 const double t0 = MPI_Wtime();
2311 if(restrict_output) {
2312 write_emi_output_mesh(mesh_m, write_binary, output_file, mesh_m.name.c_str(),
2314 bool keep = output_tags.count(mesh_m.tag[eidx]) != 0;
2315 auto it = parab_solver.map_elem_uniqueFace_to_tags.find(eidx);
2316 if(it != parab_solver.map_elem_uniqueFace_to_tags.end()) {
2318 output_tags.count(static_cast<int>(it->second.first)) != 0 ||
2319 output_tags.count(static_cast<int>(it->second.second)) != 0;
2326 log_msg(0, 0, 0,
"Wrote \"%s\" mesh in %.5f seconds.", mesh_m.name.c_str(),
float(MPI_Wtime() - t0));
2328 else if(param_globals::output_level > 1) {
2329 log_msg(0, 0, 0,
"Skipping \"%s\" mesh output.", mesh_m.name.c_str());
2337 param_globals::vofile,
"mV",
2338 restrict_output ? &vm_output_idx : NULL,
true);
2340 if(param_globals::num_trace) {
2342 open_trace(ion.miif, param_globals::num_trace, param_globals::trace_node, NULL, &imesh);
2346 IO_stats.init_logger(
"IO_stats.dat");
2349 void EMI::dump_matrices()
2351 std::string bsname = param_globals::dump_basename;
2356 fn = bsname +
"_lhs.bin";
2357 parab_solver.lhs_emi->write(fn.c_str());
2359 fn = bsname +
"_K.bin";
2360 parab_solver.stiffness_emi->write(fn.c_str());
2362 fn = bsname +
"_B.bin";
2363 parab_solver.B->write(fn.c_str());
2365 fn = bsname +
"_Bi.bin";
2366 parab_solver.Bi->write(fn.c_str());
2368 fn = bsname +
"_BsM.bin";
2369 parab_solver.BsM->write(fn.c_str());
2371 fn = bsname +
"_M.bin";
2372 parab_solver.mass_emi->write(fn.c_str());
2374 fn = bsname +
"_Ms.bin";
2375 parab_solver.mass_surf_emi->write(fn.c_str());
2381 double EMI::timer_val(
const int timer_id)
2387 stimuli[sidx].value(val);
2390 val = std::nan(
"NaN");
2397 std::string EMI::timer_unit(
const int timer_id)
2404 s_unit = stimuli[sidx].pulse.wave.f_unit;
2409 void EMI::setup_solvers()
2412 const int log_flag = param_globals::output_level > 1 ?
ECHO : 0;
2413 double t0 = MPI_Wtime();
2414 parab_solver.init();
2415 log_msg(logger, 0, log_flag,
"EMI setup_solvers: parabolic solver init in %.5f seconds.",
float(MPI_Wtime() - t0));
2417 parab_solver.rebuild_matrices(mtype_vol, *ion.miif, stimuli, logger);
2418 log_msg(logger, 0, log_flag,
"EMI setup_solvers: matrix assembly and linear solver setup in %.5f seconds.",
float(MPI_Wtime() - t0));
2420 if(param_globals::dump2MatLab)
2428 MPI_Comm_size(comm, &size);
2429 MPI_Comm_rank(comm, &rank);
2442 MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
2443 divide(num_tags, size, num_tags_per_rank);
2446 void EMI::setup_EMI_mesh()
2448 log_msg(0,0,0,
"\n *** Processing EMI mesh ***\n");
2450 const std::string basename = param_globals::meshname;
2451 const int verb = param_globals::output_level;
2453 assert(mesh_registry.count(
emi_msh) == 1);
2462 MPI_Comm comm = emi_mesh.
comm;
2465 double t1, t2, s1, s2;
2466 const double total_setup_t0 = MPI_Wtime();
2467 MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
2472 if (emi_mesh.l_numelem > 0) {
2477 const char* type_name = (first_elem ==
SF::Line) ?
"1D (Line)" :
2478 (first_elem ==
SF::Tri) ?
"2D (Tri)" :
2481 log_msg(0, 5, 0,
"\n*** ERROR: EMI model requires a 3D volumetric mesh!");
2482 log_msg(0, 5, 0,
"*** Current mesh element type: %s", type_name);
2483 log_msg(0, 5, 0,
"*** EMI only supports 3D element types: Tetra, Pyramid, Prism, Hexa");
2484 log_msg(0, 5, 0,
"*** Please provide a 3D mesh with volume elements.\n");
2493 int total_num_tags = 0;
2494 if(verb)
log_msg(NULL, 0, 0,
"\nReading tags for extra and intra regions from input files");
2500 if(verb)
log_msg(NULL, 0, 0,
"Read extracellular tags");
2503 parab_solver.extra_tags.insert(tag);
2506 if(verb)
log_msg(NULL, 0, 0,
"Read intracellular tags");
2509 parab_solver.intra_tags.insert(tag);
2512 total_num_tags = parab_solver.extra_tags.size() + parab_solver.intra_tags.size();
2513 if(total_num_tags < size){
2514 log_msg(0,5,0,
"\nThe number of unique tags on EMI mesh is smaller than number of processors!");
2517 if(verb)
log_msg(NULL, 0, 0,
"\nextra_tags=%lu, intra_tags=%lu", parab_solver.extra_tags.size(), parab_solver.intra_tags.size());
2520 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2528 if(verb)
log_msg(NULL, 0, 0,
"\nReading points with data on each vertex");
2532 assert(ptsidx.
size()==ptsData.
size());
2534 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2536 std::list< sf_mesh* > meshlist;
2537 meshlist.push_back(&emi_mesh);
2542 if(verb)
log_msg(NULL, 0, 0,
"\nDistribute mesh based on tags");
2545 distribute_elements_based_tags(emi_mesh, total_num_tags);
2547 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2553 if(verb)
log_msg(NULL, 0, 0,
"\nInserting points");
2557 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2562 if(verb)
log_msg(NULL, 0, 0,
"\nCompute location of all DOFs on EMI mesh (inner DOFs, membrane, gap junction) saved into ptsData from original mesh");
2564 compute_ptsdata_from_original_mesh( emi_mesh,
2567 parab_solver.extra_tags,
2568 parab_solver.intra_tags);
2570 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2578 if(verb)
log_msg(NULL, 0, 0,
"\nExtract EMI surface mesh");
2580 extract_face_based_tags(emi_mesh,
SF::NBR_REF, vertex2ptsdata,
2581 parab_solver.line_face,
2582 parab_solver.tri_face,
2583 parab_solver.quad_face,
2584 parab_solver.extra_tags,
2585 parab_solver.intra_tags,
2586 emi_surfmesh_one_side, emi_surfmesh_w_counter_face, emi_surfmesh_unique_face,
2587 parab_solver.map_elem_uniqueFace_to_elem_oneface,
2588 unused_map_elem_oneface_to_elem_uniqueFace);
2589 meshlist.push_back(&emi_surfmesh_one_side);
2591 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2595 compute_surface_mesh_with_counter_face(emi_surfmesh_w_counter_face,
SF::NBR_REF,
2596 parab_solver.line_face,
2597 parab_solver.tri_face,
2598 parab_solver.quad_face);
2600 compute_surface_mesh_with_unique_face(emi_surfmesh_unique_face,
SF::NBR_REF,
2601 parab_solver.line_face,
2602 parab_solver.tri_face,
2603 parab_solver.quad_face,
2604 parab_solver.map_elem_uniqueFace_to_tags);
2609 SF::create_reverse_elem_mapping_between_surface_meshes(parab_solver.line_face,
2610 parab_solver.tri_face,
2611 parab_solver.quad_face,
2612 parab_solver.vec_both_to_one_face,
2615 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2620 if(verb)
log_msg(NULL, 0, 0,
"\ncompute global number of interface");
2621 size_t global_count_surf = 0;
2622 size_t numelem_surface = emi_surfmesh_one_side.l_numelem;
2623 size_t local_count_surf = numelem_surface;
2624 MPI_Reduce(&local_count_surf, &global_count_surf, 1, mpi_datatype<size_t>(), MPI_SUM, 0, MPI_COMM_WORLD);
2625 if(verb && rank==0) fprintf(stdout,
"global number of interfaces = %zu\n", global_count_surf);
2632 sub_numbering(emi_mesh);
2633 emi_mesh.generate_par_layout();
2645 if(verb)
log_msg(NULL, 0, 0,
"\ndecouple emi interfaces");
2646 if(verb)
log_msg(NULL, 0, 0,
"\tcompute map oldIdx to dof");
2647 compute_map_vertex_to_dof(emi_mesh,
SF::NBR_REF, vertex2ptsdata, parab_solver.extra_tags, parab_solver.map_vertex_tag_to_dof);
2652 if(verb)
log_msg(NULL, 0, 0,
"\tcomplete map oldIdx to dof with counter interface");
2654 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);
2659 if(verb)
log_msg(NULL, 0, 0,
"\tupdate mesh with dof");
2660 update_emi_mesh_with_dofs(emi_mesh,
SF::NBR_REF, parab_solver.map_vertex_tag_to_dof, parab_solver.dof2vertex);
2662 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2668 if(verb)
log_msg(NULL, 0, 0,
"\nInitialize petsc =0 for map<oldIdx,tag> -><dof, petsc>");
2670 for(
const auto & key_value : parab_solver.map_vertex_tag_to_dof)
2672 mesh_int_t gIndex_old = key_value.first.first;
2676 std::pair <mesh_int_t,mesh_int_t> dof_petsc = std::make_pair(dof,-1);
2677 parab_solver.map_vertex_tag_to_dof_petsc.insert({key_value.first,dof_petsc});
2680 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2686 if(verb)
log_msg(NULL, 0, 0,
"Inserting points and ptsData of dofs to emi_mesh");
2687 insert_points_ptsData_to_dof(tmesh_backup_old, emi_mesh,
SF::NBR_REF, parab_solver.dof2vertex, vertex2ptsdata, dof2ptsData);
2689 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2695 if(verb)
log_msg(NULL, 0, 0,
"Generating unique PETSc numberings");
2698 sub_numbering(emi_mesh);
2699 emi_mesh.generate_par_layout();
2702 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2708 if(verb)
log_msg(NULL, 0, 0,
"Generating unique PETSc numberings");
2711 petsc_numbering(emi_mesh);
2714 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2719 if(verb)
log_msg(NULL, 0, 0,
"Updating the map between indices to PETSc numberings");
2721 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);
2723 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2729 if(verb)
log_msg(NULL, 0, 0,
"Updating surface mesh with dof");
2730 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);
2732 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2738 if(verb)
log_msg(NULL, 0, 0,
"Layout for element of EMI surfmesh");
2741 SF::layout_from_count<long int>(emi_surfmesh_one_side.l_numelem, layout, emi_surfmesh_one_side.comm);
2742 size_t count = layout[rank+1] - layout[rank];
2744 for (
int i = 0; i <
count; ++i){
2745 emi_surfmesh_elem[i] = layout[rank]+i;
2751 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2757 if(verb)
log_msg(NULL, 0, 0,
"Layout for element of EMI surfmesh w counter face");
2760 SF::layout_from_count<long int>(emi_surfmesh_w_counter_face.l_numelem, layout_counter, emi_surfmesh_w_counter_face.comm);
2761 size_t count_counter = layout_counter[rank+1] - layout_counter[rank];
2762 emi_surfmesh_counter_elem.
resize(count_counter);
2763 for (
int i = 0; i < count_counter; ++i){
2764 emi_surfmesh_counter_elem[i] = layout_counter[rank]+i;
2766 emi_surfmesh_w_counter_face.localize(
SF::NBR_REF);
2769 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2776 if(verb)
log_msg(NULL, 0, 0,
"Layout for element of EMI unique-face surfmesh");
2779 SF::layout_from_count<long int>(emi_surfmesh_unique_face.l_numelem, layout_unique, emi_surfmesh_unique_face.comm);
2780 size_t count_unique = layout_unique[rank+1] - layout_unique[rank];
2781 emi_surfmesh_unique_elem.
resize(count_unique);
2782 for (
int i = 0; i < count_unique; ++i){
2783 emi_surfmesh_unique_elem[i] = layout_unique[rank]+i;
2788 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2794 if(verb)
log_msg(NULL, 0, 0,
"Inserting points to EMI surfmesh");
2795 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);
2796 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);
2797 insert_points_to_surface_mesh(tmesh_backup_old, emi_surfmesh_unique_face,
SF::NBR_REF, parab_solver.dof2vertex);
2799 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2805 if(verb)
log_msg(NULL, 0, 0,
"Generating submesh_numbering and PETSc numberings for surface mesh");
2808 sub_numbering(emi_surfmesh_one_side);
2809 emi_surfmesh_one_side.generate_par_layout();
2812 petsc_numbering(emi_surfmesh_one_side);
2817 sub_numbering(emi_surfmesh_w_counter_face);
2818 emi_surfmesh_w_counter_face.generate_par_layout();
2821 petsc_numbering(emi_surfmesh_w_counter_face);
2826 sub_numbering(emi_surfmesh_unique_face);
2827 emi_surfmesh_unique_face.generate_par_layout();
2830 petsc_numbering(emi_surfmesh_unique_face);
2832 assign_deterministic_elem_numbering(emi_surfmesh_unique_face);
2835 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2841 if(verb)
log_msg(NULL, 0, 0,
"assign PETSc numbering for new faces");
2842 SF::assign_petsc_on_counter_face(parab_solver.map_vertex_tag_to_dof_petsc,comm);
2845 for (it = parab_solver.map_vertex_tag_to_dof_petsc.begin(); it != parab_solver.map_vertex_tag_to_dof_petsc.end(); it++)
2847 std::pair <mesh_int_t,mesh_int_t> Index_tag_old = it->first;
2848 std::pair <mesh_int_t,mesh_int_t> dof_petsc = it->second;
2849 parab_solver.dof2petsc[dof_petsc.first] = dof_petsc.second;
2850 parab_solver.petsc2dof[dof_petsc.second] = dof_petsc.first;
2855 #ifdef EMI_DEBUG_MESH
2857 int invalid_count = 0;
2858 for (
const auto& [key, val] : parab_solver.map_vertex_tag_to_dof_petsc) {
2859 if (val.second < 0) {
2861 if (invalid_count <= 3) {
2862 fprintf(stderr,
"RANK %d INVALID: vertex=%ld tag=%ld dof=%ld petsc=%ld\n",
2863 rank, (
long)key.first, (
long)key.second, (
long)val.first, (
long)val.second);
2867 fprintf(stderr,
"RANK %d: After Step 26: %d invalid PETSc indices out of %zu total\n",
2868 rank, invalid_count, parab_solver.map_vertex_tag_to_dof_petsc.size());
2874 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2879 added_counter_faces_to_map(parab_solver.line_face, parab_solver.tri_face, parab_solver.quad_face);
2880 const double total_setup = MPI_Wtime() - total_setup_t0;
2881 log_msg(0,0,0,
"Total setup_EMI_mesh processing time: %.5f sec.",
float(total_setup));
2883 log_msg(0,0,0,
"\n *** EMI mesh processing Done ***\n");
2889 MPI_Comm comm = mesh.
comm;
2891 double t1, t2, s1, s2;
2892 MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
2893 const int verb = param_globals::output_level;
2895 if(total_num_tags < size)
2897 PetscPrintf(PETSC_COMM_WORLD,
"\nThe number of processors should be less than the number of tags, size = %d & ntags = %d !!!\n",
2898 size, total_num_tags);
2902 if(verb==10)
log_msg(NULL, 0, 0,
"\ncompute the number of tags which belongs to one rank");
2906 compute_tags_per_rank(total_num_tags, ntags_per_rank);
2908 if(verb==10)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2914 partition_based_tags(total_num_tags, mesh.
tag, ntags_per_rank, part_based_Tags);
2917 permute_mesh_locally_based_on_tag_elemIdx(mesh);
2919 if(verb==10)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2924 void partition_based_tags(
int num_tags,
2929 const int verb = param_globals::output_level;
2932 MPI_Comm_size(comm, &size);
2933 MPI_Comm_rank(comm, &rank);
2939 if (!load_partitions_from_file(tags_to_rank_map, num_tags, comm)) {
2940 if(verb==10)
log_msg(NULL, 0, 0,
"\ncompute the number of unique tags");
2942 double t1 = MPI_Wtime();
2943 extract_unique_tag(unique_tags);
2944 double t2 = MPI_Wtime();
2945 if(verb==10)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2947 if (unique_tags.
size() !=
static_cast<size_t>(num_tags)) {
2948 log_msg(0,5,0,
"\nerror: the number of tags in the EMI mesh does not match the total tags from *.extra and *.intra");
2951 map_tags_to_rank(size, unique_tags, num_tags_per_rank, tags_to_rank_map);
2954 for (
size_t i = 0; i < part.
size(); ++i) {
2955 if(tags_to_rank_map.
count(tag[i]))
2956 part[i] = tags_to_rank_map[tag[i]];
2964 for (
size_t r = 0; r < size; ++r) {
2965 for (
size_t count = 0;
count < num_tags_per_rank[r];) {
2969 tags_to_rank_map.
insert({tag, r});
2978 int expected_num_tags,
2982 MPI_Comm_size(comm, &size);
2983 MPI_Comm_rank(comm, &rank);
2986 const std::string basename = param_globals::meshname;
2987 FILE* fd = fopen((basename +
".part").c_str(),
"r");
2993 if (parts.
size() == 0)
return false;
2995 if (parts.
size() % 2 != 0) {
2996 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.!");
3002 for(
int i = 0; i < parts.
size(); i+=2) {
3003 min_part =
std::min(min_part, parts[i + 1]);
3004 max_part =
std::max(max_part, parts[i + 1]);
3005 tags_to_rank_map.
insert({parts[i], parts[i + 1]});
3008 if (tags_to_rank_map.
size() !=
static_cast<size_t>(expected_num_tags)) {
3009 log_msg(0,5,0,
"\nerror: the number of tags in the .part file does not match the total tags from *.extra and *.intra");
3013 if (min_part < 0 || max_part >= size) {
3015 "\nerror: EMI partition file %s.part is incompatible with this run.\n"
3016 "The file contains partition IDs in [%d, %d], but the current MPI communicator has %d rank(s).\n"
3017 "Remove/regenerate the .part file or run with a matching number of MPI tasks.",
3018 basename.c_str(), min_part, max_part, size);
3022 if (rank == 0 && max_part + 1 != size) {
3024 "Warning: EMI partition file %s.part uses %d partition ID(s), but the current run uses %d MPI rank(s).",
3025 basename.c_str(), max_part + 1, size);
opencarp::local_index_t mesh_int_t
#define SF_COMM
the default SlimFem MPI communicator
opencarp::real_t SF_real
Global scalar type.
#define SF_MPITAG
the MPI tag when communicating
#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
std::string name
the mesh name
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.
void forward(abstract_vector< T, S > &in, abstract_vector< T, S > &out, bool add=false)
Forward scattering.
Functor class applying a submesh renumbering.
size_t size() const
The current size of the vector.
void resize(size_t n)
Resize a vector.
const T * end() const
Pointer to the vector's end.
const T * begin() const
Pointer to the vector's start.
T * data()
Pointer to the vector's start.
iterator find(const K &key)
Search for key. Return iterator.
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)
hm_int count(const K &key) const
void insert(InputIterator first, InputIterator last)
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.
#define log_msg(F, L, O,...)
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 rebalance_mesh(meshdata< T, S > &mesh)
Rebalance the parallel distribution of a mesh, if a local size is 0.
void extract_mesh(const vector< bool > &keep, const meshdata< T, S > &mesh, meshdata< T, S > &submesh)
Extract a submesh from a given mesh.
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)
bool fileExists(std::string filename)
Function which checks if a given file exists.
elem_t getElemTypeID(char *eletype)
Generate element type enum from string.
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)
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)
void read_el_scale_vec(const char *file, mesh_t mt, SF::vector< double > &el_scale, int &el_scale_dpn)
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.
void region_mask(mesh_t meshspec, SF::vector< RegionSpecs > ®spec, SF::vector< int > ®ionIDs, bool mask_elem, const char *reglist, bool warn_on_default_tags)
classify elements/points as belonging to a region
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)
T get_global(T in, MPI_Op OP, MPI_Comm comm=PETSC_COMM_WORLD)
Do a global reduction on a variable.
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.
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 compute_restr_idx(sf_mesh &mesh, SF::vector< mesh_int_t > &inp_idx, SF::vector< mesh_int_t > &idx)
void log_msg(FILE_SPEC out, int level, unsigned char flag, const char *fmt,...)
@ 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
const char * get_tsav_ext(double time)
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.