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.;
1336 if(param_globals::mass_lumping == 0 && param_globals::parab_solve==0) {
1338 "Warning: explicit solve not possible without mass lumping. \n"
1339 "Switching to Crank-Nicolson!\n\n");
1341 param_globals::parab_solve = 1;
1345 if(!param_globals::extracell_monodomain_stim)
1354 if(param_globals::t_sentinel > 0 && param_globals::sentinel_ID < 0 ) {
1355 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");
1358 if(param_globals::num_external_imp > 0 ) {
1359 for(
int ext_imp_i = 0; ext_imp_i < param_globals::num_external_imp; ext_imp_i++) {
1360 if(param_globals::external_imp[ext_imp_i][0] !=
'/') {
1361 log_msg(0,5,0,
"external_imp[%d] error: absolute paths must be used for .so file loading (\'%s\')",
1362 ext_imp_i, param_globals::external_imp[ext_imp_i]);
1368 if(param_globals::experiment ==
EXP_LAPLACE && param_globals::bidomain != 1) {
1369 log_msg(0,4,0,
"Warning: Laplace experiment mode requires bidomain = 1. Setting bidomain = 1.");
1370 param_globals::bidomain = 1;
1373 if(param_globals::num_phys_regions == 0) {
1374 log_msg(0,4,0,
"Warning: No physics region defined! Please set phys_region parameters to correctly define physics.");
1377 log_msg(0,4,0,
"Intra-elec and Extra-elec domains will be derived from fibers.\n");
1378 param_globals::num_phys_regions = param_globals::bidomain ? 2 : 1;
1379 param_globals::phys_region = (p_region*) calloc(param_globals::num_phys_regions,
sizeof(p_region));
1381 param_globals::phys_region[0].name = strdup(
"Autogenerated intracellular Electrics");
1382 param_globals::phys_region[0].num_IDs = 0;
1384 if(param_globals::bidomain) {
1386 param_globals::phys_region[1].name = strdup(
"Autogenerated extracellular Electrics");
1387 param_globals::phys_region[1].num_IDs = 0;
1390 log_msg(0,4,0,
"Laplace domain will be derived from fibers.\n");
1391 param_globals::num_phys_regions = 1;
1392 param_globals::phys_region = (p_region*) calloc(param_globals::num_phys_regions,
sizeof(p_region));
1394 param_globals::phys_region[0].name = strdup(
"Autogenerated Laplace");
1395 param_globals::phys_region[0].num_IDs = 0;
1400 log_msg(0,4,0,
"Warning: Laplace experiment mode requires a laplace physics regions defined.");
1404 log_msg(0,4,0,
"Converting the defined extracellular-electrics-region to laplace-region.");
1407 log_msg(0,4,0,
"Converting the defined intracellular-electrics-region to laplace-region.");
1410 param_globals::num_phys_regions += 1;
1411 param_globals::phys_region = (p_region*) realloc(param_globals::phys_region, param_globals::num_phys_regions *
sizeof(p_region));
1413 param_globals::phys_region[param_globals::num_phys_regions - 1].ptype =
PHYSREG_LAPLACE;
1414 param_globals::phys_region[param_globals::num_phys_regions - 1].name = strdup(
"Autogenerated Laplace");
1415 param_globals::phys_region[param_globals::num_phys_regions - 1].num_IDs = 0;
1419 #ifndef WITH_PARMETIS
1420 if(param_globals::pstrat == 1) {
1421 log_msg(0,3,0,
"openCARP was built without Parmetis support. Swithing to KDtree.");
1422 param_globals::pstrat = 2;
1427 bool legacy_stim_set =
false, new_stim_set =
false;
1429 for(
int i=0; i<param_globals::num_stim; i++) {
1430 Stimulus & legacy_stim = param_globals::stimulus[i];
1431 Stim & new_stim = param_globals::stim[i];
1433 if(legacy_stim.stimtype || legacy_stim.strength)
1434 legacy_stim_set =
true;
1436 if(new_stim.crct.type || new_stim.pulse.strength)
1437 new_stim_set =
true;
1440 if(legacy_stim_set || new_stim_set) {
1441 if(legacy_stim_set && new_stim_set) {
1442 log_msg(0,4,0,
"Warning: Legacy stimuli and default stimuli are defined. Only default stimuli will be used!");
1444 else if (legacy_stim_set) {
1445 log_msg(0,1,0,
"Warning: Legacy stimuli defined. Please consider switching to stimulus definition \"stim[]\"!");
1450 log_msg(0,4,0,
"Warning: No potential or current stimuli found!");
1456 int flg = 0, err = 0, rank =
get_rank();
1458 char *ptr = getcwd(current_dir, 1024);
1459 if (ptr == NULL) err++;
1460 ptr = getcwd(input_dir, 1024);
1461 if (ptr == NULL) err++;
1467 if (strcmp(sim_ID,
"OUTPUT_DIR")) {
1468 if (mkdir(sim_ID, 0775)) {
1469 if (errno == EEXIST ) {
1470 log_msg(NULL, 2, 0,
"Output directory exists: %s\n", sim_ID);
1472 log_msg(NULL, 5, 0,
"Unable to make output directory\n");
1476 }
else if (mkdir(sim_ID, 0775) && errno != EEXIST) {
1477 log_msg(NULL, 5, 0,
"Unable to make output directory\n");
1485 err += chdir(sim_ID);
1486 ptr = getcwd(output_dir, 1024);
1487 if (ptr == NULL) err++;
1492 err += chdir(output_dir);
1495 if (rank == 0 && ((param_globals::experiment==
EXP_POSTPROCESS) || (param_globals::post_processing_opts &
LEADFIELD))) {
1497 if (strcmp(param_globals::ppID,
"POSTPROC_DIR")) {
1498 if (mkdir(param_globals::ppID, 0775)) {
1499 if (errno == EEXIST ) {
1500 log_msg(NULL, 2,
ECHO,
"Postprocessing directory exists: %s\n\n", param_globals::ppID);
1502 log_msg(NULL, 5,
ECHO,
"Unable to make postprocessing directory\n\n");
1506 }
else if (mkdir(param_globals::ppID, 0775) && errno != EEXIST) {
1507 log_msg(NULL, 5,
ECHO,
"Unable to make postprocessing directory\n\n");
1515 err += chdir(param_globals::ppID);
1516 ptr = getcwd(postproc_dir, 1024);
1517 if (ptr == NULL) err++;
1518 err = chdir(output_dir);
1527 bool io_node =
false;
1530 if (param_globals::num_io_nodes > 0) {
1533 log_msg(NULL, 5, 0,
"You cannot run with async IO on only one core.\n");
1537 if (2 * param_globals::num_io_nodes >= psize) {
1538 log_msg(NULL, 5, 0,
"The number of IO cores be less " "than the number of compute cores.");
1542 if (param_globals::num_PS_nodes && param_globals::num_io_nodes > param_globals::num_PS_nodes) {
1544 "The number of IO cores (%d) should not "
1545 "exceed the number of PS compute cores (%d).\n",
1546 param_globals::num_io_nodes, param_globals::num_PS_nodes);
1551 io_node = prank < param_globals::num_io_nodes;
1554 MPI_Comm_split(PETSC_COMM_WORLD, io_node,
get_rank(), &comm);
1555 MPI_Comm_set_name(comm, io_node ?
"IO" :
"compute");
1557 PETSC_COMM_WORLD = comm;
1561 MPI_Intercomm_create(comm, 0, MPI_COMM_WORLD, io_node ? param_globals::num_io_nodes : 0,
1565 log_msg(NULL, 4, 0,
"Global node %d, Comm rank %d != Intercomm rank %d\n",
1569 MPI_Comm_set_name(PETSC_COMM_WORLD,
"compute");
1573 if((io_node || !param_globals::num_io_nodes) && !prank)
1580 getcwd(current_dir, 1024);
1587 if (dest==
OUTPUT) err = chdir(output_dir);
1588 else if (dest==
POSTPROC) err = chdir(postproc_dir);
1589 else if (dest==
CURDIR) err = chdir(current_dir);
1590 else err = chdir(input_dir);
1598 double start_time = 0.0;
1601 double end_time = param_globals::tend;
1615 if(param_globals::num_tsav) {
1616 std::vector<double> trig(param_globals::num_tsav);
1617 for(
size_t i=0; i<trig.size(); i++) trig[i] = param_globals::tsav[i];
1622 if(param_globals::chkpt_intv)
1624 param_globals::chkpt_intv, 0,
iotm_chkpt_intv,
"interval checkpointing");
1626 if(param_globals::num_trace)
1630 #ifdef WITH_POWERCAPPING
1631 void basic_powercapping_setup()
1633 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);
1636 void basic_powercapping_cleanup()
1640 delete user_globals::pc_manager; user_globals::pc_manager =
nullptr;
1647 const short padding = 4;
1652 if(col_width[0] <
int(strlen(buff)+padding))
1653 col_width[0] = strlen(buff)+padding;
1656 if(col_width[1] <
int(strlen(buff)+padding))
1657 col_width[1] = strlen(buff)+padding;
1660 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1662 int timer_id = used_timer_ids[tid];
1672 snprintf(buff,
sizeof buff,
"%.3lf", val);
1673 if(col_width[col] <
int(strlen(buff)+padding))
1674 col_width[col] = strlen(buff)+padding;
1691 const char* smpl_endl =
"\n";
1699 log_msg(0,5,0,
"Protocol file %s could not be opened for writing!\n", fname);
1712 std::vector<std::string> col_labels = {
"time",
"tick"};
1713 std::vector<std::string> col_short_labels = {
"A",
"B"};
1714 std::vector<std::string> col_unit_labels = {
"ms",
"--" };
1715 std::vector<int> col_width = {4, 4};
1717 char c_label = {
'C'};
1718 std::string label = {
""};
1719 std::string unit = {
""};
1723 std::vector<int> used_timer_ids;
1724 std::vector<int> used_stim_ids;
1734 used_stim_ids.push_back(sidx);
1744 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1746 int timer_id = used_timer_ids[tid];
1749 int llen = strlen(t->
name);
1750 mx_llen = llen > mx_llen ? llen : mx_llen;
1753 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1755 int timer_id = used_timer_ids[tid];
1758 col_labels.push_back(t->
name);
1760 col_short_labels.push_back(label);
1765 if(unit.empty()) unit =
"--";
1766 col_unit_labels.push_back(unit);
1767 col_width.push_back(4);
1775 fh <<
"# Protocol header\n#\n" <<
"# Legend:\n";
1776 for(
size_t i = 0; i<col_short_labels.size(); i++)
1778 fh <<
"#" << std::setw(2) << col_short_labels[i] <<
" = " << std::setw(mx_llen) << col_labels[i];
1779 fh <<
" [" << std::setw(10) << col_unit_labels[i] <<
"]";
1781 if(i >= 2 && used_stim_ids[i-2] > -1) {
1786 fh <<
" ground stim" << smpl_endl;
1788 fh <<
" applied: " << std::to_string(s.
pulse.
strength) << smpl_endl;
1799 for(
size_t i = 0; i<col_short_labels.size(); i++)
1800 fh << std::setw(col_width[i] - 3) << col_short_labels[i].c_str() << std::setw(3) <<
" ";
1803 fh << smpl_endl <<
"#";
1804 for(
size_t i = 0; i<col_unit_labels.size(); i++)
1805 fh <<
"[" << std::setw(col_width[i]-2) << col_unit_labels[i].c_str() <<
"]";
1808 fh << smpl_endl << std::fixed;
1816 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1818 int timer_id = used_timer_ids[tid];
1824 fh << std::setw(col_width[col]) << On;
1832 fh << std::setw(col_width[col]) << std::setprecision(3) << val;
1854 const char* h1_prog =
"PROG\t----- \t----\t-------\t-------|";
1855 const char* h2_prog =
"time\t%%comp\ttime\t ctime \t ETA |";
1856 const char* h1_wc =
"\tELAPS |";
1857 const char* h2_wc =
"\twc |";
1862 log_msg(NULL, 0, 0,
"%s", h1_prog );
1870 int req_hours = ((int)(time)) / 3600;
1871 int req_min = (((int)(time)) % 3600) / 60;
1872 int req_sec = (((int)(time)) % 3600) % 60;
1874 snprintf(str, str_size,
"%d:%02d:%02d", req_hours, req_min, req_sec);
1887 char elapsed_time_str[256];
1888 char req_time_str[256];
1892 log_msg( NULL, 0,
NONL,
"%.2f\t%.1f\t%.1f\t%s\t%s",
1910 if(!have_timedependent_phys) {
1911 log_msg(0,0,0,
"\n no time-dependent physics region registered, skipping simulate loop..\n");
1915 log_msg(0,0,0,
"\n *** Launching simulation ***\n");
1919 if(param_globals::dump_protocol)
1926 #ifdef WITH_POWERCAPPING
1927 basic_powercapping_setup();
1928 powercapping_manager *pc = user_globals::pc_manager;
1939 #ifdef WITH_POWERCAPPING
1940 std::vector<int> flops_per_rank;
1945 #ifdef WITH_POWERCAPPING
1946 pc->iteration_begin(flops_per_rank);
1953 it.second->output_step();
1956 #ifdef WITH_POWERCAPPING
1957 pc->sample(
"output step");
1965 #ifdef WITH_POWERCAPPING
1966 pc->sample(p->
name);
1970 #ifdef WITH_POWERCAPPING
1971 pc->iteration_end(flops_per_rank);
1978 log_msg(0,0,0,
"\n\nTimings of individual physics:");
1979 log_msg(0,0,0,
"------------------------------\n");
1986 #ifdef WITH_POWERCAPPING
1988 basic_powercapping_cleanup();
1994 if(param_globals::post_processing_opts &
RECOVER_PHIE) {
1995 log_msg(NULL,0,
ECHO,
"\nPOSTPROCESSOR: Recovering Phie ...");
1996 log_msg(NULL,0,
ECHO,
"----------------------------------\n");
2002 log_msg(NULL,0,
ECHO,
"\n-----------------------------------------");
2003 log_msg(NULL,0,
ECHO,
"POSTPROCESSOR: Successfully recoverd Phie.\n");
2007 if(param_globals::post_processing_opts &
LEADFIELD) {
2008 #ifdef WITH_LEADFIELD
2009 log_msg(NULL,0,
ECHO,
"\nPOSTPROCESSOR: Computing leadfields ...");
2010 log_msg(NULL,0,
ECHO,
"-------------------------------------\n");
2014 log_msg(NULL, 5, 0,
"Error: Leadfield requires active EP physics. Aborting.");
2018 int err = leadfield.
run(*elec);
2021 log_msg(NULL,0,
ECHO,
"\n------------------------------------------");
2022 log_msg(NULL,0,
ECHO,
"POSTPROCESSOR: Successfully computed leadfields.\n");
2025 log_msg(NULL, 5, 0,
"Error: leadfield support not compiled in. Rebuild with -DENABLE_LEADFIELD=ON.");
2038 if(error_if_missing) {
2039 log_msg(0,5,0,
"%s error: required physic is not active! Usually this is due to an inconsistent experiment configuration. Aborting!", __func__);
2063 log_msg(0,5,0,
"%s warning: trying to register already registered data vector.", __func__);
2077 auto register_new_mesh = [&] (
mesh_t mt,
int pidx) {
2078 if(!mesh_registry.count(mt)) {
2079 mesh_registry[mt] =
sf_mesh();
2080 mesh_registry[mt].name = param_globals::phys_region[pidx].name;
2082 return &mesh_registry[mt];
2086 for(
int i=0; i<param_globals::num_phys_regions; i++)
2090 switch(param_globals::phys_region[i].ptype) {
2102 curmesh = register_new_mesh(
emi_msh, i);
2107 log_msg(0,5,0,
"Unsupported mesh type %d! Aborting!", param_globals::phys_region[i].ptype);
2113 for(
int j=0; j<param_globals::phys_region[i].num_IDs; j++)
2131 if(ntr == 0)
return;
2136 for (
int i=0; i<ntr; i++) {
2145 shape.
p0.
x = tagRegs[i].p0[0];
2146 shape.
p0.
y = tagRegs[i].p0[1];
2147 shape.
p0.
z = tagRegs[i].p0[2];
2148 shape.
p1.
x = tagRegs[i].p1[0];
2149 shape.
p1.
y = tagRegs[i].p1[1];
2150 shape.
p1.
z = tagRegs[i].p1[2];
2151 shape.
radius = tagRegs[i].radius;
2158 log_msg(0,3,0,
"Tag region %d is empty", i);
2160 for(
size_t j=0; j<elem_indices.
size(); j++)
2161 mesh.
tag[elem_indices[j]] = tagRegs[i].tag;
2165 if(strlen(param_globals::retagfile))
2180 size_t renormalised_count = 0;
2186 for (
size_t i = 0; i < l_numelem; i++)
2188 const mesh_real_t f0 = fib[3*i+0], f1 = fib[3*i+1], f2 = fib[3*i+2];
2189 mesh_real_t fibre_len = sqrt(f0*f0 + f1*f1 + f2*f2);
2191 if (fibre_len && fabs(fibre_len - 1) > 1e-3) {
2192 fib[3 * i + 0] /= fibre_len;
2193 fib[3 * i + 1] /= fibre_len;
2194 fib[3 * i + 2] /= fibre_len;
2195 renormalised_count++;
2199 return renormalised_count;
2204 log_msg(0,0,0,
"\n *** Processing meshes ***\n");
2206 const std::string basename = param_globals::meshname;
2207 const int verb = param_globals::output_level;
2215 MPI_Comm comm = ref_mesh.
comm;
2218 double t1, t2, s1, s2;
2219 MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
2225 std::list< sf_mesh* > ptsread_list;
2228 t1 = MPI_Wtime(); s1 = t1;
2229 if(verb)
log_msg(NULL, 0, 0,
"Reading reference mesh: %s.*", basename.c_str());
2234 if (strlen(param_globals::tagfile)) {
2235 if(verb)
log_msg(NULL, 0, 0,
"Overriding element tags from: %s", param_globals::tagfile);
2240 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2242 bool check_fibre_normality =
true;
2243 if (check_fibre_normality and ref_mesh.
fib.
size()>0) {
2249 size_t l_num_fixed_she = 0;
2253 unsigned long fixed[2] = {(
unsigned long) l_num_fixed_fib, (
unsigned long) l_num_fixed_she};
2254 MPI_Allreduce(MPI_IN_PLACE, fixed, 2, MPI_UNSIGNED_LONG, MPI_SUM, comm);
2256 if (fixed[0] + fixed[1] > 0)
2257 log_msg(NULL, 0, 0,
"Renormalised %ld longitudinal and %ld sheet-transverse fibre vectors.", fixed[0], fixed[1]);
2260 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2263 if(param_globals::numtagreg > 0) {
2264 log_msg(0, 0, 0,
"Re-tagging reference mesh");
2268 ptsread_list.push_back(&ref_mesh);
2271 retag_elements(ref_mesh, param_globals::tagreg, param_globals::numtagreg);
2274 ptsread_list.clear();
2277 if(verb)
log_msg(NULL, 0, 0,
"Processing submeshes");
2279 bool have_emi_mesh =
false;
2280 bool have_non_emi_mesh =
false;
2282 for(
auto it = mesh_registry.begin(); it != mesh_registry.end(); ++it) {
2283 mesh_t grid_type = it->first;
2284 sf_mesh & submesh = it->second;
2288 have_emi_mesh =
true;
2290 have_non_emi_mesh =
true;
2292 if(verb > 1)
log_msg(NULL, 0, 0,
"\nSubmesh name: %s", submesh.
name.c_str());
2309 if(verb > 1)
log_msg(NULL, 0, 0,
"Extraction done in %f sec.",
float(t2 - t1));
2311 ptsread_list.push_back(&submesh);
2315 if(have_emi_mesh && have_non_emi_mesh) {
2316 log_msg(NULL, 5,
ECHO,
"EMI and non-EMI submeshes cannot be mixed during mesh setup.");
2321 if(param_globals::pstrat == 2 && have_non_emi_mesh)
2324 for(
auto it = mesh_registry.begin(); it != mesh_registry.end(); ++it)
2326 mesh_t grid_type = it->first;
2327 sf_mesh & submesh = it->second;
2329 if(verb > 2)
log_msg(NULL, 0, 0,
"\nSubmesh name: %s", submesh.
name.c_str());
2334 switch(param_globals::pstrat) {
2336 if(verb > 2)
log_msg(NULL, 0, 0,
"Using linear partitioning ..");
2339 #ifdef WITH_PARMETIS
2342 if(verb > 2)
log_msg(NULL, 0, 0,
"Using Parmetis partitioner ..");
2343 SF::parmetis_partitioner<mesh_int_t, mesh_real_t> partitioner(param_globals::pstrat_imbalance, 2);
2344 partitioner(submesh, part);
2350 if(verb > 2)
log_msg(NULL, 0, 0,
"Using KDtree partitioner ..");
2352 partitioner(submesh, part);
2357 if(verb > 2)
log_msg(NULL, 0, 0,
"Partitioning done in %f sec.",
float(t2 - t1));
2359 if(param_globals::pstrat > 0) {
2360 if(param_globals::gridout_p) {
2361 std::string out_name =
get_basename(param_globals::meshname);
2366 log_msg(0,0,0,
"Writing \"%s\" partitioning to: %s", submesh.
name.c_str(), out_name.c_str());
2373 if(verb > 2)
log_msg(NULL, 0, 0,
"Redistributing done in %f sec.",
float(t2 - t1));
2378 sm_numbering(submesh);
2380 if(verb > 2)
log_msg(NULL, 0, 0,
"Canonical numbering done in %f sec.",
float(t2 - t1));
2385 p_numbering(submesh);
2387 if(verb > 2)
log_msg(NULL, 0, 0,
"PETSc numbering done in %f sec.",
float(t2 - t1));
2392 if(have_non_emi_mesh)
2398 if(verb)
log_msg(NULL, 0, 0,
"All done in %f sec.",
float(s2 - s1));
2403 static const char* parameter_name =
"gridout_tags";
2404 const std::string spec = param_globals::gridout_tags ? param_globals::gridout_tags :
"";
2406 std::vector<int> tags;
2408 if(!paramschema::parse_idset_spec(spec, &tags, &error)) {
2409 log_msg(0, 5,
ECHO,
"Could not parse %s: %s.", parameter_name, error.c_str());
2413 output_tags.
clear();
2414 output_tags.
insert(tags.begin(), tags.end());
2416 if(output_tags.
size() == 0)
return false;
2418 log_msg(0, 0, 0,
"Restricting grid output to %zu tag(s) from %s.",
2419 output_tags.
size(), parameter_name);
2430 for(
size_t eidx = 0; eidx < mesh.
l_numelem; eidx++) {
2431 if(output_tags.
count(mesh.
tag[eidx]) == 0)
continue;
2437 localize_gridout_nodes(mesh, selected_nodes, output_idx, async);
2445 for(
mesh_t mesh_id : mesh_ids) {
2449 for(
size_t eidx = 0; eidx < mesh.
l_numelem; eidx++) {
2450 if(output_tags.
count(mesh.
tag[eidx]) != 0)
2460 for(
int tag : sorted_tags) {
2461 int local_found = local_seen.
count(tag) ? 1 : 0;
2462 int global_found = 0;
2463 MPI_Allreduce(&local_found, &global_found, 1, MPI_INT, MPI_MAX, PETSC_COMM_WORLD);
2464 if(global_found == 0)
2468 if(missing_tags.
size()) {
2471 std::stringstream msg;
2472 for(
size_t i = 0; i < missing_tags.
size(); i++) {
2474 msg << missing_tags[i];
2477 log_msg(0, 3,
ECHO,
"Warning: ignoring gridout_tags not present in the selected output mesh(es): %s.",
2480 for(
int tag : missing_tags)
2481 output_tags.
erase(tag);
2483 if(output_tags.
size() == 0) {
2484 log_msg(0, 5,
ECHO,
"gridout_tags did not match any tag in the selected output mesh(es).");
2503 size_t lhs = 0, rhs = 0;
2504 while(lhs < restr->size() && rhs < selected.
size()) {
2505 if((*restr)[lhs] == selected[rhs]) {
2509 }
else if((*restr)[lhs] < selected[rhs]) {
2516 *restr = intersection;
2526 std::string output_base =
get_basename(param_globals::meshname);
2528 const bool restrict_gridout =
2530 if(restrict_gridout) {
2537 if(write_intra_elec) {
2541 if(restrict_gridout) {
2542 if(!mesh_has_gridout_tags(mesh, output_tags)) {
2543 log_msg(0, 5,
ECHO,
"gridout_tags selected no intracellular grid elements.");
2546 extract_gridout_tag_mesh(mesh, output_tags, restricted_mesh);
2547 output_mesh = &restricted_mesh;
2550 if(param_globals::gridout_i & 1) {
2551 if(param_globals::output_level > 1)
2552 log_msg(0,0,0,
"Computing \"%s\" surface ..", output_mesh->
name.c_str());
2554 std::string output_file = output_base +
"_i.surf";
2555 log_msg(0,0,0,
"Writing \"%s\" surface: %s", output_mesh->
name.c_str(), output_file.c_str());
2557 if(param_globals::gridout_i & 2) {
2565 write_gridout_surface(*output_mesh, output_base +
"_i");
2568 if(param_globals::gridout_i & 2) {
2569 bool write_binary =
false;
2571 std::string output_file = output_base +
"_i";
2572 log_msg(0,0,0,
"Writing \"%s\" mesh: %s", output_mesh->
name.c_str(), output_file.c_str());
2577 if(write_extra_elec) {
2581 if(restrict_gridout) {
2582 if(!mesh_has_gridout_tags(mesh, output_tags)) {
2583 log_msg(0, 5,
ECHO,
"gridout_tags selected no extracellular grid elements.");
2586 extract_gridout_tag_mesh(mesh, output_tags, restricted_mesh);
2587 output_mesh = &restricted_mesh;
2590 if(param_globals::gridout_e & 1) {
2591 if(param_globals::output_level > 1)
2592 log_msg(0,0,0,
"Computing \"%s\" surface ..", output_mesh->
name.c_str());
2594 std::string output_file = output_base +
"_e.surf";
2595 log_msg(0,0,0,
"Writing \"%s\" surface: %s", output_mesh->
name.c_str(), output_file.c_str());
2597 if(param_globals::gridout_e & 2) {
2605 write_gridout_surface(*output_mesh, output_base +
"_e");
2608 if(param_globals::gridout_e & 2) {
2609 bool write_binary =
false;
2610 std::string output_file = output_base +
"_e";
2611 log_msg(0,0,0,
"Writing \"%s\" mesh: %s", output_mesh->
name.c_str(), output_file.c_str());
2621 print_active_citation_suggestions();
2623 const paramschema::ResetResult reset = paramschema::reset_schema_state(paramschema::openCARP_schema());
2624 print_lines(stderr,
"parameter cleanup warning: ", reset.warnings);
2625 print_lines(stderr,
"parameter cleanup error: ", reset.errors);
2637 char* filecopy =
dupstr(file);
2638 char* dir =
dupstr(dirname(filecopy));
2655 PetscErrorPrintf = PetscErrorPrintfNone;
2665 for(
size_t eidx = 0; eidx < mesh.
l_numelem; eidx++) {
2668 if(mindim < cdim) mindim = cdim;
2692 mesh.
pl.algebraic_layout();
2696 for(
size_t i = 0; i < idx->
size(); i++)
2705 int gsize = inp_data->
gsize();
2711 regigb.
x(gsize / dpn);
2712 regigb.
dim_x(regigb.
x()-1);
2715 regigb.
y(1); regigb.
z(1);
2731 regigb.
inc_t(param_globals::spacedt);
2745 log_msg(0,5,0,
"%s error: Could not set up data output! Aborting!", __func__);
2752 IO.
spec = mesh_spec;
2762 buffmap[mesh_spec] = inp_copy;
2769 void igb_output_manager::register_output_async(
sf_vec* inp_data,
2797 for(
size_t i=0; i<alg_nod.
size(); i++)
2798 ioidx[i] = nbr[alg_nod[i]];
2803 for(
size_t i=0; i<idx->
size(); i++) {
2805 ioidx[i] = nbr[loc_nodal];
2825 if(param_globals::num_io_nodes == 0)
2828 register_output_async(inp_data, inp_meshid, dpn, name,
units, idx, elem_data);
2843 sc->
forward(*data_vec, *perm_vec);
2858 FILE* fd =
static_cast<FILE*
>(it.
igb.
fileptr());
2861 sf_vec* buff = fill_output_buffer(it);
2867 const int dpn = it.
spec.
v2;
2874 for(
int j = 0; j < dpn; j++)
2911 FILE* fd =
static_cast<FILE*
>(it.
igb.
fileptr());
2942 const int max_line_len = 128;
2943 const char* file_sep =
"#=======================================================";
2950 fprintf(out->
fd,
"# CARP GIT commit hash: %s\n", GIT_COMMIT_HASH);
2951 fprintf(out->
fd,
"# dependency hashes: %s\n", SUBREPO_COMMITS);
2952 fprintf(out->
fd,
"\n");
2955 char line[8196] =
"# ";
2957 for (
int j=0; j<argc; j++) {
2958 strcat(line, argv[j]);
2959 if(strlen(line) > max_line_len) {
2960 fprintf(out->
fd,
"%s\n", line);
2966 fprintf(out->
fd,
"%s\n\n", line);
2970 for (
int i=1; i<argc; i++) {
2971 std::string argument(argv[i]);
2972 if (argument ==
"+F" || argument.find(
"_options_file")!= std::string::npos) {
2974 std::string init =
"";
2975 if (argument.find(
"_options_file")!= std::string::npos) {
2976 fprintf(out->
fd,
"%s = %s\n", argument.substr(1).c_str(), argv[i+1]);
2979 fprintf(out->
fd,
"%s>>\n", file_sep);
2982 fprintf(out->
fd,
"## %s ##\n", argv[i]);
2983 FILE *in = fopen(argv[i],
"r");
2984 while (fgets(line, 8196, in))
2985 fprintf(out->
fd,
"%s%s", init.c_str(), line);
2987 fprintf(out->
fd,
"\n##END of %s\n", argv[i]);
2988 fprintf(out->
fd,
"%s<<\n\n", file_sep);
2990 else if(argv[i][0] ==
'-')
2992 bool prelast = (i==argc-1);
2993 bool paramFollows = !prelast && ((argv[i+1][0] !=
'-') ||
2994 ((argv[i+1][1] >=
'0') && (argv[i+1][1] <=
'9')));
3000 char *optcpy = strdup(argv[i+1]);
3001 char *front = optcpy;
3003 while(*front==
' ' && *front) front++;
3005 while(*++front ==
' ');
3006 char *back = optcpy+strlen(optcpy)-1;
3007 while(*back==
' ' && back>front) back--;
3011 if (strstr(front,
"=") !=
nullptr)
3012 fprintf(out->
fd,
"%-40s= \"%s\"\n", argv[i]+1, front);
3014 fprintf(out->
fd,
"%-40s= %s\n", argv[i]+1, front);
3019 fprintf(out->
fd,
"%-40s= 1\n", argv[i]);
3034 snprintf(save_fn,
sizeof save_fn,
"exit.save.%.3f.roe", time);
3035 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