33 #ifdef WITH_POWERCAPPING
35 #include "powercapping.h"
45 static char input_dir[1024],
56 for(
int i=0; i < cpy_argc; i++) {
57 if(strcmp(argv[i],
"+") != 0) {
58 cpy_argv[i] =
dupstr(argv[i]);
67 int param_argc = cpy_argc;
71 status = param(PARAMETERS, ¶m_argc, cpy_argv.
data());
72 if ( status==PrMERROR||status==PrMFATAL )
73 fprintf( stderr,
"\n*** Error reading parameters\n\n");
74 else if ( status == PrMQUIT )
75 fprintf( stderr,
"\n*** Quitting by user's request\n\n");
76 }
while (status == PrMERROR);
80 if (param_globals::output_setup)
81 param_output(PARAMETERS, 1);
84 for(
int i=0; i<param_argc; i++)
88 if (status & PrMERROR) exit(status);
103 log_msg(NULL, 5,
ECHO,
"The EMI model was not compiled for this binary.\n");
117 log_msg(0,0,0,
"\n *** Initializing physics ***\n");
121 for (
int ii = 0; ii < param_globals::num_external_imp; ii++) {
123 assert(loading_succeeded);
126 if(param_globals::num_external_imp)
127 log_msg(NULL, 4,
ECHO,
"Loading of external LIMPET modules not enabled.\n"
128 "Recompile with DLOPEN set.\n" );
134 log_msg(NULL, 0, 0,
"Initializing %s ..", p->
name);
141 log_msg(0,0,0,
"\n *** Destroying physics ***\n");
169 for (
int i=0; i<ns; i++ ) {
177 log_msg( NULL, 1, 0,
"Extracellular stimulus %d ignored for monodomain", i );
180 log_msg( NULL, 1, 0,
"Intracellular stimulus %d converted to transmembrane", i );
216 if(param_globals::floating_ground)
219 Stimulus* s = param_globals::stimulus;
221 for(
int i=0; i < param_globals::num_stim; i++) {
229 log_msg( NULL, 4, 0,
"Elliptic system is singular!\n"
230 "Either set floating_ground=1 or use an explicit ground:voltage (stimulus[X].stimtype=3)\n"
231 "Do not trust the elliptic solution of this simulation run!\n");
236 printf(
"\n""*** GIT tag: %s\n", GIT_COMMIT_TAG);
237 printf(
"*** GIT hash: %s\n", GIT_COMMIT_HASH);
238 printf(
"*** GIT repo: %s\n", GIT_PATH);
239 printf(
"*** dependency commits: %s\n\n", SUBREPO_COMMITS);
245 param_globals::dt /= 1000.;
248 if(param_globals::mass_lumping == 0 && param_globals::parab_solve==0) {
250 "Warning: explicit solve not possible without mass lumping. \n"
251 "Switching to Crank-Nicolson!\n\n");
253 param_globals::parab_solve = 1;
257 if(!param_globals::extracell_monodomain_stim)
266 if(param_globals::t_sentinel > 0 && param_globals::sentinel_ID < 0 ) {
267 log_msg(0,4,0,
"Warning: t_sentinel is set but no sentinel_ID has been specified; check_quiescence() behavior may not be as expected");
270 if(param_globals::num_external_imp > 0 ) {
271 for(
int ext_imp_i = 0; ext_imp_i < param_globals::num_external_imp; ext_imp_i++) {
272 if(param_globals::external_imp[ext_imp_i][0] !=
'/') {
273 log_msg(0,5,0,
"external_imp[%d] error: absolute paths must be used for .so file loading (\'%s\')",
274 ext_imp_i, param_globals::external_imp[ext_imp_i]);
280 if(param_globals::experiment ==
EXP_LAPLACE && param_globals::bidomain != 1) {
281 log_msg(0,4,0,
"Warning: Laplace experiment mode requires bidomain = 1. Setting bidomain = 1.");
282 param_globals::bidomain = 1;
285 if(param_globals::num_phys_regions == 0) {
286 log_msg(0,4,0,
"Warning: No physics region defined! Please set phys_region parameters to correctly define physics.");
289 log_msg(0,4,0,
"Intra-elec and Extra-elec domains will be derived from fibers.\n");
290 param_globals::num_phys_regions = param_globals::bidomain ? 2 : 1;
291 param_globals::phys_region = (p_region*) malloc(param_globals::num_phys_regions *
sizeof(p_region));
293 param_globals::phys_region[0].name = strdup(
"Autogenerated intracellular Electrics");
294 param_globals::phys_region[0].num_IDs = 0;
296 if(param_globals::bidomain) {
298 param_globals::phys_region[1].name = strdup(
"Autogenerated extracellular Electrics");
299 param_globals::phys_region[1].num_IDs = 0;
302 log_msg(0,4,0,
"Laplace domain will be derived from fibers.\n");
303 param_globals::num_phys_regions = 1;
304 param_globals::phys_region = (p_region*) malloc(param_globals::num_phys_regions *
sizeof(p_region));
306 param_globals::phys_region[0].name = strdup(
"Autogenerated Laplace");
307 param_globals::phys_region[0].num_IDs = 0;
312 log_msg(0,4,0,
"Warning: Laplace experiment mode requires a laplace physics regions defined.");
316 log_msg(0,4,0,
"Converting the defined extracellular-electrics-region to laplace-region.");
319 log_msg(0,4,0,
"Converting the defined intracellular-electrics-region to laplace-region.");
322 param_globals::num_phys_regions += 1;
323 param_globals::phys_region = (p_region*) realloc(param_globals::phys_region, param_globals::num_phys_regions *
sizeof(p_region));
325 param_globals::phys_region[param_globals::num_phys_regions - 1].ptype =
PHYSREG_LAPLACE;
326 param_globals::phys_region[param_globals::num_phys_regions - 1].name = strdup(
"Autogenerated Laplace");
327 param_globals::phys_region[param_globals::num_phys_regions - 1].num_IDs = 0;
331 #ifndef WITH_PARMETIS
332 if(param_globals::pstrat == 1) {
333 log_msg(0,3,0,
"openCARP was built without Parmetis support. Swithing to KDtree.");
334 param_globals::pstrat = 2;
339 bool legacy_stim_set =
false, new_stim_set =
false;
341 for(
int i=0; i<param_globals::num_stim; i++) {
342 Stimulus & legacy_stim = param_globals::stimulus[i];
343 Stim & new_stim = param_globals::stim[i];
345 if(legacy_stim.stimtype || legacy_stim.strength)
346 legacy_stim_set =
true;
348 if(new_stim.crct.type || new_stim.pulse.strength)
352 if(legacy_stim_set || new_stim_set) {
353 if(legacy_stim_set && new_stim_set) {
354 log_msg(0,4,0,
"Warning: Legacy stimuli and default stimuli are defined. Only default stimuli will be used!");
356 else if (legacy_stim_set) {
357 log_msg(0,1,0,
"Warning: Legacy stimuli defined. Please consider switching to stimulus definition \"stim[]\"!");
362 log_msg(0,4,0,
"Warning: No potential or current stimuli found!");
368 int flg = 0, err = 0, rank =
get_rank();
370 char *ptr = getcwd(current_dir, 1024);
371 if (ptr == NULL) err++;
372 ptr = getcwd(input_dir, 1024);
373 if (ptr == NULL) err++;
379 if (strcmp(sim_ID,
"OUTPUT_DIR")) {
380 if (mkdir(sim_ID, 0775)) {
381 if (errno == EEXIST ) {
382 log_msg(NULL, 2, 0,
"Output directory exists: %s\n", sim_ID);
384 log_msg(NULL, 5, 0,
"Unable to make output directory\n");
388 }
else if (mkdir(sim_ID, 0775) && errno != EEXIST) {
389 log_msg(NULL, 5, 0,
"Unable to make output directory\n");
397 err += chdir(sim_ID);
398 ptr = getcwd(output_dir, 1024);
399 if (ptr == NULL) err++;
404 err += chdir(output_dir);
409 if (strcmp(param_globals::ppID,
"POSTPROC_DIR")) {
410 if (mkdir(param_globals::ppID, 0775)) {
411 if (errno == EEXIST ) {
412 log_msg(NULL, 2,
ECHO,
"Postprocessing directory exists: %s\n\n", param_globals::ppID);
414 log_msg(NULL, 5,
ECHO,
"Unable to make postprocessing directory\n\n");
418 }
else if (mkdir(param_globals::ppID, 0775) && errno != EEXIST) {
419 log_msg(NULL, 5,
ECHO,
"Unable to make postprocessing directory\n\n");
427 err += chdir(param_globals::ppID);
428 ptr = getcwd(postproc_dir, 1024);
429 if (ptr == NULL) err++;
430 err = chdir(output_dir);
439 bool io_node =
false;
442 if (param_globals::num_io_nodes > 0) {
445 log_msg(NULL, 5, 0,
"You cannot run with async IO on only one core.\n");
449 if (2 * param_globals::num_io_nodes >= psize) {
450 log_msg(NULL, 5, 0,
"The number of IO cores be less " "than the number of compute cores.");
454 if (param_globals::num_PS_nodes && param_globals::num_io_nodes > param_globals::num_PS_nodes) {
456 "The number of IO cores (%d) should not "
457 "exceed the number of PS compute cores (%d).\n",
458 param_globals::num_io_nodes, param_globals::num_PS_nodes);
463 io_node = prank < param_globals::num_io_nodes;
466 MPI_Comm_split(PETSC_COMM_WORLD, io_node,
get_rank(), &comm);
467 MPI_Comm_set_name(comm, io_node ?
"IO" :
"compute");
469 PETSC_COMM_WORLD = comm;
473 MPI_Intercomm_create(comm, 0, MPI_COMM_WORLD, io_node ? param_globals::num_io_nodes : 0,
477 log_msg(NULL, 4, 0,
"Global node %d, Comm rank %d != Intercomm rank %d\n",
481 MPI_Comm_set_name(PETSC_COMM_WORLD,
"compute");
485 if((io_node || !param_globals::num_io_nodes) && !prank)
492 getcwd(current_dir, 1024);
499 if (dest==
OUTPUT) err = chdir(output_dir);
500 else if (dest==
POSTPROC) err = chdir(postproc_dir);
501 else if (dest==
CURDIR) err = chdir(current_dir);
502 else err = chdir(input_dir);
510 double start_time = 0.0;
513 double end_time = param_globals::tend;
527 if(param_globals::num_tsav) {
528 std::vector<double> trig(param_globals::num_tsav);
529 for(
size_t i=0; i<trig.size(); i++) trig[i] = param_globals::tsav[i];
534 if(param_globals::chkpt_intv)
536 param_globals::chkpt_intv, 0,
iotm_chkpt_intv,
"interval checkpointing");
538 if(param_globals::num_trace)
542 #ifdef WITH_POWERCAPPING
543 void basic_powercapping_setup()
545 user_globals::pc_manager =
new powercapping_manager(param_globals::powcap_backend, param_globals::powcap_backend_policy, param_globals::powcap_metrics_backend, param_globals::powcap_policy);
552 const short padding = 4;
557 if(col_width[0] <
int(strlen(buff)+padding))
558 col_width[0] = strlen(buff)+padding;
561 if(col_width[1] <
int(strlen(buff)+padding))
562 col_width[1] = strlen(buff)+padding;
565 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
567 int timer_id = used_timer_ids[tid];
577 snprintf(buff,
sizeof buff,
"%.3lf", val);
578 if(col_width[col] <
int(strlen(buff)+padding))
579 col_width[col] = strlen(buff)+padding;
596 const char* smpl_endl =
"\n";
604 log_msg(0,5,0,
"Protocol file %s could not be opened for writing!\n", fname);
617 std::vector<std::string> col_labels = {
"time",
"tick"};
618 std::vector<std::string> col_short_labels = {
"A",
"B"};
619 std::vector<std::string> col_unit_labels = {
"ms",
"--" };
620 std::vector<int> col_width = {4, 4};
622 char c_label = {
'C'};
623 std::string label = {
""};
624 std::string unit = {
""};
628 std::vector<int> used_timer_ids;
629 std::vector<int> used_stim_ids;
639 used_stim_ids.push_back(sidx);
649 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
651 int timer_id = used_timer_ids[tid];
654 int llen = strlen(t->
name);
655 mx_llen = llen > mx_llen ? llen : mx_llen;
658 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
660 int timer_id = used_timer_ids[tid];
663 col_labels.push_back(t->
name);
665 col_short_labels.push_back(label);
670 if(unit.empty()) unit =
"--";
671 col_unit_labels.push_back(unit);
672 col_width.push_back(4);
680 fh <<
"# Protocol header\n#\n" <<
"# Legend:\n";
681 for(
size_t i = 0; i<col_short_labels.size(); i++)
683 fh <<
"#" << std::setw(2) << col_short_labels[i] <<
" = " << std::setw(mx_llen) << col_labels[i];
684 fh <<
" [" << std::setw(10) << col_unit_labels[i] <<
"]";
686 if(i >= 2 && used_stim_ids[i-2] > -1) {
691 fh <<
" ground stim" << smpl_endl;
693 fh <<
" applied: " << std::to_string(s.
pulse.
strength) << smpl_endl;
704 for(
size_t i = 0; i<col_short_labels.size(); i++)
705 fh << std::setw(col_width[i] - 3) << col_short_labels[i].c_str() << std::setw(3) <<
" ";
708 fh << smpl_endl <<
"#";
709 for(
size_t i = 0; i<col_unit_labels.size(); i++)
710 fh <<
"[" << std::setw(col_width[i]-2) << col_unit_labels[i].c_str() <<
"]";
713 fh << smpl_endl << std::fixed;
721 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
723 int timer_id = used_timer_ids[tid];
729 fh << std::setw(col_width[col]) << On;
737 fh << std::setw(col_width[col]) << std::setprecision(3) << val;
759 const char* h1_prog =
"PROG\t----- \t----\t-------\t-------|";
760 const char* h2_prog =
"time\t%%comp\ttime\t ctime \t ETA |";
761 const char* h1_wc =
"\tELAPS |";
762 const char* h2_wc =
"\twc |";
767 log_msg(NULL, 0, 0,
"%s", h1_prog );
775 int req_hours = ((int)(time)) / 3600;
776 int req_min = (((int)(time)) % 3600) / 60;
777 int req_sec = (((int)(time)) % 3600) % 60;
779 snprintf(str, str_size,
"%d:%02d:%02d", req_hours, req_min, req_sec);
792 char elapsed_time_str[256];
793 char req_time_str[256];
797 log_msg( NULL, 0,
NONL,
"%.2f\t%.1f\t%.1f\t%s\t%s",
815 if(!have_timedependent_phys) {
816 log_msg(0,0,0,
"\n no time-dependent physics region registered, skipping simulate loop..\n");
820 log_msg(0,0,0,
"\n *** Launching simulation ***\n");
824 if(param_globals::dump_protocol)
831 #ifdef WITH_POWERCAPPING
832 basic_powercapping_setup();
833 powercapping_manager &pc = *user_globals::pc_manager;
844 #ifdef WITH_POWERCAPPING
845 std::vector<int> flops_per_rank;
850 #ifdef WITH_POWERCAPPING
851 pc.iteration_begin(flops_per_rank);
858 it.second->output_step();
861 #ifdef WITH_POWERCAPPING
862 pc.sample(
"output step");
870 #ifdef WITH_POWERCAPPING
875 #ifdef WITH_POWERCAPPING
876 pc.iteration_end(flops_per_rank);
883 log_msg(0,0,0,
"\n\nTimings of individual physics:");
884 log_msg(0,0,0,
"------------------------------\n");
895 if(param_globals::post_processing_opts &
RECOVER_PHIE) {
896 log_msg(NULL,0,
ECHO,
"\nPOSTPROCESSOR: Recovering Phie ...");
897 log_msg(NULL,0,
ECHO,
"----------------------------------\n");
903 log_msg(NULL,0,
ECHO,
"\n-----------------------------------------");
904 log_msg(NULL,0,
ECHO,
"POSTPROCESSOR: Successfully recoverd Phie.\n");
916 if(error_if_missing) {
917 log_msg(0,5,0,
"%s error: required physic is not active! Usually this is due to an inconsistent experiment configuration. Aborting!", __func__);
941 log_msg(0,5,0,
"%s warning: trying to register already registered data vector.", __func__);
955 auto register_new_mesh = [&] (
mesh_t mt,
int pidx) {
956 if(!mesh_registry.count(mt)) {
958 mesh_registry[mt].name = param_globals::phys_region[pidx].name;
960 return &mesh_registry[mt];
964 for(
int i=0; i<param_globals::num_phys_regions; i++)
968 switch(param_globals::phys_region[i].ptype) {
980 curmesh = register_new_mesh(
emi_msh, i);
985 log_msg(0,5,0,
"Unsupported mesh type %d! Aborting!", param_globals::phys_region[i].ptype);
991 for(
int j=0; j<param_globals::phys_region[i].num_IDs; j++)
1009 if(ntr == 0)
return;
1014 for (
int i=0; i<ntr; i++) {
1023 shape.
p0.
x = tagRegs[i].p0[0];
1024 shape.
p0.
y = tagRegs[i].p0[1];
1025 shape.
p0.
z = tagRegs[i].p0[2];
1026 shape.
p1.
x = tagRegs[i].p1[0];
1027 shape.
p1.
y = tagRegs[i].p1[1];
1028 shape.
p1.
z = tagRegs[i].p1[2];
1029 shape.
radius = tagRegs[i].radius;
1036 log_msg(0,3,0,
"Tag region %d is empty", i);
1038 for(
size_t j=0; j<elem_indices.
size(); j++)
1039 mesh.
tag[elem_indices[j]] = tagRegs[i].tag;
1043 if(strlen(param_globals::retagfile))
1058 size_t renormalised_count = 0;
1064 for (
size_t i = 0; i < l_numelem; i++)
1066 const mesh_real_t f0 = fib[3*i+0], f1 = fib[3*i+1], f2 = fib[3*i+2];
1067 mesh_real_t fibre_len = sqrt(f0*f0 + f1*f1 + f2*f2);
1069 if (fibre_len && fabs(fibre_len - 1) > 1e-3) {
1070 fib[3 * i + 0] /= fibre_len;
1071 fib[3 * i + 1] /= fibre_len;
1072 fib[3 * i + 2] /= fibre_len;
1073 renormalised_count++;
1077 return renormalised_count;
1082 log_msg(0,0,0,
"\n *** Processing meshes ***\n");
1084 const std::string basename = param_globals::meshname;
1085 const int verb = param_globals::output_level;
1093 MPI_Comm comm = ref_mesh.
comm;
1096 double t1, t2, s1, s2;
1097 MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
1103 std::list< sf_mesh* > ptsread_list;
1106 t1 = MPI_Wtime(); s1 = t1;
1107 if(verb)
log_msg(NULL, 0, 0,
"Reading reference mesh: %s.*", basename.c_str());
1113 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1115 bool check_fibre_normality =
true;
1116 if (check_fibre_normality and ref_mesh.
fib.
size()>0) {
1122 size_t l_num_fixed_she = 0;
1126 unsigned long fixed[2] = {(
unsigned long) l_num_fixed_fib, (
unsigned long) l_num_fixed_she};
1127 MPI_Allreduce(MPI_IN_PLACE, fixed, 2, MPI_UNSIGNED_LONG, MPI_SUM, comm);
1129 if (fixed[0] + fixed[1] > 0)
1130 log_msg(NULL, 0, 0,
"Renormalised %ld longitudinal and %ld sheet-transverse fibre vectors.", fixed[0], fixed[1]);
1133 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
1136 if(param_globals::numtagreg > 0) {
1137 log_msg(0, 0, 0,
"Re-tagging reference mesh");
1141 ptsread_list.push_back(&ref_mesh);
1144 retag_elements(ref_mesh, param_globals::tagreg, param_globals::numtagreg);
1147 ptsread_list.clear();
1150 if(verb)
log_msg(NULL, 0, 0,
"Processing submeshes");
1152 for(
auto it = mesh_registry.begin(); it != mesh_registry.end(); ++it) {
1153 mesh_t grid_type = it->first;
1154 sf_mesh & submesh = it->second;
1157 if(verb > 1)
log_msg(NULL, 0, 0,
"\nSubmesh name: %s", submesh.
name.c_str());
1174 if(verb > 1)
log_msg(NULL, 0, 0,
"Extraction done in %f sec.",
float(t2 - t1));
1176 ptsread_list.push_back(&submesh);
1181 if(param_globals::pstrat == 2)
1184 for(
auto it = mesh_registry.begin(); it != mesh_registry.end(); ++it)
1186 mesh_t grid_type = it->first;
1187 sf_mesh & submesh = it->second;
1189 if(verb > 2)
log_msg(NULL, 0, 0,
"\nSubmesh name: %s", submesh.
name.c_str());
1194 switch(param_globals::pstrat) {
1196 if(verb > 2)
log_msg(NULL, 0, 0,
"Using linear partitioning ..");
1199 #ifdef WITH_PARMETIS
1202 if(verb > 2)
log_msg(NULL, 0, 0,
"Using Parmetis partitioner ..");
1203 SF::parmetis_partitioner<mesh_int_t, mesh_real_t> partitioner(param_globals::pstrat_imbalance, 2);
1204 partitioner(submesh, part);
1210 if(verb > 2)
log_msg(NULL, 0, 0,
"Using KDtree partitioner ..");
1212 partitioner(submesh, part);
1217 if(verb > 2)
log_msg(NULL, 0, 0,
"Partitioning done in %f sec.",
float(t2 - t1));
1219 if(param_globals::pstrat > 0) {
1220 if(param_globals::gridout_p) {
1221 std::string out_name =
get_basename(param_globals::meshname);
1226 log_msg(0,0,0,
"Writing \"%s\" partitioning to: %s", submesh.
name.c_str(), out_name.c_str());
1233 if(verb > 2)
log_msg(NULL, 0, 0,
"Redistributing done in %f sec.",
float(t2 - t1));
1238 sm_numbering(submesh);
1240 if(verb > 2)
log_msg(NULL, 0, 0,
"Canonical numbering done in %f sec.",
float(t2 - t1));
1245 p_numbering(submesh);
1247 if(verb > 2)
log_msg(NULL, 0, 0,
"PETSc numbering done in %f sec.",
float(t2 - t1));
1256 if(verb)
log_msg(NULL, 0, 0,
"All done in %f sec.",
float(s2 - s1));
1266 std::string output_base =
get_basename(param_globals::meshname);
1268 if(write_intra_elec) {
1271 if(param_globals::gridout_i & 1) {
1273 if(param_globals::output_level > 1)
1274 log_msg(0,0,0,
"Computing \"%s\" surface ..", mesh.
name.c_str());
1277 std::string output_file = output_base +
"_i.surf";
1278 log_msg(0,0,0,
"Writing \"%s\" surface: %s", mesh.
name.c_str(), output_file.c_str());
1281 if(param_globals::gridout_i & 2) {
1282 bool write_binary =
false;
1284 std::string output_file = output_base +
"_i";
1285 log_msg(0,0,0,
"Writing \"%s\" mesh: %s", mesh.
name.c_str(), output_file.c_str());
1290 if(write_extra_elec) {
1293 if(param_globals::gridout_e & 1) {
1295 if(param_globals::output_level > 1)
1296 log_msg(0,0,0,
"Computing \"%s\" surface ..", mesh.
name.c_str());
1299 std::string output_file = output_base +
"_e.surf";
1300 log_msg(0,0,0,
"Writing \"%s\" surface: %s", mesh.
name.c_str(), output_file.c_str());
1303 if(param_globals::gridout_e & 2) {
1304 bool write_binary =
false;
1305 std::string output_file = output_base +
"_e";
1306 log_msg(0,0,0,
"Writing \"%s\" mesh: %s", mesh.
name.c_str(), output_file.c_str());
1329 char* filecopy =
dupstr(file);
1330 char* dir =
dupstr(dirname(filecopy));
1347 PetscErrorPrintf = PetscErrorPrintfNone;
1357 for(
size_t eidx = 0; eidx < mesh.
l_numelem; eidx++) {
1360 if(mindim < cdim) mindim = cdim;
1387 int gsize = inp_data->
gsize();
1393 regigb.
x(gsize / dpn);
1394 regigb.
dim_x(regigb.
x()-1);
1397 regigb.
y(1); regigb.
z(1);
1413 regigb.
inc_t(param_globals::spacedt);
1427 log_msg(0,5,0,
"%s error: Could not set up data output! Aborting!", __func__);
1434 IO.
spec = mesh_spec;
1444 buffmap[mesh_spec] = inp_copy;
1451 void igb_output_manager::register_output_async(
sf_vec* inp_data,
1479 for(
size_t i=0; i<alg_nod.
size(); i++)
1480 ioidx[i] = nbr[alg_nod[i]];
1485 for(
size_t i=0; i<idx->
size(); i++) {
1487 ioidx[i] = nbr[loc_nodal];
1507 if(param_globals::num_io_nodes == 0)
1510 register_output_async(inp_data, inp_meshid, dpn, name,
units, idx, elem_data);
1525 sc->
forward(*data_vec, *perm_vec);
1540 FILE* fd =
static_cast<FILE*
>(it.
igb.
fileptr());
1543 sf_vec* buff = fill_output_buffer(it);
1556 root_write(fd, restr_buff, PETSC_COMM_WORLD);
1584 FILE* fd =
static_cast<FILE*
>(it.
igb.
fileptr());
1615 const int max_line_len = 128;
1616 const char* file_sep =
"#=======================================================";
1623 fprintf(out->
fd,
"# CARP GIT commit hash: %s\n", GIT_COMMIT_HASH);
1624 fprintf(out->
fd,
"# dependency hashes: %s\n", SUBREPO_COMMITS);
1625 fprintf(out->
fd,
"\n");
1628 char line[8196] =
"# ";
1630 for (
int j=0; j<argc; j++) {
1631 strcat(line, argv[j]);
1632 if(strlen(line) > max_line_len) {
1633 fprintf(out->
fd,
"%s\n", line);
1639 fprintf(out->
fd,
"%s\n\n", line);
1643 for (
int i=1; i<argc; i++) {
1644 std::string argument(argv[i]);
1645 if (argument ==
"+F" || argument.find(
"_options_file")!= std::string::npos) {
1647 std::string init =
"";
1648 if (argument.find(
"_options_file")!= std::string::npos) {
1649 fprintf(out->
fd,
"%s = %s\n", argument.substr(1).c_str(), argv[i+1]);
1652 fprintf(out->
fd,
"%s>>\n", file_sep);
1655 fprintf(out->
fd,
"## %s ##\n", argv[i]);
1656 FILE *in = fopen(argv[i],
"r");
1657 while (fgets(line, 8196, in))
1658 fprintf(out->
fd,
"%s%s", init.c_str(), line);
1660 fprintf(out->
fd,
"\n##END of %s\n", argv[i]);
1661 fprintf(out->
fd,
"%s<<\n\n", file_sep);
1663 else if(argv[i][0] ==
'-')
1665 bool prelast = (i==argc-1);
1666 bool paramFollows = !prelast && ((argv[i+1][0] !=
'-') ||
1667 ((argv[i+1][1] >=
'0') && (argv[i+1][1] <=
'9')));
1673 char *optcpy = strdup(argv[i+1]);
1674 char *front = optcpy;
1676 while(*front==
' ' && *front) front++;
1678 while(*++front ==
' ');
1679 char *back = optcpy+strlen(optcpy)-1;
1680 while(*back==
' ' && back>front) back--;
1684 if (strstr(front,
"=") !=
nullptr)
1685 fprintf(out->
fd,
"%-40s= \"%s\"\n", argv[i]+1, front);
1687 fprintf(out->
fd,
"%-40s= %s\n", argv[i]+1, front);
1692 fprintf(out->
fd,
"%-40s= 1\n", argv[i]);
1707 snprintf(save_fn,
sizeof save_fn,
"exit.save.%.3f.roe", time);
1708 log_msg(NULL, 0, 0,
"savequit called at time %g\n", time);
double SF_real
Use the general double as real type.
Basic utility structs and functions, mostly IO related.
virtual void release_ptr(S *&p)=0
virtual T gsize() const =0
size_t write_binary(FILE *fd)
Write a vector to HD in binary. File descriptor is already set up.
virtual T lsize() const =0
Comfort class. Provides getter functions to access the mesh member variables more comfortably.
void set_elem(size_t eidx)
Set the view to a new element.
void clear_data()
Clear the mesh data from memory.
overlapping_layout< T > pl
nodal parallel layout
vector< S > she
sheet direction
vector< S > fib
fiber direction
size_t l_numelem
local number of elements
std::string name
the mesh name
void generate_par_layout()
Set up the parallel layout.
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
hashmap::unordered_set< int > extr_tag
the element tags based on which the mesh has been extracted
Functor class generating a numbering optimized for PETSc.
void free_scatterings()
Free the registered scatterings.
Container for a PETSc VecScatter.
void forward(abstract_vector< T, S > &in, abstract_vector< T, S > &out, bool add=false)
Forward scattering.
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.
T * data()
Pointer to the vector's start.
void insert(InputIterator first, InputIterator last)
void dump_state(char *, float, opencarp::mesh_t gid, bool, unsigned int)
The abstract physics interface we can use to trigger all physics.
int timer_idx
the timer index received from the timer manager
virtual void output_timings()
virtual void compute_step()=0
virtual void initialize()=0
const char * name
The name of the physic, each physic should have one.
SF::vector< stimulus > stimuli
the electrical stimuli
std::string timer_unit(const int timer_id)
figure out units of a signal linked to a given timer
double timer_val(const int timer_id)
figure out current value of a signal linked to a given timer
class to store shape definitions
std::map< SF::mixed_tuple< mesh_t, int >, sf_vec * > buffmap_elem
IGBheader * get_igb_header(const sf_vec *vec)
Get the pointer to the igb header for a vector that was registered for output.
void write_data()
write registered data to disk
SF::vector< async_io_item > async_IOs
void register_output_sync(sf_vec *inp_data, const mesh_t inp_meshid, const int dpn, const char *name, const char *units, const SF::vector< mesh_int_t > *idx=NULL, bool elem_data=false)
std::map< SF::mixed_tuple< mesh_t, int >, sf_vec * > buffmap
map data spec -> PETSc vector buffer
void close_files_and_cleanup()
close file descriptors
void register_output(sf_vec *inp_data, const mesh_t inp_meshid, const int dpn, const char *name, const char *units, const SF::vector< mesh_int_t > *idx=NULL, bool elem_data=false)
Register a data vector for output.
SF::vector< sync_io_item > sync_IOs
stim_t type
type of stimulus
int timer_id
timer for stimulus
double strength
strength of stimulus
stim_protocol ptcl
applied stimulation protocol used
stim_pulse pulse
stimulus wave form
stim_physics phys
physics of stimulus
centralize time managment and output triggering
void initialize_neq_timer(const std::vector< double > &itrig, double idur, int ID, const char *iname, const char *poolname=nullptr)
long d_time
current time instance index
bool trigger(int ID) const
void initialize_eq_timer(double istart, double iend, int ntrig, double iintv, double idur, int ID, const char *iname, const char *poolname=nullptr)
void reset_timers()
Reset time in timer_manager and then reset registered timers.
double start
initial time (nonzero when restarting)
void initialize_singlestep_timer(double tg, double idur, int ID, const char *iname, const char *poolname=nullptr)
std::vector< base_timer * > timers
vector containing individual timers
Base class for tracking progress.
Top-level header of FEM module.
void extract_tagbased(const meshdata< T, S > &mesh, meshdata< T, S > &submesh)
Extract a submesh based on element tags.
void write_data_ascii(const MPI_Comm comm, const vector< T > &idx, const vector< S > &data, std::string file, short dpn=1)
void compute_surface_mesh(const meshdata< T, S > &mesh, const SF_nbr numbering, const hashmap::unordered_set< T > &tags, meshdata< T, S > &surfmesh)
Compute the surface of a given mesh.
void print_DD_info(const meshdata< T, S > &mesh)
Print some basic information on the domain decomposition of a mesh.
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 write_surface(const meshdata< T, S > &surfmesh, std::string surffile)
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 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.
T local_nodal_to_local_petsc(const meshdata< T, S > &mesh, int rank, T local_nodal)
void init_vector(SF::abstract_vector< T, S > **vec)
void extract_myocardium(const meshdata< T, S > &mesh, meshdata< T, S > &submesh, bool require_fibers=true)
Extract the myocardium submesh.
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.
size_t root_write(FILE *fd, const vector< V > &vec, MPI_Comm comm)
Write vector data binary to disk.
void read_elements(meshdata< T, S > &mesh, std::string basename, bool require_fibers=true)
Read the element data (elements and fibers) of a CARP mesh.
@ NBR_PETSC
PETSc numbering of nodes.
@ NBR_ELEM_REF
The element 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.
int load_ionic_module(const char *)
void COMPUTE_do_output(SF_real *dat, const int lsize, const int IO_id)
int COMPUTE_register_output(const SF::vector< mesh_int_t > &idx, const int dpn, const char *name, const char *units)
std::map< int, std::string > units
MPI_Comm IO_Intercomm
Communicator between IO and compute worlds.
FILE * petsc_error_fd
file descriptor for petsc error output
timer_manager * tm_manager
a manager for the various physics timers
std::map< datavec_t, sf_vec * > datavec_reg
important solution vectors from different physics
bool using_legacy_stimuli
flag storing whether legacy stimuli are used
SF::scatter_registry scatter_reg
Registry for the different scatter objects.
std::map< mesh_t, sf_mesh > mesh_reg
Registry for the different meshes used in a multi-physics simulation.
std::map< physic_t, Basic_physic * > physics_reg
the physics
void time_to_string(float time, char *str, short str_size)
physic_t
Identifier for the different physics we want to set up.
sf_vec * get_data(datavec_t d)
Retrieve a petsc data vector from the data registry.
void output_parameter_file(const char *fname, int argc, char **argv)
void initialize_physics()
Initialize all physics in the registry.
bool setup_IO(int argc, char **argv)
void retag_elements(sf_mesh &mesh, TagRegion *tagRegs, int ntr)
void parse_params_cpy(int argc, char **argv)
Initialize input parameters on a copy of the real command line parameters.
sf_mesh & get_mesh(const mesh_t gt)
Get a mesh by specifying the gridID.
int set_ignore_flags(int mode)
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
datavec_t
Enum used to adress the different data vectors stored in the data registry.
void register_physics()
Register physics to the physics registry.
void post_process()
do postprocessing
void check_and_convert_params()
Here we want to put all parameter checks, conversions and modifications that have been littered throu...
int get_rank(MPI_Comm comm=PETSC_COMM_WORLD)
short get_mesh_dim(mesh_t id)
get (lowest) dimension of the mesh used in the experiment
T get_global(T in, MPI_Op OP, MPI_Comm comm=PETSC_COMM_WORLD)
Do a global reduction on a variable.
bool is_potential(stim_t type)
uses current for stimulation
bool phys_defined(int physreg)
function to check if certain physics are defined
void update_console_output(const timer_manager &tm, prog_stats &p)
void show_build_info()
show the build info, exit if -buildinfo was provided. This code runs before MPI_Init().
FILE_SPEC f_open(const char *fname, const char *mode)
Open a FILE_SPEC.
void savequit()
save state and quit simulator
bool have_permutation(const int mesh_id, const int perm_id, const int dpn)
void set_io_dirs(char *sim_ID, char *pp_ID, IO_t init)
void register_data(sf_vec *dat, datavec_t d)
Register a data vector in the global registry.
void basic_timer_setup()
Here we set up the timers that we always want to have, independent of physics.
char * get_file_dir(const char *file)
IO_t
The different output (directory) types.
void get_protocol_column_widths(std::vector< int > &col_width, std::vector< int > &used_timer_ids)
int get_phys_index(int physreg)
get index in param_globals::phys_region array for a certain phys region
void check_nullspace_ok()
int postproc_recover_phie()
char * dupstr(const char *old_str)
int plot_protocols(const char *fname)
plot simulation protocols (I/O timers, stimuli, boundary conditions, etc)
void indices_from_geom_shape(SF::vector< mesh_int_t > &idx, const sf_mesh &mesh, const geom_shape shape, const bool nodal)
Populate vertex data with the vertices inside a defined box shape.
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.
void setup_meshes(bool require_fibers=true)
Read in the reference mesh and use its data to populate all meshes registered in the mesh registry.
void get_time(double &tm)
bool mesh_is_registered(const mesh_t gt)
check wheter a SF mesh is set
int get_size(MPI_Comm comm=PETSC_COMM_WORLD)
Basic_physic * get_physics(physic_t p, bool error_if_missing)
Convinience function to get a physics.
size_t renormalise_fibres(SF::vector< mesh_real_t > &fib, size_t l_numelem)
void destroy_physics()
Destroy all physics in the registry.
void ignore_extracellular_stim(Stimulus *st, int ns, int ignore)
void init_console_output(const timer_manager &tm, prog_stats &p)
tagreg_t
tag regions types. must be in line with carp.prm
V timing(V &t2, const V &t1)
void read_indices(SF::vector< T > &idx, const std::string filename, const hashmap::unordered_map< mesh_int_t, mesh_int_t > &dd_map, MPI_Comm comm)
Read indices from a file.
std::string get_basename(const std::string &path)
void update_cwd()
save the current working directory to curdir so that we can switch back to it if needed.
void f_close(FILE_SPEC &f)
Close a FILE_SPEC.
void setup_petsc_err_log()
set up error logs for PETSc, so that it doesnt print errors to stderr.
void simulate()
Main simulate loop.
void parse_mesh_types()
Parse the phys_type CLI parameters and set up (empty) SF::meshdata meshes.
Top-level header of physics module.
#define PHYSREG_INTRA_ELEC
#define PETSC_TO_CANONICAL
Permute algebraic data from PETSC to canonical ordering.
#define ELEM_PETSC_TO_CANONICAL
Permute algebraic element data from PETSC to canonical ordering.
#define PHYSREG_EXTRA_ELEC
Simulator-level utility execution control functions.
#define STM_IGNORE_PSEUDO_BIDM
#define STM_IGNORE_MONODOMAIN
#define STM_IGNORE_BIDOMAIN
#define Extracellular_Ground
#define Extracellular_V_OL
const SF::vector< mesh_int_t > * restr_idx
when using asyncIO, here we store the different IDs associated to the vectors we output
SF::vector< mesh_int_t > restr_petsc_idx
pointer to index vector with nodal indices we restrict to.
int IO_id
pointer to data registered for output
long d_trigger_dur
discrete duration
const char * name
timer name
bool triggered
flag indicating trigger at current time step
for display execution progress and statistical data of electrical solve
double curr
current output wallclock time
double start
output start wallclock time
double last
last output wallclock time
bool elem_flag
igb header we use for output
IGBheader igb
pointer to index vector used for restricting output.
const SF::vector< mesh_int_t > * restr_idx
pointer to data registered for output
SF::mixed_tuple< mesh_t, int > spec
flag whether the data is elements-wise