19 #include "opencarp_types.h"
23 #ifdef WITH_POWERCAPPING
25 #include "powercapping.h"
40 #include "openCARP_schema.hpp"
41 #include "runtime.hpp"
42 #include "snapshot_file_io.hpp"
48 enum class ParserCompareMode {
54 enum class ParserFallbackMode {
59 struct RuntimeCompatOptions {
63 struct LegacyCompareInput {
70 struct LegacySnapshotHelperRunResult {
77 std::vector<paramschema::CitationSuggestion> active_citation_suggestions;
79 std::string trim_copy(
const std::string& value);
80 std::string to_lower_ascii(std::string value);
81 bool build_legacy_compare_input(
int argc,
char** argv, LegacyCompareInput* input, std::string* error);
83 std::string join_paths_for_message(
const std::vector<std::string>& paths)
89 std::ostringstream joined;
90 for (std::size_t i = 0; i < paths.size(); ++i) {
99 bool match_long_option(
const std::string& token,
const char* option, std::string* attached_value)
101 *attached_value = std::string();
102 if (token == option) {
106 const std::string prefix = std::string(option) +
"=";
107 if (token.size() > prefix.size() && token.compare(0, prefix.size(), prefix) == 0) {
108 *attached_value = token.substr(prefix.size());
115 bool is_help_topic_candidate(
const char* token)
117 return token != NULL && token[0] !=
'\0' && token[0] !=
'-' && token[0] !=
'+';
120 bool is_long_option_argument_error(
const std::string& token,
122 const std::string& attached_value,
125 if (attached_value.empty()) {
129 *error =
"Unexpected argument for " + std::string(option) +
" in '" + token +
"'";
133 bool normalize_runtime_args(
int argc,
135 std::vector<std::string>* normalized,
136 RuntimeCompatOptions* compat,
140 compat->fallback_mode = ParserFallbackMode::Off;
142 if (argc <= 0 || argv == NULL || argv[0] == NULL) {
143 *error =
"Missing program name";
147 normalized->push_back(argv[0]);
149 for (
int i = 1; i < argc; ++i) {
150 const std::string token = argv[i];
155 std::string attached_value;
157 if (token ==
"+Help" || match_long_option(token,
"--help", &attached_value)) {
158 std::string topic =
"PrM";
159 if (!attached_value.empty()) {
160 topic = attached_value;
161 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
164 normalized->push_back(
"+Help");
165 normalized->push_back(topic);
169 if (token ==
"+Doc" || match_long_option(token,
"--doc", &attached_value)) {
170 std::string topic =
"ALL";
171 if (!attached_value.empty()) {
172 topic = attached_value;
173 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
176 normalized->push_back(
"+Doc");
177 normalized->push_back(topic);
181 if (token ==
"+Default" || match_long_option(token,
"--default", &attached_value)) {
182 if (token !=
"+Default" && is_long_option_argument_error(token,
"--default", attached_value, error)) {
185 normalized->push_back(
"+Default");
189 if (token ==
"+Run" || match_long_option(token,
"--run", &attached_value)) {
190 if (token !=
"+Run" && is_long_option_argument_error(token,
"--run", attached_value, error)) {
193 normalized->push_back(
"+Run");
197 if (token ==
"+I" || match_long_option(token,
"--interactive", &attached_value)) {
198 if (!attached_value.empty()) {
199 *error =
"Unexpected argument for --interactive in '" + token +
"'";
201 *error =
"Unsupported option " + token +
" (interactive mode is not available)";
206 if (match_long_option(token,
"--param-fallback", &attached_value)) {
207 std::string mode = attached_value;
210 *error =
"Missing argument after --param-fallback";
216 if (to_lower_ascii(trim_copy(mode)) !=
"legacy") {
217 *error =
"Unsupported value '" + mode +
"' for --param-fallback (expected legacy)";
221 compat->fallback_mode = ParserFallbackMode::Legacy;
225 if (token ==
"+F" || match_long_option(token,
"--file", &attached_value)) {
226 std::string
filename = attached_value;
229 *error =
"Missing filename after " + token;
234 normalized->push_back(
"+F");
239 if (token ==
"+Save" || match_long_option(token,
"--save", &attached_value)) {
240 std::string
filename = attached_value;
243 *error =
"Missing argument after " + token;
248 normalized->push_back(
"+Save");
253 normalized->push_back(token);
259 bool parse_option_argument(
int* index,
262 const std::string& token,
263 const std::string& attached_value,
264 const char* option_name,
268 if (!attached_value.empty()) {
269 *value = attached_value;
272 if (*index + 1 >= argc) {
273 *error =
"Missing argument after " + std::string(option_name);
276 *value = argv[++(*index)];
280 bool filename_has_suffix(
const std::string&
filename,
const char* suffix)
282 const std::string normalized_filename = to_lower_ascii(trim_copy(
filename));
283 const std::string normalized_suffix = to_lower_ascii(std::string(suffix == NULL ?
"" : suffix));
284 return normalized_filename.size() >= normalized_suffix.size() &&
285 normalized_filename.compare(normalized_filename.size() - normalized_suffix.size(),
286 normalized_suffix.size(),
287 normalized_suffix) == 0;
290 bool build_legacy_compare_input(
int argc,
char** argv, LegacyCompareInput* input, std::string* error)
292 input->available =
false;
293 input->warn_when_unavailable =
true;
294 input->runtime_args.clear();
295 input->unavailable_reason.clear();
297 if (argc <= 0 || argv == NULL || argv[0] == NULL) {
298 *error =
"Missing program name";
302 input->runtime_args.push_back(argv[0]);
303 bool saw_passthrough_token_before_file =
false;
305 for (
int i = 1; i < argc; ++i) {
306 const std::string token = argv[i];
311 std::string attached_value;
313 if (match_long_option(token,
"--param-fallback", &attached_value)) {
315 if (!parse_option_argument(&i, argc, argv, token, attached_value,
"--param-fallback", &ignored, error)) {
321 if (token ==
"+Save" || match_long_option(token,
"--save", &attached_value)) {
323 if (!parse_option_argument(&i, argc, argv, token, attached_value, token.c_str(), &ignored, error)) {
329 if (token ==
"+Help" || match_long_option(token,
"--help", &attached_value)) {
330 std::string topic =
"PrM";
331 if (!attached_value.empty()) {
332 topic = attached_value;
333 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
336 input->runtime_args.push_back(
"+Help");
337 input->runtime_args.push_back(topic);
338 input->available =
true;
342 if (token ==
"+Doc" || match_long_option(token,
"--doc", &attached_value)) {
343 std::string topic =
"ALL";
344 if (!attached_value.empty()) {
345 topic = attached_value;
346 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
349 input->runtime_args.push_back(
"+Doc");
350 input->runtime_args.push_back(topic);
351 input->available =
true;
355 if (token ==
"+Default" || match_long_option(token,
"--default", &attached_value)) {
356 if (token !=
"+Default" && is_long_option_argument_error(token,
"--default", attached_value, error)) {
359 input->runtime_args.push_back(
"+Default");
363 if (token ==
"+Run" || match_long_option(token,
"--run", &attached_value)) {
364 if (token !=
"+Run" && is_long_option_argument_error(token,
"--run", attached_value, error)) {
367 input->runtime_args.push_back(
"+Run");
371 if (token ==
"+F" || match_long_option(token,
"--file", &attached_value)) {
373 if (!parse_option_argument(&i, argc, argv, token, attached_value, token.c_str(), &
filename, error)) {
377 if (saw_passthrough_token_before_file) {
378 input->unavailable_reason =
379 "legacy compare is unavailable when direct parameter arguments precede a .par input file";
380 input->runtime_args.clear();
381 input->runtime_args.push_back(argv[0]);
385 if (!filename_has_suffix(
filename,
".par")) {
386 input->unavailable_reason =
387 "legacy compare is unavailable for non-.par input file '" +
filename +
"'";
388 input->runtime_args.clear();
389 input->runtime_args.push_back(argv[0]);
393 input->runtime_args.push_back(
"+F");
394 input->runtime_args.push_back(
filename);
398 input->runtime_args.push_back(token);
399 saw_passthrough_token_before_file =
true;
402 input->available = input->runtime_args.size() > 1;
403 if (!input->available && input->unavailable_reason.empty()) {
404 input->unavailable_reason =
"unable to reconstruct a legacy-compatible parameter input";
409 std::string trim_copy(
const std::string& value)
411 std::string::size_type first = 0;
412 while (first < value.size() && std::isspace(
static_cast<unsigned char>(value[first]))) {
416 std::string::size_type last = value.size();
417 while (last > first && std::isspace(
static_cast<unsigned char>(value[last - 1]))) {
421 return value.substr(first, last - first);
424 std::string to_lower_ascii(std::string value)
426 for (std::string::size_type i = 0; i < value.size(); ++i) {
427 value[i] =
static_cast<char>(std::tolower(
static_cast<unsigned char>(value[i])));
432 ParserCompareMode parser_compare_mode()
434 const char* raw = std::getenv(
"OPENCARP_PARAM_COMPARE");
436 return ParserCompareMode::Strict;
439 const std::string normalized = to_lower_ascii(trim_copy(raw));
440 if (normalized.empty() || normalized ==
"1" || normalized ==
"on" || normalized ==
"true" ||
441 normalized ==
"yes" || normalized ==
"strict" || normalized ==
"fail" || normalized ==
"error") {
442 return ParserCompareMode::Strict;
444 if (normalized ==
"warn") {
445 return ParserCompareMode::Warn;
447 if (normalized ==
"0" || normalized ==
"off" || normalized ==
"false" || normalized ==
"no") {
448 return ParserCompareMode::Off;
451 return ParserCompareMode::Strict;
454 ParserFallbackMode parser_fallback_mode()
456 const char* raw = std::getenv(
"OPENCARP_PARAM_FALLBACK");
458 return ParserFallbackMode::Off;
461 const std::string normalized = to_lower_ascii(trim_copy(raw));
462 if (normalized.empty() || normalized ==
"0" || normalized ==
"off" || normalized ==
"false" ||
463 normalized ==
"no") {
464 return ParserFallbackMode::Off;
466 if (normalized ==
"legacy") {
467 return ParserFallbackMode::Legacy;
470 return ParserFallbackMode::Off;
473 void populate_arg_pointers(
const std::vector<std::string>& values, std::vector<char*>* argv)
475 argv->assign(values.size(), NULL);
476 for (std::size_t i = 0; i < values.size(); ++i) {
477 (*argv)[i] =
const_cast<char*
>(values[i].c_str());
481 void print_lines(FILE* stream,
const char* label,
const std::vector<std::string>& lines)
483 for (std::size_t i = 0; i < lines.size(); ++i) {
484 fprintf(stream,
"%s%s\n", label, lines[i].c_str());
488 void clear_active_citation_suggestions()
490 active_citation_suggestions.clear();
493 void store_active_citation_suggestions(
const std::vector<paramschema::CitationSuggestion>& suggestions)
495 active_citation_suggestions = suggestions;
498 void print_active_citation_suggestions()
500 if (active_citation_suggestions.empty() ||
get_rank() != 0) {
504 const std::vector<std::string> lines =
505 paramschema::format_citation_suggestions(active_citation_suggestions);
510 std::fprintf(stdout,
"\nIf you publish studies based on this simulation, the following references are likely relevant:\n");
511 for (std::size_t i = 0; i < lines.size(); ++i) {
512 std::fprintf(stdout,
" %s\n", lines[i].c_str());
514 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");
515 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");
520 void print_parser_runtime_state(
const std::vector<std::string>&
runtime_args)
522 const auto& schema = paramschema::openCARP_schema();
523 const std::string program_name =
526 std::string rendered;
527 std::vector<std::string> errors;
528 if (!paramschema::render_save_text(schema, program_name, &rendered, &errors)) {
529 print_lines(stderr,
"parameter parser warning: ", errors);
533 fputs(rendered.c_str(), stderr);
536 paramschema::ExecutionResult execute_parser_runtime_args(
const std::vector<std::string>&
runtime_args,
537 const bool allow_save)
539 std::vector<char*> argv;
542 const auto& schema = paramschema::openCARP_schema();
544 paramschema::ExecutionOptions options;
545 options.allow_save = allow_save;
546 return paramschema::execute_legacy_cli(schema,
static_cast<int>(argv.size()), argv.data(), options);
549 bool apply_parser_runtime_args(
const std::vector<std::string>&
runtime_args,
550 const bool allow_save,
551 paramschema::ExecutionResult* executed_out)
553 clear_active_citation_suggestions();
554 const paramschema::ExecutionResult executed = execute_parser_runtime_args(
runtime_args, allow_save);
555 if (executed_out != NULL) {
556 *executed_out = executed;
558 print_lines(stderr,
"parameter parser warning: ", executed.warnings);
559 if (!executed.rendered_output.empty()) {
560 fputs(executed.rendered_output.c_str(), stdout);
562 if (!executed.errors.empty() || executed.status == paramschema::ExecutionStatus::Fatal) {
563 print_lines(stderr,
"parameter parser error: ", executed.errors);
567 if (param_globals::output_setup) {
571 if (executed.status == paramschema::ExecutionStatus::Help) {
575 store_active_citation_suggestions(executed.citations);
579 std::string parent_directory(
const std::string& path)
581 const std::string::size_type slash = path.rfind(
'/');
582 if (slash == std::string::npos) {
588 return path.substr(0, slash);
591 std::string join_path(
const std::string& left,
const std::string& right)
593 if (left.empty() || left ==
".") {
596 if (!left.empty() && left[left.size() - 1] ==
'/') {
599 return left +
"/" + right;
602 bool find_executable_on_path(
const std::string& name, std::string* resolved_path)
604 if (name.empty() || name.find(
'/') != std::string::npos) {
608 const char* path_env = std::getenv(
"PATH");
609 if (path_env == NULL || path_env[0] ==
'\0') {
613 const std::string path_list = path_env;
614 std::string::size_type start = 0;
615 while (start <= path_list.size()) {
616 std::string::size_type end = path_list.find(
':', start);
617 if (end == std::string::npos) {
618 end = path_list.size();
621 const std::string directory = path_list.substr(start, end - start);
622 const std::string candidate = join_path(directory.empty() ?
"." : directory, name);
623 if (access(candidate.c_str(), X_OK) == 0) {
624 *resolved_path = candidate;
628 if (end == path_list.size()) {
637 bool resolve_legacy_snapshot_helper(
const std::string& program_path, std::string* helper_path)
639 std::vector<std::string> resolved_program_paths;
640 if (!program_path.empty()) {
641 resolved_program_paths.push_back(program_path);
644 if (program_path.find(
'/') == std::string::npos) {
645 std::string resolved_program_path;
646 if (find_executable_on_path(program_path, &resolved_program_path)) {
647 resolved_program_paths.push_back(resolved_program_path);
651 for (std::size_t i = 0; i < resolved_program_paths.size(); ++i) {
652 const std::string program_dir = parent_directory(resolved_program_paths[i]);
653 const std::string parent_dir = parent_directory(program_dir);
655 const std::vector<std::string> candidates = {
656 join_path(program_dir,
"param-parser-legacy-snapshot"),
657 join_path(join_path(parent_dir,
"simulator"),
"param-parser-legacy-snapshot"),
660 for (std::size_t j = 0; j < candidates.size(); ++j) {
661 if (access(candidates[j].c_str(), X_OK) == 0) {
662 *helper_path = candidates[j];
668 return find_executable_on_path(
"param-parser-legacy-snapshot", helper_path);
671 bool create_temp_output_path(
const char* suffix, std::string* path)
674 if (suffix != NULL && suffix[0] !=
'\0') {
675 std::snprintf(temp_path,
sizeof temp_path,
"/tmp/opencarp-parser-compare-XXXXXX%s", suffix);
677 std::snprintf(temp_path,
sizeof temp_path,
"/tmp/opencarp-parser-compare-XXXXXX");
680 const int fd = suffix != NULL && suffix[0] !=
'\0' ? mkstemps(temp_path,
static_cast<int>(std::strlen(suffix))) :
690 bool capture_current_parser_snapshot(paramschema::SnapshotResult* snapshot)
692 *snapshot = paramschema::snapshot_schema_state(paramschema::openCARP_schema());
693 print_lines(stderr,
"parameter compare warning: ", snapshot->warnings);
694 if (!snapshot->errors.empty()) {
695 print_lines(stderr,
"parameter compare error: ", snapshot->errors);
701 void maybe_force_test_mismatch(paramschema::SnapshotResult* snapshot)
703 const char* raw = std::getenv(
"OPENCARP_PARAM_TEST_FORCE_MISMATCH");
708 const std::string normalized = to_lower_ascii(trim_copy(raw));
709 if (normalized.empty() || normalized ==
"0" || normalized ==
"off" || normalized ==
"false" ||
710 normalized ==
"no") {
714 if (!snapshot->entries.empty()) {
715 snapshot->entries[0].value +=
"__forced_parser_compare_mismatch__";
719 paramschema::SnapshotEntry entry;
720 entry.path =
"buildinfo";
721 entry.value =
"__forced_parser_compare_mismatch__";
722 snapshot->entries.push_back(entry);
725 LegacySnapshotHelperRunResult run_legacy_snapshot_helper(
const std::string& helper_path,
727 const std::string& output_path)
729 LegacySnapshotHelperRunResult result;
730 std::vector<std::string> child_values;
732 child_values.push_back(helper_path);
733 child_values.push_back(
"--snapshot-out");
734 child_values.push_back(output_path);
735 child_values.push_back(
"--");
738 std::vector<char*> child_argv;
739 child_argv.reserve(child_values.size() + 1);
740 for (std::size_t i = 0; i < child_values.size(); ++i) {
741 child_argv.push_back(
const_cast<char*
>(child_values[i].c_str()));
743 child_argv.push_back(NULL);
745 const pid_t pid = fork();
752 execv(helper_path.c_str(), child_argv.data());
753 std::perror(
"execv");
758 if (waitpid(pid, &status, 0) < 0) {
759 std::perror(
"waitpid");
763 if (WIFSIGNALED(status)) {
764 result.signaled =
true;
765 result.signal = WTERMSIG(status);
769 if (WIFEXITED(status)) {
770 result.exited =
true;
771 result.exit_status = WEXITSTATUS(status);
777 bool legacy_snapshot_helper_rejected_input(
const LegacySnapshotHelperRunResult& result)
779 return result.exited && (result.exit_status == 3 || result.exit_status == 4);
782 bool legacy_fallback_requested(
const RuntimeCompatOptions& compat)
784 return compat.fallback_mode == ParserFallbackMode::Legacy ||
785 parser_fallback_mode() == ParserFallbackMode::Legacy;
788 bool should_print_global_message()
791 return get_rank(MPI_COMM_WORLD) == 0;
794 const char*
const rank_variables[] = {
795 "OMPI_COMM_WORLD_RANK",
798 "MV2_COMM_WORLD_RANK",
802 for (std::size_t i = 0; i <
sizeof rank_variables /
sizeof rank_variables[0]; ++i) {
803 const char* raw_rank = std::getenv(rank_variables[i]);
804 if (raw_rank == NULL || raw_rank[0] ==
'\0') {
809 const long rank = std::strtol(raw_rank, &end, 10);
810 if (end != raw_rank && end != NULL && end[0] ==
'\0') {
818 void print_legacy_fallback_workaround()
821 "parameter compare error: rerun with OPENCARP_PARAM_FALLBACK=legacy to continue with the legacy parameter state\n");
823 "parameter compare error: or add --param-fallback=legacy to the openCARP command line\n");
826 bool restore_legacy_snapshot_state(
const paramschema::SnapshotResult& legacy_snapshot)
828 const paramschema::SnapshotRestoreResult restored =
829 paramschema::restore_snapshot_state(paramschema::openCARP_schema(), legacy_snapshot);
830 print_lines(stderr,
"parameter compare warning: ", restored.warnings);
831 if (!restored.errors.empty()) {
832 print_lines(stderr,
"parameter compare error: ", restored.errors);
838 bool run_parser_legacy_compare(
const std::vector<std::string>&
runtime_args,
839 const LegacyCompareInput& legacy_input,
840 const RuntimeCompatOptions& compat)
842 const ParserCompareMode mode = parser_compare_mode();
843 if (mode == ParserCompareMode::Off) {
846 const bool fallback_to_legacy = legacy_fallback_requested(compat);
849 std::fprintf(stderr,
"parameter compare error: unable to reconstruct simulator argv\n");
850 if (fallback_to_legacy) {
851 print_legacy_fallback_workaround();
853 return mode != ParserCompareMode::Strict;
856 paramschema::SnapshotResult parser_snapshot;
857 if (!capture_current_parser_snapshot(&parser_snapshot)) {
858 std::fprintf(stderr,
"parameter compare error: unable to snapshot the parser runtime state\n");
859 if (fallback_to_legacy) {
860 print_legacy_fallback_workaround();
862 return mode != ParserCompareMode::Strict;
864 maybe_force_test_mismatch(&parser_snapshot);
866 std::string helper_path;
867 if (!resolve_legacy_snapshot_helper(
runtime_args[0], &helper_path)) {
868 std::fprintf(stderr,
"parameter compare error: unable to locate param-parser-legacy-snapshot\n");
869 if (fallback_to_legacy) {
870 print_legacy_fallback_workaround();
873 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
875 return mode != ParserCompareMode::Strict;
878 if (!legacy_input.available) {
879 const bool print_message = should_print_global_message();
880 if (print_message && legacy_input.warn_when_unavailable && !legacy_input.unavailable_reason.empty()) {
881 std::fprintf(stderr,
"parameter compare warning: %s\n", legacy_input.unavailable_reason.c_str());
883 if (print_message && fallback_to_legacy) {
885 "parameter compare warning: legacy fallback is unavailable because no legacy baseline exists for this input\n");
890 std::string snapshot_path;
891 if (!create_temp_output_path(
"", &snapshot_path)) {
892 std::fprintf(stderr,
"parameter compare error: unable to create temporary snapshot file\n");
893 if (fallback_to_legacy) {
894 print_legacy_fallback_workaround();
897 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
899 return mode != ParserCompareMode::Strict;
902 const LegacySnapshotHelperRunResult helper_result =
903 run_legacy_snapshot_helper(helper_path, legacy_input.runtime_args, snapshot_path);
904 const bool helper_ok = helper_result.exited && helper_result.exit_status == 0;
905 paramschema::SnapshotResult legacy_snapshot;
906 std::string read_error;
907 const bool loaded = helper_ok &&
908 paramschema::snapshotio::read_snapshot_file(snapshot_path, &legacy_snapshot, &read_error);
909 unlink(snapshot_path.c_str());
912 if (legacy_snapshot_helper_rejected_input(helper_result)) {
914 "parameter compare error: legacy param() rejected the normalized input while the parser runtime accepted it\n");
916 "parameter compare error: this indicates a parser/legacy validation mismatch, not snapshot helper infrastructure\n");
918 "parameter compare error: please open an issue and include the triggering command line and parameter files\n");
920 "https://git.opencarp.org/openCARP/openCARP/-/issues/new?type=ISSUE&initialCreationContext=list-route\n");
921 if (fallback_to_legacy) {
923 "parameter compare error: legacy fallback is unavailable because legacy produced no valid parameter state\n");
925 }
else if (!read_error.empty()) {
926 std::fprintf(stderr,
"parameter compare error: %s\n", read_error.c_str());
927 }
else if (helper_result.signaled) {
928 std::fprintf(stderr,
"parameter compare error: legacy snapshot helper terminated with signal %d\n",
929 helper_result.signal);
930 }
else if (helper_result.exited) {
931 std::fprintf(stderr,
"parameter compare error: legacy snapshot helper exited with status %d\n",
932 helper_result.exit_status);
934 std::fprintf(stderr,
"parameter compare error: unable to capture the legacy parameter state\n");
936 if (fallback_to_legacy && !legacy_snapshot_helper_rejected_input(helper_result)) {
937 print_legacy_fallback_workaround();
938 }
else if (!legacy_snapshot_helper_rejected_input(helper_result)) {
940 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
942 return mode != ParserCompareMode::Strict;
945 const paramschema::SnapshotComparisonResult comparison =
946 paramschema::compare_snapshot_results(paramschema::openCARP_schema(), parser_snapshot, legacy_snapshot);
947 if (!comparison.errors.empty() || !comparison.mismatches.empty()) {
948 print_lines(stderr,
"", paramschema::format_snapshot_comparison_report(comparison,
"parser compare"));
950 "parameter compare error: the parser runtime and legacy param() produced different parameter states\n");
952 "parameter compare error: please open an issue and include the triggering command line and parameter files\n");
954 "https://git.opencarp.org/openCARP/openCARP/-/issues/new?type=ISSUE&initialCreationContext=list-route\n");
956 if (fallback_to_legacy) {
957 if (!restore_legacy_snapshot_state(legacy_snapshot)) {
958 print_legacy_fallback_workaround();
961 clear_active_citation_suggestions();
962 std::fprintf(stderr,
"parameter compare warning: continuing with the legacy parameter state\n");
966 print_legacy_fallback_workaround();
967 return mode != ParserCompareMode::Strict;
975 static char input_dir[1024],
982 void localize_gridout_nodes(
sf_mesh& mesh,
1005 alg_map[nbr[local_node]] = local_node;
1008 size_t buffsize = 0;
1009 for(
int pid = 0; pid < mpi_size; pid++) {
1010 if(mpi_rank == pid) {
1011 recv_nodes = selected_nodes;
1012 buffsize = recv_nodes.
size();
1015 MPI_Bcast(&buffsize,
sizeof(
size_t), MPI_BYTE, pid, mesh.comm);
1016 recv_nodes.
resize(buffsize);
1017 MPI_Bcast(recv_nodes.
data(), buffsize *
sizeof(
mesh_int_t), MPI_BYTE, pid, mesh.comm);
1020 auto it = alg_map.
find(node);
1021 if(it != alg_map.
end())
1028 const mesh_int_t stop = layout[mpi_rank + 1];
1031 size_t buffsize = 0;
1032 for(
int pid = 0; pid < mpi_size; pid++) {
1033 if(mpi_rank == pid) {
1034 recv_nodes = selected_nodes;
1035 buffsize = recv_nodes.
size();
1038 MPI_Bcast(&buffsize,
sizeof(
size_t), MPI_BYTE, pid, mesh.comm);
1039 recv_nodes.
resize(buffsize);
1040 MPI_Bcast(recv_nodes.
data(), buffsize *
sizeof(
mesh_int_t), MPI_BYTE, pid, mesh.comm);
1043 if(node >= start && node < stop)
1053 bool mesh_has_gridout_tags(
const sf_mesh& mesh,
1056 bool local_has_tag =
false;
1057 for(
size_t eidx = 0; eidx < mesh.l_numelem; eidx++) {
1058 if(output_tags.
count(mesh.tag[eidx]) != 0) {
1059 local_has_tag =
true;
1064 int local = local_has_tag ? 1 : 0;
1066 MPI_Allreduce(&local, &global, 1, MPI_INT, MPI_MAX, mesh.comm);
1070 void extract_gridout_tag_mesh(
const sf_mesh& mesh,
1075 for(
size_t eidx = 0; eidx < mesh.l_numelem; eidx++)
1076 keep_elem[eidx] = output_tags.
count(mesh.tag[eidx]) != 0;
1078 out_mesh.name = mesh.name;
1080 if(out_mesh.g_numelem == 0) {
1081 log_msg(0, 5,
ECHO,
"gridout_tags selected no elements in \"%s\".", mesh.name.c_str());
1089 std::list<sf_mesh*> meshlist;
1090 meshlist.push_back(&out_mesh);
1094 numbering(out_mesh);
1095 out_mesh.generate_par_layout();
1101 void extract_gridout_surface_mesh(
const sf_mesh& mesh,
sf_mesh& out_mesh)
1104 out_mesh.name = mesh.name;
1112 ref_eidx.
resize(out_mesh.l_numelem);
1114 long int num_local = out_mesh.l_numelem, offset = 0;
1115 MPI_Exscan(&num_local, &offset, 1, MPI_LONG, MPI_SUM, out_mesh.comm);
1117 for(
size_t eidx = 0; eidx < out_mesh.l_numelem; eidx++)
1118 ref_eidx[eidx] = offset + eidx;
1122 std::list<sf_mesh*> meshlist;
1123 meshlist.push_back(&out_mesh);
1127 numbering(out_mesh);
1128 out_mesh.generate_par_layout();
1134 void write_gridout_surface(
const sf_mesh& mesh,
const std::string& basename)
1137 extract_gridout_surface_mesh(mesh, surf);
1143 for(
size_t i = 0; i < surf_global.con.size(); i++)
1144 surf_global.con[i] = nbr[surf_global.con[i]];
1153 LegacyCompareInput legacy_input;
1154 std::vector<std::string> normalized_args;
1155 RuntimeCompatOptions compat;
1156 std::string normalize_error;
1157 if (!build_legacy_compare_input(argc, argv, &legacy_input, &normalize_error) ||
1158 !normalize_runtime_args(argc, argv, &normalized_args, &compat, &normalize_error)) {
1159 fprintf(stderr,
"\n*** %s\n\n", normalize_error.c_str());
1163 paramschema::ExecutionResult executed;
1164 if (!apply_parser_runtime_args(normalized_args,
true, &executed)) {
1168 if (legacy_input.available) {
1169 for (std::size_t i = 0; i < executed.validation.assignments.size(); ++i) {
1170 if (!executed.validation.assignments[i].synthesized) {
1173 legacy_input.available =
false;
1174 legacy_input.runtime_args.clear();
1175 legacy_input.runtime_args.push_back(normalized_args[0]);
1176 legacy_input.unavailable_reason =
1177 "legacy compare is unavailable because the parser inferred optional controller counts from the original input";
1182 if (legacy_input.available && !executed.validation.legacy_compare_incompatible_paths.empty()) {
1183 legacy_input.available =
false;
1184 legacy_input.warn_when_unavailable =
false;
1185 legacy_input.runtime_args.clear();
1186 legacy_input.runtime_args.push_back(normalized_args[0]);
1187 legacy_input.unavailable_reason =
1188 "legacy compare is unavailable because the original input uses aggregate ID syntax without a legacy baseline for " +
1189 join_paths_for_message(executed.validation.legacy_compare_incompatible_paths);
1192 if (!run_parser_legacy_compare(normalized_args, legacy_input, compat)) {
1208 log_msg(NULL, 5,
ECHO,
"The EMI model was not compiled for this binary.\n");
1222 log_msg(0,0,0,
"\n *** Initializing physics ***\n");
1226 for (
int ii = 0; ii < param_globals::num_external_imp; ii++) {
1228 assert(loading_succeeded);
1231 if(param_globals::num_external_imp)
1232 log_msg(NULL, 4,
ECHO,
"Loading of external LIMPET modules not enabled.\n"
1233 "Recompile with DLOPEN set.\n" );
1239 log_msg(NULL, 0, 0,
"Initializing %s ..", p->
name);
1246 log_msg(0,0,0,
"\n *** Destroying physics ***\n");
1274 for (
int i=0; i<ns; i++ ) {
1282 log_msg( NULL, 1, 0,
"Extracellular stimulus %d ignored for monodomain", i );
1285 log_msg( NULL, 1, 0,
"Intracellular stimulus %d converted to transmembrane", i );
1320 Stimulus* s = param_globals::stimulus;
1322 for(
int i=0; i < param_globals::num_stim; i++) {
1330 log_msg( NULL, 4, 0,
"Elliptic system is singular!\n"
1331 "Use an explicit ground:voltage (stimulus[X].stimtype=3)\n"
1332 "Do not trust the elliptic solution of this simulation run!\n");
1337 const char* real_precision = OPENCARP_REAL_BITS == 32 ?
"single" :
"double";
1338 printf(
"\n""*** GIT tag: %s\n", GIT_COMMIT_TAG);
1339 printf(
"*** GIT hash: %s\n", GIT_COMMIT_HASH);
1340 printf(
"*** GIT repo: %s\n", GIT_PATH);
1341 printf(
"*** local index bits: %d\n", OPENCARP_LOCAL_INDEX_BITS);
1342 printf(
"*** global index bits: %d\n", OPENCARP_GLOBAL_INDEX_BITS);
1343 printf(
"*** real precision: %s (%d-bit)\n", real_precision, OPENCARP_REAL_BITS);
1344 printf(
"*** dependency commits: %s\n\n", SUBREPO_COMMITS);
1350 param_globals::dt /= 1000.;
1354 if(param_globals::mass_lumping == 0 && param_globals::parab_solve==0) {
1355 log_msg(0,5,0,
"parab_solve = 0 (explicit) requires mass_lumping = 1. "
1356 "Either enable mass lumping or choose an implicit parab_solve method.");
1361 if(!param_globals::extracell_monodomain_stim)
1370 if(param_globals::t_sentinel > 0 && param_globals::sentinel_ID < 0 ) {
1371 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");
1374 if(param_globals::num_external_imp > 0 ) {
1375 for(
int ext_imp_i = 0; ext_imp_i < param_globals::num_external_imp; ext_imp_i++) {
1376 if(param_globals::external_imp[ext_imp_i][0] !=
'/') {
1377 log_msg(0,5,0,
"external_imp[%d] error: absolute paths must be used for .so file loading (\'%s\')",
1378 ext_imp_i, param_globals::external_imp[ext_imp_i]);
1384 if(param_globals::experiment ==
EXP_LAPLACE && param_globals::bidomain != 1) {
1385 log_msg(0,4,0,
"Warning: Laplace experiment mode requires bidomain = 1. Setting bidomain = 1.");
1386 param_globals::bidomain = 1;
1389 if(param_globals::num_phys_regions == 0) {
1390 log_msg(0,4,0,
"Warning: No physics region defined! Please set phys_region parameters to correctly define physics.");
1393 log_msg(0,4,0,
"Intra-elec and Extra-elec domains will be derived from fibers.\n");
1394 param_globals::num_phys_regions = param_globals::bidomain ? 2 : 1;
1395 param_globals::phys_region = (p_region*) calloc(param_globals::num_phys_regions,
sizeof(p_region));
1397 param_globals::phys_region[0].name = strdup(
"Autogenerated intracellular Electrics");
1398 param_globals::phys_region[0].num_IDs = 0;
1400 if(param_globals::bidomain) {
1402 param_globals::phys_region[1].name = strdup(
"Autogenerated extracellular Electrics");
1403 param_globals::phys_region[1].num_IDs = 0;
1406 log_msg(0,4,0,
"Laplace domain will be derived from fibers.\n");
1407 param_globals::num_phys_regions = 1;
1408 param_globals::phys_region = (p_region*) calloc(param_globals::num_phys_regions,
sizeof(p_region));
1410 param_globals::phys_region[0].name = strdup(
"Autogenerated Laplace");
1411 param_globals::phys_region[0].num_IDs = 0;
1416 log_msg(0,4,0,
"Warning: Laplace experiment mode requires a laplace physics regions defined.");
1420 log_msg(0,4,0,
"Converting the defined extracellular-electrics-region to laplace-region.");
1423 log_msg(0,4,0,
"Converting the defined intracellular-electrics-region to laplace-region.");
1426 param_globals::num_phys_regions += 1;
1427 param_globals::phys_region = (p_region*) realloc(param_globals::phys_region, param_globals::num_phys_regions *
sizeof(p_region));
1429 param_globals::phys_region[param_globals::num_phys_regions - 1].ptype =
PHYSREG_LAPLACE;
1430 param_globals::phys_region[param_globals::num_phys_regions - 1].name = strdup(
"Autogenerated Laplace");
1431 param_globals::phys_region[param_globals::num_phys_regions - 1].num_IDs = 0;
1435 #ifndef WITH_PARMETIS
1436 if(param_globals::pstrat == 1) {
1437 log_msg(0,3,0,
"openCARP was built without Parmetis support. Swithing to KDtree.");
1438 param_globals::pstrat = 2;
1443 bool legacy_stim_set =
false, new_stim_set =
false;
1445 for(
int i=0; i<param_globals::num_stim; i++) {
1446 Stimulus & legacy_stim = param_globals::stimulus[i];
1447 Stim & new_stim = param_globals::stim[i];
1449 if(legacy_stim.stimtype || legacy_stim.strength)
1450 legacy_stim_set =
true;
1452 if(new_stim.crct.type || new_stim.pulse.strength)
1453 new_stim_set =
true;
1456 if(legacy_stim_set || new_stim_set) {
1457 if(legacy_stim_set && new_stim_set) {
1458 log_msg(0,4,0,
"Warning: Legacy stimuli and default stimuli are defined. Only default stimuli will be used!");
1460 else if (legacy_stim_set) {
1461 log_msg(0,1,0,
"Warning: Legacy stimuli defined. Please consider switching to stimulus definition \"stim[]\"!");
1466 log_msg(0,4,0,
"Warning: No potential or current stimuli found!");
1476 log_msg(0, 0, 0,
"LICENSE NOTICE:");
1478 "This run uses openCARP components licensed under the Academic Public License v1.1.");
1480 "That includes code from simulator/**, param/**, and APL-licensed parts of physics/**.");
1482 "Commercial use is not allowed without a separate commercial license.");
1487 int flg = 0, err = 0, rank =
get_rank();
1489 char *ptr = getcwd(current_dir, 1024);
1490 if (ptr == NULL) err++;
1491 ptr = getcwd(input_dir, 1024);
1492 if (ptr == NULL) err++;
1498 if (strcmp(sim_ID,
"OUTPUT_DIR")) {
1499 if (mkdir(sim_ID, 0775)) {
1500 if (errno == EEXIST ) {
1501 log_msg(NULL, 2, 0,
"Output directory exists: %s\n", sim_ID);
1503 log_msg(NULL, 5, 0,
"Unable to make output directory\n");
1507 }
else if (mkdir(sim_ID, 0775) && errno != EEXIST) {
1508 log_msg(NULL, 5, 0,
"Unable to make output directory\n");
1516 err += chdir(sim_ID);
1517 ptr = getcwd(output_dir, 1024);
1518 if (ptr == NULL) err++;
1523 err += chdir(output_dir);
1526 if (rank == 0 && ((param_globals::experiment==
EXP_POSTPROCESS) || (param_globals::post_processing_opts &
LEADFIELD))) {
1528 if (strcmp(param_globals::ppID,
"POSTPROC_DIR")) {
1529 if (mkdir(param_globals::ppID, 0775)) {
1530 if (errno == EEXIST ) {
1531 log_msg(NULL, 2,
ECHO,
"Postprocessing directory exists: %s\n\n", param_globals::ppID);
1533 log_msg(NULL, 5,
ECHO,
"Unable to make postprocessing directory\n\n");
1537 }
else if (mkdir(param_globals::ppID, 0775) && errno != EEXIST) {
1538 log_msg(NULL, 5,
ECHO,
"Unable to make postprocessing directory\n\n");
1546 err += chdir(param_globals::ppID);
1547 ptr = getcwd(postproc_dir, 1024);
1548 if (ptr == NULL) err++;
1549 err = chdir(output_dir);
1558 bool io_node =
false;
1561 if (param_globals::num_io_nodes > 0) {
1564 log_msg(NULL, 5, 0,
"You cannot run with async IO on only one core.\n");
1568 if (2 * param_globals::num_io_nodes >= psize) {
1569 log_msg(NULL, 5, 0,
"The number of IO cores be less " "than the number of compute cores.");
1573 if (param_globals::num_PS_nodes && param_globals::num_io_nodes > param_globals::num_PS_nodes) {
1575 "The number of IO cores (%d) should not "
1576 "exceed the number of PS compute cores (%d).\n",
1577 param_globals::num_io_nodes, param_globals::num_PS_nodes);
1582 io_node = prank < param_globals::num_io_nodes;
1586 MPI_Comm_set_name(comm, io_node ?
"IO" :
"compute");
1595 MPI_Intercomm_create(comm, 0, MPI_COMM_WORLD, io_node ? param_globals::num_io_nodes : 0,
1599 log_msg(NULL, 4, 0,
"Global node %d, Comm rank %d != Intercomm rank %d\n",
1603 MPI_Comm_set_name(
SF_COMM,
"compute");
1608 if((io_node || !param_globals::num_io_nodes) && !prank)
1616 getcwd(current_dir, 1024);
1623 if (dest==
OUTPUT) err = chdir(output_dir);
1624 else if (dest==
POSTPROC) err = chdir(postproc_dir);
1625 else if (dest==
CURDIR) err = chdir(current_dir);
1626 else err = chdir(input_dir);
1634 double start_time = 0.0;
1637 double end_time = param_globals::tend;
1651 if(param_globals::num_tsav) {
1652 std::vector<double> trig(param_globals::num_tsav);
1653 for(
size_t i=0; i<trig.size(); i++) trig[i] = param_globals::tsav[i];
1658 if(param_globals::chkpt_intv)
1660 param_globals::chkpt_intv, 0,
iotm_chkpt_intv,
"interval checkpointing");
1662 if(param_globals::num_trace)
1666 #ifdef WITH_POWERCAPPING
1667 void basic_powercapping_setup()
1669 user_globals::pc_manager =
new powercapping_manager();
1672 void basic_powercapping_cleanup()
1676 delete user_globals::pc_manager; user_globals::pc_manager =
nullptr;
1683 const short padding = 4;
1688 if(col_width[0] <
int(strlen(buff)+padding))
1689 col_width[0] = strlen(buff)+padding;
1692 if(col_width[1] <
int(strlen(buff)+padding))
1693 col_width[1] = strlen(buff)+padding;
1696 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1698 int timer_id = used_timer_ids[tid];
1708 snprintf(buff,
sizeof buff,
"%.3lf", val);
1709 if(col_width[col] <
int(strlen(buff)+padding))
1710 col_width[col] = strlen(buff)+padding;
1727 const char* smpl_endl =
"\n";
1735 log_msg(0,5,0,
"Protocol file %s could not be opened for writing!\n", fname);
1748 std::vector<std::string> col_labels = {
"time",
"tick"};
1749 std::vector<std::string> col_short_labels = {
"A",
"B"};
1750 std::vector<std::string> col_unit_labels = {
"ms",
"--" };
1751 std::vector<int> col_width = {4, 4};
1753 char c_label = {
'C'};
1754 std::string label = {
""};
1755 std::string unit = {
""};
1759 std::vector<int> used_timer_ids;
1760 std::vector<int> used_stim_ids;
1770 used_stim_ids.push_back(sidx);
1780 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1782 int timer_id = used_timer_ids[tid];
1785 int llen = strlen(t->
name);
1786 mx_llen = llen > mx_llen ? llen : mx_llen;
1789 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1791 int timer_id = used_timer_ids[tid];
1794 col_labels.push_back(t->
name);
1796 col_short_labels.push_back(label);
1801 if(unit.empty()) unit =
"--";
1802 col_unit_labels.push_back(unit);
1803 col_width.push_back(4);
1811 fh <<
"# Protocol header\n#\n" <<
"# Legend:\n";
1812 for(
size_t i = 0; i<col_short_labels.size(); i++)
1814 fh <<
"#" << std::setw(2) << col_short_labels[i] <<
" = " << std::setw(mx_llen) << col_labels[i];
1815 fh <<
" [" << std::setw(10) << col_unit_labels[i] <<
"]";
1817 if(i >= 2 && used_stim_ids[i-2] > -1) {
1822 fh <<
" ground stim" << smpl_endl;
1824 fh <<
" applied: " << std::to_string(s.
pulse.
strength) << smpl_endl;
1835 for(
size_t i = 0; i<col_short_labels.size(); i++)
1836 fh << std::setw(col_width[i] - 3) << col_short_labels[i].c_str() << std::setw(3) <<
" ";
1839 fh << smpl_endl <<
"#";
1840 for(
size_t i = 0; i<col_unit_labels.size(); i++)
1841 fh <<
"[" << std::setw(col_width[i]-2) << col_unit_labels[i].c_str() <<
"]";
1844 fh << smpl_endl << std::fixed;
1852 for (
size_t tid = 0; tid < used_timer_ids.size(); tid++)
1854 int timer_id = used_timer_ids[tid];
1860 fh << std::setw(col_width[col]) << On;
1868 fh << std::setw(col_width[col]) << std::setprecision(3) << val;
1890 const char* h1_prog =
"PROG\t----- \t----\t-------\t-------|";
1891 const char* h2_prog =
"time\t%%comp\ttime\t ctime \t ETA |";
1892 const char* h1_wc =
"\tELAPS |";
1893 const char* h2_wc =
"\twc |";
1898 log_msg(NULL, 0, 0,
"%s", h1_prog );
1906 int req_hours = ((int)(time)) / 3600;
1907 int req_min = (((int)(time)) % 3600) / 60;
1908 int req_sec = (((int)(time)) % 3600) % 60;
1910 snprintf(str, str_size,
"%d:%02d:%02d", req_hours, req_min, req_sec);
1923 char elapsed_time_str[256];
1924 char req_time_str[256];
1928 log_msg( NULL, 0,
NONL,
"%.2f\t%.1f\t%.1f\t%s\t%s",
1946 if(!have_timedependent_phys) {
1947 log_msg(0,0,0,
"\n no time-dependent physics region registered, skipping simulate loop..\n");
1951 log_msg(0,0,0,
"\n *** Launching simulation ***\n");
1955 if(param_globals::dump_protocol)
1962 #ifdef WITH_POWERCAPPING
1963 basic_powercapping_setup();
1964 powercapping_manager *pc = user_globals::pc_manager;
1975 #ifdef WITH_POWERCAPPING
1976 std::vector<int> flops_per_rank;
1981 #ifdef WITH_POWERCAPPING
1982 pc->iteration_begin(flops_per_rank);
1989 it.second->output_step();
1992 #ifdef WITH_POWERCAPPING
1993 pc->sample(
"output step");
2001 #ifdef WITH_POWERCAPPING
2002 pc->sample(p->
name);
2006 #ifdef WITH_POWERCAPPING
2007 pc->iteration_end(flops_per_rank);
2015 log_msg(0,0,0,
"\n\nTimings of individual physics:");
2016 log_msg(0,0,0,
"------------------------------\n");
2023 #ifdef WITH_POWERCAPPING
2025 basic_powercapping_cleanup();
2031 if(param_globals::post_processing_opts &
RECOVER_PHIE) {
2032 log_msg(NULL,0,
ECHO,
"\nPOSTPROCESSOR: Recovering Phie ...");
2033 log_msg(NULL,0,
ECHO,
"----------------------------------\n");
2039 log_msg(NULL,0,
ECHO,
"\n-----------------------------------------");
2040 log_msg(NULL,0,
ECHO,
"POSTPROCESSOR: Successfully recoverd Phie.\n");
2044 if(param_globals::post_processing_opts &
LEADFIELD) {
2045 #ifdef WITH_LEADFIELD
2046 log_msg(NULL,0,
ECHO,
"\nPOSTPROCESSOR: Computing leadfields ...");
2047 log_msg(NULL,0,
ECHO,
"-------------------------------------\n");
2051 log_msg(NULL, 5, 0,
"Error: Leadfield requires active EP physics. Aborting.");
2055 int err = leadfield.
run(*elec);
2058 log_msg(NULL,0,
ECHO,
"\n------------------------------------------");
2059 log_msg(NULL,0,
ECHO,
"POSTPROCESSOR: Successfully computed leadfields.\n");
2062 log_msg(NULL, 5, 0,
"Error: leadfield support not compiled in. Rebuild with -DENABLE_LEADFIELD=ON.");
2075 if(error_if_missing) {
2076 log_msg(0,5,0,
"%s error: required physic is not active! Usually this is due to an inconsistent experiment configuration. Aborting!", __func__);
2100 log_msg(0,5,0,
"%s warning: trying to register already registered data vector.", __func__);
2114 auto register_new_mesh = [&] (
mesh_t mt,
int pidx) {
2115 if(!mesh_registry.count(mt)) {
2116 mesh_registry[mt] =
sf_mesh();
2117 mesh_registry[mt].name = param_globals::phys_region[pidx].name;
2119 return &mesh_registry[mt];
2123 for(
int i=0; i<param_globals::num_phys_regions; i++)
2127 switch(param_globals::phys_region[i].ptype) {
2139 curmesh = register_new_mesh(
emi_msh, i);
2144 log_msg(0,5,0,
"Unsupported mesh type %d! Aborting!", param_globals::phys_region[i].ptype);
2150 for(
int j=0; j<param_globals::phys_region[i].num_IDs; j++)
2168 if(ntr == 0)
return;
2173 for (
int i=0; i<ntr; i++) {
2182 shape.
p0.
x = tagRegs[i].p0[0];
2183 shape.
p0.
y = tagRegs[i].p0[1];
2184 shape.
p0.
z = tagRegs[i].p0[2];
2185 shape.
p1.
x = tagRegs[i].p1[0];
2186 shape.
p1.
y = tagRegs[i].p1[1];
2187 shape.
p1.
z = tagRegs[i].p1[2];
2188 shape.
radius = tagRegs[i].radius;
2195 log_msg(0,3,0,
"Tag region %d is empty", i);
2197 for(
size_t j=0; j<elem_indices.
size(); j++)
2198 mesh.
tag[elem_indices[j]] = tagRegs[i].tag;
2202 if(strlen(param_globals::retagfile))
2217 size_t renormalised_count = 0;
2223 for (
size_t i = 0; i < l_numelem; i++)
2225 const mesh_real_t f0 = fib[3*i+0], f1 = fib[3*i+1], f2 = fib[3*i+2];
2226 mesh_real_t fibre_len = sqrt(f0*f0 + f1*f1 + f2*f2);
2228 if (fibre_len && fabs(fibre_len - 1) > 1e-3) {
2229 fib[3 * i + 0] /= fibre_len;
2230 fib[3 * i + 1] /= fibre_len;
2231 fib[3 * i + 2] /= fibre_len;
2232 renormalised_count++;
2236 return renormalised_count;
2241 log_msg(0,0,0,
"\n *** Processing meshes ***\n");
2243 const std::string basename = param_globals::meshname;
2244 const int verb = param_globals::output_level;
2252 MPI_Comm comm = ref_mesh.
comm;
2255 double t1, t2, s1, s2;
2256 MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
2262 std::list< sf_mesh* > ptsread_list;
2265 t1 = MPI_Wtime(); s1 = t1;
2266 if(verb)
log_msg(NULL, 0, 0,
"Reading reference mesh: %s.*", basename.c_str());
2271 if (strlen(param_globals::tagfile)) {
2272 if(verb)
log_msg(NULL, 0, 0,
"Overriding element tags from: %s", param_globals::tagfile);
2277 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2279 bool check_fibre_normality =
true;
2280 if (check_fibre_normality and ref_mesh.
fib.
size()>0) {
2286 size_t l_num_fixed_she = 0;
2290 unsigned long fixed[2] = {(
unsigned long) l_num_fixed_fib, (
unsigned long) l_num_fixed_she};
2291 MPI_Allreduce(MPI_IN_PLACE, fixed, 2, MPI_UNSIGNED_LONG, MPI_SUM, comm);
2293 if (fixed[0] + fixed[1] > 0)
2294 log_msg(NULL, 0, 0,
"Renormalised %ld longitudinal and %ld sheet-transverse fibre vectors.", fixed[0], fixed[1]);
2297 if(verb)
log_msg(NULL, 0, 0,
"Done in %f sec.",
float(t2 - t1));
2300 if(param_globals::numtagreg > 0) {
2301 log_msg(0, 0, 0,
"Re-tagging reference mesh");
2305 ptsread_list.push_back(&ref_mesh);
2308 retag_elements(ref_mesh, param_globals::tagreg, param_globals::numtagreg);
2311 ptsread_list.clear();
2314 if(verb)
log_msg(NULL, 0, 0,
"Processing submeshes");
2316 bool have_emi_mesh =
false;
2317 bool have_non_emi_mesh =
false;
2319 for(
auto it = mesh_registry.begin(); it != mesh_registry.end(); ++it) {
2320 mesh_t grid_type = it->first;
2321 sf_mesh & submesh = it->second;
2325 have_emi_mesh =
true;
2327 have_non_emi_mesh =
true;
2329 if(verb > 1)
log_msg(NULL, 0, 0,
"\nSubmesh name: %s", submesh.
name.c_str());
2346 if(verb > 1)
log_msg(NULL, 0, 0,
"Extraction done in %f sec.",
float(t2 - t1));
2348 ptsread_list.push_back(&submesh);
2352 if(have_emi_mesh && have_non_emi_mesh) {
2353 log_msg(NULL, 5,
ECHO,
"EMI and non-EMI submeshes cannot be mixed during mesh setup.");
2358 if(param_globals::pstrat == 2 && have_non_emi_mesh)
2361 for(
auto it = mesh_registry.begin(); it != mesh_registry.end(); ++it)
2363 mesh_t grid_type = it->first;
2364 sf_mesh & submesh = it->second;
2366 if(verb > 2)
log_msg(NULL, 0, 0,
"\nSubmesh name: %s", submesh.
name.c_str());
2371 switch(param_globals::pstrat) {
2373 if(verb > 2)
log_msg(NULL, 0, 0,
"Using linear partitioning ..");
2376 #ifdef WITH_PARMETIS
2379 if(verb > 2)
log_msg(NULL, 0, 0,
"Using Parmetis partitioner ..");
2380 SF::parmetis_partitioner<mesh_int_t, mesh_real_t> partitioner(param_globals::pstrat_imbalance, 2);
2381 partitioner(submesh, part);
2387 if(verb > 2)
log_msg(NULL, 0, 0,
"Using KDtree partitioner ..");
2389 partitioner(submesh, part);
2394 if(verb > 2)
log_msg(NULL, 0, 0,
"Partitioning done in %f sec.",
float(t2 - t1));
2396 if(param_globals::pstrat > 0) {
2397 if(param_globals::gridout_p) {
2398 std::string out_name =
get_basename(param_globals::meshname);
2403 log_msg(0,0,0,
"Writing \"%s\" partitioning to: %s", submesh.
name.c_str(), out_name.c_str());
2410 if(verb > 2)
log_msg(NULL, 0, 0,
"Redistributing done in %f sec.",
float(t2 - t1));
2415 sm_numbering(submesh);
2417 if(verb > 2)
log_msg(NULL, 0, 0,
"Canonical numbering done in %f sec.",
float(t2 - t1));
2422 p_numbering(submesh);
2424 if(verb > 2)
log_msg(NULL, 0, 0,
"PETSc numbering done in %f sec.",
float(t2 - t1));
2429 if(have_non_emi_mesh)
2435 if(verb)
log_msg(NULL, 0, 0,
"All done in %f sec.",
float(s2 - s1));
2440 static const char* parameter_name =
"gridout_tags";
2441 const std::string spec = param_globals::gridout_tags ? param_globals::gridout_tags :
"";
2443 std::vector<int> tags;
2445 if(!paramschema::parse_idset_spec(spec, &tags, &error)) {
2446 log_msg(0, 5,
ECHO,
"Could not parse %s: %s.", parameter_name, error.c_str());
2450 output_tags.
clear();
2451 output_tags.
insert(tags.begin(), tags.end());
2453 if(output_tags.
size() == 0)
return false;
2455 log_msg(0, 0, 0,
"Restricting grid output to %zu tag(s) from %s.",
2456 output_tags.
size(), parameter_name);
2467 for(
size_t eidx = 0; eidx < mesh.
l_numelem; eidx++) {
2468 if(output_tags.
count(mesh.
tag[eidx]) == 0)
continue;
2474 localize_gridout_nodes(mesh, selected_nodes, output_idx, async);
2482 for(
mesh_t mesh_id : mesh_ids) {
2486 for(
size_t eidx = 0; eidx < mesh.
l_numelem; eidx++) {
2487 if(output_tags.
count(mesh.
tag[eidx]) != 0)
2497 for(
int tag : sorted_tags) {
2498 int local_found = local_seen.
count(tag) ? 1 : 0;
2499 int global_found = 0;
2500 MPI_Allreduce(&local_found, &global_found, 1, MPI_INT, MPI_MAX,
SF_COMM);
2501 if(global_found == 0)
2505 if(missing_tags.
size()) {
2508 std::stringstream msg;
2509 for(
size_t i = 0; i < missing_tags.
size(); i++) {
2511 msg << missing_tags[i];
2514 log_msg(0, 3,
ECHO,
"Warning: ignoring gridout_tags not present in the selected output mesh(es): %s.",
2517 for(
int tag : missing_tags)
2518 output_tags.
erase(tag);
2520 if(output_tags.
size() == 0) {
2521 log_msg(0, 5,
ECHO,
"gridout_tags did not match any tag in the selected output mesh(es).");
2540 size_t lhs = 0, rhs = 0;
2541 while(lhs < restr->size() && rhs < selected.
size()) {
2542 if((*restr)[lhs] == selected[rhs]) {
2546 }
else if((*restr)[lhs] < selected[rhs]) {
2553 *restr = intersection;
2563 std::string output_base =
get_basename(param_globals::meshname);
2565 const bool restrict_gridout =
2567 if(restrict_gridout) {
2574 if(write_intra_elec) {
2578 if(restrict_gridout) {
2579 if(!mesh_has_gridout_tags(mesh, output_tags)) {
2580 log_msg(0, 5,
ECHO,
"gridout_tags selected no intracellular grid elements.");
2583 extract_gridout_tag_mesh(mesh, output_tags, restricted_mesh);
2584 output_mesh = &restricted_mesh;
2587 if(param_globals::gridout_i & 1) {
2588 if(param_globals::output_level > 1)
2589 log_msg(0,0,0,
"Computing \"%s\" surface ..", output_mesh->
name.c_str());
2591 std::string output_file = output_base +
"_i.surf";
2592 log_msg(0,0,0,
"Writing \"%s\" surface: %s", output_mesh->
name.c_str(), output_file.c_str());
2594 if(param_globals::gridout_i & 2) {
2602 write_gridout_surface(*output_mesh, output_base +
"_i");
2605 if(param_globals::gridout_i & 2) {
2606 bool write_binary =
false;
2608 std::string output_file = output_base +
"_i";
2609 log_msg(0,0,0,
"Writing \"%s\" mesh: %s", output_mesh->
name.c_str(), output_file.c_str());
2614 if(write_extra_elec) {
2618 if(restrict_gridout) {
2619 if(!mesh_has_gridout_tags(mesh, output_tags)) {
2620 log_msg(0, 5,
ECHO,
"gridout_tags selected no extracellular grid elements.");
2623 extract_gridout_tag_mesh(mesh, output_tags, restricted_mesh);
2624 output_mesh = &restricted_mesh;
2627 if(param_globals::gridout_e & 1) {
2628 if(param_globals::output_level > 1)
2629 log_msg(0,0,0,
"Computing \"%s\" surface ..", output_mesh->
name.c_str());
2631 std::string output_file = output_base +
"_e.surf";
2632 log_msg(0,0,0,
"Writing \"%s\" surface: %s", output_mesh->
name.c_str(), output_file.c_str());
2634 if(param_globals::gridout_e & 2) {
2642 write_gridout_surface(*output_mesh, output_base +
"_e");
2645 if(param_globals::gridout_e & 2) {
2646 bool write_binary =
false;
2647 std::string output_file = output_base +
"_e";
2648 log_msg(0,0,0,
"Writing \"%s\" mesh: %s", output_mesh->
name.c_str(), output_file.c_str());
2658 print_active_citation_suggestions();
2660 const paramschema::ResetResult reset = paramschema::reset_schema_state(paramschema::openCARP_schema());
2661 print_lines(stderr,
"parameter cleanup warning: ", reset.warnings);
2662 print_lines(stderr,
"parameter cleanup error: ", reset.errors);
2674 char* filecopy =
dupstr(file);
2675 char* dir =
dupstr(dirname(filecopy));
2694 petsc_redirect_errors(NULL);
2705 for(
size_t eidx = 0; eidx < mesh.
l_numelem; eidx++) {
2708 if(mindim < cdim) mindim = cdim;
2732 mesh.
pl.algebraic_layout();
2736 for(
size_t i = 0; i < idx->
size(); i++)
2745 int gsize = inp_data->
gsize();
2751 regigb.
x(gsize / dpn);
2752 regigb.
dim_x(regigb.
x()-1);
2755 regigb.
y(1); regigb.
z(1);
2771 regigb.
inc_t(param_globals::spacedt);
2785 log_msg(0,5,0,
"%s error: Could not set up data output! Aborting!", __func__);
2792 IO.
spec = mesh_spec;
2802 buffmap[mesh_spec] = inp_copy;
2809 void igb_output_manager::register_output_async(
sf_vec* inp_data,
2837 for(
size_t i=0; i<alg_nod.
size(); i++)
2838 ioidx[i] = nbr[alg_nod[i]];
2843 for(
size_t i=0; i<idx->
size(); i++) {
2845 ioidx[i] = nbr[loc_nodal];
2865 if(param_globals::num_io_nodes == 0)
2868 register_output_async(inp_data, inp_meshid, dpn, name,
units, idx, elem_data);
2883 sc->
forward(*data_vec, *perm_vec);
2898 FILE* fd =
static_cast<FILE*
>(it.
igb.
fileptr());
2901 sf_vec* buff = fill_output_buffer(it);
2907 const int dpn = it.
spec.
v2;
2914 for(
int j = 0; j < dpn; j++)
2951 FILE* fd =
static_cast<FILE*
>(it.
igb.
fileptr());
2982 bool render_output_parameter_arguments(
int argc,
2984 std::vector<std::string>* rendered_values)
2986 rendered_values->assign(
static_cast<std::size_t
>(argc), std::string());
2988 for (
int i = 1; i < argc; ++i) {
2989 const std::string argument(argv[i]);
2990 if (argument ==
"+F" || argument.find(
"_options_file") != std::string::npos) {
2991 if (i + 1 >= argc) {
2992 std::fprintf(stderr,
"parameter file error: missing value for '%s'\n", argument.c_str());
2995 if (argument.find(
"_options_file") != std::string::npos) {
2996 std::string render_error;
2998 std::fprintf(stderr,
"parameter file error: cannot render '%s': %s\n",
2999 argument.substr(1).c_str(), render_error.c_str());
3007 if (argument.empty() || argument[0] !=
'-') {
3011 const bool prelast = (i == argc - 1);
3012 const bool parameter_follows = !prelast &&
3013 ((argv[i + 1][0] !=
'-') ||
3014 ((argv[i + 1][1] >=
'0') && (argv[i + 1][1] <=
'9')));
3015 if (!parameter_follows) {
3019 const std::string raw_value(argv[i + 1]);
3020 std::string::size_type front = 0;
3021 while (front < raw_value.size() && raw_value[front] ==
' ') {
3025 bool aggregate_value =
false;
3026 std::string value = raw_value.substr(front);
3027 if (front < raw_value.size() && raw_value[front] ==
'{') {
3028 aggregate_value =
true;
3030 while (front < raw_value.size() && raw_value[front] ==
' ') {
3034 std::string::size_type back = raw_value.size();
3035 while (back > front && raw_value[back - 1] ==
' ') {
3038 if (back > front && raw_value[back - 1] ==
'}') {
3040 value = raw_value.substr(front, back - front);
3042 value = raw_value.substr(front);
3046 std::string render_error;
3048 std::fprintf(stderr,
"parameter file error: cannot render '%s': %s\n",
3049 argv[i] + 1, render_error.c_str());
3062 const int max_line_len = 128;
3063 const char* file_sep =
"#=======================================================";
3069 std::vector<std::string> rendered_values;
3070 if (!render_output_parameter_arguments(argc, argv, &rendered_values)) {
3075 fprintf(out->
fd,
"# CARP GIT commit hash: %s\n", GIT_COMMIT_HASH);
3076 fprintf(out->
fd,
"# dependency hashes: %s\n", SUBREPO_COMMITS);
3077 fprintf(out->
fd,
"\n");
3080 char line[8196] =
"# ";
3082 for (
int j=0; j<argc; j++) {
3083 strcat(line, argv[j]);
3084 if(strlen(line) > max_line_len) {
3085 fprintf(out->
fd,
"%s\n", line);
3091 fprintf(out->
fd,
"%s\n\n", line);
3095 for (
int i=1; i<argc; i++) {
3096 std::string argument(argv[i]);
3097 if (argument ==
"+F" || argument.find(
"_options_file")!= std::string::npos) {
3099 std::string init =
"";
3100 if (argument.find(
"_options_file")!= std::string::npos) {
3101 fprintf(out->
fd,
"%s = %s\n", argument.substr(1).c_str(), rendered_values[i].c_str());
3104 fprintf(out->
fd,
"%s>>\n", file_sep);
3107 fprintf(out->
fd,
"## %s ##\n", argv[i]);
3108 FILE *in = fopen(argv[i],
"r");
3109 while (fgets(line, 8196, in))
3110 fprintf(out->
fd,
"%s%s", init.c_str(), line);
3112 fprintf(out->
fd,
"\n##END of %s\n", argv[i]);
3113 fprintf(out->
fd,
"%s<<\n\n", file_sep);
3115 else if(argv[i][0] ==
'-')
3117 bool prelast = (i==argc-1);
3118 bool paramFollows = !prelast && ((argv[i+1][0] !=
'-') ||
3119 ((argv[i+1][1] >=
'0') && (argv[i+1][1] <=
'9')));
3124 fprintf(out->
fd,
"%-40s= %s\n", argv[i]+1, rendered_values[i].c_str());
3128 fprintf(out->
fd,
"%-40s= 1\n", argv[i]);
3136 bool aggregate_value,
3137 std::string* rendered,
3140 if (aggregate_value) {
3142 if (error != NULL) {
3147 return paramschema::quote_legacy_par_value(value, rendered, error);
3159 snprintf(save_fn,
sizeof save_fn,
"exit.save.%.3f.roe", time);
3160 log_msg(NULL, 0, 0,
"savequit called at time %g\n", time);
opencarp::local_index_t mesh_int_t
#define SF_COMM
the default SlimFem MPI communicator
opencarp::real_t SF_real
Global scalar type.
Starts and stops the linear algebra backend the flavor parameter selects.
#define EXIT(A)
finalize the backend, if it runs, and exit
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.
Redistributes vector data from indexing "a" to indexing "b".
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)
int get_size(MPI_Comm comm=SF_COMM)
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...
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=SF_COMM)
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.
Basic_physic * get_physics(physic_t p, bool error_if_missing)
Convinience function to get a physics.
int get_rank(MPI_Comm comm=SF_COMM)
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