18 #include "opencarp_types.h"
22 #ifdef WITH_POWERCAPPING
24 #include "powercapping.h"
39 #include "openCARP_schema.hpp"
40 #include "runtime.hpp"
41 #include "snapshot_file_io.hpp"
47 enum class ParserCompareMode {
53 enum class ParserFallbackMode {
58 struct RuntimeCompatOptions {
62 struct LegacyCompareInput {
69 struct LegacySnapshotHelperRunResult {
76 std::vector<paramschema::CitationSuggestion> active_citation_suggestions;
78 std::string trim_copy(
const std::string& value);
79 std::string to_lower_ascii(std::string value);
80 bool build_legacy_compare_input(
int argc,
char** argv, LegacyCompareInput* input, std::string* error);
82 std::string join_paths_for_message(
const std::vector<std::string>& paths)
88 std::ostringstream joined;
89 for (std::size_t i = 0; i < paths.size(); ++i) {
98 bool match_long_option(
const std::string& token,
const char* option, std::string* attached_value)
100 *attached_value = std::string();
101 if (token == option) {
105 const std::string prefix = std::string(option) +
"=";
106 if (token.size() > prefix.size() && token.compare(0, prefix.size(), prefix) == 0) {
107 *attached_value = token.substr(prefix.size());
114 bool is_help_topic_candidate(
const char* token)
116 return token != NULL && token[0] !=
'\0' && token[0] !=
'-' && token[0] !=
'+';
119 bool is_long_option_argument_error(
const std::string& token,
121 const std::string& attached_value,
124 if (attached_value.empty()) {
128 *error =
"Unexpected argument for " + std::string(option) +
" in '" + token +
"'";
132 bool normalize_runtime_args(
int argc,
134 std::vector<std::string>* normalized,
135 RuntimeCompatOptions* compat,
139 compat->fallback_mode = ParserFallbackMode::Off;
141 if (argc <= 0 || argv == NULL || argv[0] == NULL) {
142 *error =
"Missing program name";
146 normalized->push_back(argv[0]);
148 for (
int i = 1; i < argc; ++i) {
149 const std::string token = argv[i];
154 std::string attached_value;
156 if (token ==
"+Help" || match_long_option(token,
"--help", &attached_value)) {
157 std::string topic =
"PrM";
158 if (!attached_value.empty()) {
159 topic = attached_value;
160 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
163 normalized->push_back(
"+Help");
164 normalized->push_back(topic);
168 if (token ==
"+Doc" || match_long_option(token,
"--doc", &attached_value)) {
169 std::string topic =
"ALL";
170 if (!attached_value.empty()) {
171 topic = attached_value;
172 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
175 normalized->push_back(
"+Doc");
176 normalized->push_back(topic);
180 if (token ==
"+Default" || match_long_option(token,
"--default", &attached_value)) {
181 if (token !=
"+Default" && is_long_option_argument_error(token,
"--default", attached_value, error)) {
184 normalized->push_back(
"+Default");
188 if (token ==
"+Run" || match_long_option(token,
"--run", &attached_value)) {
189 if (token !=
"+Run" && is_long_option_argument_error(token,
"--run", attached_value, error)) {
192 normalized->push_back(
"+Run");
196 if (token ==
"+I" || match_long_option(token,
"--interactive", &attached_value)) {
197 if (!attached_value.empty()) {
198 *error =
"Unexpected argument for --interactive in '" + token +
"'";
200 *error =
"Unsupported option " + token +
" (interactive mode is not available)";
205 if (match_long_option(token,
"--param-fallback", &attached_value)) {
206 std::string mode = attached_value;
209 *error =
"Missing argument after --param-fallback";
215 if (to_lower_ascii(trim_copy(mode)) !=
"legacy") {
216 *error =
"Unsupported value '" + mode +
"' for --param-fallback (expected legacy)";
220 compat->fallback_mode = ParserFallbackMode::Legacy;
224 if (token ==
"+F" || match_long_option(token,
"--file", &attached_value)) {
225 std::string
filename = attached_value;
228 *error =
"Missing filename after " + token;
233 normalized->push_back(
"+F");
238 if (token ==
"+Save" || match_long_option(token,
"--save", &attached_value)) {
239 std::string
filename = attached_value;
242 *error =
"Missing argument after " + token;
247 normalized->push_back(
"+Save");
252 normalized->push_back(token);
258 bool parse_option_argument(
int* index,
261 const std::string& token,
262 const std::string& attached_value,
263 const char* option_name,
267 if (!attached_value.empty()) {
268 *value = attached_value;
271 if (*index + 1 >= argc) {
272 *error =
"Missing argument after " + std::string(option_name);
275 *value = argv[++(*index)];
279 bool filename_has_suffix(
const std::string&
filename,
const char* suffix)
281 const std::string normalized_filename = to_lower_ascii(trim_copy(
filename));
282 const std::string normalized_suffix = to_lower_ascii(std::string(suffix == NULL ?
"" : suffix));
283 return normalized_filename.size() >= normalized_suffix.size() &&
284 normalized_filename.compare(normalized_filename.size() - normalized_suffix.size(),
285 normalized_suffix.size(),
286 normalized_suffix) == 0;
289 bool build_legacy_compare_input(
int argc,
char** argv, LegacyCompareInput* input, std::string* error)
291 input->available =
false;
292 input->warn_when_unavailable =
true;
293 input->runtime_args.clear();
294 input->unavailable_reason.clear();
296 if (argc <= 0 || argv == NULL || argv[0] == NULL) {
297 *error =
"Missing program name";
301 input->runtime_args.push_back(argv[0]);
302 bool saw_passthrough_token_before_file =
false;
304 for (
int i = 1; i < argc; ++i) {
305 const std::string token = argv[i];
310 std::string attached_value;
312 if (match_long_option(token,
"--param-fallback", &attached_value)) {
314 if (!parse_option_argument(&i, argc, argv, token, attached_value,
"--param-fallback", &ignored, error)) {
320 if (token ==
"+Save" || match_long_option(token,
"--save", &attached_value)) {
322 if (!parse_option_argument(&i, argc, argv, token, attached_value, token.c_str(), &ignored, error)) {
328 if (token ==
"+Help" || match_long_option(token,
"--help", &attached_value)) {
329 std::string topic =
"PrM";
330 if (!attached_value.empty()) {
331 topic = attached_value;
332 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
335 input->runtime_args.push_back(
"+Help");
336 input->runtime_args.push_back(topic);
337 input->available =
true;
341 if (token ==
"+Doc" || match_long_option(token,
"--doc", &attached_value)) {
342 std::string topic =
"ALL";
343 if (!attached_value.empty()) {
344 topic = attached_value;
345 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
348 input->runtime_args.push_back(
"+Doc");
349 input->runtime_args.push_back(topic);
350 input->available =
true;
354 if (token ==
"+Default" || match_long_option(token,
"--default", &attached_value)) {
355 if (token !=
"+Default" && is_long_option_argument_error(token,
"--default", attached_value, error)) {
358 input->runtime_args.push_back(
"+Default");
362 if (token ==
"+Run" || match_long_option(token,
"--run", &attached_value)) {
363 if (token !=
"+Run" && is_long_option_argument_error(token,
"--run", attached_value, error)) {
366 input->runtime_args.push_back(
"+Run");
370 if (token ==
"+F" || match_long_option(token,
"--file", &attached_value)) {
372 if (!parse_option_argument(&i, argc, argv, token, attached_value, token.c_str(), &
filename, error)) {
376 if (saw_passthrough_token_before_file) {
377 input->unavailable_reason =
378 "legacy compare is unavailable when direct parameter arguments precede a .par input file";
379 input->runtime_args.clear();
380 input->runtime_args.push_back(argv[0]);
384 if (!filename_has_suffix(
filename,
".par")) {
385 input->unavailable_reason =
386 "legacy compare is unavailable for non-.par input file '" +
filename +
"'";
387 input->runtime_args.clear();
388 input->runtime_args.push_back(argv[0]);
392 input->runtime_args.push_back(
"+F");
393 input->runtime_args.push_back(
filename);
397 input->runtime_args.push_back(token);
398 saw_passthrough_token_before_file =
true;
401 input->available = input->runtime_args.size() > 1;
402 if (!input->available && input->unavailable_reason.empty()) {
403 input->unavailable_reason =
"unable to reconstruct a legacy-compatible parameter input";
408 std::string trim_copy(
const std::string& value)
410 std::string::size_type first = 0;
411 while (first < value.size() && std::isspace(
static_cast<unsigned char>(value[first]))) {
415 std::string::size_type last = value.size();
416 while (last > first && std::isspace(
static_cast<unsigned char>(value[last - 1]))) {
420 return value.substr(first, last - first);
423 std::string to_lower_ascii(std::string value)
425 for (std::string::size_type i = 0; i < value.size(); ++i) {
426 value[i] =
static_cast<char>(std::tolower(
static_cast<unsigned char>(value[i])));
431 ParserCompareMode parser_compare_mode()
433 const char* raw = std::getenv(
"OPENCARP_PARAM_COMPARE");
435 return ParserCompareMode::Strict;
438 const std::string normalized = to_lower_ascii(trim_copy(raw));
439 if (normalized.empty() || normalized ==
"1" || normalized ==
"on" || normalized ==
"true" ||
440 normalized ==
"yes" || normalized ==
"strict" || normalized ==
"fail" || normalized ==
"error") {
441 return ParserCompareMode::Strict;
443 if (normalized ==
"warn") {
444 return ParserCompareMode::Warn;
446 if (normalized ==
"0" || normalized ==
"off" || normalized ==
"false" || normalized ==
"no") {
447 return ParserCompareMode::Off;
450 return ParserCompareMode::Strict;
453 ParserFallbackMode parser_fallback_mode()
455 const char* raw = std::getenv(
"OPENCARP_PARAM_FALLBACK");
457 return ParserFallbackMode::Off;
460 const std::string normalized = to_lower_ascii(trim_copy(raw));
461 if (normalized.empty() || normalized ==
"0" || normalized ==
"off" || normalized ==
"false" ||
462 normalized ==
"no") {
463 return ParserFallbackMode::Off;
465 if (normalized ==
"legacy") {
466 return ParserFallbackMode::Legacy;
469 return ParserFallbackMode::Off;
472 void populate_arg_pointers(
const std::vector<std::string>& values, std::vector<char*>* argv)
474 argv->assign(values.size(), NULL);
475 for (std::size_t i = 0; i < values.size(); ++i) {
476 (*argv)[i] =
const_cast<char*
>(values[i].c_str());
480 void print_lines(FILE* stream,
const char* label,
const std::vector<std::string>& lines)
482 for (std::size_t i = 0; i < lines.size(); ++i) {
483 fprintf(stream,
"%s%s\n", label, lines[i].c_str());
487 void clear_active_citation_suggestions()
489 active_citation_suggestions.clear();
492 void store_active_citation_suggestions(
const std::vector<paramschema::CitationSuggestion>& suggestions)
494 active_citation_suggestions = suggestions;
497 void print_active_citation_suggestions()
499 if (active_citation_suggestions.empty() ||
get_rank() != 0) {
503 const std::vector<std::string> lines =
504 paramschema::format_citation_suggestions(active_citation_suggestions);
509 std::fprintf(stdout,
"\nIf you publish studies based on this simulation, the following references are likely relevant:\n");
510 for (std::size_t i = 0; i < lines.size(); ++i) {
511 std::fprintf(stdout,
" %s\n", lines[i].c_str());
513 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");
514 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");
519 void print_parser_runtime_state(
const std::vector<std::string>&
runtime_args)
521 const auto& schema = paramschema::openCARP_schema();
522 const std::string program_name =
525 std::string rendered;
526 std::vector<std::string> errors;
527 if (!paramschema::render_save_text(schema, program_name, &rendered, &errors)) {
528 print_lines(stderr,
"parameter parser warning: ", errors);
532 fputs(rendered.c_str(), stderr);
535 paramschema::ExecutionResult execute_parser_runtime_args(
const std::vector<std::string>&
runtime_args,
536 const bool allow_save)
538 std::vector<char*> argv;
541 const auto& schema = paramschema::openCARP_schema();
543 paramschema::ExecutionOptions options;
544 options.allow_save = allow_save;
545 return paramschema::execute_legacy_cli(schema,
static_cast<int>(argv.size()), argv.data(), options);
548 bool apply_parser_runtime_args(
const std::vector<std::string>&
runtime_args,
549 const bool allow_save,
550 paramschema::ExecutionResult* executed_out)
552 clear_active_citation_suggestions();
553 const paramschema::ExecutionResult executed = execute_parser_runtime_args(
runtime_args, allow_save);
554 if (executed_out != NULL) {
555 *executed_out = executed;
557 print_lines(stderr,
"parameter parser warning: ", executed.warnings);
558 if (!executed.rendered_output.empty()) {
559 fputs(executed.rendered_output.c_str(), stdout);
561 if (!executed.errors.empty() || executed.status == paramschema::ExecutionStatus::Fatal) {
562 print_lines(stderr,
"parameter parser error: ", executed.errors);
566 if (param_globals::output_setup) {
570 if (executed.status == paramschema::ExecutionStatus::Help) {
574 store_active_citation_suggestions(executed.citations);
578 std::string parent_directory(
const std::string& path)
580 const std::string::size_type slash = path.rfind(
'/');
581 if (slash == std::string::npos) {
587 return path.substr(0, slash);
590 std::string join_path(
const std::string& left,
const std::string& right)
592 if (left.empty() || left ==
".") {
595 if (!left.empty() && left[left.size() - 1] ==
'/') {
598 return left +
"/" + right;
601 bool find_executable_on_path(
const std::string& name, std::string* resolved_path)
603 if (name.empty() || name.find(
'/') != std::string::npos) {
607 const char* path_env = std::getenv(
"PATH");
608 if (path_env == NULL || path_env[0] ==
'\0') {
612 const std::string path_list = path_env;
613 std::string::size_type start = 0;
614 while (start <= path_list.size()) {
615 std::string::size_type end = path_list.find(
':', start);
616 if (end == std::string::npos) {
617 end = path_list.size();
620 const std::string directory = path_list.substr(start, end - start);
621 const std::string candidate = join_path(directory.empty() ?
"." : directory, name);
622 if (access(candidate.c_str(), X_OK) == 0) {
623 *resolved_path = candidate;
627 if (end == path_list.size()) {
636 bool resolve_legacy_snapshot_helper(
const std::string& program_path, std::string* helper_path)
638 std::vector<std::string> resolved_program_paths;
639 if (!program_path.empty()) {
640 resolved_program_paths.push_back(program_path);
643 if (program_path.find(
'/') == std::string::npos) {
644 std::string resolved_program_path;
645 if (find_executable_on_path(program_path, &resolved_program_path)) {
646 resolved_program_paths.push_back(resolved_program_path);
650 for (std::size_t i = 0; i < resolved_program_paths.size(); ++i) {
651 const std::string program_dir = parent_directory(resolved_program_paths[i]);
652 const std::string parent_dir = parent_directory(program_dir);
654 const std::vector<std::string> candidates = {
655 join_path(program_dir,
"param-parser-legacy-snapshot"),
656 join_path(join_path(parent_dir,
"simulator"),
"param-parser-legacy-snapshot"),
659 for (std::size_t j = 0; j < candidates.size(); ++j) {
660 if (access(candidates[j].c_str(), X_OK) == 0) {
661 *helper_path = candidates[j];
667 return find_executable_on_path(
"param-parser-legacy-snapshot", helper_path);
670 bool create_temp_output_path(
const char* suffix, std::string* path)
673 if (suffix != NULL && suffix[0] !=
'\0') {
674 std::snprintf(temp_path,
sizeof temp_path,
"/tmp/opencarp-parser-compare-XXXXXX%s", suffix);
676 std::snprintf(temp_path,
sizeof temp_path,
"/tmp/opencarp-parser-compare-XXXXXX");
679 const int fd = suffix != NULL && suffix[0] !=
'\0' ? mkstemps(temp_path,
static_cast<int>(std::strlen(suffix))) :
689 bool capture_current_parser_snapshot(paramschema::SnapshotResult* snapshot)
691 *snapshot = paramschema::snapshot_schema_state(paramschema::openCARP_schema());
692 print_lines(stderr,
"parameter compare warning: ", snapshot->warnings);
693 if (!snapshot->errors.empty()) {
694 print_lines(stderr,
"parameter compare error: ", snapshot->errors);
700 void maybe_force_test_mismatch(paramschema::SnapshotResult* snapshot)
702 const char* raw = std::getenv(
"OPENCARP_PARAM_TEST_FORCE_MISMATCH");
707 const std::string normalized = to_lower_ascii(trim_copy(raw));
708 if (normalized.empty() || normalized ==
"0" || normalized ==
"off" || normalized ==
"false" ||
709 normalized ==
"no") {
713 if (!snapshot->entries.empty()) {
714 snapshot->entries[0].value +=
"__forced_parser_compare_mismatch__";
718 paramschema::SnapshotEntry entry;
719 entry.path =
"buildinfo";
720 entry.value =
"__forced_parser_compare_mismatch__";
721 snapshot->entries.push_back(entry);
724 LegacySnapshotHelperRunResult run_legacy_snapshot_helper(
const std::string& helper_path,
726 const std::string& output_path)
728 LegacySnapshotHelperRunResult result;
729 std::vector<std::string> child_values;
731 child_values.push_back(helper_path);
732 child_values.push_back(
"--snapshot-out");
733 child_values.push_back(output_path);
734 child_values.push_back(
"--");
737 std::vector<char*> child_argv;
738 child_argv.reserve(child_values.size() + 1);
739 for (std::size_t i = 0; i < child_values.size(); ++i) {
740 child_argv.push_back(
const_cast<char*
>(child_values[i].c_str()));
742 child_argv.push_back(NULL);
744 const pid_t pid = fork();
751 execv(helper_path.c_str(), child_argv.data());
752 std::perror(
"execv");
757 if (waitpid(pid, &status, 0) < 0) {
758 std::perror(
"waitpid");
762 if (WIFSIGNALED(status)) {
763 result.signaled =
true;
764 result.signal = WTERMSIG(status);
768 if (WIFEXITED(status)) {
769 result.exited =
true;
770 result.exit_status = WEXITSTATUS(status);
776 bool legacy_snapshot_helper_rejected_input(
const LegacySnapshotHelperRunResult& result)
778 return result.exited && (result.exit_status == 3 || result.exit_status == 4);
781 bool legacy_fallback_requested(
const RuntimeCompatOptions& compat)
783 return compat.fallback_mode == ParserFallbackMode::Legacy ||
784 parser_fallback_mode() == ParserFallbackMode::Legacy;
787 bool should_print_global_message()
790 return get_rank(MPI_COMM_WORLD) == 0;
793 const char*
const rank_variables[] = {
794 "OMPI_COMM_WORLD_RANK",
797 "MV2_COMM_WORLD_RANK",
801 for (std::size_t i = 0; i <
sizeof rank_variables /
sizeof rank_variables[0]; ++i) {
802 const char* raw_rank = std::getenv(rank_variables[i]);
803 if (raw_rank == NULL || raw_rank[0] ==
'\0') {
808 const long rank = std::strtol(raw_rank, &end, 10);
809 if (end != raw_rank && end != NULL && end[0] ==
'\0') {
817 void print_legacy_fallback_workaround()
820 "parameter compare error: rerun with OPENCARP_PARAM_FALLBACK=legacy to continue with the legacy parameter state\n");
822 "parameter compare error: or add --param-fallback=legacy to the openCARP command line\n");
825 bool restore_legacy_snapshot_state(
const paramschema::SnapshotResult& legacy_snapshot)
827 const paramschema::SnapshotRestoreResult restored =
828 paramschema::restore_snapshot_state(paramschema::openCARP_schema(), legacy_snapshot);
829 print_lines(stderr,
"parameter compare warning: ", restored.warnings);
830 if (!restored.errors.empty()) {
831 print_lines(stderr,
"parameter compare error: ", restored.errors);
837 bool run_parser_legacy_compare(
const std::vector<std::string>&
runtime_args,
838 const LegacyCompareInput& legacy_input,
839 const RuntimeCompatOptions& compat)
841 const ParserCompareMode mode = parser_compare_mode();
842 if (mode == ParserCompareMode::Off) {
845 const bool fallback_to_legacy = legacy_fallback_requested(compat);
848 std::fprintf(stderr,
"parameter compare error: unable to reconstruct simulator argv\n");
849 if (fallback_to_legacy) {
850 print_legacy_fallback_workaround();
852 return mode != ParserCompareMode::Strict;
855 paramschema::SnapshotResult parser_snapshot;
856 if (!capture_current_parser_snapshot(&parser_snapshot)) {
857 std::fprintf(stderr,
"parameter compare error: unable to snapshot the parser runtime state\n");
858 if (fallback_to_legacy) {
859 print_legacy_fallback_workaround();
861 return mode != ParserCompareMode::Strict;
863 maybe_force_test_mismatch(&parser_snapshot);
865 std::string helper_path;
866 if (!resolve_legacy_snapshot_helper(
runtime_args[0], &helper_path)) {
867 std::fprintf(stderr,
"parameter compare error: unable to locate param-parser-legacy-snapshot\n");
868 if (fallback_to_legacy) {
869 print_legacy_fallback_workaround();
872 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
874 return mode != ParserCompareMode::Strict;
877 if (!legacy_input.available) {
878 const bool print_message = should_print_global_message();
879 if (print_message && legacy_input.warn_when_unavailable && !legacy_input.unavailable_reason.empty()) {
880 std::fprintf(stderr,
"parameter compare warning: %s\n", legacy_input.unavailable_reason.c_str());
882 if (print_message && fallback_to_legacy) {
884 "parameter compare warning: legacy fallback is unavailable because no legacy baseline exists for this input\n");
889 std::string snapshot_path;
890 if (!create_temp_output_path(
"", &snapshot_path)) {
891 std::fprintf(stderr,
"parameter compare error: unable to create temporary snapshot file\n");
892 if (fallback_to_legacy) {
893 print_legacy_fallback_workaround();
896 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
898 return mode != ParserCompareMode::Strict;
901 const LegacySnapshotHelperRunResult helper_result =
902 run_legacy_snapshot_helper(helper_path, legacy_input.runtime_args, snapshot_path);
903 const bool helper_ok = helper_result.exited && helper_result.exit_status == 0;
904 paramschema::SnapshotResult legacy_snapshot;
905 std::string read_error;
906 const bool loaded = helper_ok &&
907 paramschema::snapshotio::read_snapshot_file(snapshot_path, &legacy_snapshot, &read_error);
908 unlink(snapshot_path.c_str());
911 if (legacy_snapshot_helper_rejected_input(helper_result)) {
913 "parameter compare error: legacy param() rejected the normalized input while the parser runtime accepted it\n");
915 "parameter compare error: this indicates a parser/legacy validation mismatch, not snapshot helper infrastructure\n");
917 "parameter compare error: please open an issue and include the triggering command line and parameter files\n");
919 "https://git.opencarp.org/openCARP/openCARP/-/issues/new?type=ISSUE&initialCreationContext=list-route\n");
920 if (fallback_to_legacy) {
922 "parameter compare error: legacy fallback is unavailable because legacy produced no valid parameter state\n");
924 }
else if (!read_error.empty()) {
925 std::fprintf(stderr,
"parameter compare error: %s\n", read_error.c_str());
926 }
else if (helper_result.signaled) {
927 std::fprintf(stderr,
"parameter compare error: legacy snapshot helper terminated with signal %d\n",
928 helper_result.signal);
929 }
else if (helper_result.exited) {
930 std::fprintf(stderr,
"parameter compare error: legacy snapshot helper exited with status %d\n",
931 helper_result.exit_status);
933 std::fprintf(stderr,
"parameter compare error: unable to capture the legacy parameter state\n");
935 if (fallback_to_legacy && !legacy_snapshot_helper_rejected_input(helper_result)) {
936 print_legacy_fallback_workaround();
937 }
else if (!legacy_snapshot_helper_rejected_input(helper_result)) {
939 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
941 return mode != ParserCompareMode::Strict;
944 const paramschema::SnapshotComparisonResult comparison =
945 paramschema::compare_snapshot_results(paramschema::openCARP_schema(), parser_snapshot, legacy_snapshot);
946 if (!comparison.errors.empty() || !comparison.mismatches.empty()) {
947 print_lines(stderr,
"", paramschema::format_snapshot_comparison_report(comparison,
"parser compare"));
949 "parameter compare error: the parser runtime and legacy param() produced different parameter states\n");
951 "parameter compare error: please open an issue and include the triggering command line and parameter files\n");
953 "https://git.opencarp.org/openCARP/openCARP/-/issues/new?type=ISSUE&initialCreationContext=list-route\n");
955 if (fallback_to_legacy) {
956 if (!restore_legacy_snapshot_state(legacy_snapshot)) {
957 print_legacy_fallback_workaround();
960 clear_active_citation_suggestions();
961 std::fprintf(stderr,
"parameter compare warning: continuing with the legacy parameter state\n");
965 print_legacy_fallback_workaround();
966 return mode != ParserCompareMode::Strict;
974 static char input_dir[1024],
981 void localize_gridout_nodes(
sf_mesh& mesh,
1004 alg_map[nbr[local_node]] = local_node;
1007 size_t buffsize = 0;
1008 for(
int pid = 0; pid < mpi_size; pid++) {
1009 if(mpi_rank == pid) {
1010 recv_nodes = selected_nodes;
1011 buffsize = recv_nodes.
size();
1014 MPI_Bcast(&buffsize,
sizeof(
size_t), MPI_BYTE, pid, mesh.comm);
1015 recv_nodes.
resize(buffsize);
1016 MPI_Bcast(recv_nodes.
data(), buffsize *
sizeof(
mesh_int_t), MPI_BYTE, pid, mesh.comm);
1019 auto it = alg_map.
find(node);
1020 if(it != alg_map.
end())
1027 const mesh_int_t stop = layout[mpi_rank + 1];
1030 size_t buffsize = 0;
1031 for(
int pid = 0; pid < mpi_size; pid++) {
1032 if(mpi_rank == pid) {
1033 recv_nodes = selected_nodes;
1034 buffsize = recv_nodes.
size();
1037 MPI_Bcast(&buffsize,
sizeof(
size_t), MPI_BYTE, pid, mesh.comm);
1038 recv_nodes.
resize(buffsize);
1039 MPI_Bcast(recv_nodes.
data(), buffsize *
sizeof(
mesh_int_t), MPI_BYTE, pid, mesh.comm);
1042 if(node >= start && node < stop)
1052 bool mesh_has_gridout_tags(
const sf_mesh& mesh,
1055 bool local_has_tag =
false;
1056 for(
size_t eidx = 0; eidx < mesh.l_numelem; eidx++) {
1057 if(output_tags.
count(mesh.tag[eidx]) != 0) {
1058 local_has_tag =
true;
1063 int local = local_has_tag ? 1 : 0;
1065 MPI_Allreduce(&local, &global, 1, MPI_INT, MPI_MAX, mesh.comm);
1069 void extract_gridout_tag_mesh(
const sf_mesh& mesh,
1074 for(
size_t eidx = 0; eidx < mesh.l_numelem; eidx++)
1075 keep_elem[eidx] = output_tags.
count(mesh.tag[eidx]) != 0;
1077 out_mesh.name = mesh.name;
1079 if(out_mesh.g_numelem == 0) {
1080 log_msg(0, 5,
ECHO,
"gridout_tags selected no elements in \"%s\".", mesh.name.c_str());
1088 std::list<sf_mesh*> meshlist;
1089 meshlist.push_back(&out_mesh);
1093 numbering(out_mesh);
1094 out_mesh.generate_par_layout();
1100 void extract_gridout_surface_mesh(
const sf_mesh& mesh,
sf_mesh& out_mesh)
1103 out_mesh.name = mesh.name;
1111 ref_eidx.
resize(out_mesh.l_numelem);
1113 long int num_local = out_mesh.l_numelem, offset = 0;
1114 MPI_Exscan(&num_local, &offset, 1, MPI_LONG, MPI_SUM, out_mesh.comm);
1116 for(
size_t eidx = 0; eidx < out_mesh.l_numelem; eidx++)
1117 ref_eidx[eidx] = offset + eidx;
1121 std::list<sf_mesh*> meshlist;
1122 meshlist.push_back(&out_mesh);
1126 numbering(out_mesh);
1127 out_mesh.generate_par_layout();
1133 void write_gridout_surface(
const sf_mesh& mesh,
const std::string& basename)
1136 extract_gridout_surface_mesh(mesh, surf);
1142 for(
size_t i = 0; i < surf_global.con.size(); i++)
1143 surf_global.con[i] = nbr[surf_global.con[i]];
1152 LegacyCompareInput legacy_input;
1153 std::vector<std::string> normalized_args;
1154 RuntimeCompatOptions compat;
1155 std::string normalize_error;
1156 if (!build_legacy_compare_input(argc, argv, &legacy_input, &normalize_error) ||
1157 !normalize_runtime_args(argc, argv, &normalized_args, &compat, &normalize_error)) {
1158 fprintf(stderr,
"\n*** %s\n\n", normalize_error.c_str());
1162 paramschema::ExecutionResult executed;
1163 if (!apply_parser_runtime_args(normalized_args,
true, &executed)) {
1167 if (legacy_input.available) {
1168 for (std::size_t i = 0; i < executed.validation.assignments.size(); ++i) {
1169 if (!executed.validation.assignments[i].synthesized) {
1172 legacy_input.available =
false;
1173 legacy_input.runtime_args.clear();
1174 legacy_input.runtime_args.push_back(normalized_args[0]);
1175 legacy_input.unavailable_reason =
1176 "legacy compare is unavailable because the parser inferred optional controller counts from the original input";
1181 if (legacy_input.available && !executed.validation.legacy_compare_incompatible_paths.empty()) {
1182 legacy_input.available =
false;
1183 legacy_input.warn_when_unavailable =
false;
1184 legacy_input.runtime_args.clear();
1185 legacy_input.runtime_args.push_back(normalized_args[0]);
1186 legacy_input.unavailable_reason =
1187 "legacy compare is unavailable because the original input uses aggregate ID syntax without a legacy baseline for " +
1188 join_paths_for_message(executed.validation.legacy_compare_incompatible_paths);
1191 if (!run_parser_legacy_compare(normalized_args, legacy_input, compat)) {
1207 log_msg(NULL, 5,
ECHO,
"The EMI model was not compiled for this binary.\n");
1221 log_msg(0,0,0,
"\n *** Initializing physics ***\n");
1225 for (
int ii = 0; ii < param_globals::num_external_imp; ii++) {
1227 assert(loading_succeeded);
1230 if(param_globals::num_external_imp)
1231 log_msg(NULL, 4,
ECHO,
"Loading of external LIMPET modules not enabled.\n"
1232 "Recompile with DLOPEN set.\n" );
1238 log_msg(NULL, 0, 0,
"Initializing %s ..", p->
name);
1245 log_msg(0,0,0,
"\n *** Destroying physics ***\n");
1273 for (
int i=0; i<ns; i++ ) {
1281 log_msg( NULL, 1, 0,
"Extracellular stimulus %d ignored for monodomain", i );
1284 log_msg( NULL, 1, 0,
"Intracellular stimulus %d converted to transmembrane", i );
1319 Stimulus* s = param_globals::stimulus;
1321 for(
int i=0; i < param_globals::num_stim; i++) {
1329 log_msg( NULL, 4, 0,
"Elliptic system is singular!\n"
1330 "Use an explicit ground:voltage (stimulus[X].stimtype=3)\n"
1331 "Do not trust the elliptic solution of this simulation run!\n");
1336 const char* real_precision = OPENCARP_REAL_BITS == 32 ?
"single" :
"double";
1337 printf(
"\n""*** GIT tag: %s\n", GIT_COMMIT_TAG);
1338 printf(
"*** GIT hash: %s\n", GIT_COMMIT_HASH);
1339 printf(
"*** GIT repo: %s\n", GIT_PATH);
1340 printf(
"*** local index bits: %d\n", OPENCARP_LOCAL_INDEX_BITS);
1341 printf(
"*** global index bits: %d\n", OPENCARP_GLOBAL_INDEX_BITS);
1342 printf(
"*** real precision: %s (%d-bit)\n", real_precision, OPENCARP_REAL_BITS);
1343 printf(
"*** dependency commits: %s\n\n", SUBREPO_COMMITS);
1349 param_globals::dt /= 1000.;
1353 if(param_globals::mass_lumping == 0 && param_globals::parab_solve==0) {
1354 log_msg(0,5,0,
"parab_solve = 0 (explicit) requires mass_lumping = 1. "
1355 "Either enable mass lumping or choose an implicit parab_solve method.");
1360 if(!param_globals::extracell_monodomain_stim)
1369 if(param_globals::t_sentinel > 0 && param_globals::sentinel_ID < 0 ) {
1370 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");
1373 if(param_globals::num_external_imp > 0 ) {
1374 for(
int ext_imp_i = 0; ext_imp_i < param_globals::num_external_imp; ext_imp_i++) {
1375 if(param_globals::external_imp[ext_imp_i][0] !=
'/') {
1376 log_msg(0,5,0,
"external_imp[%d] error: absolute paths must be used for .so file loading (\'%s\')",
1377 ext_imp_i, param_globals::external_imp[ext_imp_i]);
1383 if(param_globals::experiment ==
EXP_LAPLACE && param_globals::bidomain != 1) {
1384 log_msg(0,4,0,
"Warning: Laplace experiment mode requires bidomain = 1. Setting bidomain = 1.");
1385 param_globals::bidomain = 1;
1388 if(param_globals::num_phys_regions == 0) {
1389 log_msg(0,4,0,
"Warning: No physics region defined! Please set phys_region parameters to correctly define physics.");
1392 log_msg(0,4,0,
"Intra-elec and Extra-elec domains will be derived from fibers.\n");
1393 param_globals::num_phys_regions = param_globals::bidomain ? 2 : 1;
1394 param_globals::phys_region = (p_region*) calloc(param_globals::num_phys_regions,
sizeof(p_region));
1396 param_globals::phys_region[0].name = strdup(
"Autogenerated intracellular Electrics");
1397 param_globals::phys_region[0].num_IDs = 0;
1399 if(param_globals::bidomain) {
1401 param_globals::phys_region[1].name = strdup(
"Autogenerated extracellular Electrics");
1402 param_globals::phys_region[1].num_IDs = 0;
1405 log_msg(0,4,0,
"Laplace domain will be derived from fibers.\n");
1406 param_globals::num_phys_regions = 1;
1407 param_globals::phys_region = (p_region*) calloc(param_globals::num_phys_regions,
sizeof(p_region));
1409 param_globals::phys_region[0].name = strdup(
"Autogenerated Laplace");
1410 param_globals::phys_region[0].num_IDs = 0;
1415 log_msg(0,4,0,
"Warning: Laplace experiment mode requires a laplace physics regions defined.");
1419 log_msg(0,4,0,
"Converting the defined extracellular-electrics-region to laplace-region.");
1422 log_msg(0,4,0,
"Converting the defined intracellular-electrics-region to laplace-region.");
1425 param_globals::num_phys_regions += 1;
1426 param_globals::phys_region = (p_region*) realloc(param_globals::phys_region, param_globals::num_phys_regions *
sizeof(p_region));
1428 param_globals::phys_region[param_globals::num_phys_regions - 1].ptype =
PHYSREG_LAPLACE;
1429 param_globals::phys_region[param_globals::num_phys_regions - 1].name = strdup(
"Autogenerated Laplace");
1430 param_globals::phys_region[param_globals::num_phys_regions - 1].num_IDs = 0;
1434 #ifndef WITH_PARMETIS
1435 if(param_globals::pstrat == 1) {
1436 log_msg(0,3,0,
"openCARP was built without Parmetis support. Swithing to KDtree.");
1437 param_globals::pstrat = 2;
1442 bool legacy_stim_set =
false, new_stim_set =
false;
1444 for(
int i=0; i<param_globals::num_stim; i++) {
1445 Stimulus & legacy_stim = param_globals::stimulus[i];
1446 Stim & new_stim = param_globals::stim[i];
1448 if(legacy_stim.stimtype || legacy_stim.strength)
1449 legacy_stim_set =
true;
1451 if(new_stim.crct.type || new_stim.pulse.strength)
1452 new_stim_set =
true;
1455 if(legacy_stim_set || new_stim_set) {
1456 if(legacy_stim_set && new_stim_set) {
1457 log_msg(0,4,0,
"Warning: Legacy stimuli and default stimuli are defined. Only default stimuli will be used!");
1459 else if (legacy_stim_set) {
1460 log_msg(0,1,0,
"Warning: Legacy stimuli defined. Please consider switching to stimulus definition \"stim[]\"!");
1465 log_msg(0,4,0,
"Warning: No potential or current stimuli found!");
1475 log_msg(0, 0, 0,
"LICENSE NOTICE:");
1477 "This run uses openCARP components licensed under the Academic Public License v1.1.");
1479 "That includes code from simulator/**, param/**, and APL-licensed parts of physics/**.");
1481 "Commercial use is not allowed without a separate commercial license.");
1486 int flg = 0, err = 0, rank =
get_rank();
1488 char *ptr = getcwd(current_dir, 1024);
1489 if (ptr == NULL) err++;
1490 ptr = getcwd(input_dir, 1024);
1491 if (ptr == NULL) err++;
1497 if (strcmp(sim_ID,
"OUTPUT_DIR")) {
1498 if (mkdir(sim_ID, 0775)) {
1499 if (errno == EEXIST ) {
1500 log_msg(NULL, 2, 0,
"Output directory exists: %s\n", sim_ID);
1502 log_msg(NULL, 5, 0,
"Unable to make output directory\n");
1506 }
else if (mkdir(sim_ID, 0775) && errno != EEXIST) {
1507 log_msg(NULL, 5, 0,
"Unable to make output directory\n");
1515 err += chdir(sim_ID);
1516 ptr = getcwd(output_dir, 1024);
1517 if (ptr == NULL) err++;
1522 err += chdir(output_dir);
1525 if (rank == 0 && ((param_globals::experiment==
EXP_POSTPROCESS) || (param_globals::post_processing_opts &
LEADFIELD))) {
1527 if (strcmp(param_globals::ppID,
"POSTPROC_DIR")) {
1528 if (mkdir(param_globals::ppID, 0775)) {
1529 if (errno == EEXIST ) {
1530 log_msg(NULL, 2,
ECHO,
"Postprocessing directory exists: %s\n\n", param_globals::ppID);
1532 log_msg(NULL, 5,
ECHO,
"Unable to make postprocessing directory\n\n");
1536 }
else if (mkdir(param_globals::ppID, 0775) && errno != EEXIST) {
1537 log_msg(NULL, 5,
ECHO,
"Unable to make postprocessing directory\n\n");
1545 err += chdir(param_globals::ppID);
1546 ptr = getcwd(postproc_dir, 1024);
1547 if (ptr == NULL) err++;
1548 err = chdir(output_dir);
1557 bool io_node =
false;
1560 if (param_globals::num_io_nodes > 0) {
1563 log_msg(NULL, 5, 0,
"You cannot run with async IO on only one core.\n");
1567 if (2 * param_globals::num_io_nodes >= psize) {
1568 log_msg(NULL, 5, 0,
"The number of IO cores be less " "than the number of compute cores.");
1572 if (param_globals::num_PS_nodes && param_globals::num_io_nodes > param_globals::num_PS_nodes) {
1574 "The number of IO cores (%d) should not "
1575 "exceed the number of PS compute cores (%d).\n",
1576 param_globals::num_io_nodes, param_globals::num_PS_nodes);
1581 io_node = prank < param_globals::num_io_nodes;
1584 MPI_Comm_split(PETSC_COMM_WORLD, io_node,
get_rank(), &comm);
1585 MPI_Comm_set_name(comm, io_node ?
"IO" :
"compute");
1587 PETSC_COMM_WORLD = comm;
1591 MPI_Intercomm_create(comm, 0, MPI_COMM_WORLD, io_node ? param_globals::num_io_nodes : 0,
1595 log_msg(NULL, 4, 0,
"Global node %d, Comm rank %d != Intercomm rank %d\n",
1599 MPI_Comm_set_name(PETSC_COMM_WORLD,
"compute");
1604 if((io_node || !param_globals::num_io_nodes) && !prank)
1606 if(
get_global(flg, MPI_SUM)) EXIT(EXIT_FAILURE);
1612 getcwd(current_dir, 1024);
1619 if (dest==
OUTPUT) err = chdir(output_dir);
1620 else if (dest==
POSTPROC) err = chdir(postproc_dir);
1621 else if (dest==
CURDIR) err = chdir(current_dir);
1622 else err = chdir(input_dir);
1630 double start_time = 0.0;
1633 double end_time = param_globals::tend;
1647 if(param_globals::num_tsav) {
1648 std::vector<double> trig(param_globals::num_tsav);
1649 for(
size_t i=0; i<trig.size(); i++) trig[i] = param_globals::tsav[i];
1654 if(param_globals::chkpt_intv)
1656 param_globals::chkpt_intv, 0,
iotm_chkpt_intv,
"interval checkpointing");
1658 if(param_globals::num_trace)
1662 #ifdef WITH_POWERCAPPING
1663 void basic_powercapping_setup()
1665 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);
1668 void basic_powercapping_cleanup()
1672 delete user_globals::pc_manager; user_globals::pc_manager =
nullptr;
1679 const short padding = 4;
1684 if(col_width[0] <
int(strlen(buff)+padding))
1685 col_width[0] = strlen(buff)+padding;
1688 if(col_width[1] <
int(strlen(buff)+padding))
1689 col_width[1] = strlen(buff)+padding;
1692 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1694 int timer_id = used_timer_ids[tid];
1704 snprintf(buff,
sizeof buff,
"%.3lf", val);
1705 if(col_width[col] <
int(strlen(buff)+padding))
1706 col_width[col] = strlen(buff)+padding;
1723 const char* smpl_endl =
"\n";
1731 log_msg(0,5,0,
"Protocol file %s could not be opened for writing!\n", fname);
1744 std::vector<std::string> col_labels = {
"time",
"tick"};
1745 std::vector<std::string> col_short_labels = {
"A",
"B"};
1746 std::vector<std::string> col_unit_labels = {
"ms",
"--" };
1747 std::vector<int> col_width = {4, 4};
1749 char c_label = {
'C'};
1750 std::string label = {
""};
1751 std::string unit = {
""};
1755 std::vector<int> used_timer_ids;
1756 std::vector<int> used_stim_ids;
1766 used_stim_ids.push_back(sidx);
1776 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1778 int timer_id = used_timer_ids[tid];
1781 int llen = strlen(t->
name);
1782 mx_llen = llen > mx_llen ? llen : mx_llen;
1785 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1787 int timer_id = used_timer_ids[tid];
1790 col_labels.push_back(t->
name);
1792 col_short_labels.push_back(label);
1797 if(unit.empty()) unit =
"--";
1798 col_unit_labels.push_back(unit);
1799 col_width.push_back(4);
1807 fh <<
"# Protocol header\n#\n" <<
"# Legend:\n";
1808 for(
size_t i = 0; i<col_short_labels.size(); i++)
1810 fh <<
"#" << std::setw(2) << col_short_labels[i] <<
" = " << std::setw(mx_llen) << col_labels[i];
1811 fh <<
" [" << std::setw(10) << col_unit_labels[i] <<
"]";
1813 if(i >= 2 && used_stim_ids[i-2] > -1) {
1818 fh <<
" ground stim" << smpl_endl;
1820 fh <<
" applied: " << std::to_string(s.
pulse.
strength) << smpl_endl;
1831 for(
size_t i = 0; i<col_short_labels.size(); i++)
1832 fh << std::setw(col_width[i] - 3) << col_short_labels[i].c_str() << std::setw(3) <<
" ";
1835 fh << smpl_endl <<
"#";
1836 for(
size_t i = 0; i<col_unit_labels.size(); i++)
1837 fh <<
"[" << std::setw(col_width[i]-2) << col_unit_labels[i].c_str() <<
"]";
1840 fh << smpl_endl << std::fixed;
1848 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1850 int timer_id = used_timer_ids[tid];
1856 fh << std::setw(col_width[col]) << On;
1864 fh << std::setw(col_width[col]) << std::setprecision(3) << val;
1886 const char* h1_prog =
"PROG\t----- \t----\t-------\t-------|";
1887 const char* h2_prog =
"time\t%%comp\ttime\t ctime \t ETA |";
1888 const char* h1_wc =
"\tELAPS |";
1889 const char* h2_wc =
"\twc |";
1894 log_msg(NULL, 0, 0,
"%s", h1_prog );
1902 int req_hours = ((int)(time)) / 3600;
1903 int req_min = (((int)(time)) % 3600) / 60;
1904 int req_sec = (((int)(time)) % 3600) % 60;
1906 snprintf(str, str_size,
"%d:%02d:%02d", req_hours, req_min, req_sec);
1919 char elapsed_time_str[256];
1920 char req_time_str[256];
1924 log_msg( NULL, 0,
NONL,
"%.2f\t%.1f\t%.1f\t%s\t%s",
1942 if(!have_timedependent_phys) {
1943 log_msg(0,0,0,
"\n no time-dependent physics region registered, skipping simulate loop..\n");
1947 log_msg(0,0,0,
"\n *** Launching simulation ***\n");
1951 if(param_globals::dump_protocol)
1958 #ifdef WITH_POWERCAPPING
1959 basic_powercapping_setup();
1960 powercapping_manager *pc = user_globals::pc_manager;
1971 #ifdef WITH_POWERCAPPING
1972 std::vector<int> flops_per_rank;
1977 #ifdef WITH_POWERCAPPING
1978 pc->iteration_begin(flops_per_rank);
1985 it.second->output_step();
1988 #ifdef WITH_POWERCAPPING
1989 pc->sample(
"output step");
1997 #ifdef WITH_POWERCAPPING
1998 pc->sample(p->
name);
2002 #ifdef WITH_POWERCAPPING
2003 pc->iteration_end(flops_per_rank);
2011 log_msg(0,0,0,
"\n\nTimings of individual physics:");
2012 log_msg(0,0,0,
"------------------------------\n");
2019 #ifdef WITH_POWERCAPPING
2021 basic_powercapping_cleanup();
2027 if(param_globals::post_processing_opts &
RECOVER_PHIE) {
2028 log_msg(NULL,0,
ECHO,
"\nPOSTPROCESSOR: Recovering Phie ...");
2029 log_msg(NULL,0,
ECHO,
"----------------------------------\n");
2035 log_msg(NULL,0,
ECHO,
"\n-----------------------------------------");
2036 log_msg(NULL,0,
ECHO,
"POSTPROCESSOR: Successfully recoverd Phie.\n");
2040 if(param_globals::post_processing_opts &
LEADFIELD) {
2041 #ifdef WITH_LEADFIELD
2042 log_msg(NULL,0,
ECHO,
"\nPOSTPROCESSOR: Computing leadfields ...");
2043 log_msg(NULL,0,
ECHO,
"-------------------------------------\n");
2047 log_msg(NULL, 5, 0,
"Error: Leadfield requires active EP physics. Aborting.");
2051 int err = leadfield.
run(*elec);
2054 log_msg(NULL,0,
ECHO,
"\n------------------------------------------");
2055 log_msg(NULL,0,
ECHO,
"POSTPROCESSOR: Successfully computed leadfields.\n");
2058 log_msg(NULL, 5, 0,
"Error: leadfield support not compiled in. Rebuild with -DENABLE_LEADFIELD=ON.");
2071 if(error_if_missing) {
2072 log_msg(0,5,0,
"%s error: required physic is not active! Usually this is due to an inconsistent experiment configuration. Aborting!", __func__);
2096 log_msg(0,5,0,
"%s warning: trying to register already registered data vector.", __func__);
2110 auto register_new_mesh = [&] (
mesh_t mt,
int pidx) {
2111 if(!mesh_registry.count(mt)) {
2112 mesh_registry[mt] =
sf_mesh();
2113 mesh_registry[mt].name = param_globals::phys_region[pidx].name;
2115 return &mesh_registry[mt];
2119 for(
int i=0; i<param_globals::num_phys_regions; i++)
2123 switch(param_globals::phys_region[i].ptype) {
2135 curmesh = register_new_mesh(
emi_msh, i);
2140 log_msg(0,5,0,
"Unsupported mesh type %d! Aborting!", param_globals::phys_region[i].ptype);
2146 for(
int j=0; j<param_globals::phys_region[i].num_IDs; j++)
2164 if(ntr == 0)
return;
2169 for (
int i=0; i<ntr; i++) {
2178 shape.
p0.
x = tagRegs[i].p0[0];
2179 shape.
p0.
y = tagRegs[i].p0[1];
2180 shape.
p0.
z = tagRegs[i].p0[2];
2181 shape.
p1.
x = tagRegs[i].p1[0];
2182 shape.
p1.
y = tagRegs[i].p1[1];
2183 shape.
p1.
z = tagRegs[i].p1[2];
2184 shape.
radius = tagRegs[i].radius;
2191 log_msg(0,3,0,
"Tag region %d is empty", i);
2193 for(
size_t j=0; j<elem_indices.
size(); j++)
2194 mesh.
tag[elem_indices[j]] = tagRegs[i].tag;
2198 if(strlen(param_globals::retagfile))
2213 size_t renormalised_count = 0;
2219 for (
size_t i = 0; i < l_numelem; i++)
2221 const mesh_real_t f0 = fib[3*i+0], f1 = fib[3*i+1], f2 = fib[3*i+2];
2222 mesh_real_t fibre_len = sqrt(f0*f0 + f1*f1 + f2*f2);
2224 if (fibre_len && fabs(fibre_len - 1) > 1e-3) {
2225 fib[3 * i + 0] /= fibre_len;
2226 fib[3 * i + 1] /= fibre_len;
2227 fib[3 * i + 2] /= fibre_len;
2228 renormalised_count++;
2232 return renormalised_count;
2237 log_msg(0,0,0,
"\n *** Processing meshes ***\n");
2239 const std::string basename = param_globals::meshname;
2240 const int verb = param_globals::output_level;
2248 MPI_Comm comm = ref_mesh.
comm;
2251 double t1, t2, s1, s2;
2252 MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
2258 std::list< sf_mesh* > ptsread_list;
2261 t1 = MPI_Wtime(); s1 = t1;
2262 if(verb)
log_msg(NULL, 0, 0,
"Reading reference mesh: %s.*", basename.c_str());
2267 if (strlen(param_globals::tagfile)) {
2268 if(verb)
log_msg(NULL, 0, 0,
"Overriding element tags from: %s", param_globals::tagfile);
2273 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2275 bool check_fibre_normality =
true;
2276 if (check_fibre_normality and ref_mesh.
fib.
size()>0) {
2282 size_t l_num_fixed_she = 0;
2286 unsigned long fixed[2] = {(
unsigned long) l_num_fixed_fib, (
unsigned long) l_num_fixed_she};
2287 MPI_Allreduce(MPI_IN_PLACE, fixed, 2, MPI_UNSIGNED_LONG, MPI_SUM, comm);
2289 if (fixed[0] + fixed[1] > 0)
2290 log_msg(NULL, 0, 0,
"Renormalised %ld longitudinal and %ld sheet-transverse fibre vectors.", fixed[0], fixed[1]);
2293 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2296 if(param_globals::numtagreg > 0) {
2297 log_msg(0, 0, 0,
"Re-tagging reference mesh");
2301 ptsread_list.push_back(&ref_mesh);
2304 retag_elements(ref_mesh, param_globals::tagreg, param_globals::numtagreg);
2307 ptsread_list.clear();
2310 if(verb)
log_msg(NULL, 0, 0,
"Processing submeshes");
2312 bool have_emi_mesh =
false;
2313 bool have_non_emi_mesh =
false;
2315 for(
auto it = mesh_registry.begin(); it != mesh_registry.end(); ++it) {
2316 mesh_t grid_type = it->first;
2317 sf_mesh & submesh = it->second;
2321 have_emi_mesh =
true;
2323 have_non_emi_mesh =
true;
2325 if(verb > 1)
log_msg(NULL, 0, 0,
"\nSubmesh name: %s", submesh.
name.c_str());
2342 if(verb > 1)
log_msg(NULL, 0, 0,
"Extraction done in %f sec.",
float(t2 - t1));
2344 ptsread_list.push_back(&submesh);
2348 if(have_emi_mesh && have_non_emi_mesh) {
2349 log_msg(NULL, 5,
ECHO,
"EMI and non-EMI submeshes cannot be mixed during mesh setup.");
2354 if(param_globals::pstrat == 2 && have_non_emi_mesh)
2357 for(
auto it = mesh_registry.begin(); it != mesh_registry.end(); ++it)
2359 mesh_t grid_type = it->first;
2360 sf_mesh & submesh = it->second;
2362 if(verb > 2)
log_msg(NULL, 0, 0,
"\nSubmesh name: %s", submesh.
name.c_str());
2367 switch(param_globals::pstrat) {
2369 if(verb > 2)
log_msg(NULL, 0, 0,
"Using linear partitioning ..");
2372 #ifdef WITH_PARMETIS
2375 if(verb > 2)
log_msg(NULL, 0, 0,
"Using Parmetis partitioner ..");
2376 SF::parmetis_partitioner<mesh_int_t, mesh_real_t> partitioner(param_globals::pstrat_imbalance, 2);
2377 partitioner(submesh, part);
2383 if(verb > 2)
log_msg(NULL, 0, 0,
"Using KDtree partitioner ..");
2385 partitioner(submesh, part);
2390 if(verb > 2)
log_msg(NULL, 0, 0,
"Partitioning done in %f sec.",
float(t2 - t1));
2392 if(param_globals::pstrat > 0) {
2393 if(param_globals::gridout_p) {
2394 std::string out_name =
get_basename(param_globals::meshname);
2399 log_msg(0,0,0,
"Writing \"%s\" partitioning to: %s", submesh.
name.c_str(), out_name.c_str());
2406 if(verb > 2)
log_msg(NULL, 0, 0,
"Redistributing done in %f sec.",
float(t2 - t1));
2411 sm_numbering(submesh);
2413 if(verb > 2)
log_msg(NULL, 0, 0,
"Canonical numbering done in %f sec.",
float(t2 - t1));
2418 p_numbering(submesh);
2420 if(verb > 2)
log_msg(NULL, 0, 0,
"PETSc numbering done in %f sec.",
float(t2 - t1));
2425 if(have_non_emi_mesh)
2431 if(verb)
log_msg(NULL, 0, 0,
"All done in %f sec.",
float(s2 - s1));
2436 static const char* parameter_name =
"gridout_tags";
2437 const std::string spec = param_globals::gridout_tags ? param_globals::gridout_tags :
"";
2439 std::vector<int> tags;
2441 if(!paramschema::parse_idset_spec(spec, &tags, &error)) {
2442 log_msg(0, 5,
ECHO,
"Could not parse %s: %s.", parameter_name, error.c_str());
2446 output_tags.
clear();
2447 output_tags.
insert(tags.begin(), tags.end());
2449 if(output_tags.
size() == 0)
return false;
2451 log_msg(0, 0, 0,
"Restricting grid output to %zu tag(s) from %s.",
2452 output_tags.
size(), parameter_name);
2463 for(
size_t eidx = 0; eidx < mesh.
l_numelem; eidx++) {
2464 if(output_tags.
count(mesh.
tag[eidx]) == 0)
continue;
2470 localize_gridout_nodes(mesh, selected_nodes, output_idx, async);
2478 for(
mesh_t mesh_id : mesh_ids) {
2482 for(
size_t eidx = 0; eidx < mesh.
l_numelem; eidx++) {
2483 if(output_tags.
count(mesh.
tag[eidx]) != 0)
2493 for(
int tag : sorted_tags) {
2494 int local_found = local_seen.
count(tag) ? 1 : 0;
2495 int global_found = 0;
2496 MPI_Allreduce(&local_found, &global_found, 1, MPI_INT, MPI_MAX, PETSC_COMM_WORLD);
2497 if(global_found == 0)
2501 if(missing_tags.
size()) {
2504 std::stringstream msg;
2505 for(
size_t i = 0; i < missing_tags.
size(); i++) {
2507 msg << missing_tags[i];
2510 log_msg(0, 3,
ECHO,
"Warning: ignoring gridout_tags not present in the selected output mesh(es): %s.",
2513 for(
int tag : missing_tags)
2514 output_tags.
erase(tag);
2516 if(output_tags.
size() == 0) {
2517 log_msg(0, 5,
ECHO,
"gridout_tags did not match any tag in the selected output mesh(es).");
2536 size_t lhs = 0, rhs = 0;
2537 while(lhs < restr->size() && rhs < selected.
size()) {
2538 if((*restr)[lhs] == selected[rhs]) {
2542 }
else if((*restr)[lhs] < selected[rhs]) {
2549 *restr = intersection;
2559 std::string output_base =
get_basename(param_globals::meshname);
2561 const bool restrict_gridout =
2563 if(restrict_gridout) {
2570 if(write_intra_elec) {
2574 if(restrict_gridout) {
2575 if(!mesh_has_gridout_tags(mesh, output_tags)) {
2576 log_msg(0, 5,
ECHO,
"gridout_tags selected no intracellular grid elements.");
2579 extract_gridout_tag_mesh(mesh, output_tags, restricted_mesh);
2580 output_mesh = &restricted_mesh;
2583 if(param_globals::gridout_i & 1) {
2584 if(param_globals::output_level > 1)
2585 log_msg(0,0,0,
"Computing \"%s\" surface ..", output_mesh->
name.c_str());
2587 std::string output_file = output_base +
"_i.surf";
2588 log_msg(0,0,0,
"Writing \"%s\" surface: %s", output_mesh->
name.c_str(), output_file.c_str());
2590 if(param_globals::gridout_i & 2) {
2598 write_gridout_surface(*output_mesh, output_base +
"_i");
2601 if(param_globals::gridout_i & 2) {
2602 bool write_binary =
false;
2604 std::string output_file = output_base +
"_i";
2605 log_msg(0,0,0,
"Writing \"%s\" mesh: %s", output_mesh->
name.c_str(), output_file.c_str());
2610 if(write_extra_elec) {
2614 if(restrict_gridout) {
2615 if(!mesh_has_gridout_tags(mesh, output_tags)) {
2616 log_msg(0, 5,
ECHO,
"gridout_tags selected no extracellular grid elements.");
2619 extract_gridout_tag_mesh(mesh, output_tags, restricted_mesh);
2620 output_mesh = &restricted_mesh;
2623 if(param_globals::gridout_e & 1) {
2624 if(param_globals::output_level > 1)
2625 log_msg(0,0,0,
"Computing \"%s\" surface ..", output_mesh->
name.c_str());
2627 std::string output_file = output_base +
"_e.surf";
2628 log_msg(0,0,0,
"Writing \"%s\" surface: %s", output_mesh->
name.c_str(), output_file.c_str());
2630 if(param_globals::gridout_e & 2) {
2638 write_gridout_surface(*output_mesh, output_base +
"_e");
2641 if(param_globals::gridout_e & 2) {
2642 bool write_binary =
false;
2643 std::string output_file = output_base +
"_e";
2644 log_msg(0,0,0,
"Writing \"%s\" mesh: %s", output_mesh->
name.c_str(), output_file.c_str());
2654 print_active_citation_suggestions();
2656 const paramschema::ResetResult reset = paramschema::reset_schema_state(paramschema::openCARP_schema());
2657 print_lines(stderr,
"parameter cleanup warning: ", reset.warnings);
2658 print_lines(stderr,
"parameter cleanup error: ", reset.errors);
2670 char* filecopy =
dupstr(file);
2671 char* dir =
dupstr(dirname(filecopy));
2688 PetscErrorPrintf = PetscErrorPrintfNone;
2698 for(
size_t eidx = 0; eidx < mesh.
l_numelem; eidx++) {
2701 if(mindim < cdim) mindim = cdim;
2725 mesh.
pl.algebraic_layout();
2729 for(
size_t i = 0; i < idx->
size(); i++)
2738 int gsize = inp_data->
gsize();
2744 regigb.
x(gsize / dpn);
2745 regigb.
dim_x(regigb.
x()-1);
2748 regigb.
y(1); regigb.
z(1);
2764 regigb.
inc_t(param_globals::spacedt);
2778 log_msg(0,5,0,
"%s error: Could not set up data output! Aborting!", __func__);
2785 IO.
spec = mesh_spec;
2795 buffmap[mesh_spec] = inp_copy;
2802 void igb_output_manager::register_output_async(
sf_vec* inp_data,
2830 for(
size_t i=0; i<alg_nod.
size(); i++)
2831 ioidx[i] = nbr[alg_nod[i]];
2836 for(
size_t i=0; i<idx->
size(); i++) {
2838 ioidx[i] = nbr[loc_nodal];
2858 if(param_globals::num_io_nodes == 0)
2861 register_output_async(inp_data, inp_meshid, dpn, name,
units, idx, elem_data);
2876 sc->
forward(*data_vec, *perm_vec);
2891 FILE* fd =
static_cast<FILE*
>(it.
igb.
fileptr());
2894 sf_vec* buff = fill_output_buffer(it);
2900 const int dpn = it.
spec.
v2;
2907 for(
int j = 0; j < dpn; j++)
2944 FILE* fd =
static_cast<FILE*
>(it.
igb.
fileptr());
2975 bool render_output_parameter_arguments(
int argc,
2977 std::vector<std::string>* rendered_values)
2979 rendered_values->assign(
static_cast<std::size_t
>(argc), std::string());
2981 for (
int i = 1; i < argc; ++i) {
2982 const std::string argument(argv[i]);
2983 if (argument ==
"+F" || argument.find(
"_options_file") != std::string::npos) {
2984 if (i + 1 >= argc) {
2985 std::fprintf(stderr,
"parameter file error: missing value for '%s'\n", argument.c_str());
2988 if (argument.find(
"_options_file") != std::string::npos) {
2989 std::string render_error;
2991 std::fprintf(stderr,
"parameter file error: cannot render '%s': %s\n",
2992 argument.substr(1).c_str(), render_error.c_str());
3000 if (argument.empty() || argument[0] !=
'-') {
3004 const bool prelast = (i == argc - 1);
3005 const bool parameter_follows = !prelast &&
3006 ((argv[i + 1][0] !=
'-') ||
3007 ((argv[i + 1][1] >=
'0') && (argv[i + 1][1] <=
'9')));
3008 if (!parameter_follows) {
3012 const std::string raw_value(argv[i + 1]);
3013 std::string::size_type front = 0;
3014 while (front < raw_value.size() && raw_value[front] ==
' ') {
3018 bool aggregate_value =
false;
3019 std::string value = raw_value.substr(front);
3020 if (front < raw_value.size() && raw_value[front] ==
'{') {
3021 aggregate_value =
true;
3023 while (front < raw_value.size() && raw_value[front] ==
' ') {
3027 std::string::size_type back = raw_value.size();
3028 while (back > front && raw_value[back - 1] ==
' ') {
3031 if (back > front && raw_value[back - 1] ==
'}') {
3033 value = raw_value.substr(front, back - front);
3035 value = raw_value.substr(front);
3039 std::string render_error;
3041 std::fprintf(stderr,
"parameter file error: cannot render '%s': %s\n",
3042 argv[i] + 1, render_error.c_str());
3055 const int max_line_len = 128;
3056 const char* file_sep =
"#=======================================================";
3062 std::vector<std::string> rendered_values;
3063 if (!render_output_parameter_arguments(argc, argv, &rendered_values)) {
3068 fprintf(out->
fd,
"# CARP GIT commit hash: %s\n", GIT_COMMIT_HASH);
3069 fprintf(out->
fd,
"# dependency hashes: %s\n", SUBREPO_COMMITS);
3070 fprintf(out->
fd,
"\n");
3073 char line[8196] =
"# ";
3075 for (
int j=0; j<argc; j++) {
3076 strcat(line, argv[j]);
3077 if(strlen(line) > max_line_len) {
3078 fprintf(out->
fd,
"%s\n", line);
3084 fprintf(out->
fd,
"%s\n\n", line);
3088 for (
int i=1; i<argc; i++) {
3089 std::string argument(argv[i]);
3090 if (argument ==
"+F" || argument.find(
"_options_file")!= std::string::npos) {
3092 std::string init =
"";
3093 if (argument.find(
"_options_file")!= std::string::npos) {
3094 fprintf(out->
fd,
"%s = %s\n", argument.substr(1).c_str(), rendered_values[i].c_str());
3097 fprintf(out->
fd,
"%s>>\n", file_sep);
3100 fprintf(out->
fd,
"## %s ##\n", argv[i]);
3101 FILE *in = fopen(argv[i],
"r");
3102 while (fgets(line, 8196, in))
3103 fprintf(out->
fd,
"%s%s", init.c_str(), line);
3105 fprintf(out->
fd,
"\n##END of %s\n", argv[i]);
3106 fprintf(out->
fd,
"%s<<\n\n", file_sep);
3108 else if(argv[i][0] ==
'-')
3110 bool prelast = (i==argc-1);
3111 bool paramFollows = !prelast && ((argv[i+1][0] !=
'-') ||
3112 ((argv[i+1][1] >=
'0') && (argv[i+1][1] <=
'9')));
3117 fprintf(out->
fd,
"%-40s= %s\n", argv[i]+1, rendered_values[i].c_str());
3121 fprintf(out->
fd,
"%-40s= 1\n", argv[i]);
3129 bool aggregate_value,
3130 std::string* rendered,
3133 if (aggregate_value) {
3135 if (error != NULL) {
3140 return paramschema::quote_legacy_par_value(value, rendered, error);
3152 snprintf(save_fn,
sizeof save_fn,
"exit.save.%.3f.roe", time);
3153 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.
Lead field ECG computation.
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 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)
bool output_parameter_file(const char *fname, int argc, char **argv)
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.
bool render_parameter_file_value(const std::string &value, bool aggregate_value, std::string *rendered, std::string *error)
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 log_runtime_apl_notice()
log the runtime APL notice for the simulator binary.
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