33 #include "opencarp_types.h"
37 #ifdef WITH_POWERCAPPING
39 #include "powercapping.h"
54 #include "openCARP_schema.hpp"
55 #include "runtime.hpp"
56 #include "snapshot_file_io.hpp"
62 enum class ParserCompareMode {
68 enum class ParserFallbackMode {
73 struct RuntimeCompatOptions {
77 struct LegacyCompareInput {
84 struct LegacySnapshotHelperRunResult {
91 std::vector<paramschema::CitationSuggestion> active_citation_suggestions;
93 std::string trim_copy(
const std::string& value);
94 std::string to_lower_ascii(std::string value);
95 bool build_legacy_compare_input(
int argc,
char** argv, LegacyCompareInput* input, std::string* error);
97 std::string join_paths_for_message(
const std::vector<std::string>& paths)
100 return std::string();
103 std::ostringstream joined;
104 for (std::size_t i = 0; i < paths.size(); ++i) {
113 bool match_long_option(
const std::string& token,
const char* option, std::string* attached_value)
115 *attached_value = std::string();
116 if (token == option) {
120 const std::string prefix = std::string(option) +
"=";
121 if (token.size() > prefix.size() && token.compare(0, prefix.size(), prefix) == 0) {
122 *attached_value = token.substr(prefix.size());
129 bool is_help_topic_candidate(
const char* token)
131 return token != NULL && token[0] !=
'\0' && token[0] !=
'-' && token[0] !=
'+';
134 bool is_long_option_argument_error(
const std::string& token,
136 const std::string& attached_value,
139 if (attached_value.empty()) {
143 *error =
"Unexpected argument for " + std::string(option) +
" in '" + token +
"'";
147 bool normalize_runtime_args(
int argc,
149 std::vector<std::string>* normalized,
150 RuntimeCompatOptions* compat,
154 compat->fallback_mode = ParserFallbackMode::Off;
156 if (argc <= 0 || argv == NULL || argv[0] == NULL) {
157 *error =
"Missing program name";
161 normalized->push_back(argv[0]);
163 for (
int i = 1; i < argc; ++i) {
164 const std::string token = argv[i];
169 std::string attached_value;
171 if (token ==
"+Help" || match_long_option(token,
"--help", &attached_value)) {
172 std::string topic =
"PrM";
173 if (!attached_value.empty()) {
174 topic = attached_value;
175 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
178 normalized->push_back(
"+Help");
179 normalized->push_back(topic);
183 if (token ==
"+Doc" || match_long_option(token,
"--doc", &attached_value)) {
184 std::string topic =
"ALL";
185 if (!attached_value.empty()) {
186 topic = attached_value;
187 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
190 normalized->push_back(
"+Doc");
191 normalized->push_back(topic);
195 if (token ==
"+Default" || match_long_option(token,
"--default", &attached_value)) {
196 if (token !=
"+Default" && is_long_option_argument_error(token,
"--default", attached_value, error)) {
199 normalized->push_back(
"+Default");
203 if (token ==
"+Run" || match_long_option(token,
"--run", &attached_value)) {
204 if (token !=
"+Run" && is_long_option_argument_error(token,
"--run", attached_value, error)) {
207 normalized->push_back(
"+Run");
211 if (token ==
"+I" || match_long_option(token,
"--interactive", &attached_value)) {
212 if (!attached_value.empty()) {
213 *error =
"Unexpected argument for --interactive in '" + token +
"'";
215 *error =
"Unsupported option " + token +
" (interactive mode is not available)";
220 if (match_long_option(token,
"--param-fallback", &attached_value)) {
221 std::string mode = attached_value;
224 *error =
"Missing argument after --param-fallback";
230 if (to_lower_ascii(trim_copy(mode)) !=
"legacy") {
231 *error =
"Unsupported value '" + mode +
"' for --param-fallback (expected legacy)";
235 compat->fallback_mode = ParserFallbackMode::Legacy;
239 if (token ==
"+F" || match_long_option(token,
"--file", &attached_value)) {
240 std::string
filename = attached_value;
243 *error =
"Missing filename after " + token;
248 normalized->push_back(
"+F");
253 if (token ==
"+Save" || match_long_option(token,
"--save", &attached_value)) {
254 std::string
filename = attached_value;
257 *error =
"Missing argument after " + token;
262 normalized->push_back(
"+Save");
267 normalized->push_back(token);
273 bool parse_option_argument(
int* index,
276 const std::string& token,
277 const std::string& attached_value,
278 const char* option_name,
282 if (!attached_value.empty()) {
283 *value = attached_value;
286 if (*index + 1 >= argc) {
287 *error =
"Missing argument after " + std::string(option_name);
290 *value = argv[++(*index)];
294 bool filename_has_suffix(
const std::string&
filename,
const char* suffix)
296 const std::string normalized_filename = to_lower_ascii(trim_copy(
filename));
297 const std::string normalized_suffix = to_lower_ascii(std::string(suffix == NULL ?
"" : suffix));
298 return normalized_filename.size() >= normalized_suffix.size() &&
299 normalized_filename.compare(normalized_filename.size() - normalized_suffix.size(),
300 normalized_suffix.size(),
301 normalized_suffix) == 0;
304 bool build_legacy_compare_input(
int argc,
char** argv, LegacyCompareInput* input, std::string* error)
306 input->available =
false;
307 input->warn_when_unavailable =
true;
308 input->runtime_args.clear();
309 input->unavailable_reason.clear();
311 if (argc <= 0 || argv == NULL || argv[0] == NULL) {
312 *error =
"Missing program name";
316 input->runtime_args.push_back(argv[0]);
317 bool saw_passthrough_token_before_file =
false;
319 for (
int i = 1; i < argc; ++i) {
320 const std::string token = argv[i];
325 std::string attached_value;
327 if (match_long_option(token,
"--param-fallback", &attached_value)) {
329 if (!parse_option_argument(&i, argc, argv, token, attached_value,
"--param-fallback", &ignored, error)) {
335 if (token ==
"+Save" || match_long_option(token,
"--save", &attached_value)) {
337 if (!parse_option_argument(&i, argc, argv, token, attached_value, token.c_str(), &ignored, error)) {
343 if (token ==
"+Help" || match_long_option(token,
"--help", &attached_value)) {
344 std::string topic =
"PrM";
345 if (!attached_value.empty()) {
346 topic = attached_value;
347 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
350 input->runtime_args.push_back(
"+Help");
351 input->runtime_args.push_back(topic);
352 input->available =
true;
356 if (token ==
"+Doc" || match_long_option(token,
"--doc", &attached_value)) {
357 std::string topic =
"ALL";
358 if (!attached_value.empty()) {
359 topic = attached_value;
360 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
363 input->runtime_args.push_back(
"+Doc");
364 input->runtime_args.push_back(topic);
365 input->available =
true;
369 if (token ==
"+Default" || match_long_option(token,
"--default", &attached_value)) {
370 if (token !=
"+Default" && is_long_option_argument_error(token,
"--default", attached_value, error)) {
373 input->runtime_args.push_back(
"+Default");
377 if (token ==
"+Run" || match_long_option(token,
"--run", &attached_value)) {
378 if (token !=
"+Run" && is_long_option_argument_error(token,
"--run", attached_value, error)) {
381 input->runtime_args.push_back(
"+Run");
385 if (token ==
"+F" || match_long_option(token,
"--file", &attached_value)) {
387 if (!parse_option_argument(&i, argc, argv, token, attached_value, token.c_str(), &
filename, error)) {
391 if (saw_passthrough_token_before_file) {
392 input->unavailable_reason =
393 "legacy compare is unavailable when direct parameter arguments precede a .par input file";
394 input->runtime_args.clear();
395 input->runtime_args.push_back(argv[0]);
399 if (!filename_has_suffix(
filename,
".par")) {
400 input->unavailable_reason =
401 "legacy compare is unavailable for non-.par input file '" +
filename +
"'";
402 input->runtime_args.clear();
403 input->runtime_args.push_back(argv[0]);
407 input->runtime_args.push_back(
"+F");
408 input->runtime_args.push_back(
filename);
412 input->runtime_args.push_back(token);
413 saw_passthrough_token_before_file =
true;
416 input->available = input->runtime_args.size() > 1;
417 if (!input->available && input->unavailable_reason.empty()) {
418 input->unavailable_reason =
"unable to reconstruct a legacy-compatible parameter input";
423 std::string trim_copy(
const std::string& value)
425 std::string::size_type first = 0;
426 while (first < value.size() && std::isspace(
static_cast<unsigned char>(value[first]))) {
430 std::string::size_type last = value.size();
431 while (last > first && std::isspace(
static_cast<unsigned char>(value[last - 1]))) {
435 return value.substr(first, last - first);
438 std::string to_lower_ascii(std::string value)
440 for (std::string::size_type i = 0; i < value.size(); ++i) {
441 value[i] =
static_cast<char>(std::tolower(
static_cast<unsigned char>(value[i])));
446 ParserCompareMode parser_compare_mode()
448 const char* raw = std::getenv(
"OPENCARP_PARAM_COMPARE");
450 return ParserCompareMode::Strict;
453 const std::string normalized = to_lower_ascii(trim_copy(raw));
454 if (normalized.empty() || normalized ==
"1" || normalized ==
"on" || normalized ==
"true" ||
455 normalized ==
"yes" || normalized ==
"strict" || normalized ==
"fail" || normalized ==
"error") {
456 return ParserCompareMode::Strict;
458 if (normalized ==
"warn") {
459 return ParserCompareMode::Warn;
461 if (normalized ==
"0" || normalized ==
"off" || normalized ==
"false" || normalized ==
"no") {
462 return ParserCompareMode::Off;
465 return ParserCompareMode::Strict;
468 ParserFallbackMode parser_fallback_mode()
470 const char* raw = std::getenv(
"OPENCARP_PARAM_FALLBACK");
472 return ParserFallbackMode::Off;
475 const std::string normalized = to_lower_ascii(trim_copy(raw));
476 if (normalized.empty() || normalized ==
"0" || normalized ==
"off" || normalized ==
"false" ||
477 normalized ==
"no") {
478 return ParserFallbackMode::Off;
480 if (normalized ==
"legacy") {
481 return ParserFallbackMode::Legacy;
484 return ParserFallbackMode::Off;
487 void populate_arg_pointers(
const std::vector<std::string>& values, std::vector<char*>* argv)
489 argv->assign(values.size(), NULL);
490 for (std::size_t i = 0; i < values.size(); ++i) {
491 (*argv)[i] =
const_cast<char*
>(values[i].c_str());
495 void print_lines(FILE* stream,
const char* label,
const std::vector<std::string>& lines)
497 for (std::size_t i = 0; i < lines.size(); ++i) {
498 fprintf(stream,
"%s%s\n", label, lines[i].c_str());
502 void clear_active_citation_suggestions()
504 active_citation_suggestions.clear();
507 void store_active_citation_suggestions(
const std::vector<paramschema::CitationSuggestion>& suggestions)
509 active_citation_suggestions = suggestions;
512 void print_active_citation_suggestions()
514 if (active_citation_suggestions.empty() ||
get_rank() != 0) {
518 const std::vector<std::string> lines =
519 paramschema::format_citation_suggestions(active_citation_suggestions);
524 std::fprintf(stdout,
"\nIf you publish studies based on this simulation, the following references are likely relevant:\n");
525 for (std::size_t i = 0; i < lines.size(); ++i) {
526 std::fprintf(stdout,
" %s\n", lines[i].c_str());
528 std::fprintf(stdout,
" You can use https://citation.doi.org to format references in your preferred format or https://www.doi2bib.org to turn the DOIs into BibTeX format.\n");
529 std::fprintf(stdout,
"\nWe'd be happy to also see your experiment published to boost its impact and foster reproducibility.\nSee https://opencarp.org/community/upload-experiment for details.\n\n");
534 void print_parser_runtime_state(
const std::vector<std::string>&
runtime_args)
536 const auto& schema = paramschema::openCARP_schema();
537 const std::string program_name =
540 std::string rendered;
541 std::vector<std::string> errors;
542 if (!paramschema::render_save_text(schema, program_name, &rendered, &errors)) {
543 print_lines(stderr,
"parameter parser warning: ", errors);
547 fputs(rendered.c_str(), stderr);
550 paramschema::ExecutionResult execute_parser_runtime_args(
const std::vector<std::string>&
runtime_args,
551 const bool allow_save)
553 std::vector<char*> argv;
556 const auto& schema = paramschema::openCARP_schema();
558 paramschema::ExecutionOptions options;
559 options.allow_save = allow_save;
560 return paramschema::execute_legacy_cli(schema,
static_cast<int>(argv.size()), argv.data(), options);
563 bool apply_parser_runtime_args(
const std::vector<std::string>&
runtime_args,
564 const bool allow_save,
565 paramschema::ExecutionResult* executed_out)
567 clear_active_citation_suggestions();
568 const paramschema::ExecutionResult executed = execute_parser_runtime_args(
runtime_args, allow_save);
569 if (executed_out != NULL) {
570 *executed_out = executed;
572 print_lines(stderr,
"parameter parser warning: ", executed.warnings);
573 if (!executed.rendered_output.empty()) {
574 fputs(executed.rendered_output.c_str(), stdout);
576 if (!executed.errors.empty() || executed.status == paramschema::ExecutionStatus::Fatal) {
577 print_lines(stderr,
"parameter parser error: ", executed.errors);
581 if (param_globals::output_setup) {
585 if (executed.status == paramschema::ExecutionStatus::Help) {
589 store_active_citation_suggestions(executed.citations);
593 std::string parent_directory(
const std::string& path)
595 const std::string::size_type slash = path.rfind(
'/');
596 if (slash == std::string::npos) {
602 return path.substr(0, slash);
605 std::string join_path(
const std::string& left,
const std::string& right)
607 if (left.empty() || left ==
".") {
610 if (!left.empty() && left[left.size() - 1] ==
'/') {
613 return left +
"/" + right;
616 bool find_executable_on_path(
const std::string& name, std::string* resolved_path)
618 if (name.empty() || name.find(
'/') != std::string::npos) {
622 const char* path_env = std::getenv(
"PATH");
623 if (path_env == NULL || path_env[0] ==
'\0') {
627 const std::string path_list = path_env;
628 std::string::size_type start = 0;
629 while (start <= path_list.size()) {
630 std::string::size_type end = path_list.find(
':', start);
631 if (end == std::string::npos) {
632 end = path_list.size();
635 const std::string directory = path_list.substr(start, end - start);
636 const std::string candidate = join_path(directory.empty() ?
"." : directory, name);
637 if (access(candidate.c_str(), X_OK) == 0) {
638 *resolved_path = candidate;
642 if (end == path_list.size()) {
651 bool resolve_legacy_snapshot_helper(
const std::string& program_path, std::string* helper_path)
653 std::vector<std::string> resolved_program_paths;
654 if (!program_path.empty()) {
655 resolved_program_paths.push_back(program_path);
658 if (program_path.find(
'/') == std::string::npos) {
659 std::string resolved_program_path;
660 if (find_executable_on_path(program_path, &resolved_program_path)) {
661 resolved_program_paths.push_back(resolved_program_path);
665 for (std::size_t i = 0; i < resolved_program_paths.size(); ++i) {
666 const std::string program_dir = parent_directory(resolved_program_paths[i]);
667 const std::string parent_dir = parent_directory(program_dir);
669 const std::vector<std::string> candidates = {
670 join_path(program_dir,
"param-parser-legacy-snapshot"),
671 join_path(join_path(parent_dir,
"simulator"),
"param-parser-legacy-snapshot"),
674 for (std::size_t j = 0; j < candidates.size(); ++j) {
675 if (access(candidates[j].c_str(), X_OK) == 0) {
676 *helper_path = candidates[j];
682 return find_executable_on_path(
"param-parser-legacy-snapshot", helper_path);
685 bool create_temp_output_path(
const char* suffix, std::string* path)
688 if (suffix != NULL && suffix[0] !=
'\0') {
689 std::snprintf(temp_path,
sizeof temp_path,
"/tmp/opencarp-parser-compare-XXXXXX%s", suffix);
691 std::snprintf(temp_path,
sizeof temp_path,
"/tmp/opencarp-parser-compare-XXXXXX");
694 const int fd = suffix != NULL && suffix[0] !=
'\0' ? mkstemps(temp_path,
static_cast<int>(std::strlen(suffix))) :
704 bool capture_current_parser_snapshot(paramschema::SnapshotResult* snapshot)
706 *snapshot = paramschema::snapshot_schema_state(paramschema::openCARP_schema());
707 print_lines(stderr,
"parameter compare warning: ", snapshot->warnings);
708 if (!snapshot->errors.empty()) {
709 print_lines(stderr,
"parameter compare error: ", snapshot->errors);
715 void maybe_force_test_mismatch(paramschema::SnapshotResult* snapshot)
717 const char* raw = std::getenv(
"OPENCARP_PARAM_TEST_FORCE_MISMATCH");
722 const std::string normalized = to_lower_ascii(trim_copy(raw));
723 if (normalized.empty() || normalized ==
"0" || normalized ==
"off" || normalized ==
"false" ||
724 normalized ==
"no") {
728 if (!snapshot->entries.empty()) {
729 snapshot->entries[0].value +=
"__forced_parser_compare_mismatch__";
733 paramschema::SnapshotEntry entry;
734 entry.path =
"buildinfo";
735 entry.value =
"__forced_parser_compare_mismatch__";
736 snapshot->entries.push_back(entry);
739 LegacySnapshotHelperRunResult run_legacy_snapshot_helper(
const std::string& helper_path,
741 const std::string& output_path)
743 LegacySnapshotHelperRunResult result;
744 std::vector<std::string> child_values;
746 child_values.push_back(helper_path);
747 child_values.push_back(
"--snapshot-out");
748 child_values.push_back(output_path);
749 child_values.push_back(
"--");
752 std::vector<char*> child_argv;
753 child_argv.reserve(child_values.size() + 1);
754 for (std::size_t i = 0; i < child_values.size(); ++i) {
755 child_argv.push_back(
const_cast<char*
>(child_values[i].c_str()));
757 child_argv.push_back(NULL);
759 const pid_t pid = fork();
766 execv(helper_path.c_str(), child_argv.data());
767 std::perror(
"execv");
772 if (waitpid(pid, &status, 0) < 0) {
773 std::perror(
"waitpid");
777 if (WIFSIGNALED(status)) {
778 result.signaled =
true;
779 result.signal = WTERMSIG(status);
783 if (WIFEXITED(status)) {
784 result.exited =
true;
785 result.exit_status = WEXITSTATUS(status);
791 bool legacy_snapshot_helper_rejected_input(
const LegacySnapshotHelperRunResult& result)
793 return result.exited && (result.exit_status == 3 || result.exit_status == 4);
796 bool legacy_fallback_requested(
const RuntimeCompatOptions& compat)
798 return compat.fallback_mode == ParserFallbackMode::Legacy ||
799 parser_fallback_mode() == ParserFallbackMode::Legacy;
802 void print_legacy_fallback_workaround()
805 "parameter compare error: rerun with OPENCARP_PARAM_FALLBACK=legacy to continue with the legacy parameter state\n");
807 "parameter compare error: or add --param-fallback=legacy to the openCARP command line\n");
810 bool restore_legacy_snapshot_state(
const paramschema::SnapshotResult& legacy_snapshot)
812 const paramschema::SnapshotRestoreResult restored =
813 paramschema::restore_snapshot_state(paramschema::openCARP_schema(), legacy_snapshot);
814 print_lines(stderr,
"parameter compare warning: ", restored.warnings);
815 if (!restored.errors.empty()) {
816 print_lines(stderr,
"parameter compare error: ", restored.errors);
822 bool run_parser_legacy_compare(
const std::vector<std::string>&
runtime_args,
823 const LegacyCompareInput& legacy_input,
824 const RuntimeCompatOptions& compat)
826 const ParserCompareMode mode = parser_compare_mode();
827 if (mode == ParserCompareMode::Off) {
830 const bool fallback_to_legacy = legacy_fallback_requested(compat);
833 std::fprintf(stderr,
"parameter compare error: unable to reconstruct simulator argv\n");
834 if (fallback_to_legacy) {
835 print_legacy_fallback_workaround();
837 return mode != ParserCompareMode::Strict;
840 paramschema::SnapshotResult parser_snapshot;
841 if (!capture_current_parser_snapshot(&parser_snapshot)) {
842 std::fprintf(stderr,
"parameter compare error: unable to snapshot the parser runtime state\n");
843 if (fallback_to_legacy) {
844 print_legacy_fallback_workaround();
846 return mode != ParserCompareMode::Strict;
848 maybe_force_test_mismatch(&parser_snapshot);
850 std::string helper_path;
851 if (!resolve_legacy_snapshot_helper(
runtime_args[0], &helper_path)) {
852 std::fprintf(stderr,
"parameter compare error: unable to locate param-parser-legacy-snapshot\n");
853 if (fallback_to_legacy) {
854 print_legacy_fallback_workaround();
857 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
859 return mode != ParserCompareMode::Strict;
862 if (!legacy_input.available) {
863 if (legacy_input.warn_when_unavailable && !legacy_input.unavailable_reason.empty()) {
864 std::fprintf(stderr,
"parameter compare warning: %s\n", legacy_input.unavailable_reason.c_str());
866 if (fallback_to_legacy) {
868 "parameter compare warning: legacy fallback is unavailable because no legacy baseline exists for this input\n");
873 std::string snapshot_path;
874 if (!create_temp_output_path(
"", &snapshot_path)) {
875 std::fprintf(stderr,
"parameter compare error: unable to create temporary snapshot file\n");
876 if (fallback_to_legacy) {
877 print_legacy_fallback_workaround();
880 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
882 return mode != ParserCompareMode::Strict;
885 const LegacySnapshotHelperRunResult helper_result =
886 run_legacy_snapshot_helper(helper_path, legacy_input.runtime_args, snapshot_path);
887 const bool helper_ok = helper_result.exited && helper_result.exit_status == 0;
888 paramschema::SnapshotResult legacy_snapshot;
889 std::string read_error;
890 const bool loaded = helper_ok &&
891 paramschema::snapshotio::read_snapshot_file(snapshot_path, &legacy_snapshot, &read_error);
892 unlink(snapshot_path.c_str());
895 if (legacy_snapshot_helper_rejected_input(helper_result)) {
897 "parameter compare error: legacy param() rejected the normalized input while the parser runtime accepted it\n");
899 "parameter compare error: this indicates a parser/legacy validation mismatch, not snapshot helper infrastructure\n");
901 "parameter compare error: please open an issue and include the triggering command line and parameter files\n");
903 "https://git.opencarp.org/openCARP/openCARP/-/issues/new?type=ISSUE&initialCreationContext=list-route\n");
904 if (fallback_to_legacy) {
906 "parameter compare error: legacy fallback is unavailable because legacy produced no valid parameter state\n");
908 }
else if (!read_error.empty()) {
909 std::fprintf(stderr,
"parameter compare error: %s\n", read_error.c_str());
910 }
else if (helper_result.signaled) {
911 std::fprintf(stderr,
"parameter compare error: legacy snapshot helper terminated with signal %d\n",
912 helper_result.signal);
913 }
else if (helper_result.exited) {
914 std::fprintf(stderr,
"parameter compare error: legacy snapshot helper exited with status %d\n",
915 helper_result.exit_status);
917 std::fprintf(stderr,
"parameter compare error: unable to capture the legacy parameter state\n");
919 if (fallback_to_legacy && !legacy_snapshot_helper_rejected_input(helper_result)) {
920 print_legacy_fallback_workaround();
921 }
else if (!legacy_snapshot_helper_rejected_input(helper_result)) {
923 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
925 return mode != ParserCompareMode::Strict;
928 const paramschema::SnapshotComparisonResult comparison =
929 paramschema::compare_snapshot_results(paramschema::openCARP_schema(), parser_snapshot, legacy_snapshot);
930 if (!comparison.errors.empty() || !comparison.mismatches.empty()) {
931 print_lines(stderr,
"", paramschema::format_snapshot_comparison_report(comparison,
"parser compare"));
933 "parameter compare error: the parser runtime and legacy param() produced different parameter states\n");
935 "parameter compare error: please open an issue and include the triggering command line and parameter files\n");
937 "https://git.opencarp.org/openCARP/openCARP/-/issues/new?type=ISSUE&initialCreationContext=list-route\n");
939 if (fallback_to_legacy) {
940 if (!restore_legacy_snapshot_state(legacy_snapshot)) {
941 print_legacy_fallback_workaround();
944 clear_active_citation_suggestions();
945 std::fprintf(stderr,
"parameter compare warning: continuing with the legacy parameter state\n");
949 print_legacy_fallback_workaround();
950 return mode != ParserCompareMode::Strict;
958 static char input_dir[1024],
965 void localize_gridout_nodes(
sf_mesh& mesh,
988 alg_map[nbr[local_node]] = local_node;
992 for(
int pid = 0; pid < mpi_size; pid++) {
993 if(mpi_rank == pid) {
994 recv_nodes = selected_nodes;
995 buffsize = recv_nodes.
size();
998 MPI_Bcast(&buffsize,
sizeof(
size_t), MPI_BYTE, pid, mesh.comm);
999 recv_nodes.
resize(buffsize);
1000 MPI_Bcast(recv_nodes.
data(), buffsize *
sizeof(
mesh_int_t), MPI_BYTE, pid, mesh.comm);
1003 auto it = alg_map.
find(node);
1004 if(it != alg_map.
end())
1011 const mesh_int_t stop = layout[mpi_rank + 1];
1014 size_t buffsize = 0;
1015 for(
int pid = 0; pid < mpi_size; pid++) {
1016 if(mpi_rank == pid) {
1017 recv_nodes = selected_nodes;
1018 buffsize = recv_nodes.
size();
1021 MPI_Bcast(&buffsize,
sizeof(
size_t), MPI_BYTE, pid, mesh.comm);
1022 recv_nodes.
resize(buffsize);
1023 MPI_Bcast(recv_nodes.
data(), buffsize *
sizeof(
mesh_int_t), MPI_BYTE, pid, mesh.comm);
1026 if(node >= start && node < stop)
1036 bool mesh_has_gridout_tags(
const sf_mesh& mesh,
1039 bool local_has_tag =
false;
1040 for(
size_t eidx = 0; eidx < mesh.l_numelem; eidx++) {
1041 if(output_tags.
count(mesh.tag[eidx]) != 0) {
1042 local_has_tag =
true;
1047 int local = local_has_tag ? 1 : 0;
1049 MPI_Allreduce(&local, &global, 1, MPI_INT, MPI_MAX, mesh.comm);
1053 void extract_gridout_tag_mesh(
const sf_mesh& mesh,
1058 for(
size_t eidx = 0; eidx < mesh.l_numelem; eidx++)
1059 keep_elem[eidx] = output_tags.
count(mesh.tag[eidx]) != 0;
1061 out_mesh.name = mesh.name;
1063 if(out_mesh.g_numelem == 0) {
1064 log_msg(0, 5,
ECHO,
"gridout_tags selected no elements in \"%s\".", mesh.name.c_str());
1072 std::list<sf_mesh*> meshlist;
1073 meshlist.push_back(&out_mesh);
1077 numbering(out_mesh);
1078 out_mesh.generate_par_layout();
1084 void extract_gridout_surface_mesh(
const sf_mesh& mesh,
sf_mesh& out_mesh)
1087 out_mesh.name = mesh.name;
1095 ref_eidx.
resize(out_mesh.l_numelem);
1097 long int num_local = out_mesh.l_numelem, offset = 0;
1098 MPI_Exscan(&num_local, &offset, 1, MPI_LONG, MPI_SUM, out_mesh.comm);
1100 for(
size_t eidx = 0; eidx < out_mesh.l_numelem; eidx++)
1101 ref_eidx[eidx] = offset + eidx;
1105 std::list<sf_mesh*> meshlist;
1106 meshlist.push_back(&out_mesh);
1110 numbering(out_mesh);
1111 out_mesh.generate_par_layout();
1117 void write_gridout_surface(
const sf_mesh& mesh,
const std::string& basename)
1120 extract_gridout_surface_mesh(mesh, surf);
1126 for(
size_t i = 0; i < surf_global.con.size(); i++)
1127 surf_global.con[i] = nbr[surf_global.con[i]];
1136 LegacyCompareInput legacy_input;
1137 std::vector<std::string> normalized_args;
1138 RuntimeCompatOptions compat;
1139 std::string normalize_error;
1140 if (!build_legacy_compare_input(argc, argv, &legacy_input, &normalize_error) ||
1141 !normalize_runtime_args(argc, argv, &normalized_args, &compat, &normalize_error)) {
1142 fprintf(stderr,
"\n*** %s\n\n", normalize_error.c_str());
1146 paramschema::ExecutionResult executed;
1147 if (!apply_parser_runtime_args(normalized_args,
true, &executed)) {
1151 if (legacy_input.available) {
1152 for (std::size_t i = 0; i < executed.validation.assignments.size(); ++i) {
1153 if (!executed.validation.assignments[i].synthesized) {
1156 legacy_input.available =
false;
1157 legacy_input.runtime_args.clear();
1158 legacy_input.runtime_args.push_back(normalized_args[0]);
1159 legacy_input.unavailable_reason =
1160 "legacy compare is unavailable because the parser inferred optional controller counts from the original input";
1165 if (legacy_input.available && !executed.validation.legacy_compare_incompatible_paths.empty()) {
1166 legacy_input.available =
false;
1167 legacy_input.warn_when_unavailable =
false;
1168 legacy_input.runtime_args.clear();
1169 legacy_input.runtime_args.push_back(normalized_args[0]);
1170 legacy_input.unavailable_reason =
1171 "legacy compare is unavailable because the original input uses aggregate ID syntax without a legacy baseline for " +
1172 join_paths_for_message(executed.validation.legacy_compare_incompatible_paths);
1175 if (!run_parser_legacy_compare(normalized_args, legacy_input, compat)) {
1191 log_msg(NULL, 5,
ECHO,
"The EMI model was not compiled for this binary.\n");
1205 log_msg(0,0,0,
"\n *** Initializing physics ***\n");
1209 for (
int ii = 0; ii < param_globals::num_external_imp; ii++) {
1211 assert(loading_succeeded);
1214 if(param_globals::num_external_imp)
1215 log_msg(NULL, 4,
ECHO,
"Loading of external LIMPET modules not enabled.\n"
1216 "Recompile with DLOPEN set.\n" );
1222 log_msg(NULL, 0, 0,
"Initializing %s ..", p->
name);
1229 log_msg(0,0,0,
"\n *** Destroying physics ***\n");
1257 for (
int i=0; i<ns; i++ ) {
1265 log_msg( NULL, 1, 0,
"Extracellular stimulus %d ignored for monodomain", i );
1268 log_msg( NULL, 1, 0,
"Intracellular stimulus %d converted to transmembrane", i );
1303 Stimulus* s = param_globals::stimulus;
1305 for(
int i=0; i < param_globals::num_stim; i++) {
1313 log_msg( NULL, 4, 0,
"Elliptic system is singular!\n"
1314 "Use an explicit ground:voltage (stimulus[X].stimtype=3)\n"
1315 "Do not trust the elliptic solution of this simulation run!\n");
1320 const char* real_precision = OPENCARP_REAL_BITS == 32 ?
"single" :
"double";
1321 printf(
"\n""*** GIT tag: %s\n", GIT_COMMIT_TAG);
1322 printf(
"*** GIT hash: %s\n", GIT_COMMIT_HASH);
1323 printf(
"*** GIT repo: %s\n", GIT_PATH);
1324 printf(
"*** local index bits: %d\n", OPENCARP_LOCAL_INDEX_BITS);
1325 printf(
"*** global index bits: %d\n", OPENCARP_GLOBAL_INDEX_BITS);
1326 printf(
"*** real precision: %s (%d-bit)\n", real_precision, OPENCARP_REAL_BITS);
1327 printf(
"*** dependency commits: %s\n\n", SUBREPO_COMMITS);
1333 param_globals::dt /= 1000.;
1337 if(param_globals::mass_lumping == 0 && param_globals::parab_solve==0) {
1338 log_msg(0,5,0,
"parab_solve = 0 (explicit) requires mass_lumping = 1. "
1339 "Either enable mass lumping or choose an implicit parab_solve method.");
1344 if(!param_globals::extracell_monodomain_stim)
1353 if(param_globals::t_sentinel > 0 && param_globals::sentinel_ID < 0 ) {
1354 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");
1357 if(param_globals::num_external_imp > 0 ) {
1358 for(
int ext_imp_i = 0; ext_imp_i < param_globals::num_external_imp; ext_imp_i++) {
1359 if(param_globals::external_imp[ext_imp_i][0] !=
'/') {
1360 log_msg(0,5,0,
"external_imp[%d] error: absolute paths must be used for .so file loading (\'%s\')",
1361 ext_imp_i, param_globals::external_imp[ext_imp_i]);
1367 if(param_globals::experiment ==
EXP_LAPLACE && param_globals::bidomain != 1) {
1368 log_msg(0,4,0,
"Warning: Laplace experiment mode requires bidomain = 1. Setting bidomain = 1.");
1369 param_globals::bidomain = 1;
1372 if(param_globals::num_phys_regions == 0) {
1373 log_msg(0,4,0,
"Warning: No physics region defined! Please set phys_region parameters to correctly define physics.");
1376 log_msg(0,4,0,
"Intra-elec and Extra-elec domains will be derived from fibers.\n");
1377 param_globals::num_phys_regions = param_globals::bidomain ? 2 : 1;
1378 param_globals::phys_region = (p_region*) calloc(param_globals::num_phys_regions,
sizeof(p_region));
1380 param_globals::phys_region[0].name = strdup(
"Autogenerated intracellular Electrics");
1381 param_globals::phys_region[0].num_IDs = 0;
1383 if(param_globals::bidomain) {
1385 param_globals::phys_region[1].name = strdup(
"Autogenerated extracellular Electrics");
1386 param_globals::phys_region[1].num_IDs = 0;
1389 log_msg(0,4,0,
"Laplace domain will be derived from fibers.\n");
1390 param_globals::num_phys_regions = 1;
1391 param_globals::phys_region = (p_region*) calloc(param_globals::num_phys_regions,
sizeof(p_region));
1393 param_globals::phys_region[0].name = strdup(
"Autogenerated Laplace");
1394 param_globals::phys_region[0].num_IDs = 0;
1399 log_msg(0,4,0,
"Warning: Laplace experiment mode requires a laplace physics regions defined.");
1403 log_msg(0,4,0,
"Converting the defined extracellular-electrics-region to laplace-region.");
1406 log_msg(0,4,0,
"Converting the defined intracellular-electrics-region to laplace-region.");
1409 param_globals::num_phys_regions += 1;
1410 param_globals::phys_region = (p_region*) realloc(param_globals::phys_region, param_globals::num_phys_regions *
sizeof(p_region));
1412 param_globals::phys_region[param_globals::num_phys_regions - 1].ptype =
PHYSREG_LAPLACE;
1413 param_globals::phys_region[param_globals::num_phys_regions - 1].name = strdup(
"Autogenerated Laplace");
1414 param_globals::phys_region[param_globals::num_phys_regions - 1].num_IDs = 0;
1418 #ifndef WITH_PARMETIS
1419 if(param_globals::pstrat == 1) {
1420 log_msg(0,3,0,
"openCARP was built without Parmetis support. Swithing to KDtree.");
1421 param_globals::pstrat = 2;
1426 bool legacy_stim_set =
false, new_stim_set =
false;
1428 for(
int i=0; i<param_globals::num_stim; i++) {
1429 Stimulus & legacy_stim = param_globals::stimulus[i];
1430 Stim & new_stim = param_globals::stim[i];
1432 if(legacy_stim.stimtype || legacy_stim.strength)
1433 legacy_stim_set =
true;
1435 if(new_stim.crct.type || new_stim.pulse.strength)
1436 new_stim_set =
true;
1439 if(legacy_stim_set || new_stim_set) {
1440 if(legacy_stim_set && new_stim_set) {
1441 log_msg(0,4,0,
"Warning: Legacy stimuli and default stimuli are defined. Only default stimuli will be used!");
1443 else if (legacy_stim_set) {
1444 log_msg(0,1,0,
"Warning: Legacy stimuli defined. Please consider switching to stimulus definition \"stim[]\"!");
1449 log_msg(0,4,0,
"Warning: No potential or current stimuli found!");
1455 int flg = 0, err = 0, rank =
get_rank();
1457 char *ptr = getcwd(current_dir, 1024);
1458 if (ptr == NULL) err++;
1459 ptr = getcwd(input_dir, 1024);
1460 if (ptr == NULL) err++;
1466 if (strcmp(sim_ID,
"OUTPUT_DIR")) {
1467 if (mkdir(sim_ID, 0775)) {
1468 if (errno == EEXIST ) {
1469 log_msg(NULL, 2, 0,
"Output directory exists: %s\n", sim_ID);
1471 log_msg(NULL, 5, 0,
"Unable to make output directory\n");
1475 }
else if (mkdir(sim_ID, 0775) && errno != EEXIST) {
1476 log_msg(NULL, 5, 0,
"Unable to make output directory\n");
1484 err += chdir(sim_ID);
1485 ptr = getcwd(output_dir, 1024);
1486 if (ptr == NULL) err++;
1491 err += chdir(output_dir);
1494 if (rank == 0 && ((param_globals::experiment==
EXP_POSTPROCESS) || (param_globals::post_processing_opts &
LEADFIELD))) {
1496 if (strcmp(param_globals::ppID,
"POSTPROC_DIR")) {
1497 if (mkdir(param_globals::ppID, 0775)) {
1498 if (errno == EEXIST ) {
1499 log_msg(NULL, 2,
ECHO,
"Postprocessing directory exists: %s\n\n", param_globals::ppID);
1501 log_msg(NULL, 5,
ECHO,
"Unable to make postprocessing directory\n\n");
1505 }
else if (mkdir(param_globals::ppID, 0775) && errno != EEXIST) {
1506 log_msg(NULL, 5,
ECHO,
"Unable to make postprocessing directory\n\n");
1514 err += chdir(param_globals::ppID);
1515 ptr = getcwd(postproc_dir, 1024);
1516 if (ptr == NULL) err++;
1517 err = chdir(output_dir);
1526 bool io_node =
false;
1529 if (param_globals::num_io_nodes > 0) {
1532 log_msg(NULL, 5, 0,
"You cannot run with async IO on only one core.\n");
1536 if (2 * param_globals::num_io_nodes >= psize) {
1537 log_msg(NULL, 5, 0,
"The number of IO cores be less " "than the number of compute cores.");
1541 if (param_globals::num_PS_nodes && param_globals::num_io_nodes > param_globals::num_PS_nodes) {
1543 "The number of IO cores (%d) should not "
1544 "exceed the number of PS compute cores (%d).\n",
1545 param_globals::num_io_nodes, param_globals::num_PS_nodes);
1550 io_node = prank < param_globals::num_io_nodes;
1553 MPI_Comm_split(PETSC_COMM_WORLD, io_node,
get_rank(), &comm);
1554 MPI_Comm_set_name(comm, io_node ?
"IO" :
"compute");
1556 PETSC_COMM_WORLD = comm;
1560 MPI_Intercomm_create(comm, 0, MPI_COMM_WORLD, io_node ? param_globals::num_io_nodes : 0,
1564 log_msg(NULL, 4, 0,
"Global node %d, Comm rank %d != Intercomm rank %d\n",
1568 MPI_Comm_set_name(PETSC_COMM_WORLD,
"compute");
1572 if((io_node || !param_globals::num_io_nodes) && !prank)
1579 getcwd(current_dir, 1024);
1586 if (dest==
OUTPUT) err = chdir(output_dir);
1587 else if (dest==
POSTPROC) err = chdir(postproc_dir);
1588 else if (dest==
CURDIR) err = chdir(current_dir);
1589 else err = chdir(input_dir);
1597 double start_time = 0.0;
1600 double end_time = param_globals::tend;
1614 if(param_globals::num_tsav) {
1615 std::vector<double> trig(param_globals::num_tsav);
1616 for(
size_t i=0; i<trig.size(); i++) trig[i] = param_globals::tsav[i];
1621 if(param_globals::chkpt_intv)
1623 param_globals::chkpt_intv, 0,
iotm_chkpt_intv,
"interval checkpointing");
1625 if(param_globals::num_trace)
1629 #ifdef WITH_POWERCAPPING
1630 void basic_powercapping_setup()
1632 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);
1635 void basic_powercapping_cleanup()
1639 delete user_globals::pc_manager; user_globals::pc_manager =
nullptr;
1646 const short padding = 4;
1651 if(col_width[0] <
int(strlen(buff)+padding))
1652 col_width[0] = strlen(buff)+padding;
1655 if(col_width[1] <
int(strlen(buff)+padding))
1656 col_width[1] = strlen(buff)+padding;
1659 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1661 int timer_id = used_timer_ids[tid];
1671 snprintf(buff,
sizeof buff,
"%.3lf", val);
1672 if(col_width[col] <
int(strlen(buff)+padding))
1673 col_width[col] = strlen(buff)+padding;
1690 const char* smpl_endl =
"\n";
1698 log_msg(0,5,0,
"Protocol file %s could not be opened for writing!\n", fname);
1711 std::vector<std::string> col_labels = {
"time",
"tick"};
1712 std::vector<std::string> col_short_labels = {
"A",
"B"};
1713 std::vector<std::string> col_unit_labels = {
"ms",
"--" };
1714 std::vector<int> col_width = {4, 4};
1716 char c_label = {
'C'};
1717 std::string label = {
""};
1718 std::string unit = {
""};
1722 std::vector<int> used_timer_ids;
1723 std::vector<int> used_stim_ids;
1733 used_stim_ids.push_back(sidx);
1743 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1745 int timer_id = used_timer_ids[tid];
1748 int llen = strlen(t->
name);
1749 mx_llen = llen > mx_llen ? llen : mx_llen;
1752 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1754 int timer_id = used_timer_ids[tid];
1757 col_labels.push_back(t->
name);
1759 col_short_labels.push_back(label);
1764 if(unit.empty()) unit =
"--";
1765 col_unit_labels.push_back(unit);
1766 col_width.push_back(4);
1774 fh <<
"# Protocol header\n#\n" <<
"# Legend:\n";
1775 for(
size_t i = 0; i<col_short_labels.size(); i++)
1777 fh <<
"#" << std::setw(2) << col_short_labels[i] <<
" = " << std::setw(mx_llen) << col_labels[i];
1778 fh <<
" [" << std::setw(10) << col_unit_labels[i] <<
"]";
1780 if(i >= 2 && used_stim_ids[i-2] > -1) {
1785 fh <<
" ground stim" << smpl_endl;
1787 fh <<
" applied: " << std::to_string(s.
pulse.
strength) << smpl_endl;
1798 for(
size_t i = 0; i<col_short_labels.size(); i++)
1799 fh << std::setw(col_width[i] - 3) << col_short_labels[i].c_str() << std::setw(3) <<
" ";
1802 fh << smpl_endl <<
"#";
1803 for(
size_t i = 0; i<col_unit_labels.size(); i++)
1804 fh <<
"[" << std::setw(col_width[i]-2) << col_unit_labels[i].c_str() <<
"]";
1807 fh << smpl_endl << std::fixed;
1815 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1817 int timer_id = used_timer_ids[tid];
1823 fh << std::setw(col_width[col]) << On;
1831 fh << std::setw(col_width[col]) << std::setprecision(3) << val;
1853 const char* h1_prog =
"PROG\t----- \t----\t-------\t-------|";
1854 const char* h2_prog =
"time\t%%comp\ttime\t ctime \t ETA |";
1855 const char* h1_wc =
"\tELAPS |";
1856 const char* h2_wc =
"\twc |";
1861 log_msg(NULL, 0, 0,
"%s", h1_prog );
1869 int req_hours = ((int)(time)) / 3600;
1870 int req_min = (((int)(time)) % 3600) / 60;
1871 int req_sec = (((int)(time)) % 3600) % 60;
1873 snprintf(str, str_size,
"%d:%02d:%02d", req_hours, req_min, req_sec);
1886 char elapsed_time_str[256];
1887 char req_time_str[256];
1891 log_msg( NULL, 0,
NONL,
"%.2f\t%.1f\t%.1f\t%s\t%s",
1909 if(!have_timedependent_phys) {
1910 log_msg(0,0,0,
"\n no time-dependent physics region registered, skipping simulate loop..\n");
1914 log_msg(0,0,0,
"\n *** Launching simulation ***\n");
1918 if(param_globals::dump_protocol)
1925 #ifdef WITH_POWERCAPPING
1926 basic_powercapping_setup();
1927 powercapping_manager *pc = user_globals::pc_manager;
1938 #ifdef WITH_POWERCAPPING
1939 std::vector<int> flops_per_rank;
1944 #ifdef WITH_POWERCAPPING
1945 pc->iteration_begin(flops_per_rank);
1952 it.second->output_step();
1955 #ifdef WITH_POWERCAPPING
1956 pc->sample(
"output step");
1964 #ifdef WITH_POWERCAPPING
1965 pc->sample(p->
name);
1969 #ifdef WITH_POWERCAPPING
1970 pc->iteration_end(flops_per_rank);
1977 log_msg(0,0,0,
"\n\nTimings of individual physics:");
1978 log_msg(0,0,0,
"------------------------------\n");
1985 #ifdef WITH_POWERCAPPING
1987 basic_powercapping_cleanup();
1993 if(param_globals::post_processing_opts &
RECOVER_PHIE) {
1994 log_msg(NULL,0,
ECHO,
"\nPOSTPROCESSOR: Recovering Phie ...");
1995 log_msg(NULL,0,
ECHO,
"----------------------------------\n");
2001 log_msg(NULL,0,
ECHO,
"\n-----------------------------------------");
2002 log_msg(NULL,0,
ECHO,
"POSTPROCESSOR: Successfully recoverd Phie.\n");
2006 if(param_globals::post_processing_opts &
LEADFIELD) {
2007 #ifdef WITH_LEADFIELD
2008 log_msg(NULL,0,
ECHO,
"\nPOSTPROCESSOR: Computing leadfields ...");
2009 log_msg(NULL,0,
ECHO,
"-------------------------------------\n");
2013 log_msg(NULL, 5, 0,
"Error: Leadfield requires active EP physics. Aborting.");
2017 int err = leadfield.
run(*elec);
2020 log_msg(NULL,0,
ECHO,
"\n------------------------------------------");
2021 log_msg(NULL,0,
ECHO,
"POSTPROCESSOR: Successfully computed leadfields.\n");
2024 log_msg(NULL, 5, 0,
"Error: leadfield support not compiled in. Rebuild with -DENABLE_LEADFIELD=ON.");
2037 if(error_if_missing) {
2038 log_msg(0,5,0,
"%s error: required physic is not active! Usually this is due to an inconsistent experiment configuration. Aborting!", __func__);
2062 log_msg(0,5,0,
"%s warning: trying to register already registered data vector.", __func__);
2076 auto register_new_mesh = [&] (
mesh_t mt,
int pidx) {
2077 if(!mesh_registry.count(mt)) {
2078 mesh_registry[mt] =
sf_mesh();
2079 mesh_registry[mt].name = param_globals::phys_region[pidx].name;
2081 return &mesh_registry[mt];
2085 for(
int i=0; i<param_globals::num_phys_regions; i++)
2089 switch(param_globals::phys_region[i].ptype) {
2101 curmesh = register_new_mesh(
emi_msh, i);
2106 log_msg(0,5,0,
"Unsupported mesh type %d! Aborting!", param_globals::phys_region[i].ptype);
2112 for(
int j=0; j<param_globals::phys_region[i].num_IDs; j++)
2130 if(ntr == 0)
return;
2135 for (
int i=0; i<ntr; i++) {
2144 shape.
p0.
x = tagRegs[i].p0[0];
2145 shape.
p0.
y = tagRegs[i].p0[1];
2146 shape.
p0.
z = tagRegs[i].p0[2];
2147 shape.
p1.
x = tagRegs[i].p1[0];
2148 shape.
p1.
y = tagRegs[i].p1[1];
2149 shape.
p1.
z = tagRegs[i].p1[2];
2150 shape.
radius = tagRegs[i].radius;
2157 log_msg(0,3,0,
"Tag region %d is empty", i);
2159 for(
size_t j=0; j<elem_indices.
size(); j++)
2160 mesh.
tag[elem_indices[j]] = tagRegs[i].tag;
2164 if(strlen(param_globals::retagfile))
2179 size_t renormalised_count = 0;
2185 for (
size_t i = 0; i < l_numelem; i++)
2187 const mesh_real_t f0 = fib[3*i+0], f1 = fib[3*i+1], f2 = fib[3*i+2];
2188 mesh_real_t fibre_len = sqrt(f0*f0 + f1*f1 + f2*f2);
2190 if (fibre_len && fabs(fibre_len - 1) > 1e-3) {
2191 fib[3 * i + 0] /= fibre_len;
2192 fib[3 * i + 1] /= fibre_len;
2193 fib[3 * i + 2] /= fibre_len;
2194 renormalised_count++;
2198 return renormalised_count;
2203 log_msg(0,0,0,
"\n *** Processing meshes ***\n");
2205 const std::string basename = param_globals::meshname;
2206 const int verb = param_globals::output_level;
2214 MPI_Comm comm = ref_mesh.
comm;
2217 double t1, t2, s1, s2;
2218 MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
2224 std::list< sf_mesh* > ptsread_list;
2227 t1 = MPI_Wtime(); s1 = t1;
2228 if(verb)
log_msg(NULL, 0, 0,
"Reading reference mesh: %s.*", basename.c_str());
2233 if (strlen(param_globals::tagfile)) {
2234 if(verb)
log_msg(NULL, 0, 0,
"Overriding element tags from: %s", param_globals::tagfile);
2239 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2241 bool check_fibre_normality =
true;
2242 if (check_fibre_normality and ref_mesh.
fib.
size()>0) {
2248 size_t l_num_fixed_she = 0;
2252 unsigned long fixed[2] = {(
unsigned long) l_num_fixed_fib, (
unsigned long) l_num_fixed_she};
2253 MPI_Allreduce(MPI_IN_PLACE, fixed, 2, MPI_UNSIGNED_LONG, MPI_SUM, comm);
2255 if (fixed[0] + fixed[1] > 0)
2256 log_msg(NULL, 0, 0,
"Renormalised %ld longitudinal and %ld sheet-transverse fibre vectors.", fixed[0], fixed[1]);
2259 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2262 if(param_globals::numtagreg > 0) {
2263 log_msg(0, 0, 0,
"Re-tagging reference mesh");
2267 ptsread_list.push_back(&ref_mesh);
2270 retag_elements(ref_mesh, param_globals::tagreg, param_globals::numtagreg);
2273 ptsread_list.clear();
2276 if(verb)
log_msg(NULL, 0, 0,
"Processing submeshes");
2278 bool have_emi_mesh =
false;
2279 bool have_non_emi_mesh =
false;
2281 for(
auto it = mesh_registry.begin(); it != mesh_registry.end(); ++it) {
2282 mesh_t grid_type = it->first;
2283 sf_mesh & submesh = it->second;
2287 have_emi_mesh =
true;
2289 have_non_emi_mesh =
true;
2291 if(verb > 1)
log_msg(NULL, 0, 0,
"\nSubmesh name: %s", submesh.
name.c_str());
2308 if(verb > 1)
log_msg(NULL, 0, 0,
"Extraction done in %f sec.",
float(t2 - t1));
2310 ptsread_list.push_back(&submesh);
2314 if(have_emi_mesh && have_non_emi_mesh) {
2315 log_msg(NULL, 5,
ECHO,
"EMI and non-EMI submeshes cannot be mixed during mesh setup.");
2320 if(param_globals::pstrat == 2 && have_non_emi_mesh)
2323 for(
auto it = mesh_registry.begin(); it != mesh_registry.end(); ++it)
2325 mesh_t grid_type = it->first;
2326 sf_mesh & submesh = it->second;
2328 if(verb > 2)
log_msg(NULL, 0, 0,
"\nSubmesh name: %s", submesh.
name.c_str());
2333 switch(param_globals::pstrat) {
2335 if(verb > 2)
log_msg(NULL, 0, 0,
"Using linear partitioning ..");
2338 #ifdef WITH_PARMETIS
2341 if(verb > 2)
log_msg(NULL, 0, 0,
"Using Parmetis partitioner ..");
2342 SF::parmetis_partitioner<mesh_int_t, mesh_real_t> partitioner(param_globals::pstrat_imbalance, 2);
2343 partitioner(submesh, part);
2349 if(verb > 2)
log_msg(NULL, 0, 0,
"Using KDtree partitioner ..");
2351 partitioner(submesh, part);
2356 if(verb > 2)
log_msg(NULL, 0, 0,
"Partitioning done in %f sec.",
float(t2 - t1));
2358 if(param_globals::pstrat > 0) {
2359 if(param_globals::gridout_p) {
2360 std::string out_name =
get_basename(param_globals::meshname);
2365 log_msg(0,0,0,
"Writing \"%s\" partitioning to: %s", submesh.
name.c_str(), out_name.c_str());
2372 if(verb > 2)
log_msg(NULL, 0, 0,
"Redistributing done in %f sec.",
float(t2 - t1));
2377 sm_numbering(submesh);
2379 if(verb > 2)
log_msg(NULL, 0, 0,
"Canonical numbering done in %f sec.",
float(t2 - t1));
2384 p_numbering(submesh);
2386 if(verb > 2)
log_msg(NULL, 0, 0,
"PETSc numbering done in %f sec.",
float(t2 - t1));
2391 if(have_non_emi_mesh)
2397 if(verb)
log_msg(NULL, 0, 0,
"All done in %f sec.",
float(s2 - s1));
2402 static const char* parameter_name =
"gridout_tags";
2403 const std::string spec = param_globals::gridout_tags ? param_globals::gridout_tags :
"";
2405 std::vector<int> tags;
2407 if(!paramschema::parse_idset_spec(spec, &tags, &error)) {
2408 log_msg(0, 5,
ECHO,
"Could not parse %s: %s.", parameter_name, error.c_str());
2412 output_tags.
clear();
2413 output_tags.
insert(tags.begin(), tags.end());
2415 if(output_tags.
size() == 0)
return false;
2417 log_msg(0, 0, 0,
"Restricting grid output to %zu tag(s) from %s.",
2418 output_tags.
size(), parameter_name);
2429 for(
size_t eidx = 0; eidx < mesh.
l_numelem; eidx++) {
2430 if(output_tags.
count(mesh.
tag[eidx]) == 0)
continue;
2436 localize_gridout_nodes(mesh, selected_nodes, output_idx, async);
2444 for(
mesh_t mesh_id : mesh_ids) {
2448 for(
size_t eidx = 0; eidx < mesh.
l_numelem; eidx++) {
2449 if(output_tags.
count(mesh.
tag[eidx]) != 0)
2459 for(
int tag : sorted_tags) {
2460 int local_found = local_seen.
count(tag) ? 1 : 0;
2461 int global_found = 0;
2462 MPI_Allreduce(&local_found, &global_found, 1, MPI_INT, MPI_MAX, PETSC_COMM_WORLD);
2463 if(global_found == 0)
2467 if(missing_tags.
size()) {
2470 std::stringstream msg;
2471 for(
size_t i = 0; i < missing_tags.
size(); i++) {
2473 msg << missing_tags[i];
2476 log_msg(0, 3,
ECHO,
"Warning: ignoring gridout_tags not present in the selected output mesh(es): %s.",
2479 for(
int tag : missing_tags)
2480 output_tags.
erase(tag);
2482 if(output_tags.
size() == 0) {
2483 log_msg(0, 5,
ECHO,
"gridout_tags did not match any tag in the selected output mesh(es).");
2502 size_t lhs = 0, rhs = 0;
2503 while(lhs < restr->size() && rhs < selected.
size()) {
2504 if((*restr)[lhs] == selected[rhs]) {
2508 }
else if((*restr)[lhs] < selected[rhs]) {
2515 *restr = intersection;
2525 std::string output_base =
get_basename(param_globals::meshname);
2527 const bool restrict_gridout =
2529 if(restrict_gridout) {
2536 if(write_intra_elec) {
2540 if(restrict_gridout) {
2541 if(!mesh_has_gridout_tags(mesh, output_tags)) {
2542 log_msg(0, 5,
ECHO,
"gridout_tags selected no intracellular grid elements.");
2545 extract_gridout_tag_mesh(mesh, output_tags, restricted_mesh);
2546 output_mesh = &restricted_mesh;
2549 if(param_globals::gridout_i & 1) {
2550 if(param_globals::output_level > 1)
2551 log_msg(0,0,0,
"Computing \"%s\" surface ..", output_mesh->
name.c_str());
2553 std::string output_file = output_base +
"_i.surf";
2554 log_msg(0,0,0,
"Writing \"%s\" surface: %s", output_mesh->
name.c_str(), output_file.c_str());
2556 if(param_globals::gridout_i & 2) {
2564 write_gridout_surface(*output_mesh, output_base +
"_i");
2567 if(param_globals::gridout_i & 2) {
2568 bool write_binary =
false;
2570 std::string output_file = output_base +
"_i";
2571 log_msg(0,0,0,
"Writing \"%s\" mesh: %s", output_mesh->
name.c_str(), output_file.c_str());
2576 if(write_extra_elec) {
2580 if(restrict_gridout) {
2581 if(!mesh_has_gridout_tags(mesh, output_tags)) {
2582 log_msg(0, 5,
ECHO,
"gridout_tags selected no extracellular grid elements.");
2585 extract_gridout_tag_mesh(mesh, output_tags, restricted_mesh);
2586 output_mesh = &restricted_mesh;
2589 if(param_globals::gridout_e & 1) {
2590 if(param_globals::output_level > 1)
2591 log_msg(0,0,0,
"Computing \"%s\" surface ..", output_mesh->
name.c_str());
2593 std::string output_file = output_base +
"_e.surf";
2594 log_msg(0,0,0,
"Writing \"%s\" surface: %s", output_mesh->
name.c_str(), output_file.c_str());
2596 if(param_globals::gridout_e & 2) {
2604 write_gridout_surface(*output_mesh, output_base +
"_e");
2607 if(param_globals::gridout_e & 2) {
2608 bool write_binary =
false;
2609 std::string output_file = output_base +
"_e";
2610 log_msg(0,0,0,
"Writing \"%s\" mesh: %s", output_mesh->
name.c_str(), output_file.c_str());
2620 print_active_citation_suggestions();
2622 const paramschema::ResetResult reset = paramschema::reset_schema_state(paramschema::openCARP_schema());
2623 print_lines(stderr,
"parameter cleanup warning: ", reset.warnings);
2624 print_lines(stderr,
"parameter cleanup error: ", reset.errors);
2636 char* filecopy =
dupstr(file);
2637 char* dir =
dupstr(dirname(filecopy));
2654 PetscErrorPrintf = PetscErrorPrintfNone;
2664 for(
size_t eidx = 0; eidx < mesh.
l_numelem; eidx++) {
2667 if(mindim < cdim) mindim = cdim;
2691 mesh.
pl.algebraic_layout();
2695 for(
size_t i = 0; i < idx->
size(); i++)
2704 int gsize = inp_data->
gsize();
2710 regigb.
x(gsize / dpn);
2711 regigb.
dim_x(regigb.
x()-1);
2714 regigb.
y(1); regigb.
z(1);
2730 regigb.
inc_t(param_globals::spacedt);
2744 log_msg(0,5,0,
"%s error: Could not set up data output! Aborting!", __func__);
2751 IO.
spec = mesh_spec;
2761 buffmap[mesh_spec] = inp_copy;
2768 void igb_output_manager::register_output_async(
sf_vec* inp_data,
2796 for(
size_t i=0; i<alg_nod.
size(); i++)
2797 ioidx[i] = nbr[alg_nod[i]];
2802 for(
size_t i=0; i<idx->
size(); i++) {
2804 ioidx[i] = nbr[loc_nodal];
2824 if(param_globals::num_io_nodes == 0)
2827 register_output_async(inp_data, inp_meshid, dpn, name,
units, idx, elem_data);
2842 sc->
forward(*data_vec, *perm_vec);
2857 FILE* fd =
static_cast<FILE*
>(it.
igb.
fileptr());
2860 sf_vec* buff = fill_output_buffer(it);
2866 const int dpn = it.
spec.
v2;
2873 for(
int j = 0; j < dpn; j++)
2910 FILE* fd =
static_cast<FILE*
>(it.
igb.
fileptr());
2941 const int max_line_len = 128;
2942 const char* file_sep =
"#=======================================================";
2949 fprintf(out->
fd,
"# CARP GIT commit hash: %s\n", GIT_COMMIT_HASH);
2950 fprintf(out->
fd,
"# dependency hashes: %s\n", SUBREPO_COMMITS);
2951 fprintf(out->
fd,
"\n");
2954 char line[8196] =
"# ";
2956 for (
int j=0; j<argc; j++) {
2957 strcat(line, argv[j]);
2958 if(strlen(line) > max_line_len) {
2959 fprintf(out->
fd,
"%s\n", line);
2965 fprintf(out->
fd,
"%s\n\n", line);
2969 for (
int i=1; i<argc; i++) {
2970 std::string argument(argv[i]);
2971 if (argument ==
"+F" || argument.find(
"_options_file")!= std::string::npos) {
2973 std::string init =
"";
2974 if (argument.find(
"_options_file")!= std::string::npos) {
2975 fprintf(out->
fd,
"%s = %s\n", argument.substr(1).c_str(), argv[i+1]);
2978 fprintf(out->
fd,
"%s>>\n", file_sep);
2981 fprintf(out->
fd,
"## %s ##\n", argv[i]);
2982 FILE *in = fopen(argv[i],
"r");
2983 while (fgets(line, 8196, in))
2984 fprintf(out->
fd,
"%s%s", init.c_str(), line);
2986 fprintf(out->
fd,
"\n##END of %s\n", argv[i]);
2987 fprintf(out->
fd,
"%s<<\n\n", file_sep);
2989 else if(argv[i][0] ==
'-')
2991 bool prelast = (i==argc-1);
2992 bool paramFollows = !prelast && ((argv[i+1][0] !=
'-') ||
2993 ((argv[i+1][1] >=
'0') && (argv[i+1][1] <=
'9')));
2999 char *optcpy = strdup(argv[i+1]);
3000 char *front = optcpy;
3002 while(*front==
' ' && *front) front++;
3004 while(*++front ==
' ');
3005 char *back = optcpy+strlen(optcpy)-1;
3006 while(*back==
' ' && back>front) back--;
3010 if (strstr(front,
"=") !=
nullptr)
3011 fprintf(out->
fd,
"%-40s= \"%s\"\n", argv[i]+1, front);
3013 fprintf(out->
fd,
"%-40s= %s\n", argv[i]+1, front);
3018 fprintf(out->
fd,
"%-40s= 1\n", argv[i]);
3033 snprintf(save_fn,
sizeof save_fn,
"exit.save.%.3f.roe", time);
3034 log_msg(NULL, 0, 0,
"savequit called at time %g\n", time);
opencarp::local_index_t mesh_int_t
opencarp::real_t SF_real
Global scalar 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< T > dsp
connectivity starting index of each element
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
non_overlapping_layout< T > epl
element parallel layout
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.
size_t size() const
The current size of the vector.
void resize(size_t n)
Resize a vector.
void assign(InputIterator s, InputIterator e)
Assign a memory range.
T * data()
Pointer to the vector's start.
iterator find(const K &key)
Search for key. Return iterator.
hm_int erase(const K &key)
hm_int count(const K &key) const
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
int run(Electrics &elec)
Full pipeline: construct or load Z, stream vm.igb, write ECG_*.dat.
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 rebalance_mesh(meshdata< T, S > &mesh)
Rebalance the parallel distribution of a mesh, if a local size is 0.
void write_surface(const meshdata< T, S > &surfmesh, std::string surffile)
void extract_mesh(const vector< bool > &keep, const meshdata< T, S > &mesh, meshdata< T, S > &submesh)
Extract a submesh from a given mesh.
void unique_resize(vector< T > &_P)
void count(const vector< T > &data, vector< S > &cnt)
Count number of occurrences of indices.
size_t root_write_ordered(FILE *fd, const vector< T > &idx, const vector< V > &vec, MPI_Comm comm)
Write index value pairs to disk in ordered permutation.
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 read_element_tags(meshdata< T, S > &mesh, std::string filename)
Override element tags from an ASCII file (one int per element, global element order).
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 write_points_parallel(const meshdata< T, S > &mesh, bool binary, std::string basename)
Write a parallel mesh to harddisk without gathering it on one rank.
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 binary_sort(vector< T > &_V)
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)
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_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.
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 intersect_output_restriction(SF::vector< mesh_int_t > *&restr, const SF::vector< mesh_int_t > &selected_idx)
Intersect an existing output restriction with another local index set.
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
bool parse_gridout_tags(hashmap::unordered_set< int > &output_tags)
Parse the gridout_tags idset into a unique set of region IDs.
void validate_gridout_tags(hashmap::unordered_set< int > &output_tags, const SF::vector< mesh_t > &mesh_ids)
Warn about selected gridout tags that are absent from the relevant meshes.
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 build_tagged_nodal_output_restriction(sf_mesh &mesh, const hashmap::unordered_set< int > &output_tags, SF::vector< mesh_int_t > &output_idx, bool async)
Build a local output-vector restriction from mesh element tags.
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
std::vector< std::string > runtime_args
bool warn_when_unavailable
ParserFallbackMode fallback_mode
std::string unavailable_reason
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
SF::vector< mesh_int_t > restr_global_idx
pointer to index vector used for restricting output.
bool elem_flag
igb header we use for output
IGBheader igb
global canonical indices matching restr_idx.
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