diff --git a/Code/Source/solver/CMakeLists.txt b/Code/Source/solver/CMakeLists.txt index 18823439a..bf0be794b 100644 --- a/Code/Source/solver/CMakeLists.txt +++ b/Code/Source/solver/CMakeLists.txt @@ -31,6 +31,8 @@ include_directories(${SV_SOURCE_DIR}/ThirdParty/parmetis_internal/simvascular_pa include_directories(${SV_SOURCE_DIR}/ThirdParty/tetgen/simvascular_tetgen) include_directories(${SV_SOURCE_DIR}/ThirdParty/tinyxml/simvascular_tinyxml) include_directories(${MPI_C_INCLUDE_PATH}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/Core) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/FE/Common) # Find Trilinos package if requested if(SV_USE_TRILINOS) @@ -230,6 +232,22 @@ set(CSRCS CoupledBoundaryCondition.h CoupledBoundaryCondition.cpp ) +file(GLOB SOLVER_CORE_SRCS CONFIGURE_DEPENDS + Core/*.cpp + Core/*.h + Core/*.inl +) + +file(GLOB SOLVER_FE_COMMON_SRCS CONFIGURE_DEPENDS + FE/Common/*.cpp + FE/Common/*.h +) + +list(APPEND CSRCS + ${SOLVER_CORE_SRCS} + ${SOLVER_FE_COMMON_SRCS} +) + # Set PETSc interace code. #if(USE_PETSC) # set(CSRCS ${CSRCS} petsc_linear_solver.h petsc_linear_solver.c) @@ -372,4 +390,4 @@ if(ENABLE_UNIT_TEST) # gtest_discover_tests(runUnitTest) add_test(NAME all_unit_tests COMMAND run_all_unit_tests) -endif() \ No newline at end of file +endif() diff --git a/Code/Source/solver/Core/Exception.h b/Code/Source/solver/Core/Exception.h new file mode 100644 index 000000000..bc7012271 --- /dev/null +++ b/Code/Source/solver/Core/Exception.h @@ -0,0 +1,418 @@ +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the University of California, and others. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef SVMP_CORE_EXCEPTION_H +#define SVMP_CORE_EXCEPTION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(NDEBUG) || defined(DEBUG) || defined(_DEBUG) +# define SVMP_EXCEPTION_DEBUG_MODE 1 +#else +# define SVMP_EXCEPTION_DEBUG_MODE 0 +#endif + +namespace svmp { + +enum class StatusCode : std::uint8_t { + Success = 0, + InvalidArgument, + InvalidState, + ParseError, + IOError, + ResourceExhausted, + DependencyError, + MPIError, + NotImplemented, + UnsupportedOperation, + InternalError, + Unknown = 255 +}; + +inline const char* status_code_to_string(StatusCode status) noexcept +{ + switch (status) { + case StatusCode::Success: + return "Success"; + case StatusCode::InvalidArgument: + return "Invalid argument"; + case StatusCode::InvalidState: + return "Invalid state"; + case StatusCode::ParseError: + return "Parse error"; + case StatusCode::IOError: + return "I/O error"; + case StatusCode::ResourceExhausted: + return "Resource exhausted"; + case StatusCode::DependencyError: + return "Dependency error"; + case StatusCode::MPIError: + return "MPI error"; + case StatusCode::NotImplemented: + return "Not implemented"; + case StatusCode::UnsupportedOperation: + return "Unsupported operation"; + case StatusCode::InternalError: + return "Internal error"; + default: + return "Unknown error"; + } +} + +struct SourceLocation { + const char* file; + int line; + const char* function; +}; + +class StackTraceFrame final { +public: + StackTraceFrame() = default; + + StackTraceFrame(std::string symbol, std::string module, std::string file, + int line, std::uintptr_t address) noexcept + : symbol_(std::move(symbol)), + module_(std::move(module)), + file_(std::move(file)), + line_(line), + address_(address) + { + } + + const std::string& symbol() const noexcept { return symbol_; } + const std::string& module() const noexcept { return module_; } + const std::string& file() const noexcept { return file_; } + int line() const noexcept { return line_; } + std::uintptr_t address() const noexcept { return address_; } + +private: + std::string symbol_; + std::string module_; + std::string file_; + int line_ = 0; + std::uintptr_t address_ = 0; +}; + +class StackTrace final { +public: + bool empty() const noexcept { return frames_.empty(); } + std::size_t size() const noexcept { return frames_.size(); } + const std::vector& frames() const noexcept { return frames_; } + void add_frame(StackTraceFrame frame) { frames_.push_back(std::move(frame)); } + +private: + std::vector frames_; +}; + +struct PlatformCapabilities final { + bool has_stack_trace; + bool has_symbol_resolution; + bool has_demangling; +}; + +class PlatformSupport final { +public: + static PlatformCapabilities capabilities() noexcept; + static int query_mpi_rank() noexcept; + static StackTrace capture_stack_trace(); + static std::string demangle_symbol(const char* symbol); + static void finalize_mpi_if_needed() noexcept; + static void abort_mpi_if_needed(int exit_code) noexcept; +}; +} // namespace svmp + +#define SVMP_CORE_EXCEPTION_INCLUDE_PLATFORM_SUPPORT +#include "PlatformSupport.inl" +#undef SVMP_CORE_EXCEPTION_INCLUDE_PLATFORM_SUPPORT + +namespace svmp { + +class ExceptionContext final { +public: + StatusCode status_code() const noexcept { return status_code_; } + const std::string& file() const noexcept { return file_; } + int line() const noexcept { return line_; } + const std::string& function() const noexcept { return function_; } + int mpi_rank() const noexcept { return mpi_rank_; } + const StackTrace& stack_trace() const noexcept { return stack_trace_; } + + void set_status_code(StatusCode status_code) noexcept + { + status_code_ = status_code; + } + + void set_source_location(const char* file, int line, const char* function) + { + file_ = (file == nullptr) ? std::string() : std::string(file); + line_ = line; + function_ = + (function == nullptr) ? std::string() : std::string(function); + } + + void set_mpi_rank(int mpi_rank) noexcept { mpi_rank_ = mpi_rank; } + void set_stack_trace(StackTrace stack_trace) + { + stack_trace_ = std::move(stack_trace); + } + +private: + StatusCode status_code_ = StatusCode::Unknown; + std::string file_; + int line_ = 0; + std::string function_; + int mpi_rank_ = -1; + StackTrace stack_trace_; +}; + +namespace ExceptionFormatter { + +inline std::string format(const ExceptionContext& context, + const std::string& message, + std::string_view subsystem_label = "Exception") +{ + if (subsystem_label.empty()) { + subsystem_label = "Exception"; + } + + std::ostringstream oss; + + oss << "[" << subsystem_label << "] " + << status_code_to_string(context.status_code()); + if (context.mpi_rank() >= 0) { + oss << " (Rank " << context.mpi_rank() << ")"; + } + oss << "\n"; + + if (!context.file().empty()) { + oss << " Location: " << context.file() << ":" << context.line(); + if (!context.function().empty()) { + oss << " in " << context.function() << "()"; + } + oss << "\n"; + } + + oss << " Message: " << message << "\n"; + + if (!context.stack_trace().empty()) { + oss << " Stack trace:\n"; + std::size_t frame_index = 0; + for (const auto& frame : context.stack_trace().frames()) { + oss << " #" << frame_index++ << " "; + if (!frame.symbol().empty()) { + oss << frame.symbol(); + } else { + std::ostringstream address; + address << "0x" << std::hex << frame.address(); + oss << address.str(); + } + + if (!frame.module().empty()) { + oss << " [" << frame.module() << "]"; + } + + if (!frame.file().empty()) { + oss << " (" << frame.file(); + if (frame.line() > 0) { + oss << ":" << frame.line(); + } + oss << ")"; + } + + oss << "\n"; + } + } + + return oss.str(); +} + +} // namespace ExceptionFormatter + +class ExceptionBase; + +namespace ExceptionRuntime { + + inline int query_mpi_rank() noexcept + { + return PlatformSupport::query_mpi_rank(); + } + + inline StackTrace capture_stack_trace() + { + return PlatformSupport::capture_stack_trace(); + } + + inline void finalize_mpi_if_needed() noexcept + { + PlatformSupport::finalize_mpi_if_needed(); + } + + void install_terminate_handler(); + + inline void report_unhandled_exception(const std::exception& exception) + { + std::cerr << exception.what() << std::endl; + } + + inline void abort_mpi_if_needed(int exit_code) noexcept + { + PlatformSupport::abort_mpi_if_needed(exit_code); + } + +} // namespace ExceptionRuntime + +class ExceptionBase : public std::exception { +public: + const char* what() const noexcept override { return what_.c_str(); } + StatusCode status_code() const noexcept { return context_.status_code(); } + const std::string& message() const noexcept { return message_; } + const ExceptionContext& context() const noexcept { return context_; } + + void add_context(const std::string& context) + { + message_ = context + "\n -> " + message_; + rebuild_what(); + } + + virtual ~ExceptionBase() noexcept = default; + +protected: + ExceptionBase(std::string message, StatusCode status, + std::string_view subsystem_label, const char* file, + int line, const char* function) + : message_(std::move(message)), + subsystem_label_(subsystem_label.empty() ? std::string_view("Exception") + : subsystem_label) + { + context_.set_status_code(status); + context_.set_source_location(file, line, function); + context_.set_mpi_rank(ExceptionRuntime::query_mpi_rank()); +#if SVMP_EXCEPTION_DEBUG_MODE + context_.set_stack_trace(ExceptionRuntime::capture_stack_trace()); +#endif + rebuild_what(); + } + + void rebuild_what() + { + what_ = ExceptionFormatter::format(context_, message_, subsystem_label_); + } + + std::string message_; + ExceptionContext context_; + std::string_view subsystem_label_; + std::string what_; +}; + +class CoreException : public ExceptionBase { +public: + CoreException(const std::string& message, + StatusCode status = StatusCode::Unknown, + const char* file = "", + int line = 0, + const char* function = "") + : ExceptionBase(message, status, "Core Exception", file, line, function) + { + } +}; + +class ParseException : public CoreException { +public: + ParseException(const std::string& message, + const char* file = "", + int line = 0, + const char* function = "") + : CoreException(message, StatusCode::ParseError, file, line, function) + { + } +}; + +class DependencyException : public CoreException { +public: + DependencyException(const std::string& message, + const char* file = "", + int line = 0, + const char* function = "") + : CoreException(message, StatusCode::DependencyError, file, line, + function) + { + } +}; + +inline void ExceptionRuntime::install_terminate_handler() +{ + std::set_terminate([]() { + try { + const std::exception_ptr current = std::current_exception(); + if (current != nullptr) { + std::rethrow_exception(current); + } + } catch (const std::exception& exception) { + ExceptionRuntime::report_unhandled_exception(exception); + } catch (...) { + std::cerr << "[Unhandled Exception] Unknown non-std exception" + << std::endl; + } + + ExceptionRuntime::abort_mpi_if_needed(EXIT_FAILURE); + std::abort(); + }); +} + +template +[[noreturn]] void raise(SourceLocation location, Args&&... args) +{ + throw ExceptionT(std::forward(args)..., location.file, location.line, + location.function); +} + +template +void check(bool condition, SourceLocation location, Args&&... args) +{ + if (!condition) { + raise(location, std::forward(args)...); + } +} + +template +void check_arg(bool condition, SourceLocation location, Args&&... args) +{ + if (!condition) { + raise(location, std::forward(args)...); + } +} + +template +PointerT* check_not_null(PointerT* ptr, SourceLocation location, Args&&... args) +{ + if (ptr == nullptr) { + raise(location, std::forward(args)...); + } + return ptr; +} + +} // namespace svmp + +#define SVMP_HERE ::svmp::SourceLocation{__FILE__, __LINE__, __func__} + +#if SVMP_EXCEPTION_DEBUG_MODE +#define SVMP_DEBUG_CHECK(ExceptionT, condition, ...) \ + do { \ + if (!(condition)) { \ + ::svmp::raise(SVMP_HERE, __VA_ARGS__); \ + } \ + } while (false) +#else +#define SVMP_DEBUG_CHECK(ExceptionT, condition, ...) \ + do { \ + } while (false) +#endif + +#endif // SVMP_CORE_EXCEPTION_H diff --git a/Code/Source/solver/Core/PlatformSupport.inl b/Code/Source/solver/Core/PlatformSupport.inl new file mode 100644 index 000000000..010a27f06 --- /dev/null +++ b/Code/Source/solver/Core/PlatformSupport.inl @@ -0,0 +1,167 @@ +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the University of California, and others. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef SVMP_CORE_PLATFORM_SUPPORT_INL +#define SVMP_CORE_PLATFORM_SUPPORT_INL + +#ifndef SVMP_CORE_EXCEPTION_INCLUDE_PLATFORM_SUPPORT +#error "PlatformSupport.inl is private; include Core/Exception.h instead." +#endif + +#include + +#include +#include +#include +#include +#include + +#if defined(_WIN32) +# include +#elif defined(__GNUC__) || defined(__clang__) +# include +# include +#endif + +namespace svmp { + +inline PlatformCapabilities PlatformSupport::capabilities() noexcept +{ +#if defined(_WIN32) + return PlatformCapabilities{true, false, false}; +#elif defined(__GNUC__) || defined(__clang__) + return PlatformCapabilities{true, true, true}; +#else + return PlatformCapabilities{false, false, false}; +#endif +} + +inline int PlatformSupport::query_mpi_rank() noexcept +{ + int initialized = 0; + MPI_Initialized(&initialized); + if (!initialized) { + return -1; + } + + int finalized = 0; + MPI_Finalized(&finalized); + if (finalized) { + return -1; + } + + int rank = -1; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + return rank; +} + +inline std::string PlatformSupport::demangle_symbol(const char* symbol) +{ + if (symbol == nullptr) { + return std::string(); + } + +#if defined(__GNUC__) || defined(__clang__) + int status = 0; + char* demangled = abi::__cxa_demangle(symbol, nullptr, nullptr, &status); + if (status == 0 && demangled != nullptr) { + std::string result(demangled); + std::free(demangled); + return result; + } +#endif + + return std::string(symbol); +} + +inline StackTrace PlatformSupport::capture_stack_trace() +{ + StackTrace trace; + +#if defined(_WIN32) + constexpr unsigned long max_frames = 32; + void* frames[max_frames] = {}; + const USHORT count = CaptureStackBackTrace(0, max_frames, frames, nullptr); + + for (USHORT i = 1; i < count; ++i) { + const std::uintptr_t address = + reinterpret_cast(frames[i]); + std::ostringstream symbol; + symbol << "0x" << std::hex << address; + trace.add_frame(StackTraceFrame(symbol.str(), std::string(), + std::string(), 0, address)); + } +#elif defined(__GNUC__) || defined(__clang__) + constexpr int max_frames = 32; + void* frames[max_frames] = {}; + const int count = backtrace(frames, max_frames); + char** symbols = backtrace_symbols(frames, count); + + if (symbols != nullptr) { + for (int i = 2; i < count; ++i) { + const std::uintptr_t address = + reinterpret_cast(frames[i]); + std::string symbol(symbols[i]); + + const std::size_t start = symbol.find('('); + const std::size_t end = symbol.find('+', start); + if (start != std::string::npos && end != std::string::npos && + end > start + 1) { + const std::string mangled = + symbol.substr(start + 1, end - start - 1); + const std::string demangled = + PlatformSupport::demangle_symbol(mangled.c_str()); + if (!demangled.empty()) { + symbol.replace(start + 1, end - start - 1, demangled); + } + } + + trace.add_frame( + StackTraceFrame(symbol, std::string(), std::string(), 0, address)); + } + std::free(symbols); + } +#endif + + return trace; +} + +inline void PlatformSupport::finalize_mpi_if_needed() noexcept +{ + int initialized = 0; + MPI_Initialized(&initialized); + if (!initialized) { + return; + } + + int finalized = 0; + MPI_Finalized(&finalized); + if (!finalized) { + MPI_Finalize(); + } +} + +inline void PlatformSupport::abort_mpi_if_needed(int exit_code) noexcept +{ + int initialized = 0; + MPI_Initialized(&initialized); + if (!initialized) { + return; + } + + int finalized = 0; + MPI_Finalized(&finalized); + if (finalized) { + return; + } + + int size = 1; + MPI_Comm_size(MPI_COMM_WORLD, &size); + if (size > 1) { + MPI_Abort(MPI_COMM_WORLD, exit_code); + } +} + +} // namespace svmp + +#endif // SVMP_CORE_PLATFORM_SUPPORT_INL diff --git a/Code/Source/solver/FE/Common/FEException.h b/Code/Source/solver/FE/Common/FEException.h new file mode 100644 index 000000000..d4791eeb9 --- /dev/null +++ b/Code/Source/solver/FE/Common/FEException.h @@ -0,0 +1,327 @@ +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the University of California, and others. +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef SVMP_FE_EXCEPTION_H +#define SVMP_FE_EXCEPTION_H + +/** + * @file FEException.h + * @brief Exception hierarchy for error handling in the FE library + * + * This header defines FE-specific exception types that derive from the shared + * solver exception infrastructure in Exception.h. FEException marks + * failures from finite-element assembly, backend, DOF, and element operations. + */ + +#include "Exception.h" + +#include +#include +#include + +namespace svmp { +namespace FE { + +class FEException : public ExceptionBase { +public: + FEException(const std::string& message, + StatusCode status = StatusCode::Unknown, + const char* file = "", + int line = 0, + const char* function = "") + : ExceptionBase(message, + status, + "FE Exception", + file, + line, + function) + { + } + + FEException(const std::string& message, + const char* file, + int line, + const char* function = "") + : FEException(message, StatusCode::Unknown, file, line, function) + { + } + + StatusCode status() const noexcept { return status_code(); } +}; + +class InvalidArgumentException : public FEException { +public: + InvalidArgumentException(const std::string& message, + const char* file = "", + int line = 0, + const char* function = "") + : FEException(message, StatusCode::InvalidArgument, file, line, + function) + { + } +}; + +class InvalidElementException : public FEException { +public: + InvalidElementException(const std::string& message, + std::string element_type = "", + const char* file = "", + int line = 0, + const char* function = "") + : FEException(build_message(message, element_type), + StatusCode::InvalidArgument, + file, + line, + function), + element_type_(std::move(element_type)) + { + } + + const std::string& element_type() const noexcept { return element_type_; } + +private: + static std::string build_message(const std::string& message, + const std::string& element_type) + { + if (element_type.empty()) { + return message; + } + + return message + " (Element type: " + element_type + ")"; + } + + std::string element_type_; +}; + +class DofException : public FEException { +public: + DofException(const std::string& message, + long long dof_index = invalid_dof_index(), + const char* file = "", + int line = 0, + const char* function = "") + : FEException(build_message(message, dof_index), + StatusCode::InvalidArgument, + file, + line, + function), + dof_index_(dof_index) + { + } + + long long dof_index() const noexcept { return dof_index_; } + static constexpr long long invalid_dof_index() noexcept { return -1; } + +private: + static std::string build_message(const std::string& message, + long long dof_index) + { + if (dof_index == invalid_dof_index()) { + return message; + } + + return message + " (DOF index: " + std::to_string(dof_index) + ")"; + } + + long long dof_index_ = invalid_dof_index(); +}; + +class AssemblyException : public FEException { +public: + AssemblyException(const std::string& message, + const char* file = "", + int line = 0, + const char* function = "") + : FEException(message, StatusCode::InvalidState, file, line, function) + { + } +}; + +class BackendException : public FEException { +public: + BackendException(const std::string& message, + std::string backend_name = "", + int error_code = 0, + const char* file = "", + int line = 0, + const char* function = "") + : FEException(build_message(message, backend_name, error_code), + StatusCode::DependencyError, + file, + line, + function), + backend_name_(std::move(backend_name)), + error_code_(error_code) + { + } + + const std::string& backend_name() const noexcept { return backend_name_; } + int error_code() const noexcept { return error_code_; } + +private: + static std::string build_message(const std::string& message, + const std::string& backend_name, + int error_code) + { + std::ostringstream oss; + oss << message; + if (!backend_name.empty() || error_code != 0) { + oss << " ("; + if (!backend_name.empty()) { + oss << "Backend: " << backend_name; + } + if (error_code != 0) { + if (!backend_name.empty()) { + oss << ", "; + } + oss << "Error code: " << error_code; + } + oss << ")"; + } + return oss.str(); + } + + std::string backend_name_; + int error_code_ = 0; +}; + +class NotImplementedException : public FEException { +public: + NotImplementedException(const std::string& feature, + const char* file = "", + int line = 0, + const char* function = "") + : FEException("Feature not implemented: " + feature, + StatusCode::NotImplemented, + file, + line, + function) + { + } +}; + +class ConvergenceException : public FEException { +public: + ConvergenceException(const std::string& message, + int iteration = -1, + double residual = 0.0, + const char* file = "", + int line = 0, + const char* function = "") + : FEException(build_message(message, iteration, residual), + StatusCode::InvalidState, + file, + line, + function), + iteration_(iteration), + residual_(residual) + { + } + + int iteration() const noexcept { return iteration_; } + double residual() const noexcept { return residual_; } + +private: + static std::string build_message(const std::string& message, + int iteration, + double residual) + { + std::ostringstream oss; + oss << message; + if (iteration >= 0) { + oss << " (Iteration: " << iteration; + if (residual > 0.0) { + oss << ", Residual: " << residual; + } + oss << ")"; + } + return oss.str(); + } + + int iteration_ = -1; + double residual_ = 0.0; +}; + +class SingularMappingException : public FEException { +public: + SingularMappingException(const std::string& message, + double jacobian_det = 0.0, + const char* file = "", + int line = 0, + const char* function = "") + : FEException(build_message(message, jacobian_det), + StatusCode::InvalidState, + file, + line, + function), + jacobian_det_(jacobian_det) + { + } + + double jacobian_det() const noexcept { return jacobian_det_; } + +private: + static std::string build_message(const std::string& message, double det) + { + return message + " (Jacobian determinant: " + std::to_string(det) + + ")"; + } + + double jacobian_det_ = 0.0; +}; + +template +[[noreturn]] inline void raise(SourceLocation location, Args&&... args) +{ + ::svmp::raise(location, std::forward(args)...); +} + +template +inline void throw_if(bool condition, SourceLocation location, Args&&... args) +{ + if (condition) { + ::svmp::FE::raise(location, std::forward(args)...); + } +} + +template +inline void check_arg(bool condition, SourceLocation location, Args&&... args) +{ + ::svmp::check_arg(condition, location, + std::forward(args)...); +} + +template +inline PointerT* check_not_null(PointerT* ptr, SourceLocation location, + Args&&... args) +{ + return ::svmp::check_not_null(ptr, location, + std::forward(args)...); +} + +template +inline void check_index(IndexT index, SizeT size, SourceLocation location) +{ + const long long fe_check_index_value = static_cast(index); + const long long fe_check_size_value = static_cast(size); + + ::svmp::FE::check_arg( + fe_check_index_value >= 0 && + fe_check_index_value < fe_check_size_value, + location, + "Index " + std::to_string(fe_check_index_value) + + " out of bounds [0, " + std::to_string(fe_check_size_value) + ")"); +} + +[[noreturn]] inline void not_implemented(const std::string& feature, + SourceLocation location) +{ + ::svmp::FE::raise(location, feature); +} + +} // namespace FE +} // namespace svmp + +#endif // SVMP_FE_EXCEPTION_H diff --git a/Code/Source/solver/Parameters.cpp b/Code/Source/solver/Parameters.cpp index 70229cf2e..062619e9a 100644 --- a/Code/Source/solver/Parameters.cpp +++ b/Code/Source/solver/Parameters.cpp @@ -48,6 +48,8 @@ #include "LinearAlgebra.h" #include "ustruct.h" +#include +#include #include #include #include @@ -55,6 +57,71 @@ #include #include +namespace { + +std::string uppercase_xml_name(const std::string& value) +{ + std::string upper = value; + std::transform(upper.begin(), upper.end(), upper.begin(), + [](unsigned char c) { return static_cast(std::toupper(c)); }); + return upper; +} + +std::string missing_xml_attribute_message(tinyxml2::XMLElement* element, + const char* attribute_name) +{ + const std::string element_name = + (element != nullptr && element->Name() != nullptr) + ? std::string(element->Name()) + : std::string("unknown"); + const std::string attribute = + (attribute_name != nullptr) ? std::string(attribute_name) + : std::string("attribute"); + const std::string attribute_upper = uppercase_xml_name(attribute); + + return "No " + attribute_upper + " given in the XML <" + element_name + " " + + attribute + "=" + attribute_upper + "> element."; +} + +const char* require_xml_attribute(tinyxml2::XMLElement* element, + const char* attribute_name, svmp::SourceLocation location, + const std::string& message = std::string()) +{ + const char* value = nullptr; + if (element == nullptr || + element->QueryStringAttribute(attribute_name, &value) != tinyxml2::XML_SUCCESS || + value == nullptr) { + svmp::raise( + location, + message.empty() ? missing_xml_attribute_message(element, attribute_name) + : message); + } + return value; +} + +const char* require_xml_text(tinyxml2::XMLElement* element, + svmp::SourceLocation location, const std::string& message) +{ + if (element == nullptr || element->GetText() == nullptr) { + svmp::raise(location, message); + } + return element->GetText(); +} + +template +typename MapT::mapped_type require_map_value(const MapT& map, + const typename MapT::key_type& key, svmp::SourceLocation location, + const std::string& message) +{ + auto iter = map.find(key); + if (iter == map.end()) { + svmp::raise(location, message); + } + return iter->second; +} + +} // namespace + /// @brief Set paramaters using a function pointing to the 'ParameterLists::set_parameter_value' method. // // Subsection names given in 'sub_sections' are ignored and processed elsewhere. @@ -73,10 +140,10 @@ void xml_util_set_parameters( std::function(SVMP_HERE, error_msg + name + "'."); } } else { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } } @@ -93,13 +160,19 @@ std::string IncludeParametersFile::NAME = "Include_xml"; IncludeParametersFile::IncludeParametersFile(const char* cfile_name) { + svmp::check( + cfile_name != nullptr, SVMP_HERE, "Include_xml requires a file name."); + std::string file_name(cfile_name); file_name.erase(std::remove_if(file_name.begin(), file_name.end(), ::isspace), file_name.end()); + svmp::check( + !file_name.empty(), SVMP_HERE, "Include_xml requires a non-empty file name."); + auto error = document.LoadFile(file_name.c_str()); root_element = document.FirstChildElement(Parameters::FSI_FILE.c_str()); - if (root_element == nullptr) { - throw std::runtime_error("The following error occured while reading the XML file '" + + if (error != tinyxml2::XML_SUCCESS || root_element == nullptr) { + svmp::raise(SVMP_HERE, "The following error occurred while reading the XML file '" + file_name + "'.\n" + "[svMultiPhysics] ERROR " + std::string(document.ErrorStr())); } } @@ -155,9 +228,9 @@ void Parameters::read_xml(std::string file_name) auto error = doc.LoadFile(file_name.c_str()); auto root_element = doc.FirstChildElement(FSI_FILE.c_str()); - if (root_element == nullptr) { - throw std::runtime_error("The following error occured while reading the XML file '" + file_name + "'.\n" + - "[svFSI] ERROR " + std::string(doc.ErrorStr())); + if (error != tinyxml2::XML_SUCCESS || root_element == nullptr) { + svmp::raise(SVMP_HERE, "The following error occurred while reading the XML file '" + file_name + "'.\n" + + "[svMultiPhysics] ERROR " + std::string(doc.ErrorStr())); } // Get general parameters. @@ -201,8 +274,7 @@ void Parameters::set_equation_values(tinyxml2::XMLElement* root_element) auto add_eq_item = root_element->FirstChildElement(EquationParameters::xml_element_name_.c_str()); while (add_eq_item) { - const char* eq_type; - auto result = add_eq_item->QueryStringAttribute("type", &eq_type); + const char* eq_type = require_xml_attribute(add_eq_item, "type", SVMP_HERE); auto eq_params = new EquationParameters(); eq_params->type.set(std::string(eq_type)); @@ -218,8 +290,7 @@ void Parameters::set_mesh_values(tinyxml2::XMLElement* root_element) auto add_mesh_item = root_element->FirstChildElement(MeshParameters::xml_element_name_.c_str()); while (add_mesh_item) { - const char* mesh_name; - auto result = add_mesh_item->QueryStringAttribute("name", &mesh_name); + const char* mesh_name = require_xml_attribute(add_mesh_item, "name", SVMP_HERE); MeshParameters* mesh_params = new MeshParameters(); mesh_params->name.set(std::string(mesh_name)); @@ -245,8 +316,7 @@ void Parameters::set_projection_values(tinyxml2::XMLElement* root_element) auto add_proj_item = root_element->FirstChildElement(ProjectionParameters::xml_element_name_.c_str()); while (add_proj_item) { - const char* proj_name; - auto result = add_proj_item->QueryStringAttribute("name", &proj_name); + const char* proj_name = require_xml_attribute(add_proj_item, "name", SVMP_HERE); ProjectionParameters* proj_params = new ProjectionParameters(); proj_params->name.set(std::string(proj_name)); @@ -262,8 +332,8 @@ void Parameters::set_RIS_projection_values(tinyxml2::XMLElement* root_element) auto add_RIS_proj_item = root_element->FirstChildElement(RISProjectionParameters::xml_element_name_.c_str()); while (add_RIS_proj_item) { - const char* RIS_proj_name; - auto result = add_RIS_proj_item->QueryStringAttribute("name", &RIS_proj_name); + const char* RIS_proj_name = + require_xml_attribute(add_RIS_proj_item, "name", SVMP_HERE); RISProjectionParameters* RIS_proj_params = new RISProjectionParameters(); RIS_proj_params->name.set(std::string(RIS_proj_name)); @@ -279,8 +349,8 @@ void Parameters::set_URIS_mesh_values(tinyxml2::XMLElement* root_element) auto add_URIS_mesh_item = root_element->FirstChildElement(URISMeshParameters::xml_element_name_.c_str()); while (add_URIS_mesh_item) { - const char* URIS_mesh_name; - auto result = add_URIS_mesh_item->QueryStringAttribute("name", &URIS_mesh_name); + const char* URIS_mesh_name = + require_xml_attribute(add_URIS_mesh_item, "name", SVMP_HERE); URISMeshParameters* URIS_mesh_params = new URISMeshParameters(); URIS_mesh_params->name.set(std::string(URIS_mesh_name)); @@ -340,11 +410,7 @@ void BodyForceParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'type' from the element. - const char* smesh; - auto result = xml_elem->QueryStringAttribute("mesh", &smesh); - if (smesh == nullptr) { - throw std::runtime_error("No MESH given in the XML element."); - } + const char* smesh = require_xml_attribute(xml_elem, "mesh", SVMP_HERE); mesh_name.set(std::string(smesh)); //auto item = xml_elem->FirstChildElement(); @@ -518,11 +584,7 @@ void BoundaryConditionParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'name' from the element. - const char* sname; - auto result = xml_elem->QueryStringAttribute("name", &sname); - if (sname == nullptr) { - throw std::runtime_error("No NAME given in the XML element."); - } + const char* sname = require_xml_attribute(xml_elem, "name", SVMP_HERE); name.set(std::string(sname)); auto item = xml_elem->FirstChildElement(); @@ -539,10 +601,10 @@ void BoundaryConditionParameters::set_values(tinyxml2::XMLElement* xml_elem) try { set_parameter_value(name, value); } catch (const std::bad_function_call& exception) { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } } else { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } item = item->NextSiblingElement(); @@ -875,17 +937,16 @@ void CANNRowParameters::print_parameters() void CANNRowParameters::set_values(tinyxml2::XMLElement* row_elem) { - if (!row_elem) { - throw std::runtime_error("CANNRowParameters::set_values: Received null XML element."); - } + row_elem = svmp::check_not_null( + row_elem, SVMP_HERE, "CANNRowParameters::set_values: Received null XML element."); using namespace tinyxml2; std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Set row_name for current row element - const char* row_name_input; - auto result = row_elem->QueryStringAttribute("row_name", &row_name_input); + const char* row_name_input = + require_xml_attribute(row_elem, "row_name", SVMP_HERE); row_name.set(std::string(row_name_input)); auto item = row_elem->FirstChildElement(); @@ -896,13 +957,13 @@ void CANNRowParameters::set_values(tinyxml2::XMLElement* row_elem) auto value = item->GetText(); if (value == nullptr) { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } try { set_parameter_value_CANN(name, value); } catch (const std::bad_function_call& exception) { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } item = item->NextSiblingElement(); @@ -946,7 +1007,7 @@ void CANNParameters::set_values(tinyxml2::XMLElement* xml_elem) } if (rows.empty()) { - throw std::runtime_error(error_msg + "Add_row'. No rows found."); + svmp::raise(SVMP_HERE, error_msg + "Add_row'. No rows found."); } value_set = true; @@ -993,11 +1054,7 @@ void ConstitutiveModelParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'type' from the element. - const char* stype; - auto result = xml_elem->QueryStringAttribute("type", &stype); - if (stype == nullptr) { - throw std::runtime_error("No TYPE given in the XML element."); - } + const char* stype = require_xml_attribute(xml_elem, "type", SVMP_HERE); type.set(std::string(stype)); // Check constitutive model type. @@ -1009,7 +1066,7 @@ void ConstitutiveModelParameters::set_values(tinyxml2::XMLElement* xml_elem) } msg_2 += "\n"; auto msg = msg_1 + msg_2; - throw std::runtime_error(msg); + svmp::raise(SVMP_HERE, msg); } auto model_type = constitutive_model_types.at(type.value()); type.set(model_type); @@ -1024,12 +1081,14 @@ void ConstitutiveModelParameters::set_values(tinyxml2::XMLElement* xml_elem) // void ConstitutiveModelParameters::check_constitutive_model(const Parameter& eq_type_str) { - auto eq_type = consts::equation_name_to_type.at(eq_type_str.value()); - auto model = consts::constitutive_model_name_to_type.at(type.value()); + auto eq_type = require_map_value(consts::equation_name_to_type, eq_type_str.value(), + SVMP_HERE, "Unknown equation type '" + eq_type_str.value() + "'."); + auto model = require_map_value(consts::constitutive_model_name_to_type, type.value(), + SVMP_HERE, "Unknown constitutive model '" + type.value() + "'."); if (eq_type == consts::EquationType::phys_ustruct) { if (! ustruct::constitutive_model_is_valid(model)) { - throw std::runtime_error("The " + type.value() + " constitutive model is not valid for ustruct equations."); + svmp::raise(SVMP_HERE, "The " + type.value() + " constitutive model is not valid for ustruct equations."); } } } @@ -1058,11 +1117,7 @@ void CoupleGenBCParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string error_msg = "Unknown Couple_to_genBC type=TYPE XML element '"; // Get the 'type' from the element. - const char* stype; - auto result = xml_elem->QueryStringAttribute("type", &stype); - if (stype == nullptr) { - throw std::runtime_error("No TYPE given in the XML element."); - } + const char* stype = require_xml_attribute(xml_elem, "type", SVMP_HERE); type.set(std::string(stype)); auto item = xml_elem->FirstChildElement(); @@ -1152,8 +1207,7 @@ void OutputParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string msg("[OutputParameters::set_values] "); std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; - const char* stype; - auto result = xml_elem->QueryStringAttribute("type", &stype); + const char* stype = require_xml_attribute(xml_elem, "type", SVMP_HERE); type.set(std::string(stype)); // Get values from XML file. @@ -1165,7 +1219,8 @@ void OutputParameters::set_values(tinyxml2::XMLElement* xml_elem) auto item = xml_elem->FirstChildElement(); while (item != nullptr) { auto name = std::string(item->Name()); - auto value = std::string(item->GetText()); + auto value = std::string(require_xml_text(item, SVMP_HERE, + "Output XML element '" + name + "' requires a value.")); Parameter param(name, "", false); param.set(value); alias_list.emplace_back(param); @@ -1175,7 +1230,8 @@ void OutputParameters::set_values(tinyxml2::XMLElement* xml_elem) auto item = xml_elem->FirstChildElement(); while (item != nullptr) { auto name = std::string(item->Name()); - auto value = std::string(item->GetText()); + auto value = std::string(require_xml_text(item, SVMP_HERE, + "Output XML element '" + name + "' requires a value.")); Parameter param(name, false, false); param.set(value); output_list.emplace_back(param); @@ -1234,11 +1290,7 @@ void VariableWallPropsParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'type' from the element. - const char* sname; - auto result = xml_elem->QueryStringAttribute("mesh_name", &sname); - if (sname == nullptr) { - throw std::runtime_error("No TYPE given in the XML element."); - } + const char* sname = require_xml_attribute(xml_elem, "mesh_name", SVMP_HERE); mesh_name.set(std::string(sname)); auto item = xml_elem->FirstChildElement(); @@ -1403,17 +1455,12 @@ void FluidViscosityParameters::set_values(tinyxml2::XMLElement* xml_elem) { using namespace tinyxml2; - const char* smodel; - auto result = xml_elem->QueryStringAttribute("model", &smodel); - - if (smodel == nullptr) { - throw std::runtime_error("No MODEL given in the XML element."); - } + const char* smodel = require_xml_attribute(xml_elem, "model", SVMP_HERE); model.set(std::string(smodel)); // Check fluid_viscosity model name. if (model_names.count(model.value()) == 0) { - throw std::runtime_error("Unknown fluid viscosity model '" + model.value() + + svmp::raise(SVMP_HERE, "Unknown fluid viscosity model '" + model.value() + "' in '" + xml_elem->Name() + "'."); } @@ -1528,17 +1575,12 @@ void SolidViscosityParameters::set_values(tinyxml2::XMLElement* xml_elem) { using namespace tinyxml2; - const char* smodel; - auto result = xml_elem->QueryStringAttribute("model", &smodel); - - if (smodel == nullptr) { - throw std::runtime_error("No MODEL given in the XML element."); - } + const char* smodel = require_xml_attribute(xml_elem, "model", SVMP_HERE); model.set(std::string(smodel)); // Check solid viscosity model name. if (model_names.count(model.value()) == 0) { - throw std::runtime_error("Unknown solid viscosity model '" + model.value() + + svmp::raise(SVMP_HERE, "Unknown solid viscosity model '" + model.value() + "' in '" + xml_elem->Name() + "'."); } @@ -1659,11 +1701,7 @@ void DomainParameters::set_values(tinyxml2::XMLElement* domain_elem, bool from_e // If not reading from an external xml file then get the 'id' attrribute. // if (!from_external_xml) { - const char* sid; - auto result = domain_elem->QueryStringAttribute("id", &sid); - if (sid == nullptr) { - throw std::runtime_error("No ID found in the XML element."); - } + const char* sid = require_xml_attribute(domain_elem, "id", SVMP_HERE); id.set(std::string(sid)); } @@ -1687,18 +1725,20 @@ void DomainParameters::set_values(tinyxml2::XMLElement* domain_elem, bool from_e ttp_initial_conditions.set_values(item); } else if (name == FluidViscosityParameters::xml_element_name_ || name == SolidViscosityParameters::xml_element_name_) { - auto eq_type = consts::equation_name_to_type.at(equation.value()); + auto eq_type = require_map_value(consts::equation_name_to_type, equation.value(), + SVMP_HERE, "Unknown equation type '" + equation.value() + "' while parsing viscosity model."); if (eq_type == consts::EquationType::phys_fluid || eq_type == consts::EquationType::phys_CMM || eq_type == consts::EquationType::phys_stokes) { fluid_viscosity.set_values(item); } else if (eq_type == consts::EquationType::phys_struct || eq_type == consts::EquationType::phys_ustruct) { solid_viscosity.set_values(item); } else { - throw std::runtime_error("Viscosity model not supported for equation '" + equation.value() + "'."); + svmp::raise(SVMP_HERE, "Viscosity model not supported for equation '" + equation.value() + "'."); } } else if (name == include_xml.name()) { - auto value = item->GetText(); + auto value = require_xml_text(item, SVMP_HERE, + "Domain Include_xml requires a file name."); IncludeParametersFile include_parameters(value); set_values(include_parameters.root_element, true); @@ -1707,11 +1747,11 @@ void DomainParameters::set_values(tinyxml2::XMLElement* domain_elem, bool from_e try { set_parameter_value(name, value); } catch (const std::bad_function_call& exception) { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } } else { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } item = item->NextSiblingElement(); @@ -1722,12 +1762,12 @@ void DomainParameters::set_values(tinyxml2::XMLElement* domain_elem, bool from_e // Check values for some parameters.. // if (Parameters::constitutive_model_names.count(constitutive_model.value()) == 0) { - throw std::runtime_error("Unknown constitutive model '" + constitutive_model.value_ + "' for '" + constitutive_model.name_ + + svmp::raise(SVMP_HERE, "Unknown constitutive model '" + constitutive_model.value_ + "' for '" + constitutive_model.name_ + "' in '" + domain_params->Name() + "'."); } if (Parameters::equation_names.count(equation.value()) == 0) { - throw std::runtime_error("Unknown equation name '" + equation.value() + "' for '" + equation.name_ + + svmp::raise(SVMP_HERE, "Unknown equation name '" + equation.value() + "' for '" + equation.name_ + "' in '" + domain_params->Name() + "'."); } */ @@ -1773,19 +1813,19 @@ void TTPInitialConditionsParameters::set_values(tinyxml2::XMLElement* xml_elem) value_set = true; } else { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } item = item->NextSiblingElement(); } if (!initial_states.defined()) { - throw std::runtime_error(xml_element_name_ + " requires an '" + + svmp::raise(SVMP_HERE, xml_element_name_ + " requires an '" + TTPInitialStatesParameters::xml_element_name_ + "' XML section."); } if (!gating_variables.defined()) { - throw std::runtime_error(xml_element_name_ + " requires a '" + + svmp::raise(SVMP_HERE, xml_element_name_ + " requires a '" + TTPGatingVariablesParameters::xml_element_name_ + "' XML section."); } } @@ -1836,10 +1876,10 @@ void TTPInitialStatesParameters::set_values(tinyxml2::XMLElement* xml_elem) set_parameter_value(name, value); value_set = true; } catch (const std::bad_function_call& exception) { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } } else { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } item = item->NextSiblingElement(); @@ -1902,10 +1942,10 @@ void TTPGatingVariablesParameters::set_values(tinyxml2::XMLElement* xml_elem) set_parameter_value(name, value); value_set = true; } catch (const std::bad_function_call& exception) { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } } else { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } item = item->NextSiblingElement(); @@ -1962,7 +2002,7 @@ void DirectionalDistributionParameters::validate() const // Empty block is invalid - if block exists, must specify all three if (num_defined == 0) { - throw std::runtime_error("Directional_distribution block is empty. " + svmp::raise(SVMP_HERE, "Directional_distribution block is empty. " "Either remove the block entirely (to use defaults: fiber=1.0, sheet=0.0, normal=0.0) " "or specify all three directions: Fiber_direction, Sheet_direction, Sheet_normal_direction."); } @@ -1977,7 +2017,7 @@ void DirectionalDistributionParameters::validate() const if (!fiber_defined) msg += "Fiber_direction "; if (!sheet_defined) msg += "Sheet_direction "; if (!normal_defined) msg += "Sheet_normal_direction "; - throw std::runtime_error(msg); + svmp::raise(SVMP_HERE, msg); } // All three are specified, validate their values @@ -1989,7 +2029,7 @@ void DirectionalDistributionParameters::validate() const double eta_sum = eta_f + eta_s + eta_n; const double tol = 1.0e-10; if (std::abs(eta_sum - 1.0) > tol) { - throw std::runtime_error("Directional distribution fractions must sum to 1.0. " + svmp::raise(SVMP_HERE, "Directional distribution fractions must sum to 1.0. " "Got: Fiber_direction=" + std::to_string(eta_f) + ", Sheet_direction=" + std::to_string(eta_s) + ", Sheet_normal_direction=" + std::to_string(eta_n) + @@ -1998,7 +2038,7 @@ void DirectionalDistributionParameters::validate() const // Validate that each eta is non-negative if (eta_f < 0.0 || eta_s < 0.0 || eta_n < 0.0) { - throw std::runtime_error("Directional distribution fractions must be non-negative. " + svmp::raise(SVMP_HERE, "Directional distribution fractions must be non-negative. " "Got: Fiber_direction=" + std::to_string(eta_f) + ", Sheet_direction=" + std::to_string(eta_s) + ", Sheet_normal_direction=" + std::to_string(eta_n)); @@ -2045,11 +2085,7 @@ void FiberReinforcementStressParameters::set_values(tinyxml2::XMLElement* xml_el std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'type' from the element attribute. - const char* stype; - auto result = xml_elem->QueryStringAttribute("type", &stype); - if (stype == nullptr) { - throw std::runtime_error("No TYPE given in the XML element."); - } + const char* stype = require_xml_attribute(xml_elem, "type", SVMP_HERE); type.set(std::string(stype)); auto item = xml_elem->FirstChildElement(); @@ -2064,10 +2100,10 @@ void FiberReinforcementStressParameters::set_values(tinyxml2::XMLElement* xml_el try { set_parameter_value(name, value); } catch (const std::bad_function_call& exception) { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } } else { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } item = item->NextSiblingElement(); @@ -2126,11 +2162,7 @@ void StimulusParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'type' from the element. - const char* stype; - auto result = xml_elem->QueryStringAttribute("type", &stype); - if (stype == nullptr) { - throw std::runtime_error("No TYPE given in the XML element."); - } + const char* stype = require_xml_attribute(xml_elem, "type", SVMP_HERE); type.set(std::string(stype)); auto item = xml_elem->FirstChildElement(); @@ -2259,11 +2291,7 @@ void ContactParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'type' from the element. - const char* mname; - auto result = xml_elem->QueryStringAttribute("model", &mname); - if (mname == nullptr) { - throw std::runtime_error("No MODEL given in the XML element."); - } + const char* mname = require_xml_attribute(xml_elem, "model", SVMP_HERE); model.set(std::string(mname)); using std::placeholders::_1; @@ -2424,14 +2452,15 @@ void EquationParameters::set_values(tinyxml2::XMLElement* eq_elem, DomainParamet domain->stimulus.set_values(item); } else if (viscosity_names.count(name)) { - auto eq_type = consts::equation_name_to_type.at(type.value()); + auto eq_type = require_map_value(consts::equation_name_to_type, type.value(), + SVMP_HERE, "Unknown equation type '" + type.value() + "' while parsing viscosity model."); if (fluid_eqs.count(eq_type)) { domain->fluid_viscosity.set_values(item); } else if (eq_type == consts::EquationType::phys_struct || eq_type == consts::EquationType::phys_ustruct) { domain->solid_viscosity.set_values(item); } else { - throw std::runtime_error("Viscosity model not supported for equation '" + type.value() + "'."); + svmp::raise(SVMP_HERE, "Viscosity model not supported for equation '" + type.value() + "'."); } } else if (name == ECGLeadsParameters::xml_element_name_) { @@ -2441,7 +2470,8 @@ void EquationParameters::set_values(tinyxml2::XMLElement* eq_elem, DomainParamet variable_wall_properties.set_values(item); } else if (name == include_xml.name()) { - auto value = item->GetText(); + auto value = require_xml_text(item, SVMP_HERE, + "Equation Include_xml requires a file name."); IncludeParametersFile include_parameters(value); set_values(include_parameters.root_element, default_domain); @@ -2457,13 +2487,13 @@ void EquationParameters::set_values(tinyxml2::XMLElement* eq_elem, DomainParamet try { default_domain->set_parameter_value(name, value); } catch (const std::bad_function_call& exception) { - throw std::runtime_error("Unknown " + xml_element_name_ + " XML element '" + name + "."); + svmp::raise(SVMP_HERE, "Unknown " + xml_element_name_ + " XML element '" + name + "'."); } } } else { - throw std::runtime_error("[Equation] Unknown " + xml_element_name_ + " XML element '" + name + "."); + svmp::raise(SVMP_HERE, "[Equation] Unknown " + xml_element_name_ + " XML element '" + name + "'."); } item = item->NextSiblingElement(); @@ -2554,24 +2584,30 @@ void GeneralSimulationParameters::set_values(tinyxml2::XMLElement* xml_element, item = xml_element->FirstChildElement(); } else { auto general_params = xml_element->FirstChildElement(xml_element_name.c_str()); + if (general_params == nullptr) { + svmp::raise(SVMP_HERE, + "No <" + xml_element_name + "> section found in the solver XML file."); + } item = general_params->FirstChildElement(); } while (item != nullptr) { std::string name = std::string(item->Value()); - auto value = item->GetText(); if (name == include_xml.name()) { - auto value = item->GetText(); + auto value = require_xml_text(item, SVMP_HERE, + "GeneralSimulationParameters Include_xml requires a file name."); IncludeParametersFile include_parameters(value); set_values(include_parameters.root_element, true); } else { + auto value = require_xml_text(item, SVMP_HERE, + "GeneralSimulationParameters XML element '" + name + "' requires a value."); try { set_parameter_value(name, value); } catch (const std::bad_function_call& exception) { - throw std::runtime_error("Unknown XML GeneralSimulationParameters element '" + name + "."); + svmp::raise(SVMP_HERE, "Unknown XML GeneralSimulationParameters element '" + name + "."); } } @@ -2623,8 +2659,7 @@ void FaceParameters::set_values(tinyxml2::XMLElement* face_elem) using namespace tinyxml2; std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; - const char* face_name; - auto result = face_elem->QueryStringAttribute("name", &face_name); + const char* face_name = require_xml_attribute(face_elem, "name", SVMP_HERE); name.set(std::string(face_name)); auto item = face_elem->FirstChildElement(); @@ -2633,13 +2668,13 @@ void FaceParameters::set_values(tinyxml2::XMLElement* face_elem) auto value = item->GetText(); if (value == nullptr) { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } try { set_parameter_value(name, value); } catch (const std::bad_function_call& exception) { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } item = item->NextSiblingElement(); @@ -2682,11 +2717,7 @@ void RemesherParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string error_msg = "Unknown " + xml_element_name + " XML element '"; // Get the 'type' from the element. - const char* stype; - auto result = xml_elem->QueryStringAttribute("type", &stype); - if (stype == nullptr) { - throw std::runtime_error("No TYPE given in the XML element."); - } + const char* stype = require_xml_attribute(xml_elem, "type", SVMP_HERE); type.set(std::string(stype)); values_set_ = true; @@ -2700,22 +2731,15 @@ void RemesherParameters::set_values(tinyxml2::XMLElement* xml_elem) if (name == "Max_edge_size") { const char* name; const char* value; - auto result = item->QueryStringAttribute("name", &name); - if (name == nullptr) { - throw std::runtime_error("No NAME given in the XML Remesher element."); - } - - result = item->QueryStringAttribute("value", &value); - if (value == nullptr) { - throw std::runtime_error("No VALUE given in the XML Remesher element."); - } + name = require_xml_attribute(item, "name", SVMP_HERE); + value = require_xml_attribute(item, "value", SVMP_HERE); auto svalue = std::string(value); try { double dvalue = std::stod(svalue); max_edge_sizes_[std::string(name)] = dvalue; } catch (...) { - throw std::runtime_error("VALUE=" + svalue + + svmp::raise(SVMP_HERE, "VALUE=" + svalue + " is not a valid float in the XML Remesher element."); } @@ -2724,11 +2748,11 @@ void RemesherParameters::set_values(tinyxml2::XMLElement* xml_elem) try { set_parameter_value(name, value); } catch (const std::bad_function_call& exception) { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } } else { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } item = item->NextSiblingElement(); @@ -2815,13 +2839,15 @@ void MeshParameters::set_values(tinyxml2::XMLElement* mesh_elem, bool from_exter // them as a list of VectorParameter. // } else if (name == "Fiber_direction") { - auto value = item->GetText(); + auto value = require_xml_text(item, SVMP_HERE, + "Mesh Fiber_direction XML element requires a value."); VectorParameter dir("Fiber_direction", {}, false, {}); dir.set(value); fiber_directions.push_back(dir); } else if (name == include_xml.name()) { - auto value = item->GetText(); + auto value = require_xml_text(item, SVMP_HERE, + "Mesh Include_xml requires a file name."); IncludeParametersFile include_parameters(value); set_values(include_parameters.root_element, true); @@ -2831,10 +2857,10 @@ void MeshParameters::set_values(tinyxml2::XMLElement* mesh_elem, bool from_exter try { set_parameter_value(name, value); } catch (const std::bad_function_call& exception) { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } } else { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } item = item->NextSiblingElement(); @@ -2902,11 +2928,7 @@ void ProjectionParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'type' from the element. - const char* sname; - auto result = xml_elem->QueryStringAttribute("name", &sname); - if (sname == nullptr) { - throw std::runtime_error("No TYPE given in the XML element."); - } + const char* sname = require_xml_attribute(xml_elem, "name", SVMP_HERE); name.set(std::string(sname)); using std::placeholders::_1; @@ -2943,11 +2965,7 @@ void RISProjectionParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'type' from the element. - const char* sname; - auto result = xml_elem->QueryStringAttribute("name", &sname); - if (sname == nullptr) { - throw std::runtime_error("No TYPE given in the XML element."); - } + const char* sname = require_xml_attribute(xml_elem, "name", SVMP_HERE); name.set(std::string(sname)); using std::placeholders::_1; @@ -3025,10 +3043,10 @@ void URISMeshParameters::set_values(tinyxml2::XMLElement* mesh_elem) try { set_parameter_value(name, value); } catch (const std::bad_function_call& exception) { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } } else { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } item = item->NextSiblingElement(); @@ -3077,8 +3095,7 @@ void URISFaceParameters::set_values(tinyxml2::XMLElement* face_elem) using namespace tinyxml2; std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; - const char* face_name; - auto result = face_elem->QueryStringAttribute("name", &face_name); + const char* face_name = require_xml_attribute(face_elem, "name", SVMP_HERE); name.set(std::string(face_name)); auto item = face_elem->FirstChildElement(); @@ -3087,13 +3104,13 @@ void URISFaceParameters::set_values(tinyxml2::XMLElement* face_elem) auto value = item->GetText(); if (value == nullptr) { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } try { set_parameter_value(name, value); } catch (const std::bad_function_call& exception) { - throw std::runtime_error(error_msg + name + "'."); + svmp::raise(SVMP_HERE, error_msg + name + "'."); } item = item->NextSiblingElement(); @@ -3148,11 +3165,7 @@ void LinearAlgebraParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string error_msg = "Unknown " + xml_element_name + " XML element '"; // Get the 'type' from the element. - const char* stype; - auto result = xml_elem->QueryStringAttribute("type", &stype); - if (stype == nullptr) { - throw std::runtime_error("No TYPE given in the XML element."); - } + const char* stype = require_xml_attribute(xml_elem, "type", SVMP_HERE); type.set(std::string(stype)); // Check Linear_algebra type=TYPE> element. @@ -3163,7 +3176,7 @@ void LinearAlgebraParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string valid_types = ""; std::for_each(LinearAlgebra::name_to_type.begin(), LinearAlgebra::name_to_type.end(), [&valid_types](std::pair p) {valid_types += p.first+" ";}); - throw std::runtime_error("Unknown TYPE '" + type.value() + + svmp::raise(SVMP_HERE, "Unknown TYPE '" + type.value() + "' given in the XML element.\nValid types are: " + valid_types); } @@ -3182,7 +3195,7 @@ void LinearAlgebraParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string valid_types = ""; std::for_each(consts::preconditioner_name_to_type.begin(), consts::preconditioner_name_to_type.end(), [&valid_types](std::pair p) {valid_types += p.first+" ";}); - throw std::runtime_error("Unknown TYPE '" + preconditioner() + + svmp::raise(SVMP_HERE, "Unknown TYPE '" + preconditioner() + "' given in the XML element.\nValid types are: " + valid_types); } @@ -3194,12 +3207,32 @@ void LinearAlgebraParameters::set_values(tinyxml2::XMLElement* xml_elem) /// @brief Check the validity of the input parameters. void LinearAlgebraParameters::check_input_parameters() { - auto linear_algebra_type = LinearAlgebra::name_to_type.at(type()); - auto prec_cond_type = consts::preconditioner_name_to_type.at(preconditioner.value()); - auto assembly_type = LinearAlgebra::name_to_type.at(assembly.value()); - - auto linear_algebra = LinearAlgebraFactory::create_interface(linear_algebra_type); - linear_algebra->check_options(prec_cond_type, assembly_type); + auto linear_algebra_type = require_map_value(LinearAlgebra::name_to_type, type(), + SVMP_HERE, "Unknown TYPE '" + type() + + "' given in the XML element."); + auto prec_cond_type = require_map_value(consts::preconditioner_name_to_type, + preconditioner.value(), SVMP_HERE, "Unknown TYPE '" + preconditioner() + + "' given in the XML element."); + auto assembly_type = require_map_value(LinearAlgebra::name_to_type, assembly.value(), + SVMP_HERE, "Unknown TYPE '" + assembly() + + "' given in the XML element."); + + LinearAlgebra* linear_algebra = nullptr; + try { + linear_algebra = LinearAlgebraFactory::create_interface(linear_algebra_type); + if (linear_algebra == nullptr) { + svmp::raise(SVMP_HERE, + "Linear_algebra type '" + type() + "' cannot be used as a solver backend."); + } + linear_algebra->check_options(prec_cond_type, assembly_type); + delete linear_algebra; + } catch (const svmp::ParseException&) { + delete linear_algebra; + throw; + } catch (const std::exception& exception) { + delete linear_algebra; + svmp::raise(SVMP_HERE, exception.what()); + } } ////////////////////////////////////////////////////////// @@ -3256,11 +3289,7 @@ void LinearSolverParameters::set_values(tinyxml2::XMLElement* xml_elem) std::string error_msg = "Unknown " + xml_element_name + " XML element '"; // Get the 'type' from the element. - const char* stype; - auto result = xml_elem->QueryStringAttribute("type", &stype); - if (stype == nullptr) { - throw std::runtime_error("No TYPE given in the XML element."); - } + const char* stype = require_xml_attribute(xml_elem, "type", SVMP_HERE); type.set(std::string(stype)); diff --git a/Code/Source/solver/Parameters.h b/Code/Source/solver/Parameters.h index 5e1ae324f..60650998f 100644 --- a/Code/Source/solver/Parameters.h +++ b/Code/Source/solver/Parameters.h @@ -16,6 +16,7 @@ #include #include +#include "Core/Exception.h" #include "tinyxml2.h" template @@ -136,7 +137,7 @@ class Parameter if (!(str_stream >> value_)) { std::istringstream str_stream(str); if (!(str_stream >> std::boolalpha >> value_)) { - throw std::runtime_error("Incorrect value '" + str + "' for '" + name_ + "'."); + svmp::raise(SVMP_HERE, "Incorrect value '" + str + "' for '" + name_ + "'."); } } @@ -325,7 +326,7 @@ class ParameterLists void set_parameter_value_CANN(const std::string& name, const std::string& value) { if (params_map.count(name) == 0) { - throw std::runtime_error("Unknown " + xml_element_name + " XML element '" + name + "'."); + svmp::raise(SVMP_HERE, "Unknown " + xml_element_name + " XML element '" + name + "'."); } auto& param_variant = params_map[name]; @@ -336,7 +337,7 @@ class ParameterLists (*vec_param)->value_.clear(); // Clear the vector before setting (*vec_param)->set(value); // Set the new value } else { - throw std::runtime_error("Activation_functions is not a VectorParameter."); + svmp::raise(SVMP_HERE, "Activation_functions is not a VectorParameter."); } } // Check for Weights @@ -345,7 +346,7 @@ class ParameterLists (*vec_param)->value_.clear(); // Clear the vector before setting (*vec_param)->set(value); // Set the new value } else { - throw std::runtime_error("Weights is not a VectorParameter."); + svmp::raise(SVMP_HERE, "Weights is not a VectorParameter."); } } // Default: everything else @@ -362,7 +363,7 @@ class ParameterLists void set_parameter_value(const std::string& name, const std::string& value) { if (params_map.count(name) == 0) { - throw std::runtime_error("Unknown " + xml_element_name + " XML element '" + name + "'."); + svmp::raise(SVMP_HERE, "Unknown " + xml_element_name + " XML element '" + name + "'."); } std::visit([value](auto&& p) { p->set(value); }, params_map[name]); @@ -377,7 +378,7 @@ class ParameterLists if (std::visit([](auto&& p) { return !p->check_required_set(); }, param)) { - throw std::runtime_error(xml_element_name + " XML element '" + key + "' has not been set."); + svmp::raise(SVMP_HERE, xml_element_name + " XML element '" + key + "' has not been set."); } } } @@ -1773,4 +1774,3 @@ class Parameters { }; #endif - diff --git a/Code/Source/solver/main.cpp b/Code/Source/solver/main.cpp index b2a3b3193..4e778f03e 100644 --- a/Code/Source/solver/main.cpp +++ b/Code/Source/solver/main.cpp @@ -13,6 +13,7 @@ #include "all_fun.h" #include "bf.h" #include "contact.h" +#include "Core/Exception.h" #include "distribute.h" #include "eq_assem.h" #include "fs.h" @@ -620,6 +621,7 @@ int main(int argc, char *argv[]) dmsg.banner(); #endif + try { // Iterate for restarting a simulation after remeshing. // while (true) { @@ -691,4 +693,30 @@ int main(int argc, char *argv[]) } MPI_Finalize(); + return 0; + + } catch (const svmp::ExceptionBase& exception) { + if (mpi_rank == 0) { + std::cerr << "[svMultiPhysics] ERROR: The svMultiPhysics program has failed due to unhandled exception." << std::endl; + std::cerr << exception.what() << std::endl; + } + svmp::ExceptionRuntime::abort_mpi_if_needed(EXIT_FAILURE); + svmp::ExceptionRuntime::finalize_mpi_if_needed(); + return EXIT_FAILURE; + } catch (const std::exception& exception) { + if (mpi_rank == 0) { + std::cerr << "[svMultiPhysics] ERROR: The svMultiPhysics program has failed due to unhandled exception." << std::endl; + std::cerr << exception.what() << std::endl; + } + svmp::ExceptionRuntime::abort_mpi_if_needed(EXIT_FAILURE); + svmp::ExceptionRuntime::finalize_mpi_if_needed(); + return EXIT_FAILURE; + } catch (...) { + if (mpi_rank == 0) { + std::cerr << "[svMultiPhysics] ERROR: The svMultiPhysics program has failed due to an unknown unhandled exception." << std::endl; + } + svmp::ExceptionRuntime::abort_mpi_if_needed(EXIT_FAILURE); + svmp::ExceptionRuntime::finalize_mpi_if_needed(); + return EXIT_FAILURE; + } }