diff --git a/Code/Source/solver/CMakeLists.txt b/Code/Source/solver/CMakeLists.txt index cee095791..c5ab81146 100644 --- a/Code/Source/solver/CMakeLists.txt +++ b/Code/Source/solver/CMakeLists.txt @@ -228,6 +228,12 @@ set(CSRCS ionic_bueno_orovio.cpp ionic_fitzhugh_nagumo.cpp ionic_ttp.cpp + + active_stress.cpp + active_stress_uniform_steady.cpp + active_stress_uniform_unsteady.cpp + active_stress_ode.cpp + active_stress_nash_panfilov.cpp SPLIT.c diff --git a/Code/Source/solver/CepMod.h b/Code/Source/solver/CepMod.h index c4d7d5b93..6d280228b 100644 --- a/Code/Source/solver/CepMod.h +++ b/Code/Source/solver/CepMod.h @@ -194,18 +194,27 @@ class cemModelType bool cpld = false; //bool cpld = .FALSE. - /// @brief Whether active stress formulation is employed - bool aStress = false; - //bool aStress = .FALSE. - /// @brief Whether active strain formulation is employed bool aStrain = false; //bool aStrain = .FALSE. - /// @brief Local variable integrated in time - /// := activation force for active stress model - /// := fiber stretch for active strain model - Vector Ya; + /// @brief Activation along fibers. + /// + /// Corresponds to active tension along fibers if using active stress, and + /// to fiber stretch if using active strain. + Vector Ya_f; + + /// @brief Activation along sheets. + /// + /// Only used if using active stress, in which case it represents the active + /// tension along sheets. + Vector Ya_s; + + /// @brief Activation along sheet normals. + /// + /// Only used if using active stress, in which case it represents the active + /// tension along sheet normals. + Vector Ya_n; }; class CepMod @@ -221,6 +230,9 @@ class CepMod /// @brief Unknowns stored at all nodes Array Xion; + /// @brief Calcium vector at all nodes. + Vector calcium; + /// @brief Cardiac electromechanics type cemModelType cem; diff --git a/Code/Source/solver/ComMod.h b/Code/Source/solver/ComMod.h index c7f0dc56e..2e9f60068 100644 --- a/Code/Source/solver/ComMod.h +++ b/Code/Source/solver/ComMod.h @@ -7,7 +7,7 @@ // All of the data structures used for the mesh, boundarsy conditions and solver parameters, etc. // are defined here. -#ifndef COMMOD_H +#ifndef COMMOD_H #define COMMOD_H #include "Array.h" @@ -22,6 +22,7 @@ #include "SolutionStates.h" #include "Timer.h" #include "Vector.h" +#include "active_stress.h" #include "DebugMsg.h" @@ -278,31 +279,6 @@ class bfType MBType bm; }; -// Fiber stress type -class fibStrsType -{ - public: - - // Type of fiber stress - int fType = 0; - - // Constant steady value - double g = 0.0; - - // Directional stress distribution parameters - // Fraction of active stress in fiber direction (default: 1.0) - double eta_f = 1.0; - - // Fraction of active stress in sheet direction (default: 0.0) - double eta_s = 0.0; - - // Fraction of active stress in sheet-normal direction (default: 0.0) - double eta_n = 0.0; - - // Unsteady time-dependent values - FourierInterpolation gt; -}; - /// @brief Structural domain type // class stModelType @@ -346,9 +322,6 @@ class stModelType double b2 = 0.0; double mu0 = 0.0; - // Fiber reinforcement stress - fibStrsType Tf; - // CANN Model/UAnisoHyper_inv ArtificialNeuralNetMaterial paramTable; @@ -420,6 +393,12 @@ class dmnType // Electrophysiology model cepModelType cep; + /// Active stress model name. + std::string active_stress_model_name = ""; + + /// Active stress model. + std::shared_ptr active_stress; + // Structure material model stModelType stM; diff --git a/Code/Source/solver/Core/Exception.h b/Code/Source/solver/Core/Exception.h index dce3e3b31..805c67bd0 100644 --- a/Code/Source/solver/Core/Exception.h +++ b/Code/Source/solver/Core/Exception.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the University of California, and others. -// SPDX-License-Identifier: BSD-3-Clause +// 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 @@ -16,117 +16,113 @@ #include #if !defined(NDEBUG) || defined(DEBUG) || defined(_DEBUG) -# define SVMP_EXCEPTION_DEBUG_MODE 1 +#define SVMP_EXCEPTION_DEBUG_MODE 1 #else -# define SVMP_EXCEPTION_DEBUG_MODE 0 +#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 + 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"; - } +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; + 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) - { - } + 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_; } + 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; + 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)); } + 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_; + std::vector frames_; }; struct PlatformCapabilities final { - bool has_stack_trace; - bool has_symbol_resolution; - bool has_demangling; + 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; + 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 @@ -138,100 +134,95 @@ 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); - } + 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_; + 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() << ")"; +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"; - - if (!context.file().empty()) { - oss << " Location: " << context.file() << ":" << context.line(); - if (!context.function().empty()) { - oss << " in " << context.function() << "()"; + } + + 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 << "\n"; - } + oss << ")"; + } - 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"; - } + oss << "\n"; } + } - return oss.str(); + return oss.str(); } } // namespace ExceptionFormatter @@ -240,109 +231,98 @@ class ExceptionBase; namespace ExceptionRuntime { - inline int query_mpi_rank() noexcept - { - return PlatformSupport::query_mpi_rank(); - } +inline int query_mpi_rank() noexcept { + return PlatformSupport::query_mpi_rank(); +} - inline StackTrace capture_stack_trace() - { - return PlatformSupport::capture_stack_trace(); - } +inline StackTrace capture_stack_trace() { + return PlatformSupport::capture_stack_trace(); +} - inline void finalize_mpi_if_needed() noexcept - { - PlatformSupport::finalize_mpi_if_needed(); - } +inline void finalize_mpi_if_needed() noexcept { + PlatformSupport::finalize_mpi_if_needed(); +} - void install_terminate_handler(); +void install_terminate_handler(); - inline void report_unhandled_exception(const std::exception& exception) - { - std::cerr << exception.what() << std::endl; - } +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); - } +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(); - } - - /// @brief Record the originating source location and refresh what(). - /// - /// @details Called by raise() after construction, so exception constructors - /// do not need to accept (and forward) the file/line/function themselves. - void set_source_location(const SourceLocation& location) - { - context_.set_source_location(location.file, location.line, - location.function); - rebuild_what(); - } - - virtual ~ExceptionBase() noexcept = default; + 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(); + } + + /// @brief Record the originating source location and refresh what(). + /// + /// @details Called by raise() after construction, so exception constructors + /// do not need to accept (and forward) the file/line/function themselves. + void set_source_location(const SourceLocation &location) { + context_.set_source_location(location.file, location.line, + location.function); + rebuild_what(); + } + + virtual ~ExceptionBase() noexcept = default; protected: - ExceptionBase(std::string message, StatusCode status, - std::string_view subsystem_label, const char* file = "", - int line = 0, 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()); + ExceptionBase(std::string message, StatusCode status, + std::string_view subsystem_label, const char *file = "", + int line = 0, 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()); + context_.set_stack_trace(ExceptionRuntime::capture_stack_trace()); #endif - rebuild_what(); - } + rebuild_what(); + } - void rebuild_what() - { - what_ = ExceptionFormatter::format(context_, message_, subsystem_label_); - } + void rebuild_what() { + what_ = ExceptionFormatter::format(context_, message_, subsystem_label_); + } - std::string message_; - ExceptionContext context_; - std::string_view subsystem_label_; - std::string what_; + 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) - { - } + 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) { + } }; /** * @brief Define a simple, message-only exception type in one line. * * @details Expands to a class @p Name deriving from @p Base with a single - * `explicit Name(const std::string& message)` constructor that records @p Status. - * Use it for exceptions that carry only a message; write the class by hand when it - * needs extra structured context (members and accessors). The source location is - * stamped by raise(), so no file/line/function constructor is needed. + * `explicit Name(const std::string& message)` constructor that records @p + * Status. Use it for exceptions that carry only a message; write the class by + * hand when it needs extra structured context (members and accessors). The + * source location is stamped by raise(), so no file/line/function constructor + * is needed. * * @p Base must be an ExceptionBase-derived type whose constructor accepts * `(const std::string&, StatusCode)` (CoreException, FEException, and the @@ -353,13 +333,10 @@ class CoreException : public ExceptionBase { * @endcode */ #define SVMP_DEFINE_EXCEPTION(Name, Base, Status) \ - class Name : public Base { \ - public: \ - explicit Name(const std::string& message) \ - : Base(message, (Status)) \ - { \ - } \ - } + class Name : public Base { \ + public: \ + explicit Name(const std::string &message) : Base(message, (Status)) {} \ + } /// @brief A parsing or input-format error. SVMP_DEFINE_EXCEPTION(ParseException, CoreException, StatusCode::ParseError); @@ -381,6 +358,17 @@ SVMP_DEFINE_EXCEPTION(NotImplementedException, CoreException, SVMP_DEFINE_EXCEPTION(IndexOutOfRangeException, CoreException, StatusCode::InvalidArgument); +/// @brief Internal error exception. +/// +/// @details An exception raising the status code InternalError, used for +/// unexpected conditions that indicate a bug in the code. +SVMP_DEFINE_EXCEPTION(InternalErrorException, CoreException, + StatusCode::InternalError); + +/// @brief An exception raised when a file cannot be opened. +/// +/// @details Raises a message in the format "Could not open file ", +/// with the file name provided as constructor argument. class FileNotFoundException : public CoreException { public: FileNotFoundException(const std::string &file_name, const char *file = "", @@ -389,6 +377,12 @@ class FileNotFoundException : public CoreException { file, line, function) {} }; +/// @brief An exception raised when a file cannot be parsed or has an invalid +/// format. +/// +/// @details Raises a message in the format "Error parsing file . +/// ", with the file name and a descriptive message provided as +/// constructor arguments. class FileFormatException : public CoreException { public: FileFormatException(const std::string &file_name, const std::string &message, @@ -398,104 +392,103 @@ class FileFormatException : public CoreException { StatusCode::IOError, 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; - } +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(); - }); + ExceptionRuntime::abort_mpi_if_needed(EXIT_FAILURE); + std::abort(); + }); } /** - * @brief A diagnostic message bundled with the source location where it was written. + * @brief A diagnostic message bundled with the source location where it was + * written. * - * @details The core helpers (raise(), check(), throw_if(), check_not_null()) take - * a Diagnostic in place of an explicit source location. Its file/line/function - * arguments default to the compiler builtins __builtin_FILE()/__builtin_LINE()/ - * __builtin_FUNCTION(), which capture the caller's location, so a string literal - * or std::string passed at the call site is implicitly wrapped into a Diagnostic - * that records exactly where the call appears -- callers pass no explicit location: + * @details The core helpers (raise(), check(), throw_if(), check_not_null()) + * take a Diagnostic in place of an explicit source location. Its + * file/line/function arguments default to the compiler builtins + * __builtin_FILE()/__builtin_LINE()/ + * __builtin_FUNCTION(), which capture the caller's location, so a string + * literal or std::string passed at the call site is implicitly wrapped into a + * Diagnostic that records exactly where the call appears -- callers pass no + * explicit location: * @code * svmp::check(ptr != nullptr, "pointer must not be null"); * @endcode */ class Diagnostic { public: - /** - * @brief Wrap a message, capturing the caller's source location by default. - * @param message The diagnostic message. - * @param file Source file; defaults to the caller's via __builtin_FILE(). - * @param line Source line; defaults to the caller's via __builtin_LINE(). - * @param function Function; defaults to the caller's via __builtin_FUNCTION(). - */ - Diagnostic(const char* message, - const char* file = __builtin_FILE(), - int line = __builtin_LINE(), - const char* function = __builtin_FUNCTION()) - : message_(message), location_{file, line, function} - { - } - /** - * @brief Wrap a message, capturing the caller's source location by default. - * @param message The diagnostic message. - * @param file Source file; defaults to the caller's via __builtin_FILE(). - * @param line Source line; defaults to the caller's via __builtin_LINE(). - * @param function Function; defaults to the caller's via __builtin_FUNCTION(). - */ - Diagnostic(std::string message, - const char* file = __builtin_FILE(), - int line = __builtin_LINE(), - const char* function = __builtin_FUNCTION()) - : message_(std::move(message)), location_{file, line, function} - { - } - - /** - * @brief The diagnostic message. - * @return The stored message. - */ - const std::string& message() const noexcept { return message_; } - /** - * @brief The source location captured when the Diagnostic was constructed. - * @return The stored source location. - */ - const SourceLocation& location() const noexcept { return location_; } + /** + * @brief Wrap a message, capturing the caller's source location by default. + * @param message The diagnostic message. + * @param file Source file; defaults to the caller's via __builtin_FILE(). + * @param line Source line; defaults to the caller's via __builtin_LINE(). + * @param function Function; defaults to the caller's via + * __builtin_FUNCTION(). + */ + Diagnostic(const char *message, const char *file = __builtin_FILE(), + int line = __builtin_LINE(), + const char *function = __builtin_FUNCTION()) + : message_(message), location_{file, line, function} {} + /** + * @brief Wrap a message, capturing the caller's source location by default. + * @param message The diagnostic message. + * @param file Source file; defaults to the caller's via __builtin_FILE(). + * @param line Source line; defaults to the caller's via __builtin_LINE(). + * @param function Function; defaults to the caller's via + * __builtin_FUNCTION(). + */ + Diagnostic(std::string message, const char *file = __builtin_FILE(), + int line = __builtin_LINE(), + const char *function = __builtin_FUNCTION()) + : message_(std::move(message)), location_{file, line, function} {} + + /** + * @brief The diagnostic message. + * @return The stored message. + */ + const std::string &message() const noexcept { return message_; } + /** + * @brief The source location captured when the Diagnostic was constructed. + * @return The stored source location. + */ + const SourceLocation &location() const noexcept { return location_; } private: - std::string message_; - SourceLocation location_; + std::string message_; + SourceLocation location_; }; /** * @brief Construct @p ExceptionT from the diagnostic message and @p args, stamp * the source location, and throw it. * - * @details @p diagnostic carries the message and the source location captured at - * the call site; @p args are forwarded to the exception constructor after the - * message. The location is recorded via ExceptionBase::set_source_location(), so - * exception types never need a file/line/function constructor -- a `(message)` - * (plus any structured-context) constructor is enough. + * @details @p diagnostic carries the message and the source location captured + * at the call site; @p args are forwarded to the exception constructor after + * the message. The location is recorded via + * ExceptionBase::set_source_location(), so exception types never need a + * file/line/function constructor -- a `(message)` (plus any structured-context) + * constructor is enough. */ template -[[noreturn]] void raise(Diagnostic diagnostic, Args&&... args) -{ - static_assert(std::is_base_of_v, - "raise<>() requires an svmp::ExceptionBase-derived exception type"); - ExceptionT exception(diagnostic.message(), std::forward(args)...); - exception.set_source_location(diagnostic.location()); - throw exception; +[[noreturn]] void raise(Diagnostic diagnostic, Args &&...args) { + static_assert( + std::is_base_of_v, + "raise<>() requires an svmp::ExceptionBase-derived exception type"); + ExceptionT exception(diagnostic.message(), std::forward(args)...); + exception.set_source_location(diagnostic.location()); + throw exception; } /** @@ -506,22 +499,20 @@ template * logical inverse -- it raises when its condition is true. */ template -void check(bool condition, Diagnostic diagnostic, Args&&... args) -{ - if (!condition) { - raise(std::move(diagnostic), std::forward(args)...); - } +void check(bool condition, Diagnostic diagnostic, Args &&...args) { + if (!condition) { + raise(std::move(diagnostic), std::forward(args)...); + } } /** * @brief Raise @p ExceptionT when @p ptr is null. */ template -void check_not_null(PointerT ptr, Diagnostic diagnostic, Args&&... args) -{ - if (ptr == nullptr) { - raise(std::move(diagnostic), std::forward(args)...); - } +void check_not_null(PointerT ptr, Diagnostic diagnostic, Args &&...args) { + if (ptr == nullptr) { + raise(std::move(diagnostic), std::forward(args)...); + } } /** @@ -532,40 +523,38 @@ void check_not_null(PointerT ptr, Diagnostic diagnostic, Args&&... args) * failure condition). The two are not interchangeable. */ template -void throw_if(bool condition, Diagnostic diagnostic, Args&&... args) -{ - if (condition) { - raise(std::move(diagnostic), std::forward(args)...); - } +void throw_if(bool condition, Diagnostic diagnostic, Args &&...args) { + if (condition) { + raise(std::move(diagnostic), std::forward(args)...); + } } /** * @brief Raise an exception when @p index is outside [0, @p size). * - * @details @p ExceptionT defaults to IndexOutOfRangeException; supply a different - * type only when a subsystem needs its own exception. The bounds message and the - * source location are generated automatically. + * @details @p ExceptionT defaults to IndexOutOfRangeException; supply a + * different type only when a subsystem needs its own exception. The bounds + * message and the source location are generated automatically. */ -template -void check_index(IndexT index, SizeT size, - const char* file = __builtin_FILE(), +template +void check_index(IndexT index, SizeT size, const char *file = __builtin_FILE(), int line = __builtin_LINE(), - const char* function = __builtin_FUNCTION()) -{ - const long long index_value = static_cast(index); - const long long size_value = static_cast(size); - check( - index_value >= 0 && index_value < size_value, - Diagnostic("Index " + std::to_string(index_value) + " out of bounds [0, " + - std::to_string(size_value) + ")", - file, line, function)); + const char *function = __builtin_FUNCTION()) { + const long long index_value = static_cast(index); + const long long size_value = static_cast(size); + check(index_value >= 0 && index_value < size_value, + Diagnostic("Index " + std::to_string(index_value) + + " out of bounds [0, " + + std::to_string(size_value) + ")", + file, line, function)); } /** * @brief Raise an exception reporting an unimplemented feature, with a message. * - * @details @p ExceptionT defaults to NotImplementedException, so most call sites - * pass only a message: + * @details @p ExceptionT defaults to NotImplementedException, so most call + * sites pass only a message: * @code * svmp::not_implemented("GPU assembly is not supported"); * @endcode @@ -573,26 +562,25 @@ void check_index(IndexT index, SizeT size, */ template [[noreturn]] void not_implemented(std::string message, - const char* file = __builtin_FILE(), + const char *file = __builtin_FILE(), int line = __builtin_LINE(), - const char* function = __builtin_FUNCTION()) -{ - raise(Diagnostic(std::move(message), file, line, function)); + const char *function = __builtin_FUNCTION()) { + raise(Diagnostic(std::move(message), file, line, function)); } } // namespace svmp #if SVMP_EXCEPTION_DEBUG_MODE -#define SVMP_DEBUG_CHECK(ExceptionT, condition, ...) \ - do { \ - if (!(condition)) { \ - ::svmp::raise(__VA_ARGS__); \ - } \ - } while (false) +#define SVMP_DEBUG_CHECK(ExceptionT, condition, ...) \ + do { \ + if (!(condition)) { \ + ::svmp::raise(__VA_ARGS__); \ + } \ + } while (false) #else -#define SVMP_DEBUG_CHECK(ExceptionT, condition, ...) \ - do { \ - } while (false) +#define SVMP_DEBUG_CHECK(ExceptionT, condition, ...) \ + do { \ + } while (false) #endif #endif // SVMP_CORE_EXCEPTION_H diff --git a/Code/Source/solver/FE/Common/FEException.h b/Code/Source/solver/FE/Common/FEException.h index 4085ce6a6..462b5dca4 100644 --- a/Code/Source/solver/FE/Common/FEException.h +++ b/Code/Source/solver/FE/Common/FEException.h @@ -13,7 +13,7 @@ * failures from finite-element assembly, backend, DOF, and element operations. */ -#include "Exception.h" +#include "Core/Exception.h" #include #include diff --git a/Code/Source/solver/Integrator.cpp b/Code/Source/solver/Integrator.cpp index 90dddb1d1..75b9b5447 100644 --- a/Code/Source/solver/Integrator.cpp +++ b/Code/Source/solver/Integrator.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: BSD-3-Clause #include "Integrator.h" +#include "Core/Exception.h" #include "all_fun.h" #include "bf.h" #include "cep_ion.h" @@ -11,6 +12,7 @@ #include "ls.h" #include "nn.h" #include "output.h" +#include "post.h" #include "ris.h" #include "set_bc.h" #include "ustruct.h" @@ -393,6 +395,7 @@ void Integrator::predictor() using namespace consts; auto& com_mod = simulation_->com_mod; + auto& cep_mod = simulation_->cep_mod; #define n_debug_picp #ifdef debug_picp @@ -457,6 +460,75 @@ void Integrator::predictor() dmsg << "dFlag: " << com_mod.dFlag; #endif + Vector fiber_stretch; + Vector fiber_stretch_rate; + + // Determine if we need to compute fiber stretch and stretch rate, by going + // through all domains of all equations until we find one for which active + // stress is enabled. + bool need_fiber_stretch = false; + bool need_fiber_stretch_rate = false; + int fiber_stretch_eq_index = -1; + for (int iEq = 0; iEq < com_mod.nEq; ++iEq) { + const auto &eq = com_mod.eq[iEq]; + + if (supports_active_stress(eq.phys)) { + fiber_stretch_eq_index = iEq; + + for (const auto &dmn : eq.dmn) { + if (dmn.active_stress != nullptr) { + need_fiber_stretch = true; + need_fiber_stretch_rate = true; + } + } + } + + else if (eq.phys == Equation_CEP) { + need_fiber_stretch = true; + } + } + + // If we need to compute fiber stretch, we iterate through all meshes, compute + // the stretch for each mesh, and then copy the mesh-local resulting vector + // into the global vector. + if (need_fiber_stretch) { + fiber_stretch.resize(com_mod.tnNo); + + if (fiber_stretch_eq_index >= 0) { + for (const auto &mesh : com_mod.msh) { + Vector tmp(mesh.nNo); + + post::fib_stretch(com_mod, fiber_stretch_eq_index, mesh, Dn, tmp); + for (int a = 0; a < mesh.nNo; ++a) + fiber_stretch[mesh.gN[a]] = tmp[a]; + } + } else { + // If we didn't find any domain solving for the displacement, then we set + // the fiber stretch to 1, corresponding to no stretch. + fiber_stretch = 1.0; + } + } + + // Same for fiber stretch rate. + if (need_fiber_stretch_rate) { + fiber_stretch_rate.resize(com_mod.tnNo); + + if (fiber_stretch_eq_index >= 0) { + for (const auto &mesh : com_mod.msh) { + Vector tmp(mesh.nNo); + + post::fib_stretch_rate(com_mod, fiber_stretch_eq_index, mesh, + solutions_, tmp); + for (int a = 0; a < mesh.nNo; ++a) + fiber_stretch_rate[mesh.gN[a]] = tmp[a]; + } + } else { + // If we didn't find any domain solving for the displacement, then we set + // the fiber stretch rate to 0, corresponding to no movement. + fiber_stretch_rate = 0.0; + } + } + for (int iEq = 0; iEq < com_mod.nEq; iEq++) { auto& eq = com_mod.eq[iEq]; int s = eq.s; // start row @@ -481,7 +553,63 @@ void Integrator::predictor() // electrophysiology if (eq.phys == Equation_CEP) { - cep_ion::cep_integ(simulation_, iEq, e, solutions_); + if (fiber_stretch.size() != com_mod.tnNo) + svmp::raise( + "Fiber stretch vector is not initialized correctly."); + + cep_ion::cep_integ(simulation_, iEq, e, solutions_, fiber_stretch); + } + + // active stress + if (supports_active_stress(eq.phys)) { + for (auto &dmn : eq.dmn) { + if (dmn.active_stress != nullptr) { + dmn.active_stress->advance_time_step(com_mod.time, com_mod.dt, + cep_mod.calcium, fiber_stretch, + fiber_stretch_rate); + } + } + + // Fill in the active tension vector. + // We go through all mesh nodes, find the domain they are associated with, + // and get the active stress from that domain. If a point is associated to + // multiple domains (which happens for points on domain interfaces), we + // average the active stresses from the domains. + for (int Ac = 0; Ac < com_mod.tnNo; Ac++) { + double Ta_f = 0.0; + double Ta_s = 0.0; + double Ta_n = 0.0; + unsigned int n_domains = 0; + + for (auto &dmn : eq.dmn) { + // Domains whose equations do not allow for active stress (e.g. fluid + // domains) do not contribute to the average, but domains that do + // allow for active stress (e.g. struct) for which active stress is + // not enabled contribute a zero value to the average. + if (!supports_active_stress(dmn.phys)) + continue; + + // Only domains that node Ac actually belongs to contribute to its + // average. Note that if there is only one domain dmnId may not be + // populated, so we only check domain membership if eq.nDmn > 1. + if (eq.nDmn > 1 && !utils::btest(com_mod.dmnId(Ac), dmn.Id)) + continue; + + if (dmn.active_stress != nullptr) { + Ta_f += dmn.active_stress->get_tension_fibers(Ac); + Ta_s += dmn.active_stress->get_tension_sheets(Ac); + Ta_n += dmn.active_stress->get_tension_sheet_normals(Ac); + } + + n_domains++; + } + + if (n_domains > 0) { + cep_mod.cem.Ya_f[Ac] = Ta_f / n_domains; + cep_mod.cem.Ya_s[Ac] = Ta_s / n_domains; + cep_mod.cem.Ya_n[Ac] = Ta_n / n_domains; + } + } } // eqn 86 of Bazilevs 2007 diff --git a/Code/Source/solver/Parameters.cpp b/Code/Source/solver/Parameters.cpp index fdc5ae50c..8f9eed3d6 100644 --- a/Code/Source/solver/Parameters.cpp +++ b/Code/Source/solver/Parameters.cpp @@ -1,16 +1,17 @@ -// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the University of California, and others. -// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the +// University of California, and others. SPDX-License-Identifier: BSD-3-Clause // -// The class methods defined here are used to process svMultiPhysics simulation parameters -// read in from an XML-format file. +// The class methods defined here are used to process svMultiPhysics simulation +// parameters read in from an XML-format file. // -// XML files are parsed using tinyxml2 (https://github.com/leethomason/tinyxml2). +// XML files are parsed using tinyxml2 +// (https://github.com/leethomason/tinyxml2). // //----------------- -// Section objects +// Section objects //----------------- -// Groups of related parameters making up the sections of an XML simulation +// Groups of related parameters making up the sections of an XML simulation // file are stored in section objects. For example // // - GeneralSimulationParameters - General parameter section @@ -20,77 +21,82 @@ // - RISProjectionParameters - RIS Projection parameter section // - URISMeshParameters - Mesh parameter section // -// These section objects may also contain objects representing the sub-sections -// defined for each section. +// These section objects may also contain objects representing the sub-sections +// defined for each section. // -// The name and default value for each parameter is defined in a section object's -// constructor using the 'ParameterLists::set_parameter()' method. +// The name and default value for each parameter is defined in a section +// object's constructor using the 'ParameterLists::set_parameter()' method. // -// Parameter values are set using the 'set_values()' method which contains calls to tinyxml2 -// to parse parameter values from an XML file. The XML elements within a section are -// extracted in a while loop. Sub-sections or data will need checked and processed. -// The 'ParameterLists::set_parameter_value()' method is used to set the value of a parameter -// from a string. See MeshParameters::set_values() for an example. +// Parameter values are set using the 'set_values()' method which contains calls +// to tinyxml2 to parse parameter values from an XML file. The XML elements +// within a section are extracted in a while loop. Sub-sections or data will +// need checked and processed. The 'ParameterLists::set_parameter_value()' +// method is used to set the value of a parameter from a string. See +// MeshParameters::set_values() for an example. // -// If a section does not contain any sub-sections then all parameters can be parsed automatically. -// See LinearSolverParameters::set_values() for an example. +// If a section does not contain any sub-sections then all parameters can be +// parsed automatically. See LinearSolverParameters::set_values() for an +// example. // -// Each section can read in parameters stored in an external XML file by adding an 'include_xml' -// 'Parameter' used to set the external XML file name. This is currently supported -// for the following sections -// - GeneralSimulationParameters -// - MeshParameters -// - DomainParameters -// - EquationParameters +// Each section can read in parameters stored in an external XML file by adding +// an 'include_xml' 'Parameter' used to set the external XML file +// name. This is currently supported for the following sections +// - GeneralSimulationParameters +// - MeshParameters +// - DomainParameters +// - EquationParameters // #include "Parameters.h" -#include "consts.h" #include "LinearAlgebra.h" +#include "consts.h" #include "ustruct.h" #include #include #include +#include +#include #include #include #include -#include -#include + +#include "FE/Common/FEException.h" namespace { -std::string uppercase_xml_name(const std::string& value) -{ +std::string uppercase_xml_name(const std::string &value) { std::string upper = value; - std::transform(upper.begin(), upper.end(), upper.begin(), + 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) -{ +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 = (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."; + attribute + "=" + attribute_upper + "> element."; } -const char* require_xml_attribute(tinyxml2::XMLElement* element, - const char* attribute_name, const std::string& message = std::string(), - const char* file = __builtin_FILE(), int line = __builtin_LINE(), - const char* function = __builtin_FUNCTION()) -{ - const char* value = nullptr; +const char *require_xml_attribute(tinyxml2::XMLElement *element, + const char *attribute_name, + const std::string &message = std::string(), + const char *file = __builtin_FILE(), + int line = __builtin_LINE(), + const char *function = __builtin_FUNCTION()) { + const char *value = nullptr; if (element == nullptr || - element->QueryStringAttribute(attribute_name, &value) != tinyxml2::XML_SUCCESS || + element->QueryStringAttribute(attribute_name, &value) != + tinyxml2::XML_SUCCESS || value == nullptr) { svmp::raise(svmp::Diagnostic( message.empty() ? missing_xml_attribute_message(element, attribute_name) @@ -100,10 +106,11 @@ const char* require_xml_attribute(tinyxml2::XMLElement* element, return value; } -const char* require_xml_text(tinyxml2::XMLElement* element, - const std::string& message, const char* file = __builtin_FILE(), - int line = __builtin_LINE(), const char* function = __builtin_FUNCTION()) -{ +const char *require_xml_text(tinyxml2::XMLElement *element, + const std::string &message, + const char *file = __builtin_FILE(), + int line = __builtin_LINE(), + const char *function = __builtin_FUNCTION()) { if (element == nullptr || element->GetText() == nullptr) { svmp::raise( svmp::Diagnostic(message, file, line, function)); @@ -112,11 +119,10 @@ const char* require_xml_text(tinyxml2::XMLElement* element, } template -typename MapT::mapped_type require_map_value(const MapT& map, - const typename MapT::key_type& key, const std::string& message, - const char* file = __builtin_FILE(), int line = __builtin_LINE(), - const char* function = __builtin_FUNCTION()) -{ +typename MapT::mapped_type require_map_value( + const MapT &map, const typename MapT::key_type &key, + const std::string &message, const char *file = __builtin_FILE(), + int line = __builtin_LINE(), const char *function = __builtin_FUNCTION()) { auto iter = map.find(key); if (iter == map.end()) { svmp::raise( @@ -127,13 +133,15 @@ typename MapT::mapped_type require_map_value(const MapT& map, } // namespace -/// @brief Set paramaters using a function pointing to the 'ParameterLists::set_parameter_value' method. +/// @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. // -void xml_util_set_parameters( std::function fn, tinyxml2::XMLElement* xml_elem, - const std::string& error_msg, std::set sub_sections = std::set()) -{ +void xml_util_set_parameters( + std::function fn, + const tinyxml2::XMLElement *xml_elem, const std::string &error_msg, + std::set sub_sections = std::set()) { auto item = xml_elem->FirstChildElement(); while (item != nullptr) { @@ -144,7 +152,7 @@ void xml_util_set_parameters( std::functionGetText(); try { fn(name, value); - } catch (const std::bad_function_call& exception) { + } catch (const std::bad_function_call &exception) { svmp::raise(error_msg + name + "'."); } } else { @@ -163,13 +171,13 @@ void xml_util_set_parameters( std::function( - cfile_name != nullptr, "Include_xml requires a file name."); +IncludeParametersFile::IncludeParametersFile(const char *cfile_name) { + svmp::check(cfile_name != nullptr, + "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()); + file_name.erase(std::remove_if(file_name.begin(), file_name.end(), ::isspace), + file_name.end()); svmp::check( !file_name.empty(), "Include_xml requires a non-empty file name."); @@ -177,8 +185,10 @@ IncludeParametersFile::IncludeParametersFile(const char* cfile_name) root_element = document.FirstChildElement(Parameters::FSI_FILE.c_str()); if (error != tinyxml2::XML_SUCCESS || root_element == nullptr) { - svmp::raise("The following error occurred while reading the XML file '" + - file_name + "'.\n" + "[svMultiPhysics] ERROR " + std::string(document.ErrorStr())); + svmp::raise( + "The following error occurred while reading the XML file '" + + file_name + "'.\n" + "[svMultiPhysics] ERROR " + + std::string(document.ErrorStr())); } } @@ -189,22 +199,19 @@ IncludeParametersFile::IncludeParametersFile(const char* cfile_name) const std::string Parameters::FSI_FILE = "svMultiPhysicsFile"; const std::set Parameters::constitutive_model_names = { - "none", - "nHK", + "none", + "nHK", }; const std::set Parameters::equation_names = { - "none", - "fluid", - "struct", + "none", + "fluid", + "struct", }; -Parameters::Parameters() -{ -} +Parameters::Parameters() {} -void Parameters::get_logging_levels(int& verbose, int& warning, int& debug) -{ +void Parameters::get_logging_levels(int &verbose, int &warning, int &debug) { /* verbose = general_simulation_parameters_.verbose; warning = general_simulation_parameters_.warning; @@ -212,30 +219,30 @@ void Parameters::get_logging_levels(int& verbose, int& warning, int& debug) */ } -void Parameters::print_parameters() -{ +void Parameters::print_parameters() { general_simulation_parameters.print_parameters(); - for (auto& mesh : mesh_parameters) { - mesh->print_parameters(); + for (auto &mesh : mesh_parameters) { + mesh->print_parameters(); } - for (auto& equation : equation_parameters) { - equation->print_parameters(); + for (auto &equation : equation_parameters) { + equation->print_parameters(); } } /// @brief Set the simulation parameter values given in an XML format file. -void Parameters::read_xml(std::string file_name) -{ +void Parameters::read_xml(std::string file_name) { tinyxml2::XMLDocument doc; - + auto error = doc.LoadFile(file_name.c_str()); - + auto root_element = doc.FirstChildElement(FSI_FILE.c_str()); if (error != tinyxml2::XML_SUCCESS || root_element == nullptr) { - svmp::raise("The following error occurred while reading the XML file '" + file_name + "'.\n" + - "[svMultiPhysics] ERROR " + std::string(doc.ErrorStr())); + svmp::raise( + "The following error occurred while reading the XML file '" + + file_name + "'.\n" + "[svMultiPhysics] ERROR " + + std::string(doc.ErrorStr())); } // Get general parameters. @@ -263,10 +270,10 @@ void Parameters::read_xml(std::string file_name) set_URIS_mesh_values(root_element); } -void Parameters::set_contact_values(tinyxml2::XMLElement* root_element) -{ - auto item = root_element->FirstChildElement(ContactParameters::xml_element_name_.c_str()); - +void Parameters::set_contact_values(tinyxml2::XMLElement *root_element) { + auto item = root_element->FirstChildElement( + ContactParameters::xml_element_name_.c_str()); + if (item == nullptr) { return; } @@ -274,95 +281,101 @@ void Parameters::set_contact_values(tinyxml2::XMLElement* root_element) contact_parameters.set_values(item); } -void Parameters::set_equation_values(tinyxml2::XMLElement* root_element) -{ - auto add_eq_item = root_element->FirstChildElement(EquationParameters::xml_element_name_.c_str()); +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 = require_xml_attribute(add_eq_item, "type"); + const char *eq_type = require_xml_attribute(add_eq_item, "type"); auto eq_params = new EquationParameters(); eq_params->type.set(std::string(eq_type)); eq_params->set_values(add_eq_item); equation_parameters.push_back(eq_params); - add_eq_item = add_eq_item->NextSiblingElement(EquationParameters::xml_element_name_.c_str()); + add_eq_item = add_eq_item->NextSiblingElement( + EquationParameters::xml_element_name_.c_str()); } } -void Parameters::set_mesh_values(tinyxml2::XMLElement* root_element) -{ - auto add_mesh_item = root_element->FirstChildElement(MeshParameters::xml_element_name_.c_str()); +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 = require_xml_attribute(add_mesh_item, "name"); + const char *mesh_name = require_xml_attribute(add_mesh_item, "name"); - MeshParameters* mesh_params = new MeshParameters(); + MeshParameters *mesh_params = new MeshParameters(); mesh_params->name.set(std::string(mesh_name)); mesh_params->set_values(add_mesh_item); mesh_parameters.push_back(mesh_params); - add_mesh_item = add_mesh_item->NextSiblingElement(MeshParameters::xml_element_name_.c_str()); + add_mesh_item = add_mesh_item->NextSiblingElement( + MeshParameters::xml_element_name_.c_str()); } } -void Parameters::set_precomputed_solution_values(tinyxml2::XMLElement* root_element) -{ - auto add_pre_sol_item = root_element->FirstChildElement(PrecomputedSolutionParameters::xml_element_name_.c_str()); - if (add_pre_sol_item == nullptr) { +void Parameters::set_precomputed_solution_values( + tinyxml2::XMLElement *root_element) { + auto add_pre_sol_item = root_element->FirstChildElement( + PrecomputedSolutionParameters::xml_element_name_.c_str()); + if (add_pre_sol_item == nullptr) { return; } precomputed_solution_parameters.set_values(add_pre_sol_item); } -void Parameters::set_projection_values(tinyxml2::XMLElement* root_element) -{ - auto add_proj_item = root_element->FirstChildElement(ProjectionParameters::xml_element_name_.c_str()); +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 = require_xml_attribute(add_proj_item, "name"); + const char *proj_name = require_xml_attribute(add_proj_item, "name"); - ProjectionParameters* proj_params = new ProjectionParameters(); + ProjectionParameters *proj_params = new ProjectionParameters(); proj_params->name.set(std::string(proj_name)); proj_params->set_values(add_proj_item); projection_parameters.push_back(proj_params); - add_proj_item = add_proj_item->NextSiblingElement(ProjectionParameters::xml_element_name_.c_str()); + add_proj_item = add_proj_item->NextSiblingElement( + ProjectionParameters::xml_element_name_.c_str()); } } -void Parameters::set_RIS_projection_values(tinyxml2::XMLElement* root_element) -{ - auto add_RIS_proj_item = root_element->FirstChildElement(RISProjectionParameters::xml_element_name_.c_str()); +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 = + const char *RIS_proj_name = require_xml_attribute(add_RIS_proj_item, "name"); - RISProjectionParameters* RIS_proj_params = new RISProjectionParameters(); + RISProjectionParameters *RIS_proj_params = new RISProjectionParameters(); RIS_proj_params->name.set(std::string(RIS_proj_name)); RIS_proj_params->set_values(add_RIS_proj_item); RIS_projection_parameters.push_back(RIS_proj_params); - add_RIS_proj_item = add_RIS_proj_item->NextSiblingElement(RISProjectionParameters::xml_element_name_.c_str()); + add_RIS_proj_item = add_RIS_proj_item->NextSiblingElement( + RISProjectionParameters::xml_element_name_.c_str()); } } -void Parameters::set_URIS_mesh_values(tinyxml2::XMLElement* root_element) -{ - auto add_URIS_mesh_item = root_element->FirstChildElement(URISMeshParameters::xml_element_name_.c_str()); +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 = + const char *URIS_mesh_name = require_xml_attribute(add_URIS_mesh_item, "name"); - URISMeshParameters* URIS_mesh_params = new URISMeshParameters(); + URISMeshParameters *URIS_mesh_params = new URISMeshParameters(); URIS_mesh_params->name.set(std::string(URIS_mesh_name)); URIS_mesh_params->set_values(add_URIS_mesh_item); URIS_mesh_parameters.push_back(URIS_mesh_params); - add_URIS_mesh_item = add_URIS_mesh_item->NextSiblingElement(URISMeshParameters::xml_element_name_.c_str()); + add_URIS_mesh_item = add_URIS_mesh_item->NextSiblingElement( + URISMeshParameters::xml_element_name_.c_str()); } } @@ -375,28 +388,30 @@ void Parameters::set_URIS_mesh_values(tinyxml2::XMLElement* root_element) /// @brief Define the XML element name for boundary condition parameters. const std::string BodyForceParameters::xml_element_name_ = "Add_BF"; -BodyForceParameters::BodyForceParameters() -{ +BodyForceParameters::BodyForceParameters() { // A parameter that must be defined. bool required = true; mesh_name = Parameter("mesh", "", required); - set_parameter("Fourier_coefficients_file_path", "", !required, fourier_coefficients_file_path); + set_parameter("Fourier_coefficients_file_path", "", !required, + fourier_coefficients_file_path); // [TODO:DaveP] I'm not sure if this is required in the Add_BF element. - //set_parameter("Ramp_function", false, !required, ramp_function); - set_parameter("Spatial_values_file_path", "", !required, spatial_values_file_path); - - set_parameter("Temporal_and_spatial_values_file_path", "", !required, temporal_and_spatial_values_file_path); - set_parameter("Temporal_values_file_path", "", !required, temporal_values_file_path); + // set_parameter("Ramp_function", false, !required, ramp_function); + set_parameter("Spatial_values_file_path", "", !required, + spatial_values_file_path); + + set_parameter("Temporal_and_spatial_values_file_path", "", !required, + temporal_and_spatial_values_file_path); + set_parameter("Temporal_values_file_path", "", !required, + temporal_values_file_path); set_parameter("Time_dependence", "Steady", !required, time_dependence); set_parameter("Type", "", required, type); set_parameter("Value", 0.0, !required, value); } -void BodyForceParameters::print_parameters() -{ +void BodyForceParameters::print_parameters() { std::cout << std::endl; std::cout << "---------------------" << std::endl; std::cout << "Body Force Parameters" << std::endl; @@ -404,26 +419,25 @@ void BodyForceParameters::print_parameters() std::cout << mesh_name.name() << ": " << mesh_name.value() << std::endl; auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } } -void BodyForceParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void BodyForceParameters::set_values(tinyxml2::XMLElement *xml_elem) { using namespace tinyxml2; std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'type' from the element. - const char* smesh = require_xml_attribute(xml_elem, "mesh"); + const char *smesh = require_xml_attribute(xml_elem, "mesh"); mesh_name.set(std::string(smesh)); - //auto item = xml_elem->FirstChildElement(); + // auto item = xml_elem->FirstChildElement(); using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &BodyForceParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&BodyForceParameters::set_parameter_value, *this, _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); } @@ -435,14 +449,16 @@ void BodyForceParameters::set_values(tinyxml2::XMLElement* xml_elem) // The BoundaryConditionParameters stores paramaters for various // type of boundary conditions under the Add_BC XML element. -/// @brief Define the XML element name for equation boundary condition parameters. +/// @brief Define the XML element name for equation boundary condition +/// parameters. const std::string BoundaryConditionParameters::xml_element_name_ = "Add_BC"; -const std::string BoundaryConditionRCRParameters::xml_element_name_ = "RCR_values"; -const std::string CouplingInterfaceParameters::xml_element_name_ = "Coupling_interface"; +const std::string BoundaryConditionRCRParameters::xml_element_name_ = + "RCR_values"; +const std::string CouplingInterfaceParameters::xml_element_name_ = + "Coupling_interface"; /// @brief RCR values for Neumann BC type. -BoundaryConditionRCRParameters::BoundaryConditionRCRParameters() -{ +BoundaryConditionRCRParameters::BoundaryConditionRCRParameters() { // A parameter that must be defined. bool required = true; @@ -456,106 +472,117 @@ BoundaryConditionRCRParameters::BoundaryConditionRCRParameters() set_parameter("Proximal_resistance", 0.0, required, proximal_resistance); } -void BoundaryConditionRCRParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ - std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; +void BoundaryConditionRCRParameters::set_values( + tinyxml2::XMLElement *xml_elem) { + std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &BoundaryConditionRCRParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&BoundaryConditionRCRParameters::set_parameter_value, *this, _1, + _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); value_set = true; } -CouplingInterfaceParameters::CouplingInterfaceParameters() -{ +CouplingInterfaceParameters::CouplingInterfaceParameters() { bool required = false; - set_parameter("svZeroDSolver_block", "", !required, svzerod_solver_block); - set_parameter("Chamber_cap_surface", "", !required, chamber_cap_surface); - set_parameter("svOneDSolver_input_file","", !required, svoned_input_file); - set_parameter("Ramp_steps", 0, !required, coupling_ramp_steps); - set_parameter("Ramp_ref_pressure", 0.0, !required, coupling_ramp_ref_pressure); - set_parameter("Relax_factor", 1.0, !required, coupling_relax_factor); + set_parameter("svZeroDSolver_block", "", !required, svzerod_solver_block); + set_parameter("Chamber_cap_surface", "", !required, chamber_cap_surface); + set_parameter("svOneDSolver_input_file", "", !required, svoned_input_file); + set_parameter("Ramp_steps", 0, !required, coupling_ramp_steps); + set_parameter("Ramp_ref_pressure", 0.0, !required, + coupling_ramp_ref_pressure); + set_parameter("Relax_factor", 1.0, !required, coupling_relax_factor); } -void CouplingInterfaceParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void CouplingInterfaceParameters::set_values(tinyxml2::XMLElement *xml_elem) { std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind(&CouplingInterfaceParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&CouplingInterfaceParameters::set_parameter_value, *this, _1, + _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); value_set = true; } -void CouplingInterfaceParameters::print_parameters() -{ +void CouplingInterfaceParameters::print_parameters() { std::cout << std::endl; std::cout << "---------------------------------" << std::endl; std::cout << "Coupling interface parameters" << std::endl; std::cout << "---------------------------------" << std::endl; auto params_name_value = get_parameter_list(); - for (auto& [key, value] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } } -void BoundaryConditionRCRParameters::print_parameters() -{ - std::cout << std::endl; +void BoundaryConditionRCRParameters::print_parameters() { + std::cout << std::endl; std::cout << "---------------------------------" << std::endl; std::cout << "Boundary Condition RCR Parameters" << std::endl; std::cout << "---------------------------------" << std::endl; auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } } -BoundaryConditionParameters::BoundaryConditionParameters() -{ +BoundaryConditionParameters::BoundaryConditionParameters() { // A parameter that must be defined. bool required = true; // Set name from Add_BC name="" XML element. name = Parameter("name", "", required); - set_parameter("Apply_along_normal_direction", false, !required, apply_along_normal_direction); + set_parameter("Apply_along_normal_direction", false, !required, + apply_along_normal_direction); set_parameter("Bct_file_path", "", !required, bct_file_path); set_parameter("Damping", 1.0, !required, damping); set_parameter("Distal_pressure", {}, !required, distal_pressure); set_parameter("Effective_direction", {}, !required, effective_direction); - //set_parameter("Effective_direction", {0,0,0}, !required, effective_direction); - set_parameter("Follower_pressure_load", false, !required, follower_pressure_load); - set_parameter("Fourier_coefficients_file_path", "", !required, fourier_coefficients_file_path); + // set_parameter("Effective_direction", {0,0,0}, !required, + // effective_direction); + set_parameter("Follower_pressure_load", false, !required, + follower_pressure_load); + set_parameter("Fourier_coefficients_file_path", "", !required, + fourier_coefficients_file_path); set_parameter("Impose_flux", false, !required, impose_flux); - set_parameter("Impose_on_state_variable_integral", false, !required, impose_on_state_variable_integral); - set_parameter("Initial_displacements_file_path", "", !required, initial_displacements_file_path); + set_parameter("Impose_on_state_variable_integral", false, !required, + impose_on_state_variable_integral); + set_parameter("Initial_displacements_file_path", "", !required, + initial_displacements_file_path); set_parameter("Penalty_parameter", 0.0, !required, penalty_parameter); - set_parameter("Penalty_parameter_normal", 0.0, !required, penalty_parameter_normal); - set_parameter("Penalty_parameter_tangential", 0.0, !required, penalty_parameter_tangential); + set_parameter("Penalty_parameter_normal", 0.0, !required, + penalty_parameter_normal); + set_parameter("Penalty_parameter_tangential", 0.0, !required, + penalty_parameter_tangential); set_parameter("Prestress_file_path", "", !required, prestress_file_path); set_parameter("Profile", "Flat", !required, profile); set_parameter("Ramp_function", false, !required, ramp_function); set_parameter("CST_shell_bc_type", "", !required, cst_shell_bc_type); - set_parameter("Spatial_profile_file_path", "", !required, spatial_profile_file_path); - set_parameter("Spatial_values_file_path", "", !required, spatial_values_file_path); + set_parameter("Spatial_profile_file_path", "", !required, + spatial_profile_file_path); + set_parameter("Spatial_values_file_path", "", !required, + spatial_values_file_path); set_parameter("Stiffness", 1.0, !required, stiffness); - set_parameter("Temporal_and_spatial_values_file_path", "", !required, temporal_and_spatial_values_file_path); - set_parameter("Temporal_values_file_path", "", !required, temporal_values_file_path); + set_parameter("Temporal_and_spatial_values_file_path", "", !required, + temporal_and_spatial_values_file_path); + set_parameter("Temporal_values_file_path", "", !required, + temporal_values_file_path); set_parameter("Time_dependence", "Steady", !required, time_dependence); - set_parameter("Traction_values_file_path", "", !required, traction_values_file_path); + set_parameter("Traction_values_file_path", "", !required, + traction_values_file_path); set_parameter("Traction_multiplier", 1.0, !required, traction_multiplier); set_parameter("Type", "", required, type); @@ -568,16 +595,15 @@ BoundaryConditionParameters::BoundaryConditionParameters() set_parameter("Resistance", 1.e5, !required, resistance); } -void BoundaryConditionParameters::print_parameters() -{ - std::cout << std::endl; +void BoundaryConditionParameters::print_parameters() { + std::cout << std::endl; std::cout << "-----------------------------" << std::endl; std::cout << "Boundary Condition Parameters" << std::endl; std::cout << "-----------------------------" << std::endl; std::cout << name.name() << ": " << name.value() << std::endl; auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } @@ -587,13 +613,12 @@ void BoundaryConditionParameters::print_parameters() } } -void BoundaryConditionParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void BoundaryConditionParameters::set_values(tinyxml2::XMLElement *xml_elem) { using namespace tinyxml2; - std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; + std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'name' from the element. - const char* sname = require_xml_attribute(xml_elem, "name"); + const char *sname = require_xml_attribute(xml_elem, "name"); name.set(std::string(sname)); auto item = xml_elem->FirstChildElement(); @@ -609,7 +634,7 @@ void BoundaryConditionParameters::set_values(tinyxml2::XMLElement* xml_elem) auto value = item->GetText(); try { set_parameter_value(name, value); - } catch (const std::bad_function_call& exception) { + } catch (const std::bad_function_call &exception) { svmp::raise(error_msg + name + "'."); } } else { @@ -627,74 +652,117 @@ void BoundaryConditionParameters::set_values(tinyxml2::XMLElement* xml_elem) /// @brief Process parameters for various constitutive models. /// /// Define the XML element name for constitutive parameters. -const std::string ConstitutiveModelParameters::xml_element_name_ = "Constitutive_model"; +const std::string ConstitutiveModelParameters::xml_element_name_ = + "Constitutive_model"; // [TODO] Should use the types defined in consts.h. const std::string ConstitutiveModelParameters::GUCCIONE_MODEL = "Guccione"; const std::string ConstitutiveModelParameters::HGO_MODEL = "HGO"; -const std::string ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MODEL = "HolzapfelOgden"; -const std::string ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MA_MODEL = "HolzapfelOgden-ModifiedAnisotropy"; +const std::string ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MODEL = + "HolzapfelOgden"; +const std::string ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MA_MODEL = + "HolzapfelOgden-ModifiedAnisotropy"; const std::string ConstitutiveModelParameters::LEE_SACKS = "Lee-Sacks"; const std::string ConstitutiveModelParameters::NEOHOOKEAN_MODEL = "neoHookean"; -const std::string ConstitutiveModelParameters::STVENANT_KIRCHHOFF_MODEL = "stVenantKirchhoff"; +const std::string ConstitutiveModelParameters::STVENANT_KIRCHHOFF_MODEL = + "stVenantKirchhoff"; const std::string ConstitutiveModelParameters::CANN_MODEL = "CANN"; /// @brief Supported constitutive model types and their aliases. -const std::map ConstitutiveModelParameters::constitutive_model_types = { - { ConstitutiveModelParameters::GUCCIONE_MODEL, ConstitutiveModelParameters::GUCCIONE_MODEL}, - { "Gucci", ConstitutiveModelParameters::GUCCIONE_MODEL}, +const std::map + ConstitutiveModelParameters::constitutive_model_types = { + {ConstitutiveModelParameters::GUCCIONE_MODEL, + ConstitutiveModelParameters::GUCCIONE_MODEL}, + {"Gucci", ConstitutiveModelParameters::GUCCIONE_MODEL}, - {ConstitutiveModelParameters::HGO_MODEL, ConstitutiveModelParameters::HGO_MODEL}, + {ConstitutiveModelParameters::HGO_MODEL, + ConstitutiveModelParameters::HGO_MODEL}, - {ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MODEL, ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MODEL}, + {ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MODEL, + ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MODEL}, - {ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MA_MODEL, ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MA_MODEL}, + {ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MA_MODEL, + ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MA_MODEL}, - {ConstitutiveModelParameters::LEE_SACKS, ConstitutiveModelParameters::LEE_SACKS}, + {ConstitutiveModelParameters::LEE_SACKS, + ConstitutiveModelParameters::LEE_SACKS}, - {ConstitutiveModelParameters::NEOHOOKEAN_MODEL, ConstitutiveModelParameters::NEOHOOKEAN_MODEL}, - {"nHK", ConstitutiveModelParameters::NEOHOOKEAN_MODEL}, + {ConstitutiveModelParameters::NEOHOOKEAN_MODEL, + ConstitutiveModelParameters::NEOHOOKEAN_MODEL}, + {"nHK", ConstitutiveModelParameters::NEOHOOKEAN_MODEL}, - {ConstitutiveModelParameters::STVENANT_KIRCHHOFF_MODEL, ConstitutiveModelParameters::STVENANT_KIRCHHOFF_MODEL}, - {"stVK", ConstitutiveModelParameters::STVENANT_KIRCHHOFF_MODEL}, + {ConstitutiveModelParameters::STVENANT_KIRCHHOFF_MODEL, + ConstitutiveModelParameters::STVENANT_KIRCHHOFF_MODEL}, + {"stVK", ConstitutiveModelParameters::STVENANT_KIRCHHOFF_MODEL}, - {ConstitutiveModelParameters::CANN_MODEL, ConstitutiveModelParameters::CANN_MODEL}, - {"CANN", ConstitutiveModelParameters::CANN_MODEL}, -}; + {ConstitutiveModelParameters::CANN_MODEL, + ConstitutiveModelParameters::CANN_MODEL}, + {"CANN", ConstitutiveModelParameters::CANN_MODEL}, +}; /// @brief Define a map to set the parameters for each constitutive model. -using CmpType = ConstitutiveModelParameters*; -using CmpXmlType = tinyxml2::XMLElement*; -using SetConstitutiveModelParamMapType = std::map>; +using CmpType = ConstitutiveModelParameters *; +using CmpXmlType = tinyxml2::XMLElement *; +using SetConstitutiveModelParamMapType = + std::map>; SetConstitutiveModelParamMapType SetConstitutiveModelParamMap = { - {ConstitutiveModelParameters::GUCCIONE_MODEL, [](CmpType cp, CmpXmlType params) -> void {cp->guccione.set_values(params);}}, - {ConstitutiveModelParameters::HGO_MODEL, [](CmpType cp, CmpXmlType params) -> void {cp->holzapfel_gasser_ogden.set_values(params);}}, - {ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MODEL, [](CmpType cp, CmpXmlType params) -> void {cp->holzapfel.set_values(params);}}, - {ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MA_MODEL, [](CmpType cp, CmpXmlType params) -> void {cp->holzapfel.set_values(params);}}, - {ConstitutiveModelParameters::LEE_SACKS, [](CmpType cp, CmpXmlType params) -> void {cp->lee_sacks.set_values(params);}}, - {ConstitutiveModelParameters::NEOHOOKEAN_MODEL, [](CmpType cp, CmpXmlType params) -> void {cp->neo_hookean.set_values(params);}}, - {ConstitutiveModelParameters::STVENANT_KIRCHHOFF_MODEL, [](CmpType cp, CmpXmlType params) -> void {cp->stvenant_kirchhoff.set_values(params);}}, - {ConstitutiveModelParameters::CANN_MODEL, [](CmpType cp, CmpXmlType params) -> void {cp->cann.set_values(params);}} -}; + {ConstitutiveModelParameters::GUCCIONE_MODEL, + [](CmpType cp, CmpXmlType params) -> void { + cp->guccione.set_values(params); + }}, + {ConstitutiveModelParameters::HGO_MODEL, + [](CmpType cp, CmpXmlType params) -> void { + cp->holzapfel_gasser_ogden.set_values(params); + }}, + {ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MODEL, + [](CmpType cp, CmpXmlType params) -> void { + cp->holzapfel.set_values(params); + }}, + {ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MA_MODEL, + [](CmpType cp, CmpXmlType params) -> void { + cp->holzapfel.set_values(params); + }}, + {ConstitutiveModelParameters::LEE_SACKS, + [](CmpType cp, CmpXmlType params) -> void { + cp->lee_sacks.set_values(params); + }}, + {ConstitutiveModelParameters::NEOHOOKEAN_MODEL, + [](CmpType cp, CmpXmlType params) -> void { + cp->neo_hookean.set_values(params); + }}, + {ConstitutiveModelParameters::STVENANT_KIRCHHOFF_MODEL, + [](CmpType cp, CmpXmlType params) -> void { + cp->stvenant_kirchhoff.set_values(params); + }}, + {ConstitutiveModelParameters::CANN_MODEL, + [](CmpType cp, CmpXmlType params) -> void { + cp->cann.set_values(params); + }}}; /// @brief Define a map to print parameters for each constitutive model. -using PrintConstitutiveModelParamMapType = std::map>; +using PrintConstitutiveModelParamMapType = + std::map>; PrintConstitutiveModelParamMapType PrintConstitutiveModelParamMap = { - {ConstitutiveModelParameters::GUCCIONE_MODEL, [](CmpType cp) -> void {cp->guccione.print_parameters();}}, - {ConstitutiveModelParameters::HGO_MODEL, [](CmpType cp) -> void {cp->holzapfel_gasser_ogden.print_parameters();}}, - {ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MODEL, [](CmpType cp) -> void {cp->holzapfel.print_parameters();}}, - {ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MA_MODEL, [](CmpType cp) -> void {cp->holzapfel.print_parameters();}}, - {ConstitutiveModelParameters::LEE_SACKS, [](CmpType cp) -> void {cp->lee_sacks.print_parameters();}}, - {ConstitutiveModelParameters::NEOHOOKEAN_MODEL, [](CmpType cp) -> void {cp->neo_hookean.print_parameters();}}, - {ConstitutiveModelParameters::STVENANT_KIRCHHOFF_MODEL, [](CmpType cp) -> void {cp->stvenant_kirchhoff.print_parameters();}}, - {ConstitutiveModelParameters::CANN_MODEL, [](CmpType cp) -> void {cp->cann.print_parameters();}} -}; - - -GuccioneParameters::GuccioneParameters() -{ + {ConstitutiveModelParameters::GUCCIONE_MODEL, + [](CmpType cp) -> void { cp->guccione.print_parameters(); }}, + {ConstitutiveModelParameters::HGO_MODEL, + [](CmpType cp) -> void { cp->holzapfel_gasser_ogden.print_parameters(); }}, + {ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MODEL, + [](CmpType cp) -> void { cp->holzapfel.print_parameters(); }}, + {ConstitutiveModelParameters::HOLZAPFEL_OGDEN_MA_MODEL, + [](CmpType cp) -> void { cp->holzapfel.print_parameters(); }}, + {ConstitutiveModelParameters::LEE_SACKS, + [](CmpType cp) -> void { cp->lee_sacks.print_parameters(); }}, + {ConstitutiveModelParameters::NEOHOOKEAN_MODEL, + [](CmpType cp) -> void { cp->neo_hookean.print_parameters(); }}, + {ConstitutiveModelParameters::STVENANT_KIRCHHOFF_MODEL, + [](CmpType cp) -> void { cp->stvenant_kirchhoff.print_parameters(); }}, + {ConstitutiveModelParameters::CANN_MODEL, + [](CmpType cp) -> void { cp->cann.print_parameters(); }}}; + +GuccioneParameters::GuccioneParameters() { // A parameter that must be defined. bool required = true; @@ -706,31 +774,29 @@ GuccioneParameters::GuccioneParameters() set_xml_element_name("Constitutive_model type=Guccione"); } -void GuccioneParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ - std::string error_msg = "Unknown Constitutive_model type=Guccione XML element '"; +void GuccioneParameters::set_values(tinyxml2::XMLElement *xml_elem) { + std::string error_msg = + "Unknown Constitutive_model type=Guccione XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &GuccioneParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&GuccioneParameters::set_parameter_value, *this, _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); value_set = true; } -void GuccioneParameters::print_parameters() -{ +void GuccioneParameters::print_parameters() { std::cout << "Guccione: " << std::endl; - auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + auto params_name_value = get_parameter_list(); + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } } -HolzapfelParameters::HolzapfelParameters() -{ +HolzapfelParameters::HolzapfelParameters() { // A parameter that must be defined. bool required = true; @@ -747,34 +813,32 @@ HolzapfelParameters::HolzapfelParameters() set_parameter("bfs", 0.0, required, bfs); set_parameter("k", 0.0, required, k); - + set_xml_element_name("Constitutive_model type=Holzapfel"); } -void HolzapfelParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ - std::string error_msg = "Unknown Constitutive_model type=Holzapfel XML element '"; - +void HolzapfelParameters::set_values(tinyxml2::XMLElement *xml_elem) { + std::string error_msg = + "Unknown Constitutive_model type=Holzapfel XML element '"; + using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &HolzapfelParameters::set_parameter_value, *this, _1, _2); - + std::function ftpr = + std::bind(&HolzapfelParameters::set_parameter_value, *this, _1, _2); + xml_util_set_parameters(ftpr, xml_elem, error_msg); - + value_set = true; } -void HolzapfelParameters::print_parameters() -{ - auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { +void HolzapfelParameters::print_parameters() { + auto params_name_value = get_parameter_list(); + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } } -HolzapfelGasserOgdenParameters::HolzapfelGasserOgdenParameters() -{ +HolzapfelGasserOgdenParameters::HolzapfelGasserOgdenParameters() { // A parameter that must be defined. bool required = true; @@ -787,29 +851,28 @@ HolzapfelGasserOgdenParameters::HolzapfelGasserOgdenParameters() set_xml_element_name("Constitutive_model type=HGO"); } -void HolzapfelGasserOgdenParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ - std::string error_msg = "Unknown Constitutive_model type=HGO XML element '"; +void HolzapfelGasserOgdenParameters::set_values( + tinyxml2::XMLElement *xml_elem) { + std::string error_msg = "Unknown Constitutive_model type=HGO XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &HolzapfelGasserOgdenParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&HolzapfelGasserOgdenParameters::set_parameter_value, *this, _1, + _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); value_set = true; } -void HolzapfelGasserOgdenParameters::print_parameters() -{ +void HolzapfelGasserOgdenParameters::print_parameters() { auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } } -LeeSacksParameters::LeeSacksParameters() -{ +LeeSacksParameters::LeeSacksParameters() { // A parameter that must be defined. bool required = true; @@ -822,31 +885,29 @@ LeeSacksParameters::LeeSacksParameters() set_xml_element_name("Constitutive_model type=Lee-Sacks"); } -void LeeSacksParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ - std::string error_msg = "Unknown Constitutive_model type=Lee-Sacks XML element '"; +void LeeSacksParameters::set_values(tinyxml2::XMLElement *xml_elem) { + std::string error_msg = + "Unknown Constitutive_model type=Lee-Sacks XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &LeeSacksParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&LeeSacksParameters::set_parameter_value, *this, _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); value_set = true; } -void LeeSacksParameters::print_parameters() -{ +void LeeSacksParameters::print_parameters() { std::cout << "Lee-Sacks: " << std::endl; auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } } -MooneyRivlinParameters::MooneyRivlinParameters() -{ +MooneyRivlinParameters::MooneyRivlinParameters() { // A parameter that must be defined. bool required = true; @@ -856,62 +917,51 @@ MooneyRivlinParameters::MooneyRivlinParameters() set_xml_element_name("Constitutive_model type=Mooney-Rivlin"); } -void MooneyRivlinParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ - std::string error_msg = "Unknown Constitutive_model type=Mooney-Rivlin XML element '"; +void MooneyRivlinParameters::set_values(tinyxml2::XMLElement *xml_elem) { + std::string error_msg = + "Unknown Constitutive_model type=Mooney-Rivlin XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &MooneyRivlinParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&MooneyRivlinParameters::set_parameter_value, *this, _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); value_set = true; } -void MooneyRivlinParameters::print_parameters() -{ +void MooneyRivlinParameters::print_parameters() { std::cout << "MooneyRivlin: " << std::endl; - std::cout << c1.name() << ": " << c1.value() << std::endl;; - std::cout << c2.name() << ": " << c2.value() << std::endl;; + std::cout << c1.name() << ": " << c1.value() << std::endl; + ; + std::cout << c2.name() << ": " << c2.value() << std::endl; + ; } /// @brief There are no parameters associated with a Neohookean model. -NeoHookeanParameters::NeoHookeanParameters() -{ -} +NeoHookeanParameters::NeoHookeanParameters() {} -void NeoHookeanParameters::set_values(tinyxml2::XMLElement* con_params) -{ +void NeoHookeanParameters::set_values(tinyxml2::XMLElement *con_params) { value_set = true; } -void NeoHookeanParameters::print_parameters() -{ -} +void NeoHookeanParameters::print_parameters() {} /// @brief There are no parameters associated with a StVenantKirchhoff model. -StVenantKirchhoffParameters::StVenantKirchhoffParameters() -{ - value_set = true; -} +StVenantKirchhoffParameters::StVenantKirchhoffParameters() { value_set = true; } -void StVenantKirchhoffParameters::set_values(tinyxml2::XMLElement* con_params) -{ +void StVenantKirchhoffParameters::set_values(tinyxml2::XMLElement *con_params) { value_set = true; } -void StVenantKirchhoffParameters::print_parameters() -{ -} +void StVenantKirchhoffParameters::print_parameters() {} /// @brief Process parameters for the "Add_row" xml element /// /// Define the xml element name for CANN row parameters const std::string CANNRowParameters::xml_element_name_ = "Add_row"; -CANNRowParameters::CANNRowParameters() -{ +CANNRowParameters::CANNRowParameters() { set_xml_element_name(xml_element_name_); // A parameter that must be defined. @@ -921,57 +971,58 @@ CANNRowParameters::CANNRowParameters() // Initialize row parameters and add it to params map int invariant = 1; - std::initializer_list activation_func = {1,1,1}; - std::initializer_list weights_vec = {1.0,1.0,1.0}; + std::initializer_list activation_func = {1, 1, 1}; + std::initializer_list weights_vec = {1.0, 1.0, 1.0}; - set_parameter("Invariant_num", invariant ,required, row.invariant_index); - set_parameter("Activation_functions", activation_func,required, row.activation_functions); - set_parameter("Weights", weights_vec,required,row.weights); + set_parameter("Invariant_num", invariant, required, row.invariant_index); + set_parameter("Activation_functions", activation_func, required, + row.activation_functions); + set_parameter("Weights", weights_vec, required, row.weights); } -void CANNRowParameters::print_parameters() -{ +void CANNRowParameters::print_parameters() { std::cout << std::endl; std::cout << "---------------" << std::endl; std::cout << "CANN Row Parameters" << std::endl; std::cout << "---------------" << std::endl; std::cout << "Invariant number: " << row.invariant_index << std::endl; - std::cout << "Activation function 0: " << row.activation_functions[0] << std::endl; - std::cout << "Activation function 1: " << row.activation_functions[1] << std::endl; - std::cout << "Activation function 2: " << row.activation_functions[2] << std::endl; + std::cout << "Activation function 0: " << row.activation_functions[0] + << std::endl; + std::cout << "Activation function 1: " << row.activation_functions[1] + << std::endl; + std::cout << "Activation function 2: " << row.activation_functions[2] + << std::endl; std::cout << "Weight 0: " << row.weights[0] << std::endl; std::cout << "Weight 1: " << row.weights[1] << std::endl; std::cout << "Weight 2: " << row.weights[2] << std::endl; } -void CANNRowParameters::set_values(tinyxml2::XMLElement* row_elem) -{ +void CANNRowParameters::set_values(tinyxml2::XMLElement *row_elem) { svmp::check_not_null( row_elem, "CANNRowParameters::set_values: Received null XML element."); using namespace tinyxml2; - std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; + std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Set row_name for current row element - const char* row_name_input = - require_xml_attribute(row_elem, "row_name"); + const char *row_name_input = require_xml_attribute(row_elem, "row_name"); row_name.set(std::string(row_name_input)); auto item = row_elem->FirstChildElement(); // Iterate over all child elements for this row - while(item != nullptr) { + while (item != nullptr) { auto name = std::string(item->Value()); auto value = item->GetText(); - if (value == nullptr) { + if (value == nullptr) { svmp::raise(error_msg + name + "'."); } try { set_parameter_value_CANN(name, value); - } catch (const std::bad_function_call& exception) { + } catch (const std::bad_function_call &exception) { svmp::raise(error_msg + name + "'."); } @@ -980,8 +1031,7 @@ void CANNRowParameters::set_values(tinyxml2::XMLElement* row_elem) } /// @brief Constructor for CANNParameters class. Initializes parameter table -CANNParameters::CANNParameters() -{ +CANNParameters::CANNParameters() { // A parameter that must be defined. bool required = true; @@ -990,27 +1040,26 @@ CANNParameters::CANNParameters() // No need to initialize rows. } -/// @brief Destructor for CANNParameters class. Deletes memory dynamically allocated -/// to the rows of the table. -CANNParameters::~CANNParameters() -{ - for (auto row : rows) { - delete row; // Free allocated memory - } - rows.clear(); +/// @brief Destructor for CANNParameters class. Deletes memory dynamically +/// allocated to the rows of the table. +CANNParameters::~CANNParameters() { + for (auto row : rows) { + delete row; // Free allocated memory + } + rows.clear(); } -void CANNParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void CANNParameters::set_values(tinyxml2::XMLElement *xml_elem) { using namespace tinyxml2; std::string error_msg = "Unknown Constitutive_model type=CANN XML element '"; - auto row_elem = xml_elem->FirstChildElement("Add_row"); // initializes pointer to first row name + auto row_elem = xml_elem->FirstChildElement( + "Add_row"); // initializes pointer to first row name while (row_elem != nullptr) { - CANNRowParameters* row = new CANNRowParameters(); + CANNRowParameters *row = new CANNRowParameters(); row->set_values(row_elem); // Populate row parameters - rows.push_back(row); // store the pointer to row in vector + rows.push_back(row); // store the pointer to row in vector row_elem = row_elem->NextSiblingElement("Add_row"); } @@ -1022,20 +1071,18 @@ void CANNParameters::set_values(tinyxml2::XMLElement* xml_elem) value_set = true; } -void CANNParameters::print_parameters() -{ +void CANNParameters::print_parameters() { std::cout << std::endl; std::cout << "---------------" << std::endl; std::cout << "CANN Parameters" << std::endl; std::cout << "---------------" << std::endl; - for (auto& row : rows) { + for (auto &row : rows) { row->print_parameters(); } } -ConstitutiveModelParameters::ConstitutiveModelParameters() -{ +ConstitutiveModelParameters::ConstitutiveModelParameters() { // A parameter that must be defined. bool required = true; @@ -1043,8 +1090,7 @@ ConstitutiveModelParameters::ConstitutiveModelParameters() type = Parameter("type", "", required); } -void ConstitutiveModelParameters::print_parameters() -{ +void ConstitutiveModelParameters::print_parameters() { if (!value_set) { return; } @@ -1057,20 +1103,20 @@ void ConstitutiveModelParameters::print_parameters() PrintConstitutiveModelParamMap[type.value()](this); } -void ConstitutiveModelParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void ConstitutiveModelParameters::set_values(tinyxml2::XMLElement *xml_elem) { using namespace tinyxml2; - std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; + std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'type' from the element. - const char* stype = require_xml_attribute(xml_elem, "type"); + const char *stype = require_xml_attribute(xml_elem, "type"); type.set(std::string(stype)); // Check constitutive model type. if (constitutive_model_types.count(type.value()) == 0) { - auto msg_1 = "Unknown constitutive model '" + type.value() + " in the '" + xml_elem->Name() + "' XML element.\n"; + auto msg_1 = "Unknown constitutive model '" + type.value() + " in the '" + + xml_elem->Name() + "' XML element.\n"; std::string msg_2 = "Valid types are:"; - for (auto& name : constitutive_model_types) { + for (auto &name : constitutive_model_types) { msg_2 += " " + name.first; } msg_2 += "\n"; @@ -1088,16 +1134,20 @@ void ConstitutiveModelParameters::set_values(tinyxml2::XMLElement* xml_elem) /// @brief Check if a constitutive model is valid for the given equation. // -void ConstitutiveModelParameters::check_constitutive_model(const Parameter& eq_type_str) -{ - auto eq_type = require_map_value(consts::equation_name_to_type, eq_type_str.value(), - "Unknown equation type '" + eq_type_str.value() + "'."); - auto model = require_map_value(consts::constitutive_model_name_to_type, type.value(), - "Unknown constitutive model '" + type.value() + "'."); +void ConstitutiveModelParameters::check_constitutive_model( + const Parameter &eq_type_str) { + auto eq_type = + require_map_value(consts::equation_name_to_type, eq_type_str.value(), + "Unknown equation type '" + eq_type_str.value() + "'."); + auto model = + require_map_value(consts::constitutive_model_name_to_type, type.value(), + "Unknown constitutive model '" + type.value() + "'."); if (eq_type == consts::EquationType::phys_ustruct) { - if (! ustruct::constitutive_model_is_valid(model)) { - svmp::raise("The " + type.value() + " constitutive model is not valid for ustruct equations."); + if (!ustruct::constitutive_model_is_valid(model)) { + svmp::raise( + "The " + type.value() + + " constitutive model is not valid for ustruct equations."); } } } @@ -1111,8 +1161,7 @@ void ConstitutiveModelParameters::check_constitutive_model(const Parameter element. - const char* stype = require_xml_attribute(xml_elem, "type"); + const char *stype = require_xml_attribute(xml_elem, "type"); type.set(std::string(stype)); auto item = xml_elem->FirstChildElement(); - + using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &CoupleGenBCParameters::set_parameter_value, *this, _1, _2); - + std::function ftpr = + std::bind(&CoupleGenBCParameters::set_parameter_value, *this, _1, _2); + xml_util_set_parameters(ftpr, xml_elem, error_msg); - + value_set = true; } @@ -1148,10 +1196,10 @@ void CoupleGenBCParameters::set_values(tinyxml2::XMLElement* xml_elem) // interface to the svZeroDSolver. // Define the XML element name for the svZeroDSolver_interface parameters. -const std::string svZeroDSolverInterfaceParameters::xml_element_name_ = "svZeroDSolver_interface"; +const std::string svZeroDSolverInterfaceParameters::xml_element_name_ = + "svZeroDSolver_interface"; -svZeroDSolverInterfaceParameters::svZeroDSolverInterfaceParameters() -{ +svZeroDSolverInterfaceParameters::svZeroDSolverInterfaceParameters() { // A parameter that must be defined. bool required = true; @@ -1163,11 +1211,10 @@ svZeroDSolverInterfaceParameters::svZeroDSolverInterfaceParameters() set_parameter("Configuration_file", "", required, configuration_file); set_parameter("Shared_library", "", required, shared_library); - }; -void svZeroDSolverInterfaceParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void svZeroDSolverInterfaceParameters::set_values( + tinyxml2::XMLElement *xml_elem) { std::string error_msg = "Unknown svZeroDSolver_interface XML element '"; // Process child elements. @@ -1176,8 +1223,9 @@ void svZeroDSolverInterfaceParameters::set_values(tinyxml2::XMLElement* xml_elem using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &svZeroDSolverInterfaceParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&svZeroDSolverInterfaceParameters::set_parameter_value, *this, + _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); value_set = true; @@ -1187,28 +1235,29 @@ void svZeroDSolverInterfaceParameters::set_values(tinyxml2::XMLElement* xml_elem // svOneDSolverInterfaceParameters // ////////////////////////////////////////////////////////// -const std::string svOneDSolverInterfaceParameters::xml_element_name_ = "svOneDSolver_interface"; +const std::string svOneDSolverInterfaceParameters::xml_element_name_ = + "svOneDSolver_interface"; -svOneDSolverInterfaceParameters::svOneDSolverInterfaceParameters() -{ +svOneDSolverInterfaceParameters::svOneDSolverInterfaceParameters() { bool required = true; - set_parameter("Coupling_type", "", required, coupling_type); - set_parameter("Shared_library", "", required, shared_library); + set_parameter("Coupling_type", "", required, coupling_type); + set_parameter("Shared_library", "", required, shared_library); // Unlike svZeroDSolver (which uses a single global Input_file for all faces), // svOneDSolver requires each coupled face to supply its own input file. // Per-face input files are specified via // inside each element. } -void svOneDSolverInterfaceParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void svOneDSolverInterfaceParameters::set_values( + tinyxml2::XMLElement *xml_elem) { std::string error_msg = "Unknown svOneDSolver_interface XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind(&svOneDSolverInterfaceParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&svOneDSolverInterfaceParameters::set_parameter_value, *this, + _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); value_set = true; @@ -1224,17 +1273,15 @@ void svOneDSolverInterfaceParameters::set_values(tinyxml2::XMLElement* xml_elem) /// @brief Define the XML element name for equation output parameters. const std::string OutputParameters::xml_element_name_ = "Output"; -OutputParameters::OutputParameters() -{ +OutputParameters::OutputParameters() { // A parameter that must be defined. bool required = true; - // Type for the xml element. + // Type for the xml element. type = Parameter("type", "", required); } -void OutputParameters::print_parameters() -{ +void OutputParameters::print_parameters() { std::cout << std::endl; std::cout << "-----------------" << std::endl; std::cout << "Output Parameters" << std::endl; @@ -1242,12 +1289,11 @@ void OutputParameters::print_parameters() std::cout << type.name() << ": " << type.value() << std::endl; } -void OutputParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void OutputParameters::set_values(tinyxml2::XMLElement *xml_elem) { std::string msg("[OutputParameters::set_values] "); - std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; + std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; - const char* stype = require_xml_attribute(xml_elem, "type"); + const char *stype = require_xml_attribute(xml_elem, "type"); type.set(std::string(stype)); // Get values from XML file. @@ -1259,7 +1305,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(require_xml_text(item, "Output XML element '" + name + "' requires a value.")); + auto value = std::string(require_xml_text( + item, "Output XML element '" + name + "' requires a value.")); Parameter param(name, "", false); param.set(value); alias_list.emplace_back(param); @@ -1269,7 +1316,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(require_xml_text(item, "Output XML element '" + name + "' requires a value.")); + auto value = std::string(require_xml_text( + item, "Output XML element '" + name + "' requires a value.")); Parameter param(name, false, false); param.set(value); output_list.emplace_back(param); @@ -1279,9 +1327,8 @@ void OutputParameters::set_values(tinyxml2::XMLElement* xml_elem) } /// @brief Get the value of an alias by name. -std::string OutputParameters::get_alias_value(const std::string& name) -{ - for (auto& param : alias_list) { +std::string OutputParameters::get_alias_value(const std::string &name) { + for (auto ¶m : alias_list) { if (param.name_ == name) { return param.value(); } @@ -1291,9 +1338,8 @@ std::string OutputParameters::get_alias_value(const std::string& name) } /// @brief Get the value of an output by name. -bool OutputParameters::get_output_value(const std::string& name) -{ - for (auto& param : output_list) { +bool OutputParameters::get_output_value(const std::string &name) { + for (auto ¶m : output_list) { if (param.name_ == name) { return param.value(); } @@ -1310,33 +1356,34 @@ bool OutputParameters::get_output_value(const std::string& name) /// variable wall properties for the CMM equation. /// /// Define the XML element name for variable wall parameters. -const std::string VariableWallPropsParameters::xml_element_name_ = "Variable_wall_properties"; +const std::string VariableWallPropsParameters::xml_element_name_ = + "Variable_wall_properties"; -VariableWallPropsParameters::VariableWallPropsParameters() -{ +VariableWallPropsParameters::VariableWallPropsParameters() { // A parameter that must be defined. bool required = true; mesh_name = Parameter("mesh_name", "", required); - set_parameter("Wall_properties_file_path", "", required, wall_properties_file_path); + set_parameter("Wall_properties_file_path", "", required, + wall_properties_file_path); } -void VariableWallPropsParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void VariableWallPropsParameters::set_values(tinyxml2::XMLElement *xml_elem) { using namespace tinyxml2; std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'type' from the element. - const char* sname = require_xml_attribute(xml_elem, "mesh_name"); + const char *sname = require_xml_attribute(xml_elem, "mesh_name"); mesh_name.set(std::string(sname)); auto item = xml_elem->FirstChildElement(); using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &VariableWallPropsParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&VariableWallPropsParameters::set_parameter_value, *this, _1, + _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); @@ -1353,132 +1400,158 @@ void VariableWallPropsParameters::set_values(tinyxml2::XMLElement* xml_elem) const std::string FluidViscosityParameters::xml_element_name_ = "Viscosity"; const std::string FluidViscosityParameters::CONSTANT_MODEL = "Constant"; -const std::string FluidViscosityParameters::CARREAU_YASUDA_MODEL = "Carreau-Yasuda"; +const std::string FluidViscosityParameters::CARREAU_YASUDA_MODEL = + "Carreau-Yasuda"; const std::string FluidViscosityParameters::CASSONS_MODEL = "Cassons"; const std::set FluidViscosityParameters::model_names = { - FluidViscosityParameters::CONSTANT_MODEL, - FluidViscosityParameters::CARREAU_YASUDA_MODEL, - FluidViscosityParameters::CASSONS_MODEL -}; + FluidViscosityParameters::CONSTANT_MODEL, + FluidViscosityParameters::CARREAU_YASUDA_MODEL, + FluidViscosityParameters::CASSONS_MODEL}; /// @brief Define a map to set parameters for each fluid viscosity model. -using FVpType = FluidViscosityParameters*; -using XmlType = tinyxml2::XMLElement*; -using SetFluidViscosityParamMapType = std::map>; +using FVpType = FluidViscosityParameters *; +using XmlType = tinyxml2::XMLElement *; +using SetFluidViscosityParamMapType = + std::map>; SetFluidViscosityParamMapType SetFluidViscosityModelParamsMap = { - {FluidViscosityParameters::CARREAU_YASUDA_MODEL, [](FVpType vp, XmlType params) -> void { vp->carreau_yasuda_model.set_values(params); }}, - {FluidViscosityParameters::CASSONS_MODEL, [](FVpType vp, XmlType params) -> void { vp->cassons_model.set_values(params); }}, - {FluidViscosityParameters::CONSTANT_MODEL, [](FVpType vp, XmlType params) -> void { vp->newtonian_model.set_values(params); }}, + {FluidViscosityParameters::CARREAU_YASUDA_MODEL, + [](FVpType vp, XmlType params) -> void { + vp->carreau_yasuda_model.set_values(params); + }}, + {FluidViscosityParameters::CASSONS_MODEL, + [](FVpType vp, XmlType params) -> void { + vp->cassons_model.set_values(params); + }}, + {FluidViscosityParameters::CONSTANT_MODEL, + [](FVpType vp, XmlType params) -> void { + vp->newtonian_model.set_values(params); + }}, }; /// @brief Define a map to print parameters for each fluid viscosity model. -using PrintFluidViscosityParamaMapType = std::map>; +using PrintFluidViscosityParamaMapType = + std::map>; PrintFluidViscosityParamaMapType PrintFluidViscosityModelParamsMap = { - {FluidViscosityParameters::CARREAU_YASUDA_MODEL, [](FVpType vp) -> void { vp->carreau_yasuda_model.print_parameters(); }}, - {FluidViscosityParameters::CASSONS_MODEL, [](FVpType vp) -> void { vp->cassons_model.print_parameters(); }}, - {FluidViscosityParameters::CONSTANT_MODEL, [](FVpType vp) -> void { vp->newtonian_model.print_parameters(); }}, + {FluidViscosityParameters::CARREAU_YASUDA_MODEL, + [](FVpType vp) -> void { vp->carreau_yasuda_model.print_parameters(); }}, + {FluidViscosityParameters::CASSONS_MODEL, + [](FVpType vp) -> void { vp->cassons_model.print_parameters(); }}, + {FluidViscosityParameters::CONSTANT_MODEL, + [](FVpType vp) -> void { vp->newtonian_model.print_parameters(); }}, }; -FluidViscosityNewtonianParameters::FluidViscosityNewtonianParameters() -{ +FluidViscosityNewtonianParameters::FluidViscosityNewtonianParameters() { // A parameter that must be defined. bool required = true; set_parameter("Value", 0.0, !required, constant_value); } -void FluidViscosityNewtonianParameters::print_parameters() -{ - std::cout << constant_value.name_ << ": " << constant_value.value_ << std::endl; +void FluidViscosityNewtonianParameters::print_parameters() { + std::cout << constant_value.name_ << ": " << constant_value.value_ + << std::endl; } -void FluidViscosityNewtonianParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ - std::string error_msg = "Unknown Constitutive_model type=Newtonian XML element '"; +void FluidViscosityNewtonianParameters::set_values( + tinyxml2::XMLElement *xml_elem) { + std::string error_msg = + "Unknown Constitutive_model type=Newtonian XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &FluidViscosityNewtonianParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&FluidViscosityNewtonianParameters::set_parameter_value, *this, + _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); } -FluidViscosityCarreauYasudaParameters::FluidViscosityCarreauYasudaParameters() -{ +FluidViscosityCarreauYasudaParameters::FluidViscosityCarreauYasudaParameters() { // A parameter that must be defined. bool required = true; - set_parameter("Limiting_high_shear_rate_viscosity", 0.0, !required, limiting_high_shear_rate_viscosity); - set_parameter("Limiting_low_shear_rate_viscosity", 0.0, !required, limiting_low_shear_rate_viscosity); + set_parameter("Limiting_high_shear_rate_viscosity", 0.0, !required, + limiting_high_shear_rate_viscosity); + set_parameter("Limiting_low_shear_rate_viscosity", 0.0, !required, + limiting_low_shear_rate_viscosity); set_parameter("Power_law_index", 0.0, !required, power_law_index); - set_parameter("Shear_rate_tensor_multiplier", 0.0, !required, shear_rate_tensor_multipler); - set_parameter("Shear_rate_tensor_exponent", 0.0, !required, shear_rate_tensor_exponent); -} - -void FluidViscosityCarreauYasudaParameters::print_parameters() -{ - std::cout << limiting_high_shear_rate_viscosity.name_ << ": " << limiting_high_shear_rate_viscosity.value_ << std::endl; - std::cout << limiting_low_shear_rate_viscosity.name_ << ": " << limiting_low_shear_rate_viscosity.value_ << std::endl; - std::cout << power_law_index.name_ << ": " << power_law_index.value_ << std::endl; - std::cout << shear_rate_tensor_exponent.name_ << ": " << shear_rate_tensor_exponent.value_ << std::endl; - std::cout << shear_rate_tensor_multipler.name_ << ": " << shear_rate_tensor_multipler.value_ << std::endl; -} - -void FluidViscosityCarreauYasudaParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ - std::string error_msg = "Unknown Constitutive_model type=CarreauYasuda XML element '"; + set_parameter("Shear_rate_tensor_multiplier", 0.0, !required, + shear_rate_tensor_multipler); + set_parameter("Shear_rate_tensor_exponent", 0.0, !required, + shear_rate_tensor_exponent); +} + +void FluidViscosityCarreauYasudaParameters::print_parameters() { + std::cout << limiting_high_shear_rate_viscosity.name_ << ": " + << limiting_high_shear_rate_viscosity.value_ << std::endl; + std::cout << limiting_low_shear_rate_viscosity.name_ << ": " + << limiting_low_shear_rate_viscosity.value_ << std::endl; + std::cout << power_law_index.name_ << ": " << power_law_index.value_ + << std::endl; + std::cout << shear_rate_tensor_exponent.name_ << ": " + << shear_rate_tensor_exponent.value_ << std::endl; + std::cout << shear_rate_tensor_multipler.name_ << ": " + << shear_rate_tensor_multipler.value_ << std::endl; +} + +void FluidViscosityCarreauYasudaParameters::set_values( + tinyxml2::XMLElement *xml_elem) { + std::string error_msg = + "Unknown Constitutive_model type=CarreauYasuda XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &FluidViscosityCarreauYasudaParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&FluidViscosityCarreauYasudaParameters::set_parameter_value, + *this, _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); } -FluidViscosityCassonsParameters::FluidViscosityCassonsParameters() -{ +FluidViscosityCassonsParameters::FluidViscosityCassonsParameters() { // A parameter that must be defined. bool required = true; - set_parameter("Asymptotic_viscosity_parameter", 0.0, !required, asymptotic_viscosity); - set_parameter("Low_shear_rate_threshold", 0.0, !required, low_shear_rate_threshold); + set_parameter("Asymptotic_viscosity_parameter", 0.0, !required, + asymptotic_viscosity); + set_parameter("Low_shear_rate_threshold", 0.0, !required, + low_shear_rate_threshold); set_parameter("Yield_stress_parameter", 0.0, !required, yield_stress); } -void FluidViscosityCassonsParameters::print_parameters() -{ - std::cout << asymptotic_viscosity.name_ << ": " << asymptotic_viscosity.value_ << std::endl; - std::cout << low_shear_rate_threshold.name_ << ": " << low_shear_rate_threshold.value_ << std::endl; +void FluidViscosityCassonsParameters::print_parameters() { + std::cout << asymptotic_viscosity.name_ << ": " << asymptotic_viscosity.value_ + << std::endl; + std::cout << low_shear_rate_threshold.name_ << ": " + << low_shear_rate_threshold.value_ << std::endl; std::cout << yield_stress.name_ << ": " << yield_stress.value_ << std::endl; } -void FluidViscosityCassonsParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ - std::string error_msg = "Unknown Constitutive_model type=Cassons XML element '"; +void FluidViscosityCassonsParameters::set_values( + tinyxml2::XMLElement *xml_elem) { + std::string error_msg = + "Unknown Constitutive_model type=Cassons XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &FluidViscosityCassonsParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&FluidViscosityCassonsParameters::set_parameter_value, *this, + _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); } -FluidViscosityParameters::FluidViscosityParameters() -{ +FluidViscosityParameters::FluidViscosityParameters() { // A parameter that must be defined. bool required = true; - // Stores model from the XML element + // Stores model from the XML element model = Parameter("model", "", required); } -void FluidViscosityParameters::print_parameters() -{ +void FluidViscosityParameters::print_parameters() { std::cout << std::endl; std::cout << "--------------------" << std::endl; std::cout << "Viscosity Parameters" << std::endl; @@ -1489,17 +1562,17 @@ void FluidViscosityParameters::print_parameters() PrintFluidViscosityModelParamsMap[model.value_](this); } -void FluidViscosityParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void FluidViscosityParameters::set_values(tinyxml2::XMLElement *xml_elem) { using namespace tinyxml2; - const char* smodel = require_xml_attribute(xml_elem, "model"); + const char *smodel = require_xml_attribute(xml_elem, "model"); model.set(std::string(smodel)); // Check fluid_viscosity model name. - if (model_names.count(model.value()) == 0) { - svmp::raise("Unknown fluid viscosity model '" + model.value() + - "' in '" + xml_elem->Name() + "'."); + if (model_names.count(model.value()) == 0) { + svmp::raise("Unknown fluid viscosity model '" + + model.value() + "' in '" + + xml_elem->Name() + "'."); } // Set parameters for the given fluid_viscosity model. @@ -1519,86 +1592,95 @@ const std::string SolidViscosityParameters::NEWTONIAN_MODEL = "Newtonian"; const std::string SolidViscosityParameters::POTENTIAL_MODEL = "Potential"; const std::set SolidViscosityParameters::model_names = { - SolidViscosityParameters::NEWTONIAN_MODEL, - SolidViscosityParameters::POTENTIAL_MODEL -}; + SolidViscosityParameters::NEWTONIAN_MODEL, + SolidViscosityParameters::POTENTIAL_MODEL}; /// @brief Define a map to set parameters for each solid viscosity model. -using SVpType = SolidViscosityParameters*; -using XmlType = tinyxml2::XMLElement*; -using SetSolidViscosityParamMapType = std::map>; +using SVpType = SolidViscosityParameters *; +using XmlType = tinyxml2::XMLElement *; +using SetSolidViscosityParamMapType = + std::map>; SetSolidViscosityParamMapType SetSolidViscosityModelParamsMap = { - {SolidViscosityParameters::NEWTONIAN_MODEL, [](SVpType vp, XmlType params) -> void { vp->newtonian_model.set_values(params); }}, - {SolidViscosityParameters::POTENTIAL_MODEL, [](SVpType vp, XmlType params) -> void { vp->potential_model.set_values(params); }}, + {SolidViscosityParameters::NEWTONIAN_MODEL, + [](SVpType vp, XmlType params) -> void { + vp->newtonian_model.set_values(params); + }}, + {SolidViscosityParameters::POTENTIAL_MODEL, + [](SVpType vp, XmlType params) -> void { + vp->potential_model.set_values(params); + }}, }; /// @brief Define a map to print parameters for each solid viscosity model. -using PrintSolidViscosityParamaMapType = std::map>; +using PrintSolidViscosityParamaMapType = + std::map>; PrintSolidViscosityParamaMapType PrintSolidViscosityModelParamsMap = { - {SolidViscosityParameters::NEWTONIAN_MODEL, [](SVpType vp) -> void { vp->newtonian_model.print_parameters(); }}, - {SolidViscosityParameters::POTENTIAL_MODEL, [](SVpType vp) -> void { vp->potential_model.print_parameters(); }}, + {SolidViscosityParameters::NEWTONIAN_MODEL, + [](SVpType vp) -> void { vp->newtonian_model.print_parameters(); }}, + {SolidViscosityParameters::POTENTIAL_MODEL, + [](SVpType vp) -> void { vp->potential_model.print_parameters(); }}, }; -SolidViscosityNewtonianParameters::SolidViscosityNewtonianParameters() -{ +SolidViscosityNewtonianParameters::SolidViscosityNewtonianParameters() { // A parameter that must be defined. bool required = true; set_parameter("Value", 0.0, !required, constant_value); } -void SolidViscosityNewtonianParameters::print_parameters() -{ - std::cout << constant_value.name_ << ": " << constant_value.value_ << std::endl; +void SolidViscosityNewtonianParameters::print_parameters() { + std::cout << constant_value.name_ << ": " << constant_value.value_ + << std::endl; } -void SolidViscosityNewtonianParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ - std::string error_msg = "Unknown Constitutive_model type=Newtonian XML element '"; +void SolidViscosityNewtonianParameters::set_values( + tinyxml2::XMLElement *xml_elem) { + std::string error_msg = + "Unknown Constitutive_model type=Newtonian XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &SolidViscosityNewtonianParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&SolidViscosityNewtonianParameters::set_parameter_value, *this, + _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); } -SolidViscosityPotentialParameters::SolidViscosityPotentialParameters() -{ +SolidViscosityPotentialParameters::SolidViscosityPotentialParameters() { // A parameter that must be defined. bool required = true; set_parameter("Value", 0.0, !required, constant_value); } -void SolidViscosityPotentialParameters::print_parameters() -{ - std::cout << constant_value.name_ << ": " << constant_value.value_ << std::endl; +void SolidViscosityPotentialParameters::print_parameters() { + std::cout << constant_value.name_ << ": " << constant_value.value_ + << std::endl; } -void SolidViscosityPotentialParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ - std::string error_msg = "Unknown Constitutive_model type=Potential XML element '"; +void SolidViscosityPotentialParameters::set_values( + tinyxml2::XMLElement *xml_elem) { + std::string error_msg = + "Unknown Constitutive_model type=Potential XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &SolidViscosityPotentialParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&SolidViscosityPotentialParameters::set_parameter_value, *this, + _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); } -SolidViscosityParameters::SolidViscosityParameters() -{ +SolidViscosityParameters::SolidViscosityParameters() { // A parameter that must be defined. bool required = true; - // Stores model from the XML element + // Stores model from the XML element model = Parameter("model", "", required); } -void SolidViscosityParameters::print_parameters() -{ +void SolidViscosityParameters::print_parameters() { std::cout << std::endl; std::cout << "--------------------" << std::endl; std::cout << "Viscosity Parameters" << std::endl; @@ -1609,17 +1691,17 @@ void SolidViscosityParameters::print_parameters() PrintSolidViscosityModelParamsMap[model.value_](this); } -void SolidViscosityParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void SolidViscosityParameters::set_values(tinyxml2::XMLElement *xml_elem) { using namespace tinyxml2; - const char* smodel = require_xml_attribute(xml_elem, "model"); + const char *smodel = require_xml_attribute(xml_elem, "model"); model.set(std::string(smodel)); // Check solid viscosity model name. - if (model_names.count(model.value()) == 0) { - svmp::raise("Unknown solid viscosity model '" + model.value() + - "' in '" + xml_elem->Name() + "'."); + if (model_names.count(model.value()) == 0) { + svmp::raise("Unknown solid viscosity model '" + + model.value() + "' in '" + + xml_elem->Name() + "'."); } // Set parameters for the given solid viscosity model. @@ -1654,7 +1736,7 @@ void IonicInitialStateParameters::set_values( const tinyxml2::XMLElement *xml_elem) { if (xml_elem->Name() != xml_element_name) { svmp::raise("Unknown " + xml_element_name + - " XML element '"); + " XML element '"); } const std::string error_msg_prefix = @@ -1749,67 +1831,213 @@ void IonicModelParameters::set_values(const tinyxml2::XMLElement *xml_elem) { // The initial values of both state and gating variables must be set. if (initial_X_parameters.required && !initial_X_parameters.defined()) { - svmp::raise( - xml_element_name + " requires an '" + - initial_X_parameters.xml_element_name + - "' XML section."); + svmp::raise(xml_element_name + " requires an '" + + initial_X_parameters.xml_element_name + + "' XML section."); } if (initial_Xg_parameters.required && !initial_Xg_parameters.defined()) { - svmp::raise( - xml_element_name + " requires an '" + - initial_Xg_parameters.xml_element_name + - "' XML section."); + svmp::raise(xml_element_name + " requires an '" + + initial_Xg_parameters.xml_element_name + + "' XML section."); } check_required(); } +////////////////////////////////////////////////////////// +// ActiveStressModelParameters // +////////////////////////////////////////////////////////// + +ActiveStressModelParameters::ActiveStressModelParameters( + const std::string &xml_element_name_) + : xml_element_name(xml_element_name_) { + set_xml_element_name(xml_element_name_); +} + +void ActiveStressModelParameters::print_parameters() const { + if (value_set) { + std::cout << "\n" + << xml_element_name << "\n" + << "---------------------------------\n"; + + if (!double_parameters.empty()) { + std::cout << "Double model parameters:" << std::endl; + for (const auto &[name, param] : double_parameters) { + std::cout << " " << name << ": " << param.value() << std::endl; + } + } + + if (!string_parameters.empty()) { + std::cout << "String model parameters:" << std::endl; + for (const auto &[name, param] : string_parameters) { + std::cout << " " << name << ": " << param.value() << std::endl; + } + } + } +} + +void ActiveStressModelParameters::set_values( + const tinyxml2::XMLElement *xml_elem) { + using namespace tinyxml2; + + for (const XMLElement *item = xml_elem->FirstChildElement(); item != nullptr; + item = item->NextSiblingElement()) { + const std::string name = item->Value(); + + const std::string text = item->GetText() ? item->GetText() : ""; + set_parameter_value(name, text); + value_set = true; + } + + check_required(); +} + +////////////////////////////////////////////////////////// +// ActiveStressParameters // +////////////////////////////////////////////////////////// + +const std::string ActiveStressParameters::xml_element_name = "Active_stress"; + +ActiveStressParameters::ActiveStressParameters() { + model_name = Parameter("Model", "", true); + + set_parameter("Model", "", /* required = */ true, model_name); + + ActiveStressFactory::visit( + [this](const std::string &name, const ActiveStress &model) { + active_stress_models.emplace(name, model.get_parameters()); + }); +} + +void ActiveStressParameters::print_parameters() const { + if (value_set) { + std::cout << "\n" + << xml_element_name << "\n" + << "---------------------------------\n"; + + for (const auto &[name, params] : active_stress_models) { + params->print_parameters(); + } + + directional_distribution.print_parameters(); + } +} + +void ActiveStressParameters::set_values(const tinyxml2::XMLElement *xml_elem) { + using namespace tinyxml2; + + for (const XMLElement *item = xml_elem->FirstChildElement(); item != nullptr; + item = item->NextSiblingElement()) { + const std::string name = item->Value(); + + if (active_stress_models.count(name) > 0) { + active_stress_models.at(name)->set_values(item); + } else if (name == DirectionalDistributionParameters::xml_element_name_) { + directional_distribution.set_values(item); + directional_distribution.validate(); + } else if (item->GetText() != nullptr) { + auto value = item->GetText(); + try { + set_parameter_value(name, value); + } catch (const std::bad_function_call &exception) { + svmp::raise("Unknown " + xml_element_name + + " XML element '" + name + "'."); + } + } else { + svmp::raise("Unknown " + xml_element_name + + " XML element '" + name + "'."); + } + } + + check_required(); + + value_set = true; +} + +std::string ActiveStressParameters::get_model_name() const { + if (!model_name.defined()) { + svmp::raise( + "Active stress model name is not defined."); + } + + return model_name.value(); +} + +double ActiveStressParameters::get_eta_f() const { + return directional_distribution.fiber_direction.value(); +} + +double ActiveStressParameters::get_eta_s() const { + return directional_distribution.sheet_direction.value(); +} + +double ActiveStressParameters::get_eta_n() const { + return directional_distribution.sheet_normal_direction.value(); +} + +const ActiveStressModelParameters & +ActiveStressParameters::get_parameters(const std::string &model_name) const { + if (active_stress_models.count(model_name) == 0) { + svmp::raise( + "Active stress model '" + model_name + "' is not defined."); + } + + return *active_stress_models.at(model_name); +} + ////////////////////////////////////////////////////////// // DomainParameters // ////////////////////////////////////////////////////////// -// Process parameters for the XML // 'Domain' element to +// Process parameters for the XML // 'Domain' element to // specify properties for solving equations. /// @brief Define the XML element name for domain parameters. const std::string DomainParameters::xml_element_name_ = "Domain"; -DomainParameters::DomainParameters() -{ +DomainParameters::DomainParameters() { // A parameter that must be defined. bool required = true; - // Set value from element. + // Set value from element. id = Parameter("id", "", required); set_parameter("Absolute_tolerance", 1e-6, !required, absolute_tolerance); - set_parameter("Anisotropic_conductivity", {}, !required, anisotropic_conductivity); - set_parameter("Backflow_stabilization_coefficient", 0.2, !required, backflow_stabilization_coefficient); + set_parameter("Anisotropic_conductivity", {}, !required, + anisotropic_conductivity); + set_parameter("Backflow_stabilization_coefficient", 0.2, !required, + backflow_stabilization_coefficient); set_parameter("Conductivity", 0.0, !required, conductivity); - //set_parameter("Constitutive_model", "", !required, constitutive_model); - set_parameter("Continuity_stabilization_coefficient", 0.0, !required, continuity_stabilization_coefficient); + // set_parameter("Constitutive_model", "", !required, constitutive_model); + set_parameter("Continuity_stabilization_coefficient", 0.0, !required, + continuity_stabilization_coefficient); set_parameter("Density", 0.5, !required, density); - set_parameter("Dilational_penalty_model", "", !required, dilational_penalty_model); + set_parameter("Dilational_penalty_model", "", !required, + dilational_penalty_model); set_parameter("Elasticity_modulus", 1.0e7, !required, elasticity_modulus); - set_parameter("Electrophysiology_model", "", !required, electrophysiology_model); + set_parameter("Electrophysiology_model", "", !required, + electrophysiology_model); set_parameter("Equation", "", !required, equation); - set_parameter("Feedback_parameter_for_stretch_activated_currents", 0.5, !required, feedback_parameter_for_stretch_activated_currents); + set_parameter("Feedback_parameter_for_stretch_activated_currents", 0.5, + !required, feedback_parameter_for_stretch_activated_currents); set_parameter("Fluid_density", 0.5, !required, fluid_density); set_parameter("Force_x", 0.0, !required, force_x); set_parameter("Force_y", 0.0, !required, force_y); set_parameter("Force_z", 0.0, !required, force_z); set_parameter("Include_xml", "", !required, include_xml); - set_parameter("Isotropic_conductivity", 0.0, !required, isotropic_conductivity); + set_parameter("Isotropic_conductivity", 0.0, !required, + isotropic_conductivity); set_parameter("Mass_damping", 0.0, !required, mass_damping); set_parameter("Maximum_iterations", 5, !required, maximum_iterations); - set_parameter("Momentum_stabilization_coefficient", 0.0, !required, momentum_stabilization_coefficient); + set_parameter("Momentum_stabilization_coefficient", 0.0, !required, + momentum_stabilization_coefficient); set_parameter("Myocardial_zone", "epicardium", !required, myocardial_zone); set_parameter("ODE_solver", "euler", !required, ode_solver); @@ -1821,9 +2049,11 @@ DomainParameters::DomainParameters() set_parameter("Shell_thickness", 0.0, !required, shell_thickness); set_parameter("Solid_density", 0.5, !required, solid_density); set_parameter("Source_term", 0.0, !required, source_term); - set_parameter("Time_step_for_integration", 0.0, !required, time_step_for_integration); + set_parameter("Time_step_for_integration", 0.0, !required, + time_step_for_integration); - set_parameter("Inverse_darcy_permeability", 0.0, !required, inverse_darcy_permeability); + set_parameter("Inverse_darcy_permeability", 0.0, !required, + inverse_darcy_permeability); // Ionic model parameters. IonicModelFactory::visit( @@ -1832,8 +2062,7 @@ DomainParameters::DomainParameters() }); } -void DomainParameters::print_parameters() -{ +void DomainParameters::print_parameters() { std::cout << std::endl; std::cout << "-----------------" << std::endl; std::cout << "Domain Parameters" << std::endl; @@ -1841,26 +2070,25 @@ void DomainParameters::print_parameters() std::cout << id.name() << ": " << id.value() << std::endl; auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } constitutive_model.print_parameters(); - fiber_reinforcement_stress.print_parameters(); - - for (const auto& stim : stimuli) { + for (const auto &stim : stimuli) { stim->print_parameters(); } - for (const auto &[cepType, params] : ionic_models) { + for (const auto &[name, params] : ionic_models) { params->print_parameters(); } + active_stress.print_parameters(); + fluid_viscosity.print_parameters(); solid_viscosity.print_parameters(); - } //------------ @@ -1868,23 +2096,24 @@ void DomainParameters::print_parameters() //------------ // Set the domain parameter values from the XML. // -// If 'from_external_xml' is true then parameter values are read from an external xml file -// using the 'Include_xml' parameter. +// If 'from_external_xml' is true then parameter values are read from an +// external xml file using the 'Include_xml' parameter. // -void DomainParameters::set_values(tinyxml2::XMLElement* domain_elem, bool from_external_xml) -{ +void DomainParameters::set_values(tinyxml2::XMLElement *domain_elem, + bool from_external_xml) { using namespace tinyxml2; - std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; + std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; - // If not reading from an external xml file then get the 'id' attrribute. + // If not reading from an external xml file then get the 'id' + // attrribute. // if (!from_external_xml) { - const char* sid = require_xml_attribute(domain_elem, "id"); + const char *sid = require_xml_attribute(domain_elem, "id"); id.set(std::string(sid)); } auto item = domain_elem->FirstChildElement(); - + // Parse XML elements for varius sub-elements (e.g. Viscosity). // while (item != nullptr) { @@ -1896,11 +2125,6 @@ void DomainParameters::set_values(tinyxml2::XMLElement* domain_elem, bool from_e item_found = true; } - if (name == FiberReinforcementStressParameters::xml_element_name_) { - fiber_reinforcement_stress.set_values(item); - item_found = true; - } - if (name == StimulusParameters::xml_element_name_) { stimuli.emplace_back(std::make_unique()); stimuli.back()->set_values(item); @@ -1913,11 +2137,17 @@ void DomainParameters::set_values(tinyxml2::XMLElement* domain_elem, bool from_e item_found = true; } + if (name == ActiveStressParameters::xml_element_name) { + active_stress.set_values(item); + item_found = true; + } + if (name == FluidViscosityParameters::xml_element_name_ || name == SolidViscosityParameters::xml_element_name_) { - auto eq_type = require_map_value( - consts::equation_name_to_type, equation.value(), "Unknown equation type '" + equation.value() + - "' while parsing viscosity model."); + auto eq_type = + require_map_value(consts::equation_name_to_type, equation.value(), + "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) { @@ -1929,13 +2159,14 @@ void DomainParameters::set_values(tinyxml2::XMLElement* domain_elem, bool from_e item_found = true; } else { svmp::raise( - "Viscosity model not supported for equation '" + - equation.value() + "'."); + "Viscosity model not supported for equation '" + equation.value() + + "'."); } } if (name == include_xml.name()) { - auto value = require_xml_text(item, "Domain Include_xml requires a file name."); + auto value = + require_xml_text(item, "Domain Include_xml requires a file name."); IncludeParametersFile include_parameters(value); set_values(include_parameters.root_element, true); @@ -1982,205 +2213,140 @@ void DomainParameters::set_values(tinyxml2::XMLElement* domain_elem, bool from_e ////////////////////////////////////////////////////////// /// @brief Define the XML element name for directional distribution parameters. -const std::string DirectionalDistributionParameters::xml_element_name_ = "Directional_distribution"; +const std::string DirectionalDistributionParameters::xml_element_name_ = + "Directional_distribution"; -DirectionalDistributionParameters::DirectionalDistributionParameters() -{ +DirectionalDistributionParameters::DirectionalDistributionParameters() { bool required = false; - + // Default: all stress in fiber direction set_parameter("Fiber_direction", 1.0, required, fiber_direction); set_parameter("Sheet_direction", 0.0, required, sheet_direction); - set_parameter("Sheet_normal_direction", 0.0, required, sheet_normal_direction); + set_parameter("Sheet_normal_direction", 0.0, required, + sheet_normal_direction); } -void DirectionalDistributionParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void DirectionalDistributionParameters::set_values( + const tinyxml2::XMLElement *xml_elem) { using namespace tinyxml2; std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind(&DirectionalDistributionParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&DirectionalDistributionParameters::set_parameter_value, *this, + _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); value_set = true; } -void DirectionalDistributionParameters::validate() const -{ +void DirectionalDistributionParameters::validate() const { if (!value_set) { - return; // No validation needed if not set (will use defaults) + return; // No validation needed if not set (will use defaults) } - + // Check how many parameters are defined bool fiber_defined = fiber_direction.defined(); bool sheet_defined = sheet_direction.defined(); bool normal_defined = sheet_normal_direction.defined(); - + int num_defined = fiber_defined + sheet_defined + normal_defined; - + // Empty block is invalid - if block exists, must specify all three if (num_defined == 0) { - svmp::raise("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."); + svmp::raise( + "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."); } - + // Partial specification is invalid if (num_defined < 3) { - std::string msg = "Directional_distribution requires all three directions to be specified. Found: "; - if (fiber_defined) msg += "Fiber_direction "; - if (sheet_defined) msg += "Sheet_direction "; - if (normal_defined) msg += "Sheet_normal_direction "; + std::string msg = "Directional_distribution requires all three directions " + "to be specified. Found: "; + if (fiber_defined) + msg += "Fiber_direction "; + if (sheet_defined) + msg += "Sheet_direction "; + if (normal_defined) + msg += "Sheet_normal_direction "; msg += "\nMissing: "; - if (!fiber_defined) msg += "Fiber_direction "; - if (!sheet_defined) msg += "Sheet_direction "; - if (!normal_defined) msg += "Sheet_normal_direction "; + if (!fiber_defined) + msg += "Fiber_direction "; + if (!sheet_defined) + msg += "Sheet_direction "; + if (!normal_defined) + msg += "Sheet_normal_direction "; svmp::raise(msg); } - + // All three are specified, validate their values double eta_f = fiber_direction.value(); double eta_s = sheet_direction.value(); double eta_n = sheet_normal_direction.value(); - + // Validate that eta_f + eta_s + eta_n = 1.0 double eta_sum = eta_f + eta_s + eta_n; const double tol = 1.0e-10; if (std::abs(eta_sum - 1.0) > tol) { - svmp::raise("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) + - ", sum=" + std::to_string(eta_sum)); + svmp::raise( + "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) + + ", sum=" + std::to_string(eta_sum)); } - + // Validate that each eta is non-negative if (eta_f < 0.0 || eta_s < 0.0 || eta_n < 0.0) { - svmp::raise("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)); + svmp::raise( + "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)); } } -void DirectionalDistributionParameters::print_parameters() -{ +void DirectionalDistributionParameters::print_parameters() const { if (!value_set) { return; } std::cout << " Directional Distribution:" << std::endl; std::cout << " Fiber_direction: " << fiber_direction.value() << std::endl; std::cout << " Sheet_direction: " << sheet_direction.value() << std::endl; - std::cout << " Sheet_normal_direction: " << sheet_normal_direction.value() << std::endl; -} - -////////////////////////////////////////////////////////// -// FiberReinforcementStressParameters // -////////////////////////////////////////////////////////// - -// Process parameters for the fiber reinforcement stress 'Fiber_reinforcement_stress` -// XML element. - -/// @brief Define the XML element name for fiber reinforcement stress parameters. -const std::string FiberReinforcementStressParameters::xml_element_name_ = "Fiber_reinforcement_stress"; - -FiberReinforcementStressParameters::FiberReinforcementStressParameters() -{ - // A parameter that must be defined. - bool required = true; - - // Define attributes. - type = Parameter("type", "", required); - - set_parameter("Ramp_function", false, !required, ramp_function); - set_parameter("Temporal_values_file_path", "", !required, temporal_values_file_path); - set_parameter("Value", 0.0, !required, value); -} - -void FiberReinforcementStressParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ - using namespace tinyxml2; - std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; - - // Get the 'type' from the element attribute. - const char* stype = require_xml_attribute(xml_elem, "type"); - type.set(std::string(stype)); - auto item = xml_elem->FirstChildElement(); - - while (item != nullptr) { - std::string name = item->Value(); - - if (name == DirectionalDistributionParameters::xml_element_name_) { - directional_distribution.set_values(item); - - } else if (item->GetText() != nullptr) { - auto value = item->GetText(); - try { - set_parameter_value(name, value); - } catch (const std::bad_function_call& exception) { - svmp::raise(error_msg + name + "'."); - } - } else { - svmp::raise(error_msg + name + "'."); - } - - item = item->NextSiblingElement(); - } - - value_set = true; -} - -void FiberReinforcementStressParameters::print_parameters() -{ - if (!value_set) { - return; - } - std::cout << std::endl; - std::cout << "-----------------------------------" << std::endl; - std::cout << "FiberReinforcementStress Parameters" << std::endl; - std::cout << "-----------------------------------" << std::endl; - std::cout << type.name() << ": " << type.value() << std::endl; - - auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { - std::cout << key << ": " << value << std::endl; - } - - // Print directional distribution if defined - directional_distribution.print_parameters(); + std::cout << " Sheet_normal_direction: " << sheet_normal_direction.value() + << std::endl; } ////////////////////////////////////////////////////////// // StimulusParameters // ////////////////////////////////////////////////////////// -// The StimulusParameters class stores parameters for -// 'Stimulus' XML element used to parameters for +// The StimulusParameters class stores parameters for +// 'Stimulus' XML element used to parameters for // pacemaker cells. const std::string StimulusBoxParameters::xml_element_name_ = "Box"; -StimulusBoxParameters::StimulusBoxParameters() -{ +StimulusBoxParameters::StimulusBoxParameters() { bool required = true; set_parameter("Minimum", {}, !required, minimum); set_parameter("Maximum", {}, !required, maximum); } -void StimulusBoxParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void StimulusBoxParameters::set_values(tinyxml2::XMLElement *xml_elem) { std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = + std::function ftpr = std::bind(&StimulusBoxParameters::set_parameter_value, *this, _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); @@ -2190,22 +2356,20 @@ void StimulusBoxParameters::set_values(tinyxml2::XMLElement* xml_elem) const std::string StimulusSphereParameters::xml_element_name_ = "Sphere"; -StimulusSphereParameters::StimulusSphereParameters() -{ +StimulusSphereParameters::StimulusSphereParameters() { bool required = true; set_parameter("Center", {}, !required, center); set_parameter("Radius", 0.0, !required, radius); } -void StimulusSphereParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void StimulusSphereParameters::set_values(tinyxml2::XMLElement *xml_elem) { std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = + std::function ftpr = std::bind(&StimulusSphereParameters::set_parameter_value, *this, _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); @@ -2213,13 +2377,15 @@ void StimulusSphereParameters::set_values(tinyxml2::XMLElement* xml_elem) value_set = true; } -const std::string StimulusSpatialBoundsParameters::xml_element_name_ = "Spatial_bounds"; +const std::string StimulusSpatialBoundsParameters::xml_element_name_ = + "Spatial_bounds"; -void StimulusSpatialBoundsParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void StimulusSpatialBoundsParameters::set_values( + tinyxml2::XMLElement *xml_elem) { std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; - for (auto item = xml_elem->FirstChildElement(); item != nullptr; item = item->NextSiblingElement()) { + for (auto item = xml_elem->FirstChildElement(); item != nullptr; + item = item->NextSiblingElement()) { auto name = std::string(item->Value()); if (name == StimulusBoxParameters::xml_element_name_) { @@ -2236,8 +2402,7 @@ void StimulusSpatialBoundsParameters::set_values(tinyxml2::XMLElement* xml_elem) const std::string StimulusParameters::xml_element_name_ = "Stimulus"; -StimulusParameters::StimulusParameters() -{ +StimulusParameters::StimulusParameters() { // A parameter that must be defined. bool required = true; @@ -2250,21 +2415,21 @@ StimulusParameters::StimulusParameters() set_parameter("Start_time", 0.0, !required, start_time); } -void StimulusParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +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 = require_xml_attribute(xml_elem, "type"); + const char *stype = require_xml_attribute(xml_elem, "type"); type.set(std::string(stype)); using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = + std::function ftpr = std::bind(&StimulusParameters::set_parameter_value, *this, _1, _2); - std::set sub_sections = {StimulusSpatialBoundsParameters::xml_element_name_}; + std::set sub_sections = { + StimulusSpatialBoundsParameters::xml_element_name_}; xml_util_set_parameters(ftpr, xml_elem, error_msg, sub_sections); auto item = xml_elem->FirstChildElement(); @@ -2282,8 +2447,7 @@ void StimulusParameters::set_values(tinyxml2::XMLElement* xml_elem) value_set = true; } -void StimulusParameters::print_parameters() -{ +void StimulusParameters::print_parameters() { if (!value_set) { return; } @@ -2294,7 +2458,7 @@ void StimulusParameters::print_parameters() std::cout << type.name() << ": " << type.value() << std::endl; auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } } @@ -2306,8 +2470,7 @@ void StimulusParameters::print_parameters() /// @brief Define the XML element name for ECG leads parameters. const std::string ECGLeadsParameters::xml_element_name_ = "ECGLeads"; -ECGLeadsParameters::ECGLeadsParameters() -{ +ECGLeadsParameters::ECGLeadsParameters() { // A parameter that must be defined. bool required = true; @@ -2317,33 +2480,31 @@ ECGLeadsParameters::ECGLeadsParameters() set_parameter("Z_coords_file_path", "", !required, z_coords_file_path); } -void ECGLeadsParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void ECGLeadsParameters::set_values(tinyxml2::XMLElement *xml_elem) { std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &ECGLeadsParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&ECGLeadsParameters::set_parameter_value, *this, _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); value_set = true; } -void ECGLeadsParameters::print_parameters() -{ +void ECGLeadsParameters::print_parameters() { if (!value_set) { return; } std::cout << std::endl; - std::cout << "--------------------" << std::endl; + std::cout << "--------------------" << std::endl; std::cout << "ECG Leads Parameters" << std::endl; - std::cout << "--------------------" << std::endl; + std::cout << "--------------------" << std::endl; auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } } @@ -2358,8 +2519,7 @@ void ECGLeadsParameters::print_parameters() /// Define the XML element name for contact parameters. const std::string ContactParameters::xml_element_name_ = "Contact"; -ContactParameters::ContactParameters() -{ +ContactParameters::ContactParameters() { set_xml_element_name(xml_element_name_); // A parameter that must be defined. @@ -2370,14 +2530,15 @@ ContactParameters::ContactParameters() // Define contact parameters. // - set_parameter("Closest_gap_to_activate_penalty", 1.0, !required, closest_gap_to_activate_penalty); + set_parameter("Closest_gap_to_activate_penalty", 1.0, !required, + closest_gap_to_activate_penalty); set_parameter("Desired_separation", 0.05, !required, desired_separation); - set_parameter("Min_norm_of_face_normals", 0.7, !required, min_norm_of_face_normals); + set_parameter("Min_norm_of_face_normals", 0.7, !required, + min_norm_of_face_normals); set_parameter("Penalty_constant", 1e5, !required, penalty_constant); } -void ContactParameters::print_parameters() -{ +void ContactParameters::print_parameters() { std::cout << std::endl; std::cout << "-------------------" << std::endl; std::cout << "Contact Parameters" << std::endl; @@ -2385,25 +2546,24 @@ void ContactParameters::print_parameters() std::cout << model.name() << ": " << model.value() << std::endl; auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } } -void ContactParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void ContactParameters::set_values(tinyxml2::XMLElement *xml_elem) { using namespace tinyxml2; std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'type' from the element. - const char* mname = require_xml_attribute(xml_elem, "model"); + const char *mname = require_xml_attribute(xml_elem, "model"); model.set(std::string(mname)); using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &ProjectionParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&ProjectionParameters::set_parameter_value, *this, _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); } @@ -2412,14 +2572,13 @@ void ContactParameters::set_values(tinyxml2::XMLElement* xml_elem) // EquationParameters // ////////////////////////////////////////////////////////// -// Process parameters for the 'Add_equation' XML element +// Process parameters for the 'Add_equation' XML element // used to specify an equation to be solved (e.g. fluid). /// @brief Define the XML element name for equation parameters. const std::string EquationParameters::xml_element_name_ = "Add_equation"; -EquationParameters::EquationParameters() -{ +EquationParameters::EquationParameters() { set_xml_element_name(xml_element_name_); // A parameter that must be defined. @@ -2434,7 +2593,8 @@ EquationParameters::EquationParameters() set_parameter(IncludeParametersFile::NAME, "", !required, include_xml); set_parameter("Initialize", "", !required, initialize); - set_parameter("Initialize_RCR_from_flow", false, !required, initialize_rcr_from_flow); + set_parameter("Initialize_RCR_from_flow", false, !required, + initialize_rcr_from_flow); set_parameter("Max_iterations", 1, !required, max_iterations); set_parameter("Min_iterations", 1, !required, min_iterations); @@ -2442,13 +2602,13 @@ EquationParameters::EquationParameters() set_parameter("Prestress", false, !required, prestress); set_parameter("Tolerance", 0.5, !required, tolerance); - set_parameter("Use_taylor_hood_type_basis", false, !required, use_taylor_hood_type_basis); - set_parameter("Explicit_geometric_coupling", false, !required, explicit_geometric_coupling); - + set_parameter("Use_taylor_hood_type_basis", false, !required, + use_taylor_hood_type_basis); + set_parameter("Explicit_geometric_coupling", false, !required, + explicit_geometric_coupling); } -void EquationParameters::print_parameters() -{ +void EquationParameters::print_parameters() { std::cout << std::endl; std::cout << "-------------------" << std::endl; std::cout << "Equation Parameters" << std::endl; @@ -2456,49 +2616,46 @@ void EquationParameters::print_parameters() std::cout << type.name() << ": " << type.value() << std::endl; auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } if (domains.size() == 0) { default_domain->print_parameters(); } else { - for (auto& domain : domains) { + for (auto &domain : domains) { domain->print_parameters(); } - } + } - //stimulus.print_parameters(); + // stimulus.print_parameters(); - for (auto& output : outputs) { + for (auto &output : outputs) { output->print_parameters(); } linear_solver.print_parameters(); - for (auto& bc : boundary_conditions) { + for (auto &bc : boundary_conditions) { bc->print_parameters(); } - for (auto& bf : body_forces) { + for (auto &bf : body_forces) { bf->print_parameters(); } ecg_leads.print_parameters(); } -void EquationParameters::set_values(tinyxml2::XMLElement* eq_elem, DomainParameters* domain) -{ - static std::set viscosity_names { - FluidViscosityParameters::xml_element_name_, - SolidViscosityParameters::xml_element_name_ - }; +void EquationParameters::set_values(tinyxml2::XMLElement *eq_elem, + DomainParameters *domain) { + static std::set viscosity_names{ + FluidViscosityParameters::xml_element_name_, + SolidViscosityParameters::xml_element_name_}; - static std::set fluid_eqs { - consts::EquationType::phys_fluid, - consts::EquationType::phys_CMM, - consts::EquationType::phys_stokes - }; + static std::set fluid_eqs{ + consts::EquationType::phys_fluid, consts::EquationType::phys_CMM, + consts::EquationType::phys_stokes}; if (domain == nullptr) { default_domain = new DomainParameters(); @@ -2542,8 +2699,10 @@ void EquationParameters::set_values(tinyxml2::XMLElement* eq_elem, DomainParamet domain_params->set_values(item); domains.push_back(domain_params); - } else if (name == FiberReinforcementStressParameters::xml_element_name_) { - domain->fiber_reinforcement_stress.set_values(item); + } else if (name == ActiveStressParameters::xml_element_name) { + // @todo[michelebucelli] The need to manually fall back onto the domain + // parameters might be avoided with a bit of refactoring. + domain->active_stress.set_values(item); } else if (name == LinearSolverParameters::xml_element_name_) { linear_solver.set_values(item); @@ -2560,16 +2719,21 @@ void EquationParameters::set_values(tinyxml2::XMLElement* eq_elem, DomainParamet domain->stimuli.emplace_back(std::make_unique()); domain->stimuli.back()->set_values(item); - } else if (viscosity_names.count(name)) { - auto eq_type = require_map_value(consts::equation_name_to_type, type.value(), - "Unknown equation type '" + type.value() + "' while parsing viscosity model."); + } else if (viscosity_names.count(name)) { + auto eq_type = + require_map_value(consts::equation_name_to_type, type.value(), + "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) { + } else if (eq_type == consts::EquationType::phys_struct || + eq_type == consts::EquationType::phys_ustruct) { domain->solid_viscosity.set_values(item); } else { - svmp::raise("Viscosity model not supported for equation '" + type.value() + "'."); + svmp::raise( + "Viscosity model not supported for equation '" + type.value() + + "'."); } } else if (name == ECGLeadsParameters::xml_element_name_) { @@ -2578,8 +2742,9 @@ void EquationParameters::set_values(tinyxml2::XMLElement* eq_elem, DomainParamet } else if (name == VariableWallPropsParameters::xml_element_name_) { variable_wall_properties.set_values(item); - } else if (name == include_xml.name()) { - auto value = require_xml_text(item, "Equation Include_xml requires a file name."); + } else if (name == include_xml.name()) { + auto value = + require_xml_text(item, "Equation Include_xml requires a file name."); IncludeParametersFile include_parameters(value); set_values(include_parameters.root_element, default_domain); @@ -2594,21 +2759,23 @@ void EquationParameters::set_values(tinyxml2::XMLElement* eq_elem, DomainParamet try { default_domain->set_parameter_value(name, value); - } catch (const std::bad_function_call& exception) { - svmp::raise("Unknown " + xml_element_name_ + " XML element '" + name + "'."); + } catch (const std::bad_function_call &exception) { + svmp::raise("Unknown " + xml_element_name_ + + " XML element '" + name + "'."); } } - } else { - svmp::raise("[Equation] Unknown " + xml_element_name_ + " XML element '" + name + "'."); + svmp::raise("[Equation] Unknown " + + xml_element_name_ + " XML element '" + + name + "'."); } item = item->NextSiblingElement(); } /* - if (domains.size() == 0) { + if (domains.size() == 0) { auto domain_params = new DomainParameters(); domain_params->set_values(item); domains.push_back(domain_params); @@ -2621,8 +2788,7 @@ void EquationParameters::set_values(tinyxml2::XMLElement* eq_elem, DomainParamet ////////////////////////////////////////////////////////// /// @brief Process paramaters for the 'GeneralSimulationParameters' XML element. -GeneralSimulationParameters::GeneralSimulationParameters() -{ +GeneralSimulationParameters::GeneralSimulationParameters() { int int_inf = std::numeric_limits::infinity(); // Define the XML element name for general simulation parameters. @@ -2633,33 +2799,51 @@ GeneralSimulationParameters::GeneralSimulationParameters() bool required = true; set_parameter("Check_IEN_order", true, !required, check_ien_order); - set_parameter("Continue_previous_simulation", false, required, continue_previous_simulation); - set_parameter("Convert_BIN_to_VTK_format", false, !required, convert_bin_to_vtk_format); + set_parameter("Continue_previous_simulation", false, required, + continue_previous_simulation); + set_parameter("Convert_BIN_to_VTK_format", false, !required, + convert_bin_to_vtk_format); set_parameter("Debug", false, !required, debug); set_parameter("Include_xml", "", !required, include_xml); - set_parameter("Increment_in_saving_restart_files", 0, !required, increment_in_saving_restart_files); - set_parameter("Increment_in_saving_VTK_files", 0, !required, increment_in_saving_vtk_files); - - set_parameter("Name_prefix_of_saved_VTK_files", "", !required, name_prefix_of_saved_vtk_files); - set_parameter("Number_of_initialization_time_steps", 0, !required, number_of_initialization_time_steps, {0,int_inf}); - set_parameter("Number_of_spatial_dimensions", 3, !required, number_of_spatial_dimensions); - set_parameter("Number_of_time_steps", 0, required, number_of_time_steps, {0,int_inf}); - - set_parameter("Overwrite_restart_file", false, !required, overwrite_restart_file); + set_parameter("Increment_in_saving_restart_files", 0, !required, + increment_in_saving_restart_files); + set_parameter("Increment_in_saving_VTK_files", 0, !required, + increment_in_saving_vtk_files); + + set_parameter("Name_prefix_of_saved_VTK_files", "", !required, + name_prefix_of_saved_vtk_files); + set_parameter("Number_of_initialization_time_steps", 0, !required, + number_of_initialization_time_steps, {0, int_inf}); + set_parameter("Number_of_spatial_dimensions", 3, !required, + number_of_spatial_dimensions); + set_parameter("Number_of_time_steps", 0, required, number_of_time_steps, + {0, int_inf}); + + set_parameter("Overwrite_restart_file", false, !required, + overwrite_restart_file); set_parameter("Restart_file_name", "stFile", !required, restart_file_name); - set_parameter("Save_averaged_results", false, !required, save_averaged_results); - set_parameter("Save_results_in_folder", "", !required, save_results_in_folder); - set_parameter("Save_results_to_VTK_format", false, required, save_results_to_vtk_format); - set_parameter("Searched_file_name_to_trigger_stop", "", !required, searched_file_name_to_trigger_stop); - set_parameter("Simulation_initialization_file_path", "", !required, simulation_initialization_file_path); - set_parameter("Simulation_requires_remeshing", false, !required, simulation_requires_remeshing); - set_parameter("Spectral_radius_of_infinite_time_step", 0.5, required, spectral_radius_of_infinite_time_step); - set_parameter("Start_averaging_from_zero", false, !required, start_averaging_from_zero); - set_parameter("Start_saving_after_time_step", 0, required, start_saving_after_time_step); + set_parameter("Save_averaged_results", false, !required, + save_averaged_results); + set_parameter("Save_results_in_folder", "", !required, + save_results_in_folder); + set_parameter("Save_results_to_VTK_format", false, required, + save_results_to_vtk_format); + set_parameter("Searched_file_name_to_trigger_stop", "", !required, + searched_file_name_to_trigger_stop); + set_parameter("Simulation_initialization_file_path", "", !required, + simulation_initialization_file_path); + set_parameter("Simulation_requires_remeshing", false, !required, + simulation_requires_remeshing); + set_parameter("Spectral_radius_of_infinite_time_step", 0.5, required, + spectral_radius_of_infinite_time_step); + set_parameter("Start_averaging_from_zero", false, !required, + start_averaging_from_zero); + set_parameter("Start_saving_after_time_step", 0, required, + start_saving_after_time_step); set_parameter("Starting time step", 0, !required, starting_time_step); set_parameter("Time_step_size", 0.0, required, time_step_size); @@ -2667,33 +2851,35 @@ GeneralSimulationParameters::GeneralSimulationParameters() set_parameter("Warning", false, !required, warning); } -void GeneralSimulationParameters::print_parameters() -{ +void GeneralSimulationParameters::print_parameters() { std::cout << std::endl; std::cout << "-----------------------------" << std::endl; std::cout << "General Simulation Parameters" << std::endl; std::cout << "-----------------------------" << std::endl; auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } } /// @brief Set general parameters values from XML. -void GeneralSimulationParameters::set_values(tinyxml2::XMLElement* xml_element, bool from_external_xml) -{ +void GeneralSimulationParameters::set_values(tinyxml2::XMLElement *xml_element, + bool from_external_xml) { using namespace tinyxml2; - tinyxml2::XMLElement* item; + tinyxml2::XMLElement *item; // Set parameter values from the XML elements. // if (from_external_xml) { item = xml_element->FirstChildElement(); } else { - auto general_params = xml_element->FirstChildElement(xml_element_name.c_str()); + auto general_params = + xml_element->FirstChildElement(xml_element_name.c_str()); if (general_params == nullptr) { - svmp::raise("No <" + xml_element_name + "> section found in the solver XML file."); + svmp::raise( + "No <" + xml_element_name + + "> section found in the solver XML file."); } item = general_params->FirstChildElement(); } @@ -2702,17 +2888,22 @@ void GeneralSimulationParameters::set_values(tinyxml2::XMLElement* xml_element, std::string name = std::string(item->Value()); if (name == include_xml.name()) { - auto value = require_xml_text(item, "GeneralSimulationParameters Include_xml requires a file name."); + auto value = require_xml_text( + item, + "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, "GeneralSimulationParameters XML element '" + name + "' requires a value."); + auto value = + require_xml_text(item, "GeneralSimulationParameters XML element '" + + name + "' requires a value."); try { set_parameter_value(name, value); - } catch (const std::bad_function_call& exception) { - svmp::raise("Unknown XML GeneralSimulationParameters element '" + name + "."); + } catch (const std::bad_function_call &exception) { + svmp::raise( + "Unknown XML GeneralSimulationParameters element '" + name + "."); } } @@ -2734,8 +2925,7 @@ void GeneralSimulationParameters::set_values(tinyxml2::XMLElement* xml_element, /// Define the XML element name for face parameters. const std::string FaceParameters::xml_element_name_ = "Add_face"; -FaceParameters::FaceParameters() -{ +FaceParameters::FaceParameters() { set_xml_element_name(xml_element_name_); // A parameter that must be defined. @@ -2743,28 +2933,29 @@ FaceParameters::FaceParameters() name = Parameter("name", "", required); - set_parameter("End_nodes_face_file_path", "", !required, end_nodes_face_file_path); + set_parameter("End_nodes_face_file_path", "", !required, + end_nodes_face_file_path); set_parameter("Face_file_path", "", !required, face_file_path); - set_parameter("Quadrature_modifier_TRI3", (2.0/3.0), !required, quadrature_modifier_TRI3); + set_parameter("Quadrature_modifier_TRI3", (2.0 / 3.0), !required, + quadrature_modifier_TRI3); } -void FaceParameters::print_parameters() -{ +void FaceParameters::print_parameters() { std::cout << std::endl; std::cout << "---------------" << std::endl; std::cout << "Face Parameters" << std::endl; std::cout << "---------------" << std::endl; std::cout << name.name() << ": " << name.value() << std::endl; - std::cout << face_file_path.name() << ": " << face_file_path.value() << std::endl; + std::cout << face_file_path.name() << ": " << face_file_path.value() + << std::endl; } -void FaceParameters::set_values(tinyxml2::XMLElement* face_elem) -{ +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 = require_xml_attribute(face_elem, "name"); + std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; + const char *face_name = require_xml_attribute(face_elem, "name"); name.set(std::string(face_name)); auto item = face_elem->FirstChildElement(); @@ -2772,13 +2963,13 @@ void FaceParameters::set_values(tinyxml2::XMLElement* face_elem) auto name = std::string(item->Value()); auto value = item->GetText(); - if (value == nullptr) { + if (value == nullptr) { svmp::raise(error_msg + name + "'."); } try { set_parameter_value(name, value); - } catch (const std::bad_function_call& exception) { + } catch (const std::bad_function_call &exception) { svmp::raise(error_msg + name + "'."); } @@ -2795,8 +2986,7 @@ void FaceParameters::set_values(tinyxml2::XMLElement* face_elem) /// @brief Define the XML element name for mesh parameters. const std::string RemesherParameters::xml_element_name_ = "Remesher"; -RemesherParameters::RemesherParameters() -{ +RemesherParameters::RemesherParameters() { bool required = true; type = Parameter("type", "", required); @@ -2804,11 +2994,11 @@ RemesherParameters::RemesherParameters() set_parameter("Min_dihedral_angle", 10.0, !required, min_dihedral_angle); set_parameter("Max_radius_ratio", 1.15, !required, max_radius_ratio); set_parameter("Remesh_frequency", 100, !required, remesh_frequency); - set_parameter("Frequency_for_copying_data", 10, !required, frequency_for_copying_data); + set_parameter("Frequency_for_copying_data", 10, !required, + frequency_for_copying_data); } -void RemesherParameters::print_parameters() -{ +void RemesherParameters::print_parameters() { std::cout << std::endl; std::cout << "-------------------" << std::endl; std::cout << "Remesher Parameters" << std::endl; @@ -2817,12 +3007,11 @@ void RemesherParameters::print_parameters() std::cout << type.name() << ": " << type.value() << std::endl; } -void RemesherParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +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 = require_xml_attribute(xml_elem, "type"); + const char *stype = require_xml_attribute(xml_elem, "type"); type.set(std::string(stype)); values_set_ = true; @@ -2834,8 +3023,8 @@ void RemesherParameters::set_values(tinyxml2::XMLElement* xml_elem) auto name = std::string(item->Value()); if (name == "Max_edge_size") { - const char* name; - const char* value; + const char *name; + const char *value; name = require_xml_attribute(item, "name"); value = require_xml_attribute(item, "value"); auto svalue = std::string(value); @@ -2844,15 +3033,17 @@ void RemesherParameters::set_values(tinyxml2::XMLElement* xml_elem) double dvalue = std::stod(svalue); max_edge_sizes_[std::string(name)] = dvalue; } catch (...) { - svmp::raise("VALUE=" + svalue + - " is not a valid float in the XML Remesher element."); + svmp::raise( + "VALUE=" + svalue + + " is not a valid float in the XML Remesher element."); } } else if (item->GetText() != nullptr) { auto value = item->GetText(); try { set_parameter_value(name, value); - } catch (const std::bad_function_call& exception) { + } catch (const std::bad_function_call &exception) { svmp::raise(error_msg + name + "'."); } @@ -2868,13 +3059,13 @@ void RemesherParameters::set_values(tinyxml2::XMLElement* xml_elem) // M e s h P a r a m e t e r s // ////////////////////////////////////////////////////////// -// Process parameters for the 'Add_mesh' XML element used for defining mesh elements. +// Process parameters for the 'Add_mesh' XML element used for defining mesh +// elements. /// @brief Define the XML element name for mesh parameters. const std::string MeshParameters::xml_element_name_ = "Add_mesh"; -MeshParameters::MeshParameters() -{ +MeshParameters::MeshParameters() { bool required = true; // Mesh name from Add_mesh element. @@ -2882,29 +3073,33 @@ MeshParameters::MeshParameters() // Parameters under Add_mesh element. // - set_parameter("Domain", 0, !required, domain_id); + set_parameter("Domain", 0, !required, domain_id); set_parameter("Domain_file_path", "", !required, domain_file_path); - //set_parameter("Fiber_direction", {}, !required, fiber_direction); - set_parameter("Fiber_direction_file_path", {}, !required, fiber_direction_file_paths); + // set_parameter("Fiber_direction", {}, !required, fiber_direction); + set_parameter("Fiber_direction_file_path", {}, !required, + fiber_direction_file_paths); set_parameter("Mesh_file_path", "", !required, mesh_file_path); set_parameter("Mesh_scale_factor", 1.0, !required, mesh_scale_factor); set_parameter("Prestress_file_path", "", !required, prestress_file_path); set_parameter("Include_xml", "", !required, include_xml); - set_parameter("Initial_displacements_file_path", "", !required, initial_displacements_file_path); - set_parameter("Initial_pressures_file_path", "", !required, initial_pressures_file_path); - set_parameter("Initial_velocities_file_path", "", !required, initial_velocities_file_path); + set_parameter("Initial_displacements_file_path", "", !required, + initial_displacements_file_path); + set_parameter("Initial_pressures_file_path", "", !required, + initial_pressures_file_path); + set_parameter("Initial_velocities_file_path", "", !required, + initial_velocities_file_path); set_parameter("Set_mesh_as_fibers", false, !required, set_mesh_as_fibers); set_parameter("Set_mesh_as_shell", false, !required, set_mesh_as_shell); - set_parameter("Quadrature_modifier_TET4", (5.0+3.0*sqrt(5.0))/20.0, !required, quadrature_modifier_TET4); + set_parameter("Quadrature_modifier_TET4", (5.0 + 3.0 * sqrt(5.0)) / 20.0, + !required, quadrature_modifier_TET4); } -void MeshParameters::print_parameters() -{ +void MeshParameters::print_parameters() { std::cout << std::endl; std::cout << "---------------" << std::endl; std::cout << "Mesh Parameters" << std::endl; @@ -2912,23 +3107,23 @@ void MeshParameters::print_parameters() std::cout << name.name() << ": " << name.value() << std::endl; auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } - for (auto& dir : fiber_directions) { + for (auto &dir : fiber_directions) { std::cout << dir.name() << ": " << dir.svalue() << std::endl; } - for (auto& face : face_parameters) { + for (auto &face : face_parameters) { face->print_parameters(); } } -void MeshParameters::set_values(tinyxml2::XMLElement* mesh_elem, bool from_external_xml) -{ +void MeshParameters::set_values(tinyxml2::XMLElement *mesh_elem, + bool from_external_xml) { using namespace tinyxml2; - std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; + std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; auto item = mesh_elem->FirstChildElement(); while (item != nullptr) { @@ -2940,26 +3135,28 @@ void MeshParameters::set_values(tinyxml2::XMLElement* mesh_elem, bool from_exter face_params->set_values(item); face_parameters.push_back(face_params); - // There may be multiple 'Fiber_direction' elements so store - // them as a list of VectorParameter. - // + // There may be multiple 'Fiber_direction' elements so store + // them as a list of VectorParameter. + // } else if (name == "Fiber_direction") { - auto value = require_xml_text(item, "Mesh Fiber_direction XML element requires a value."); + auto value = require_xml_text( + item, "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 = require_xml_text(item, "Mesh Include_xml requires a file name."); + auto value = + require_xml_text(item, "Mesh Include_xml requires a file name."); IncludeParametersFile include_parameters(value); set_values(include_parameters.root_element, true); - // Just a simple element. + // Just a simple element. } else if (item->GetText() != nullptr) { auto value = item->GetText(); try { set_parameter_value(name, value); - } catch (const std::bad_function_call& exception) { + } catch (const std::bad_function_call &exception) { svmp::raise(error_msg + name + "'."); } } else { @@ -2975,31 +3172,32 @@ void MeshParameters::set_values(tinyxml2::XMLElement* mesh_elem, bool from_exter ///////////////////////////////////////////////////////////////////////////// // The PrecomputedSolutionParameters class stores parameters for the -// 'Precomputed_solution' XML element used to read in the data from a +// 'Precomputed_solution' XML element used to read in the data from a // precomputed solution for the simulation state. -const std::string PrecomputedSolutionParameters::xml_element_name_ = "Precomputed_solution"; +const std::string PrecomputedSolutionParameters::xml_element_name_ = + "Precomputed_solution"; -PrecomputedSolutionParameters::PrecomputedSolutionParameters() -{ +PrecomputedSolutionParameters::PrecomputedSolutionParameters() { // A parameter that must be defined. bool required = true; set_parameter("Field_name", "", required, field_name); set_parameter("File_path", "", required, file_path); set_parameter("Time_step", 0.0, !required, time_step); - set_parameter("Use_precomputed_solution", false, !required, use_precomputed_solution); + set_parameter("Use_precomputed_solution", false, !required, + use_precomputed_solution); } -void PrecomputedSolutionParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void PrecomputedSolutionParameters::set_values(tinyxml2::XMLElement *xml_elem) { using namespace tinyxml2; std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &PrecomputedSolutionParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&PrecomputedSolutionParameters::set_parameter_value, *this, _1, + _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); } @@ -3009,13 +3207,13 @@ void PrecomputedSolutionParameters::set_values(tinyxml2::XMLElement* xml_elem) ////////////////////////////////////////////////////////// // The ProjectionParameters class stores parameters for the -// 'Add_projection' XML element used for fluid-structure interaction simulations. +// 'Add_projection' XML element used for fluid-structure interaction +// simulations. /// @brief Define the XML element name for mesh parameters. const std::string ProjectionParameters::xml_element_name_ = "Add_projection"; -ProjectionParameters::ProjectionParameters() -{ +ProjectionParameters::ProjectionParameters() { // A parameter that must be defined. bool required = true; @@ -3025,20 +3223,19 @@ ProjectionParameters::ProjectionParameters() set_parameter("Projection_tolerance", 0.0, !required, projection_tolerance); } -void ProjectionParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void ProjectionParameters::set_values(tinyxml2::XMLElement *xml_elem) { using namespace tinyxml2; std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'type' from the element. - const char* sname = require_xml_attribute(xml_elem, "name"); + const char *sname = require_xml_attribute(xml_elem, "name"); name.set(std::string(sname)); using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &ProjectionParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&ProjectionParameters::set_parameter_value, *this, _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); } @@ -3048,10 +3245,10 @@ void ProjectionParameters::set_values(tinyxml2::XMLElement* xml_elem) ////////////////////////////////////////////////////////// /// @brief Define the XML element name for mesh parameters. -const std::string RISProjectionParameters::xml_element_name_ = "Add_RIS_projection"; +const std::string RISProjectionParameters::xml_element_name_ = + "Add_RIS_projection"; -RISProjectionParameters::RISProjectionParameters() -{ +RISProjectionParameters::RISProjectionParameters() { // A parameter that must be defined. bool required = true; @@ -3062,37 +3259,35 @@ RISProjectionParameters::RISProjectionParameters() set_parameter("Projection_tolerance", 0.0, !required, projection_tolerance); } -void RISProjectionParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +void RISProjectionParameters::set_values(tinyxml2::XMLElement *xml_elem) { using namespace tinyxml2; std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; // Get the 'type' from the element. - const char* sname = require_xml_attribute(xml_elem, "name"); + const char *sname = require_xml_attribute(xml_elem, "name"); name.set(std::string(sname)); using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &RISProjectionParameters::set_parameter_value, *this, _1, _2); + std::function ftpr = + std::bind(&RISProjectionParameters::set_parameter_value, *this, _1, _2); xml_util_set_parameters(ftpr, xml_elem, error_msg); } - ////////////////////////////////////////////////////////// // URIS Mesh Parameters // ////////////////////////////////////////////////////////// // [HZ] implemente URIS parameters here -// Process parameters for the 'Add_URIS_mesh' XML element used for defining URIS mesh elements. +// Process parameters for the 'Add_URIS_mesh' XML element used for defining URIS +// mesh elements. /// @brief Define the XML element name for mesh parameters. const std::string URISMeshParameters::xml_element_name_ = "Add_URIS_mesh"; -URISMeshParameters::URISMeshParameters() -{ +URISMeshParameters::URISMeshParameters() { bool required = true; // Mesh name from Add_mesh element. @@ -3100,19 +3295,21 @@ URISMeshParameters::URISMeshParameters() // Parameters under Add_mesh element. // - set_parameter("Mesh_scale_factor", 1.0, !required, mesh_scale_factor); - set_parameter("Thickness", 0.2, !required, thickness); - set_parameter("Closed_thickness", 0.2, !required, close_thickness); - set_parameter("Resistance", 1.0e5, !required, resistance); - set_parameter("Valve_starts_as_closed", true, !required, valve_starts_as_closed); - set_parameter("Invert_normal", false, !required, invert_normal); - set_parameter("Positive_flow_normal_file_path", "", !required, positive_flow_normal_file_path); - set_parameter("Scaffold_file_path", "", !required, scaffold_file_path); - set_parameter("Include_URIS_velocity", false, !required, include_uris_velocity); -} - -void URISMeshParameters::print_parameters() -{ + set_parameter("Mesh_scale_factor", 1.0, !required, mesh_scale_factor); + set_parameter("Thickness", 0.2, !required, thickness); + set_parameter("Closed_thickness", 0.2, !required, close_thickness); + set_parameter("Resistance", 1.0e5, !required, resistance); + set_parameter("Valve_starts_as_closed", true, !required, + valve_starts_as_closed); + set_parameter("Invert_normal", false, !required, invert_normal); + set_parameter("Positive_flow_normal_file_path", "", !required, + positive_flow_normal_file_path); + set_parameter("Scaffold_file_path", "", !required, scaffold_file_path); + set_parameter("Include_URIS_velocity", false, !required, + include_uris_velocity); +} + +void URISMeshParameters::print_parameters() { std::cout << std::endl; std::cout << "---------------" << std::endl; std::cout << "URIS Mesh Parameters" << std::endl; @@ -3120,19 +3317,18 @@ void URISMeshParameters::print_parameters() std::cout << name.name() << ": " << name.value() << std::endl; auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } - for (auto& face : URIS_face_parameters) { + for (auto &face : URIS_face_parameters) { face->print_parameters(); } } -void URISMeshParameters::set_values(tinyxml2::XMLElement* mesh_elem) -{ +void URISMeshParameters::set_values(tinyxml2::XMLElement *mesh_elem) { using namespace tinyxml2; - std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; + std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; auto item = mesh_elem->FirstChildElement(); while (item != nullptr) { @@ -3147,7 +3343,7 @@ void URISMeshParameters::set_values(tinyxml2::XMLElement* mesh_elem) auto value = item->GetText(); try { set_parameter_value(name, value); - } catch (const std::bad_function_call& exception) { + } catch (const std::bad_function_call &exception) { svmp::raise(error_msg + name + "'."); } } else { @@ -3158,7 +3354,6 @@ void URISMeshParameters::set_values(tinyxml2::XMLElement* mesh_elem) } } - ////////////////////////////////////////////////////////// // URIS Face Parameters // ////////////////////////////////////////////////////////// @@ -3168,8 +3363,7 @@ void URISMeshParameters::set_values(tinyxml2::XMLElement* mesh_elem) /// Define the XML element name for face parameters. const std::string URISFaceParameters::xml_element_name_ = "Add_URIS_face"; -URISFaceParameters::URISFaceParameters() -{ +URISFaceParameters::URISFaceParameters() { set_xml_element_name(xml_element_name_); // A parameter that must be defined. @@ -3179,28 +3373,29 @@ URISFaceParameters::URISFaceParameters() set_parameter("Face_file_path", "", !required, face_file_path); set_parameter("Open_motion_file_path", "", !required, open_motion_file_path); - set_parameter("Close_motion_file_path", "", !required, close_motion_file_path); + set_parameter("Close_motion_file_path", "", !required, + close_motion_file_path); - // set_parameter("End_nodes_face_file_path", "", !required, end_nodes_face_file_path); - // set_parameter("Quadrature_modifier_TRI3", (2.0/3.0), !required, quadrature_modifier_TRI3); + // set_parameter("End_nodes_face_file_path", "", !required, + // end_nodes_face_file_path); set_parameter("Quadrature_modifier_TRI3", + // (2.0/3.0), !required, quadrature_modifier_TRI3); } -void URISFaceParameters::print_parameters() -{ +void URISFaceParameters::print_parameters() { std::cout << std::endl; std::cout << "---------------" << std::endl; std::cout << "URIS Face Parameters" << std::endl; std::cout << "---------------" << std::endl; std::cout << name.name() << ": " << name.value() << std::endl; - std::cout << face_file_path.name() << ": " << face_file_path.value() << std::endl; + std::cout << face_file_path.name() << ": " << face_file_path.value() + << std::endl; } -void URISFaceParameters::set_values(tinyxml2::XMLElement* face_elem) -{ +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 = require_xml_attribute(face_elem, "name"); + std::string error_msg = "Unknown " + xml_element_name_ + " XML element '"; + const char *face_name = require_xml_attribute(face_elem, "name"); name.set(std::string(face_name)); auto item = face_elem->FirstChildElement(); @@ -3208,13 +3403,13 @@ void URISFaceParameters::set_values(tinyxml2::XMLElement* face_elem) auto name = std::string(item->Value()); auto value = item->GetText(); - if (value == nullptr) { + if (value == nullptr) { svmp::raise(error_msg + name + "'."); } try { set_parameter_value(name, value); - } catch (const std::bad_function_call& exception) { + } catch (const std::bad_function_call &exception) { svmp::raise(error_msg + name + "'."); } @@ -3222,7 +3417,6 @@ void URISFaceParameters::set_values(tinyxml2::XMLElement* face_elem) } } - ////////////////////////////////////////////////////////// // LinearAlgebraParameters // ////////////////////////////////////////////////////////// @@ -3233,44 +3427,44 @@ void URISFaceParameters::set_values(tinyxml2::XMLElement* face_elem) /// @brief Define the XML element name for equation output parameters. const std::string LinearAlgebraParameters::xml_element_name_ = "Linear_algebra"; -LinearAlgebraParameters::LinearAlgebraParameters() -{ +LinearAlgebraParameters::LinearAlgebraParameters() { // A parameter that must be defined. bool required = true; - auto alg_type = LinearAlgebra::type_to_name.at(consts::LinearAlgebraType::fsils); + auto alg_type = + LinearAlgebra::type_to_name.at(consts::LinearAlgebraType::fsils); type = Parameter("type", alg_type, required); set_parameter("Configuration_file", "", !required, configuration_file); - auto prec_type = consts::preconditioner_type_to_name.at(consts::PreconditionerType::PREC_NONE); + auto prec_type = consts::preconditioner_type_to_name.at( + consts::PreconditionerType::PREC_NONE); set_parameter("Preconditioner", prec_type, !required, preconditioner); - auto assemble_type = LinearAlgebra::type_to_name.at(consts::LinearAlgebraType::none); + auto assemble_type = + LinearAlgebra::type_to_name.at(consts::LinearAlgebraType::none); set_parameter("Assembly", assemble_type, !required, assembly); } -void LinearAlgebraParameters::print_parameters() -{ +void LinearAlgebraParameters::print_parameters() { std::cout << std::endl; std::cout << "-------------------------" << std::endl; std::cout << "Linear Algebra Parameters" << std::endl; std::cout << "-------------------------" << std::endl; - + std::cout << type.name() << ": " << type.value() << std::endl; - - auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + + auto params_name_value = get_parameter_list(); + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } } -void LinearAlgebraParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +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 = require_xml_attribute(xml_elem, "type"); + const char *stype = require_xml_attribute(xml_elem, "type"); type.set(std::string(stype)); // Check Linear_algebra type=TYPE> element. @@ -3279,30 +3473,45 @@ void LinearAlgebraParameters::set_values(tinyxml2::XMLElement* xml_elem) // if (LinearAlgebra::name_to_type.count(type.value()) == 0) { 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+" ";}); + std::for_each( + LinearAlgebra::name_to_type.begin(), LinearAlgebra::name_to_type.end(), + [&valid_types]( + std::pair p) { + valid_types += p.first + " "; + }); svmp::raise("Unknown TYPE '" + type.value() + - "' given in the XML element.\nValid types are: " + valid_types); + "' given in the XML element.\nValid types are: " + + valid_types); } - // Create a function pointer 'fptr' to 'LinearAlgebraParameters::set_parameter_value'. + // Create a function pointer 'fptr' to + // 'LinearAlgebraParameters::set_parameter_value'. // using std::placeholders::_1; using std::placeholders::_2; - std::function ftpr = - std::bind( &LinearAlgebraParameters::set_parameter_value, *this, _1, _2); - + std::function ftpr = + std::bind(&LinearAlgebraParameters::set_parameter_value, *this, _1, _2); + // Parse XML and set parameter values. xml_util_set_parameters(ftpr, xml_elem, error_msg); // Check preconditioner type. if (consts::preconditioner_name_to_type.count(preconditioner.value()) == 0) { 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+" ";}); - svmp::raise("Unknown TYPE '" + preconditioner() + - "' given in the XML element.\nValid types are: " + 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 + " "; + }); + svmp::raise( + "Unknown TYPE '" + preconditioner() + + "' given in the XML element.\nValid " + "types are: " + + valid_types); + } check_input_parameters(); @@ -3310,30 +3519,35 @@ 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 = require_map_value(LinearAlgebra::name_to_type, type(), +void LinearAlgebraParameters::check_input_parameters() { + auto linear_algebra_type = require_map_value( + LinearAlgebra::name_to_type, type(), "Unknown TYPE '" + type() + - "' given in the XML element."); - auto prec_cond_type = require_map_value(consts::preconditioner_name_to_type, - preconditioner.value(), "Unknown TYPE '" + preconditioner() + - "' given in the XML element."); - auto assembly_type = require_map_value(LinearAlgebra::name_to_type, assembly.value(), + "' given in the XML element."); + auto prec_cond_type = require_map_value( + consts::preconditioner_name_to_type, preconditioner.value(), + "Unknown TYPE '" + preconditioner() + + "' given in the XML element."); + auto assembly_type = require_map_value( + LinearAlgebra::name_to_type, assembly.value(), "Unknown TYPE '" + assembly() + - "' given in the XML element."); + "' given in the XML element."); - LinearAlgebra* linear_algebra = nullptr; + LinearAlgebra *linear_algebra = nullptr; try { - linear_algebra = LinearAlgebraFactory::create_interface(linear_algebra_type); + linear_algebra = + LinearAlgebraFactory::create_interface(linear_algebra_type); if (linear_algebra == nullptr) { - svmp::raise("Linear_algebra type '" + type() + "' cannot be used as a solver backend."); + svmp::raise( + "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&) { + } catch (const svmp::ParseException &) { delete linear_algebra; throw; - } catch (const std::exception& exception) { + } catch (const std::exception &exception) { delete linear_algebra; svmp::raise(exception.what()); } @@ -3349,8 +3563,7 @@ void LinearAlgebraParameters::check_input_parameters() /// @brief Define the XML element name for equation output parameters. const std::string LinearSolverParameters::xml_element_name_ = "LS"; -LinearSolverParameters::LinearSolverParameters() -{ +LinearSolverParameters::LinearSolverParameters() { // A parameter that must be defined. bool required = true; @@ -3359,7 +3572,8 @@ LinearSolverParameters::LinearSolverParameters() set_parameter("Absolute_tolerance", 1.0e-10, !required, absolute_tolerance); - set_parameter("Krylov_space_dimension", 50, !required, krylov_space_dimension); + set_parameter("Krylov_space_dimension", 50, !required, + krylov_space_dimension); set_parameter("Max_iterations", 1000, !required, max_iterations); @@ -3368,13 +3582,12 @@ LinearSolverParameters::LinearSolverParameters() set_parameter("NS_GM_max_iterations", 1000, !required, ns_gm_max_iterations); set_parameter("NS_GM_tolerance", 1.0e-2, !required, ns_gm_tolerance); - //set_parameter("Preconditioner", "", !required, preconditioner); + // set_parameter("Preconditioner", "", !required, preconditioner); set_parameter("Tolerance", 0.5, !required, tolerance); } -void LinearSolverParameters::print_parameters() -{ +void LinearSolverParameters::print_parameters() { std::cout << std::endl; std::cout << "------------------------" << std::endl; std::cout << "Linear Solver Parameters" << std::endl; @@ -3383,29 +3596,30 @@ void LinearSolverParameters::print_parameters() std::cout << type.name() << ": " << type.value() << std::endl; auto params_name_value = get_parameter_list(); - for (auto& [ key, value ] : params_name_value) { + for (auto &[key, value] : params_name_value) { std::cout << key << ": " << value << std::endl; } } -void LinearSolverParameters::set_values(tinyxml2::XMLElement* xml_elem) -{ +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 = require_xml_attribute(xml_elem, "type"); + const char *stype = require_xml_attribute(xml_elem, "type"); type.set(std::string(stype)); using std::placeholders::_1; using std::placeholders::_2; - // Create a function pointer 'fptr' to 'LinearSolverParameters::set_parameter_value'. - std::function ftpr = - std::bind( &LinearSolverParameters::set_parameter_value, *this, _1, _2); + // Create a function pointer 'fptr' to + // 'LinearSolverParameters::set_parameter_value'. + std::function ftpr = + std::bind(&LinearSolverParameters::set_parameter_value, *this, _1, _2); // Parse XML and set parameter values. - std::set sub_sections = {LinearAlgebraParameters::xml_element_name_}; + std::set sub_sections = { + LinearAlgebraParameters::xml_element_name_}; xml_util_set_parameters(ftpr, xml_elem, error_msg, sub_sections); // Set subsection values. @@ -3419,5 +3633,4 @@ void LinearSolverParameters::set_values(tinyxml2::XMLElement* xml_elem) } item = item->NextSiblingElement(); } - } diff --git a/Code/Source/solver/Parameters.h b/Code/Source/solver/Parameters.h index 23c377b10..b18bd4661 100644 --- a/Code/Source/solver/Parameters.h +++ b/Code/Source/solver/Parameters.h @@ -1273,8 +1273,8 @@ class DirectionalDistributionParameters : public ParameterLists static const std::string xml_element_name_; bool defined() const { return value_set; }; - void print_parameters(); - void set_values(tinyxml2::XMLElement* xml_elem); + void print_parameters() const; + void set_values(const tinyxml2::XMLElement *xml_elem); void validate() const; // Validate directional fractions Parameter fiber_direction; @@ -1284,44 +1284,6 @@ class DirectionalDistributionParameters : public ParameterLists bool value_set = false; }; -/// @brief The FiberReinforcementStressParameters class stores fiber -/// reinforcement stress parameters for the 'Fiber_reinforcement_stress` -/// XML element. -/// -/// \code {.xml} -/// -/// fib_stress.dat -/// true -/// -/// 0.7 -/// 0.2 -/// 0.1 -/// -/// -/// \endcode -class FiberReinforcementStressParameters : public ParameterLists -{ - public: - FiberReinforcementStressParameters(); - - static const std::string xml_element_name_; - - bool defined() const { return value_set; }; - void print_parameters(); - void set_values(tinyxml2::XMLElement* xml_elem); - - Parameter type; - - Parameter ramp_function; - Parameter temporal_values_file_path; - Parameter value; - - // Directional stress distribution parameters - DirectionalDistributionParameters directional_distribution; - - bool value_set = false; -}; - /// @brief Generic ionic model initial conditions parameters. class IonicInitialStateParameters : public ParameterLists { public: @@ -1361,7 +1323,7 @@ class IonicInitialStateParameters : public ParameterLists { bool value_set = false; }; -/// @brief Initial conditions parameters for a generic ionic model. +/// @brief Parameters for a generic ionic model. /// /// Bundles initial conditions for the model's ionic concentrations and gating /// variables, represented by two instances of IonicInitialStateParameters. @@ -1444,6 +1406,144 @@ class IonicModelParameters : public ParameterLists { bool value_set = false; }; +/// @brief Parameters for a generic active stress model. +/// +/// This class is meant to be inherited from to implement parameters for +/// specific active stress models. Derived classes will mostly have to call +/// add_parameter in their constructor to define the model-specific parameters. +/// +/// In the XML file, this class, and the classes derived from it, correspond to +/// the element within the element, where +/// Model_name is the name of a concrete active stress model. +class ActiveStressModelParameters : public ParameterLists { +public: + /// Constructor. + ActiveStressModelParameters(const std::string &xml_element_name_); + + /// Return whether the parameters represented by this object were defined. + bool defined() const { return value_set; } + + /// Print the value of parameters. + void print_parameters() const; + + /// Set the values of parameters in this object from an XML element. + void set_values(const tinyxml2::XMLElement *xml_elem); + + /// Name of the XML element for this object. + const std::string xml_element_name; + + /// Get the value of a parameter by label. + double get_scalar(const std::string &label) const { + return double_parameters.at(label).value(); + } + + /// Get the value of a string parameter by label. + std::string get_string(const std::string &label) const { + return string_parameters.at(label).value(); + } + + /// Get the value of a bool parameter by label. + bool get_bool(const std::string &label) const { + return bool_parameters.at(label).value(); + } + +protected: + /// Add a new parameter to this object. + void add_parameter(const std::string &label, double default_value, + bool required) { + set_parameter(label, default_value, required, double_parameters[label]); + } + + /// Add a new parameter to this object. + void add_parameter(const std::string &label, const std::string &default_value, + bool required) { + set_parameter(label, default_value, required, string_parameters[label]); + } + + /// Add a new parameter to this object. + void add_parameter(const std::string &label, bool default_value, + bool required) { + set_parameter(label, default_value, required, bool_parameters[label]); + } + + /// Parameters are stored in a map as key-parameter pairs. Derived classes + /// should add parameters to this map in their constructors by calling + /// add_parameter. + /// @{ + + /// Double-valued parameters. + std::map> double_parameters; + + /// String-valued parameters. + std::map> string_parameters; + + /// Bool-valued parameters. + std::map> bool_parameters; + + /// Flag indicating whether the values of the parameters stored in this + /// object have been set. + bool value_set = false; +}; + +/// @brief Parameters for active stress models. +/// +/// This class stores all the parameters related to active stress, including +/// e.g. the name of the specific selected model. The parameters specific to an +/// individual model are managed by the class @ref ActiveStressModelParameters, +/// of which this class owns an instance for every registered model. +/// +/// In the XML file, this class corresponds to the element. +class ActiveStressParameters : public ParameterLists { +public: + /// Constructor. + ActiveStressParameters(); + + /// Return whether the parameters represented by this object were defined. + bool defined() const { return value_set; } + + /// Print the value of parameters. + void print_parameters() const; + + /// Set the values of parameters in this object from an XML element. + void set_values(const tinyxml2::XMLElement *xml_elem); + + /// Get the name of the selected model. Throws an exception if it has not been + /// set. + std::string get_model_name() const; + + /// Get the active tension coefficient along fibers. + double get_eta_f() const; + + /// Get the active tension coefficient along sheets. + double get_eta_s() const; + + /// Get the active tension coefficient along sheet normals. + double get_eta_n() const; + + /// Get the parameters for a given active stress model. + const ActiveStressModelParameters & + get_parameters(const std::string &model_name) const; + + /// Name of the XML element for this object. + static const std::string xml_element_name; + +protected: + /// Parameter for the model name. + Parameter model_name; + + /// Parameters for the directional distribution of active tension. + DirectionalDistributionParameters directional_distribution; + + /// Active stress model parameters. Keys are the model names, as registered + /// in the @ref ActiveStressModelFactory. + std::map> + active_stress_models; + + /// Flag indicating whether the values of the parameters stored in this + /// object have been set. + bool value_set = false; +}; + /// @brief The DomainParameters class stores parameters for the XML /// 'Domain' element to specify properties for solving equations. /// @@ -1469,15 +1569,16 @@ class DomainParameters : public ParameterLists // Parameters for sub-elements under the Domain element. ConstitutiveModelParameters constitutive_model; - FiberReinforcementStressParameters fiber_reinforcement_stress; /// @todo This uses `unique_ptr` unlike most similar containers because /// `ParameterLists::params_map` stores pointers to members of each object. /// Revisit this when the `Parameters` classes are refactored. std::vector> stimuli; FluidViscosityParameters fluid_viscosity; SolidViscosityParameters solid_viscosity; + ActiveStressParameters active_stress; - // Ionic model parameters. + /// Ionic model parameters. Keys are the model names, as registered in the + /// @ref IonicModelFactory. std::map> ionic_models; // Attributes. diff --git a/Code/Source/solver/Vector.h b/Code/Source/solver/Vector.h index 4843ea9b6..5241ab970 100644 --- a/Code/Source/solver/Vector.h +++ b/Code/Source/solver/Vector.h @@ -11,6 +11,8 @@ #include #include +#include "FE/Common/FEException.h" + std::string build_file_prefix(const std::string& label); #ifdef ENABLE_ARRAY_INDEX_CHECKING @@ -330,8 +332,9 @@ class Vector Vector operator+(const Vector& vec) const { if (size_ != vec.size()) { - throw std::runtime_error("[Vector dot product] Vectors have diffrenct sizes: " + - std::to_string(size_) + " != " + std::to_string(vec.size()) + "."); + throw std::runtime_error( + "[Vector dot product] Vectors have different sizes: " + + std::to_string(size_) + " != " + std::to_string(vec.size()) + "."); } Vector result(size_); for (int i = 0; i < size_; i++) { @@ -340,6 +343,25 @@ class Vector return result; } + /// @brief Increment by a rescaled vector. + /// + /// This is equivalent to *this = *this + c * vec but avoids the + /// temporary vector created by the operator+ and operator*. + Vector &add(const double &c, const Vector &vec) + { + if (size_ != vec.size()) { + svmp::raise( + "Vectors have diffrenct sizes: " + std::to_string(size_) + + " != " + std::to_string(vec.size())); + } + + for (int i = 0; i < size_; i++) { + data_[i] += c * vec[i]; + } + + return *this; + } + Vector operator-(const Vector& x) const { Vector result(size_); diff --git a/Code/Source/solver/active_stress.cpp b/Code/Source/solver/active_stress.cpp new file mode 100644 index 000000000..d8630565d --- /dev/null +++ b/Code/Source/solver/active_stress.cpp @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the +// University of California, and others. SPDX-License-Identifier: BSD-3-Clause + +#include "active_stress.h" + +bool supports_active_stress(const consts::EquationType eq_type) { + return eq_type == consts::EquationType::phys_struct || + eq_type == consts::EquationType::phys_ustruct || + eq_type == consts::EquationType::phys_FSI; +} + +void ActiveStress::read_parameters(const ActiveStressParameters ¶ms) { + eta_f = params.get_eta_f(); + eta_s = params.get_eta_s(); + eta_n = params.get_eta_n(); + + read_model_specific_parameters( + params.get_parameters(params.get_model_name())); +} + +void ActiveStress::distribute_parameters(const CmMod &cm_mod, + const cmType &cm) { + cm.bcast(cm_mod, &eta_f); + cm.bcast(cm_mod, &eta_s); + cm.bcast(cm_mod, &eta_n); + + distribute_model_specific_parameters(cm_mod, cm); +} + +void ActiveStress::init(const unsigned int tnNo) { + states.resize(n_states, tnNo); + + if (n_states > 0) { + Vector state_loc(n_states); + init_local(state_loc); + + for (unsigned int i = 0; i < tnNo; ++i) + for (unsigned int j = 0; j < n_states; ++j) + states(j, i) = state_loc(j); + } + + active_tension.resize(tnNo); +} + +void ActiveStress::advance_time_step(const double t, const double dt, + const Vector &calcium, + const Vector &fiber_stretch, + const Vector &fiber_stretch_rate) { + time = t; + + for (unsigned int i = 0; i < states.ncols(); ++i) { + Vector state_loc = states.col(i); + advance_time_step_local(t, dt, calcium[i], fiber_stretch[i], + fiber_stretch_rate[i], state_loc); + states.set_col(i, state_loc); + + active_tension[i] = compute_active_tension_local(state_loc); + } +} \ No newline at end of file diff --git a/Code/Source/solver/active_stress.h b/Code/Source/solver/active_stress.h new file mode 100644 index 000000000..da5f38c26 --- /dev/null +++ b/Code/Source/solver/active_stress.h @@ -0,0 +1,264 @@ +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the +// University of California, and others. SPDX-License-Identifier: BSD-3-Clause + +#ifndef ACTIVE_STRESS_H +#define ACTIVE_STRESS_H + +#include "Array.h" +#include "Parameters.h" +#include "Vector.h" +#include "consts.h" +#include "factory.h" + +#include "CmMod.h" + +#include + +/** + * @brief Return whether a certain equation type can be solved with active + * stress. + */ +bool supports_active_stress(const consts::EquationType eq_type); + +/** + * @brief Abstract active stress class. + * + * This class provides an interface for defining active stress models, i.e. + * models that, in the context of structural mechanics of muscular tissue, + * compute an active tension representing the contribution of muscular + * contraction to the constitiutive law. + * + * The class assumes that the active tension can be expressed as + * @f[ + * \Tact = \Tact(t, \calcium, \fiberstretch, \fiberstretchrate, + * \astressstate), + * @f] + * where @f$\calcium@f$ is the intracellular calcium concentration, + * @f$\fiberstretch@f$ is the fiber stretch, @f$\fiberstretchrate@f$ is the + * fiber stretch rate, and @f$\astressstate@f$ is a vector of internal state + * variables, representing the state of contraction. + * + * The expression assumed above implies that the active tension is a local + * function of the variables it depends on, that is the active tension at a + * given point only depends on the value of other variables at that same point. + * Accordingly, this class works nodally, by evaluating the active tension at + * every mesh node and storing it in a vector, whose values can be accessed + * through @ref ActiveStress::get_tension_fibers. + * + * ### Directional distribution of active stress + * + * In muscular mechanics models, active stress normally acts only along the + * direction of fibers @f$\fiberdirection@f$, reflecting the fact that + * contractile units are aligned with fibers. However, one might want to account + * for fiber dispersion, i.e. the fact that fibers are not perfectly and + * regularly aligned, but rather have a certain distribution of orientations + * centered around the principal direction @f$\fiberdirection@f$. + * + * This can be surrogated by defining the active stress tensor as + * @f[ + * S_\text{act} = \Tact \left( + * \eta_f \fiberdirection \otimes \fiberdirection + + * \eta_s \sheetdirection \otimes \sheetdirection + + * \eta_n \sheetnormaldirection \otimes \sheetnormaldirection + * \right), + * @f] + * where @f$\sheetdirection@f$ and @f$\sheetnormaldirection@f$ are the sheet and + * sheet-normal directions, respectively, and @f$\eta_f@f$, @f$\eta_s@f$, and + * @f$\eta_n@f$ are coefficients that define the distribution the active tension + * along the three principal directions. The coefficients must be such that + * @f$\eta_f + \eta_s + \eta_n = 1@f$. + * + * This class stores the values of @f$\eta_f@f$, @f$\eta_s@f$ and @f$\eta_n@f$, + * and provides the functions @ref ActiveStress::get_tension_fibers, + * @ref ActiveStress::get_tension_sheets and @ref + * ActiveStress::get_tension_sheet_normals to access @f$\eta_f \Tact@f$, + * @f$\eta_s \Tact@f$ and @f$\eta_n \Tact@f$, respectively. + * + * ### Implementing concrete active stress models + * + * To implement a new active stress model, the following steps need to be taken: + * + * 1. Create a new class derived from @ref ActiveStress. + * 2. Override the methods @ref init_local, @ref advance_time_step_local and + * @ref compute_active_tension_local, defining the initial condition, + * time evolution and active tension computation, respectively, for a single + * node. + * 3. Create a new class derived from @ref ActiveStressModelParameters to store + * the parameters specific to the new active stress model. + * 4. Override the methods @ref get_parameters, + * @ref read_model_specific_parameters and + * @ref distribute_model_specific_parameters to manage the parameters of the + * new active stress model. + * 5. Register the new class into the active stress model factory by using the + * macro @ref REGISTER_ACTIVE_STRESS_MODEL. The macro should be called in a + * `.cpp` file, not in a header file. + * + * Notice that if the model is expressed in terms of a system of ODEs, it can + * be implemented by deriving from @ref ActiveStressODE, which already addresses + * some of the points above. + */ +class ActiveStress { +public: + /** + * @brief Constructor. + * + * @param n_states_ Number of state variables for this model. + */ + ActiveStress(const unsigned int n_states_) : n_states(n_states_) {} + + /** + * @brief Virtual destructor. + */ + virtual ~ActiveStress() = default; + + /** + * @brief Construct an instance of model parameters for this model. + */ + virtual std::unique_ptr + get_parameters() const = 0; + + /** + * @brief Read model parameters from a parameter object. + */ + void read_parameters(const ActiveStressParameters ¶ms); + + /** + * @brief Distribute model parameters to all parallel processes. + */ + void distribute_parameters(const CmMod &cm_mod, const cmType &cm); + + /** + * @brief Get the tension along fibers @f$\eta_f \Tact@f$ at a given point. + */ + double get_tension_fibers(const int idx) const { + return eta_f * active_tension[idx]; + } + + /** + * @brief Get the tension along sheets @f$\eta_s \Tact@f$ at a given point. + */ + double get_tension_sheets(const int idx) const { + return eta_s * active_tension[idx]; + } + + /** + * @brief Get the tension along sheet normals @f$\eta_n \Tact@f$ at a given + * point. + */ + double get_tension_sheet_normals(const int idx) const { + return eta_n * active_tension[idx]; + } + + /** + * @brief Initialize the model. + * + * Allocates the internal state vector and initializes it with the model's + * initial conditions. + * + * @param[in] tnNo Total number of mesh nodes for the current rank. + */ + virtual void init(const unsigned int tnNo); + + /** + * @brief Advance in time. + * + * @param[in] t Current time (i.e. the time instant being advanced to). + * @param[in] dt Time step size. + * @param[in] calcium Calcium concentration at every node. + * @param[in] fiber_stretch Fiber stretch at every node. This is usually + * computed with post::fib_stretch. + * @param[in] fiber_stretch_rate Fiber stretch rate at every node. This is + * usually computed with post::fib_stretch_rate. + */ + virtual void advance_time_step(const double t, const double dt, + const Vector &calcium, + const Vector &fiber_stretch, + const Vector &fiber_stretch_rate); + + /// Number of state variables for this model. + const unsigned int n_states; + +protected: + /** + * @brief Read model parameters from a parameter object. + * + * This method needs to be overridden by derived classes to read the + * parameters specific to the concrete model they implement. + */ + virtual void + read_model_specific_parameters(const ActiveStressModelParameters ¶ms) = 0; + + /** + * @brief Distribute model parameters to all parallel processes. + * + * This method needs to be overridden by derived classes to distribute the + * parameters specific to the concrete model they implement to all parallel + * processes. + */ + virtual void distribute_model_specific_parameters(const CmMod &cm_mod, + const cmType &cm) = 0; + + /** + * @brief Initialize the state vector for a single node. + * + * @param[out] state State vector for a single node, to be initialized by + * this function. + */ + virtual void init_local(Vector &state) const = 0; + + /** + * @brief Advance in time for a single node. + * + * @param[in] t Current time (i.e. the time instant being advanced to). + * @param[in] dt Time step size. + * @param[in] calcium Calcium concentration at the current node. + * @param[in] fiber_stretch Fiber stretch at the current node. + * @param[in] fiber_stretch_rate Fiber stretch rate at the current node. + * @param[in,out] state State vector for a single node, to be updated by + * this function. + */ + virtual void advance_time_step_local(const double t, const double dt, + const double calcium, + const double fiber_stretch, + const double fiber_stretch_rate, + Vector &state) const = 0; + + /** + * @brief Compute the active tension for a single node. + */ + virtual double + compute_active_tension_local(const Vector &state) const = 0; + + /// Current time. Updated whenever calling @ref advance_time_step. + double time; + + /// State variables for the model. + Array states; + + /// Active tension at every node. + Vector active_tension; + + /// Active tension coefficient along the fiber direction. + double eta_f; + + /// Active tension coefficient along the sheet direction. + double eta_s; + + /// Active tension coefficient along the sheet-normal direction. + double eta_n; +}; + +/** + * @brief Alias for the active stress model factory. + * + * See the documentation for @ref Factory for more details on how this works. + */ +using ActiveStressFactory = Factory; + +/** + * @brief Macro to register an active stress model in the factory. + */ +#define REGISTER_ACTIVE_STRESS_MODEL(name, type) \ + REGISTER_IN_FACTORY(ActiveStress, type, name) + +#endif \ No newline at end of file diff --git a/Code/Source/solver/active_stress_nash_panfilov.cpp b/Code/Source/solver/active_stress_nash_panfilov.cpp new file mode 100644 index 000000000..3e23f21b2 --- /dev/null +++ b/Code/Source/solver/active_stress_nash_panfilov.cpp @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the +// University of California, and others. SPDX-License-Identifier: BSD-3-Clause + +#include "active_stress_nash_panfilov.h" + +void NashPanfilov::read_model_specific_parameters( + const ActiveStressModelParameters ¶ms) { + ActiveStressODE::read_model_specific_parameters(params); + + epsilon_0 = params.get_scalar("epsilon_0"); + epsilon_i = params.get_scalar("epsilon_i"); + xi_T = params.get_scalar("xi_T"); + calcium_rest = params.get_scalar("calcium_rest"); + calcium_crit = params.get_scalar("calcium_crit"); + eta_T = params.get_scalar("eta_T"); +} + +void NashPanfilov::distribute_model_specific_parameters(const CmMod &cm_mod, + const cmType &cm) { + ActiveStressODE::distribute_model_specific_parameters(cm_mod, cm); + + cm.bcast(cm_mod, &epsilon_0); + cm.bcast(cm_mod, &epsilon_i); + cm.bcast(cm_mod, &xi_T); + cm.bcast(cm_mod, &calcium_rest); + cm.bcast(cm_mod, &calcium_crit); + cm.bcast(cm_mod, &eta_T); +} + +void NashPanfilov::init_local(Vector &state) const { state[0] = 0.0; } + +Vector NashPanfilov::getf(const double t, const Vector &state, + const double calcium, + const double fiber_stretch, + const double fiber_stretch_rate) const { + Vector f(1); + + const double epsilon = + epsilon_0 + (epsilon_i - epsilon_0) * + std::exp(-std::exp(-xi_T * (calcium - calcium_crit))); + + f[0] = epsilon * (eta_T * (calcium - calcium_rest) - state[0]); + + return f; +} + +double +NashPanfilov::compute_active_tension_local(const Vector &state) const { + return state[0]; +} + +REGISTER_ACTIVE_STRESS_MODEL("NashPanfilov", NashPanfilov); \ No newline at end of file diff --git a/Code/Source/solver/active_stress_nash_panfilov.h b/Code/Source/solver/active_stress_nash_panfilov.h new file mode 100644 index 000000000..a164be7c6 --- /dev/null +++ b/Code/Source/solver/active_stress_nash_panfilov.h @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the +// University of California, and others. SPDX-License-Identifier: BSD-3-Clause + +#ifndef ACTIVE_STRESS_NASH_PANFILOV_H +#define ACTIVE_STRESS_NASH_PANFILOV_H + +#include "active_stress_ode.h" + +/** + * @brief Nash-Panfilov active stress model. + * + * This class implements the Nash-Panfilov active stress model [1], with the + * modifications introduced by Goktepe and Kuhl [2]. + * + * The model equations are the following: + * @f[ \begin{aligned} + * \dv{\Tact}{t} &= \varepsilon(\calcium)( + * \eta_\text{T} (\calcium - \calcium_\text{rest}) - \Tact)\;, \\ + * \varepsilon(\calcium) &= + * \varepsilon_0 + (\varepsilon_i - \varepsilon_0) + * \exp(-\exp(-\xi_T (\calcium - \calcium_\text{crit})))\;, + * \end{aligned} @f] + * where @f$\eta_\text{T}@f$, @f$\calcium_\text{rest}@f$, + * @f$\calcium_\text{crit}@f$, @f$\varepsilon_0@f$, @f$\varepsilon_i@f$ and + * @f$\xi_T@f$ are user-defined model parameters. The function + * @f$\varepsilon(\calcium)@f$ is a sigmoidal-shaped calcium-dependent time + * constant (see Figure 3 in [2] for more details). + * + * @note The sensitivity of the model to calcium is controlled by the paramter + * @f$\eta_\text{T}@f$, which has the same units of active tension over calcium. + * Therefore, if the ionic model providing the calcium is phenomenological (see + * @ref IonicModel) and calcium is non-dimensional, this parameter may need to + * be rescaled as well. Similar considerations apply to @f$\xi_T@f$. + * + * **References**: + * 1. [Nash, Panfilov (2004)](https://doi.org/10.1016/j.pbiomolbio.2004.01.016) + * 2. [Goktepe, Kuhl (2009)](https://doi.org/10.1007/s00466-009-0434-z) + */ +class NashPanfilov : public ActiveStressODE { +public: + /// Model label. + static inline const std::string label = "NashPanfilov"; + + /// Model parameters class. + class Parameters : public ActiveStressODE::Parameters { + public: + Parameters() : ActiveStressODE::Parameters(label) { + constexpr bool required = true; + + add_parameter("epsilon_0", 1.0, required); + add_parameter("epsilon_i", 1.0, required); + add_parameter("xi_T", 1.0, required); + add_parameter("calcium_rest", 1.0, required); + add_parameter("calcium_crit", 1.0, required); + add_parameter("eta_T", 1.0, required); + } + }; + + /** + * @brief Constructor. + */ + NashPanfilov() : ActiveStressODE(1) {} + + /** + * @brief Construct an instance of model parameters. + */ + virtual std::unique_ptr + get_parameters() const override { + return std::make_unique(); + } + +protected: + /** + * @brief Read model parameters from a parameter object. + */ + virtual void read_model_specific_parameters( + const ActiveStressModelParameters ¶ms) override; + + /** + * @brief Distribute model parameters to all parallel processes. + */ + virtual void distribute_model_specific_parameters(const CmMod &cm_mod, + const cmType &cm) override; + + /** + * @brief Initialize the state vector for a single node. + * + * @param[out] state State vector for a single node, to be initialized by + * this function. + */ + virtual void init_local(Vector &state) const override; + + /** + * @brief Compute the rate of change in the state variables. + */ + virtual Vector getf(const double t, const Vector &state, + const double calcium, const double fiber_stretch, + const double fiber_stretch_rate) const override; + + /** + * @brief Compute the active tension for a single node. + */ + virtual double + compute_active_tension_local(const Vector &state) const override; + + /// @name Model parameters. + /// @{ + + /// Minimum time constant @f$\varepsilon_0@f$. The unit of measure for this + /// parameter must be the inverse of the unit of measure for time. + double epsilon_0; + + /// Maximum time constant @f$\varepsilon_i@f$. The unit of measure for this + /// parameter must be the inverse of the unit of measure for time. + double epsilon_i; + + /// Sigmoidal function steepness @f$\xi_T@f$. The unit of measure for this + /// parameter must be the inverse of the unit of measure for calcium + /// concentration. + double xi_T; + + /// Resting calcium value. Active tension will increase if calcium is above + /// this value. + double calcium_rest; + + /// Critical calcium value, i.e. the threshold value for switching between + /// minimum and maximum time constant. + double calcium_crit; + + /// @f$\eta_T@f$. The unit of measure for this parameter must be the ratio of + /// the unit for tension and the unit for calcium concentration. + double eta_T; + + /// @} +}; + +#endif \ No newline at end of file diff --git a/Code/Source/solver/active_stress_ode.cpp b/Code/Source/solver/active_stress_ode.cpp new file mode 100644 index 000000000..99909cf7f --- /dev/null +++ b/Code/Source/solver/active_stress_ode.cpp @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the +// University of California, and others. SPDX-License-Identifier: BSD-3-Clause + +#include "active_stress_ode.h" + +void ActiveStressODE::read_model_specific_parameters( + const ActiveStressModelParameters ¶ms) { + const std::string solver_str = params.get_string("ODE_solver"); + + if (solver_str == "FE") { + ode_solver = ODESolver::ForwardEuler; + } else { + svmp::raise("Unknown ODE solver " + solver_str + + " for active stress models."); + } +} + +void ActiveStressODE::distribute_model_specific_parameters(const CmMod &cm_mod, + const cmType &cm) { + cm.bcast_enum(cm_mod, &ode_solver); +} + +void ActiveStressODE::advance_time_step_local(const double t, const double dt, + const double calcium, + const double fiber_stretch, + const double fiber_stretch_rate, + Vector &state) const { + if (ode_solver == ODESolver::ForwardEuler) { + const Vector f = + getf(t - dt, state, calcium, fiber_stretch, fiber_stretch_rate); + state.add(dt, f); + } else { + svmp::raise( + "Unknown ODE solver " + std::to_string(static_cast(ode_solver)) + + " for active stress models."); + } +} \ No newline at end of file diff --git a/Code/Source/solver/active_stress_ode.h b/Code/Source/solver/active_stress_ode.h new file mode 100644 index 000000000..f0696eabe --- /dev/null +++ b/Code/Source/solver/active_stress_ode.h @@ -0,0 +1,155 @@ +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the +// University of California, and others. SPDX-License-Identifier: BSD-3-Clause + +#ifndef ACTIVE_STRESS_ODE_H +#define ACTIVE_STRESS_ODE_H + +#include "active_stress.h" + +/** + * @brief Abstract ODE-based active stress model. + * + * This class provides an interface for defining active stress models for which + * the evolution of the state vector @f$\astressstate@f$ is governed by a system + * of ordinary differential equations (ODEs): + * @f[ \begin{aligned} + * \dv{\astressstate}{t} &= + * \mathbf{F}_\text{AS}(t, \astressstate, \calcium, \fiberstretch, + * \fiberstretchrate)\;, \\ + * \Tact &= \Tact(\astressstate)\;. + * \end{aligned} @f] + * + * ### Numerical methods + * + * The ODE system is advanced with one of the methods described in + * @ref ODESolver. After that, the active tension is computed for every node + * @f$i@f$ as: + * @f[ + * {\Tact}_{i}^{n+1} = \Tact(\astressstate_i^{n+1})\;. + * @f] + * + * ### Implementing derived models + * + * To implement a new ODE-based active stress model, the following steps need to + * be taken: + * + * 1. Create a new class derived from @ref ActiveStressODE. + * 2. Override the method init_local (from the base class @ref ActiveStress) to + * define the initial condition for the state vector at a single node. + * 3. Override the method @ref getf to define the right-hand side function + * @f$\mathbf{F}_\text{AS}@f$ of the ODE system. + * 4. Create a new class derived from @ref ActiveStressODE::Parameters to store + * the parameters specific to the new active stress model. + * 5. Override the methods @ref get_parameters, @ref read_parameters and + * @ref distribute_parameters to manage the parameters of the new active + * stress model. Both read_parameters and distribute_parameters need to call + * the corresponding method of ActiveStressODE. + * 6. Register the new class into the active stress model factory by using the + * macro @ref REGISTER_ACTIVE_STRESS_MODEL. The macro should be called in a + * `.cpp` file, not in a header file. + */ +class ActiveStressODE : public ActiveStress { +public: + /** + * @brief Enumeration of supported ODE solvers. + * + * In the documentation of the individual methods below, the subscript @f$i@f$ + * is used to denote the value of variables at the @f$i@f$-th node, and the + * superscript @f$n@f$ is used to denote the time step. + */ + enum class ODESolver { + /** + * @brief Forward Euler. + * + * The state vector is updated as follows: + * @f[ + * \astressstate_i^{n+1} = \astressstate_i^n + * + \Delta t \mathbf{F}_\text{AS}(t^n, \astressstate_i^n, \calcium_i^n, + * \fiberstretch_i^n, + * \fiberstretchrate_i^n)\;. + * @f] + */ + ForwardEuler + }; + + /** + * @brief Model parameters class. + * + * Declares parameters that are common to all ODE-based active stress models. + * Classes derived from ActiveStressODE one should declare their own + * Parameters class, deriving from ActiveStressODE::parameters. + */ + class Parameters : public ActiveStressModelParameters { + public: + Parameters(const std::string &label) : ActiveStressModelParameters(label) { + constexpr bool required = true; + + add_parameter("ODE_solver", std::string("FE"), required); + } + }; + + /** + * @brief Constructor. + * + * @param n_states Number of state variables for this model. + */ + ActiveStressODE(const unsigned int n_states) : ActiveStress(n_states) {} + +protected: + /** + * @brief Read model parameters from a parameter object. + */ + virtual void read_model_specific_parameters( + const ActiveStressModelParameters ¶ms) override; + + /** + * @brief Distribute model parameters to all parallel processes. + */ + virtual void distribute_model_specific_parameters(const CmMod &cm_mod, + const cmType &cm) override; + + /** + * @brief Advance in time for a single node. + * + * Solves one forward Euler time step for the ODE system. + * + * @param[in] t Current time (i.e. the time instant being advanced to). + * @param[in] dt Time step size. + * @param[in] calcium Calcium concentration at the current node. + * @param[in] fiber_stretch Fiber stretch at the current node. + * @param[in] fiber_stretch_rate Fiber stretch rate at the current node. + * @param[in,out] state State vector for a single node, to be updated by + * this function. + * + * @todo[michelebucelli] It might be necessary or useful to implement other + * timestepping schemes, e.g. Runge-Kutta. In that case, we might want to + * expand the interface to support implicit time stepping too, e.g. by + * adding a method to evaluate the Jacobian matrix of the system, as in + * @ref IonicModel. + */ + virtual void advance_time_step_local(const double t, const double dt, + const double calcium, + const double fiber_stretch, + const double fiber_stretch_rate, + Vector &state) const override; + + /** + * @brief Compute the rate of change in the state variables. + * + * @param[in] t Current time (i.e. the time instant being advanced to). + * @param[in] dt Time step size. + * @param[in] calcium Calcium concentration at the current node. + * @param[in] fiber_stretch Fiber stretch at the current node. + * @param[in] fiber_stretch_rate Fiber stretch rate at the current node. + * + * @return A vector containing the rate of change for each state variable. + */ + virtual Vector getf(const double t, const Vector &state, + const double calcium, const double fiber_stretch, + const double fiber_stretch_rate) const = 0; + + /// ODE solver. + ODESolver ode_solver; +}; + +#endif \ No newline at end of file diff --git a/Code/Source/solver/active_stress_uniform_steady.cpp b/Code/Source/solver/active_stress_uniform_steady.cpp new file mode 100644 index 000000000..3c72855de --- /dev/null +++ b/Code/Source/solver/active_stress_uniform_steady.cpp @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the +// University of California, and others. SPDX-License-Identifier: BSD-3-Clause + +#include "active_stress_uniform_steady.h" + +void UniformSteadyActiveStress::read_model_specific_parameters( + const ActiveStressModelParameters ¶ms) { + value = params.get_scalar("Value"); +} + +void UniformSteadyActiveStress::distribute_model_specific_parameters( + const CmMod &cm_mod, const cmType &cm) { + cm.bcast(cm_mod, &value); +} + +REGISTER_ACTIVE_STRESS_MODEL("UniformSteady", UniformSteadyActiveStress); \ No newline at end of file diff --git a/Code/Source/solver/active_stress_uniform_steady.h b/Code/Source/solver/active_stress_uniform_steady.h new file mode 100644 index 000000000..93676f9b0 --- /dev/null +++ b/Code/Source/solver/active_stress_uniform_steady.h @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the +// University of California, and others. SPDX-License-Identifier: BSD-3-Clause + +#ifndef ACTIVE_STRESS_UNIFORM_STEADY_H +#define ACTIVE_STRESS_UNIFORM_STEADY_H + +#include "active_stress.h" + +/** + * @brief Uniform and steady active stress model. + * + * Defines an active tension that is constant in space and time, i.e. + * @f[ + * \Tact(t, \calcium, \fiberstretch, \fiberstretchrate, \astressstate) = g\;, + * @f] + * where @f$g@f$ is a user-defined constant value. + */ +class UniformSteadyActiveStress : public ActiveStress { +public: + /// Model label. + static inline const std::string label = "UniformSteady"; + + /// Model parameters class. + class Parameters : public ActiveStressModelParameters { + public: + Parameters() : ActiveStressModelParameters(label) { + constexpr bool required = true; + + add_parameter("Value", 0.0, required); + } + }; + + /** + * @brief Constructor. + */ + UniformSteadyActiveStress() : ActiveStress(/* n_states = */ 0) {} + + /** + * @brief Construct an instance of model parameters. + */ + virtual std::unique_ptr + get_parameters() const override { + return std::make_unique(); + } + +protected: + /** + * @brief Read model parameters from a parameter object. + */ + virtual void read_model_specific_parameters( + const ActiveStressModelParameters ¶ms) override; + + /** + * @brief Distribute model parameters to all parallel processes. + */ + virtual void distribute_model_specific_parameters(const CmMod &cm_mod, + const cmType &cm) override; + + /** + * @brief Initialize the state vector for a single node. + * + * This model has no states, so this function does nothing. + */ + virtual void init_local(Vector &state) const override {} + + /** + * @brief Advance in time for a single node. + * + * This model has no states, so this function does nothing. + */ + virtual void advance_time_step_local(const double t, const double dt, + const double calcium, + const double fiber_stretch, + const double fiber_stretch_rate, + Vector &state) const override {} + + /** + * @brief Compute the active tension for a single node. + */ + virtual double + compute_active_tension_local(const Vector &state) const override { + return value; + } + + /// Active tension value. + double value; +}; + +#endif \ No newline at end of file diff --git a/Code/Source/solver/active_stress_uniform_unsteady.cpp b/Code/Source/solver/active_stress_uniform_unsteady.cpp new file mode 100644 index 000000000..5d02d76b2 --- /dev/null +++ b/Code/Source/solver/active_stress_uniform_unsteady.cpp @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the +// University of California, and others. SPDX-License-Identifier: BSD-3-Clause + +#include "active_stress_uniform_unsteady.h" + +#include +#include + +void UniformUnsteadyActiveStress::init(const unsigned int tnNo) { + ActiveStress::init(tnNo); + + fourier_interpolation = FourierInterpolation::from_time_series_file( + temporal_values_file_path, /* n_components = */ 1, ramp); +} + +void UniformUnsteadyActiveStress::read_model_specific_parameters( + const ActiveStressModelParameters ¶ms) { + ramp = params.get_bool("Ramp"); + temporal_values_file_path = params.get_string("Temporal_values_file_path"); +} + +void UniformUnsteadyActiveStress::distribute_model_specific_parameters( + const CmMod &cm_mod, const cmType &cm) { + cm.bcast(cm_mod, &ramp); + cm.bcast(cm_mod, temporal_values_file_path); +} + +double UniformUnsteadyActiveStress::compute_active_tension_local( + const Vector &state) const { + return fourier_interpolation.value(time)[0]; +} + +REGISTER_ACTIVE_STRESS_MODEL("UniformUnsteady", UniformUnsteadyActiveStress); \ No newline at end of file diff --git a/Code/Source/solver/active_stress_uniform_unsteady.h b/Code/Source/solver/active_stress_uniform_unsteady.h new file mode 100644 index 000000000..696be51ab --- /dev/null +++ b/Code/Source/solver/active_stress_uniform_unsteady.h @@ -0,0 +1,105 @@ +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the +// University of California, and others. SPDX-License-Identifier: BSD-3-Clause + +#ifndef ACTIVE_STRESS_UNIFORM_UNSTEADY_H +#define ACTIVE_STRESS_UNIFORM_UNSTEADY_H + +#include "FourierInterpolation.h" + +#include "active_stress.h" + +/** + * @brief Uniform and time dependent active stress model. + * + * Defines an active tension that is constant in space but time dependent, i.e. + * @f[ + * \Tact(t, \calcium, \fiberstretch, \fiberstretchrate, \astressstate) = + * g(t)\;, + * @f] + * where @f$g(t)@f$ is a user-defined function of time. + */ +class UniformUnsteadyActiveStress : public ActiveStress { +public: + /// Model label. + static inline const std::string label = "UniformUnsteady"; + + /// Model parameters class. + class Parameters : public ActiveStressModelParameters { + public: + Parameters() : ActiveStressModelParameters(label) { + constexpr bool required = true; + + add_parameter("Ramp", false, required); + add_parameter("Temporal_values_file_path", std::string(""), required); + } + }; + + /** + * @brief Constructor. + */ + UniformUnsteadyActiveStress() : ActiveStress(/* n_states = */ 0) {} + + /** + * @brief Construct an instance of model parameters. + */ + virtual std::unique_ptr + get_parameters() const override { + return std::make_unique(); + } + + /** + * @brief Initialization. + * + * Calls the parent class initialization method, and reads the Fourier + * coefficient from file. + */ + virtual void init(const unsigned int tnNo) override; + +protected: + /** + * @brief Read model parameters from a parameter object. + */ + virtual void read_model_specific_parameters( + const ActiveStressModelParameters ¶ms) override; + + /** + * @brief Distribute model parameters to all parallel processes. + */ + virtual void distribute_model_specific_parameters(const CmMod &cm_mod, + const cmType &cm) override; + + /** + * @brief Initialize the state vector for a single node. + * + * This model has no states, so this function does nothing. + */ + virtual void init_local(Vector &state) const override {} + + /** + * @brief Advance in time for a single node. + * + * This model has no states, so this function does nothing. + */ + virtual void advance_time_step_local(const double t, const double dt, + const double calcium, + const double fiber_stretch, + const double fiber_stretch_rate, + Vector &state) const override {} + + /** + * @brief Compute the active tension for a single node. + */ + virtual double + compute_active_tension_local(const Vector &state) const override; + + /// Toggle between ramp or Fourier transform. + bool ramp; + + /// Name of the file containing the temporal values. + std::string temporal_values_file_path; + + /// Fourier interpolation of the time dependent data. + FourierInterpolation fourier_interpolation; +}; + +#endif \ No newline at end of file diff --git a/Code/Source/solver/cep_ion.cpp b/Code/Source/solver/cep_ion.cpp index 8b26ff908..479e77610 100644 --- a/Code/Source/solver/cep_ion.cpp +++ b/Code/Source/solver/cep_ion.cpp @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the University of California, and others. -// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the +// University of California, and others. SPDX-License-Identifier: BSD-3-Clause #include "cep_ion.h" @@ -16,35 +16,34 @@ namespace cep_ion { /// cep_mod.Xion /// \endcode // -void cep_init(Simulation* simulation) -{ +void cep_init(Simulation *simulation) { using namespace consts; - auto& com_mod = simulation->com_mod; + auto &com_mod = simulation->com_mod; - #define n_debug_cep_init - #ifdef debug_cep_init +#define n_debug_cep_init +#ifdef debug_cep_init DebugMsg dmsg(__func__, com_mod.cm.idcm()); dmsg.banner(); - #endif +#endif - auto& cm = com_mod.cm; - auto& cep_mod = simulation->cep_mod; + auto &cm = com_mod.cm; + auto &cep_mod = simulation->cep_mod; const int nsd = com_mod.nsd; const int tnNo = com_mod.tnNo; const int nXion = cep_mod.nXion; - #ifdef debug_cep_init +#ifdef debug_cep_init dmsg << "tnNo: " << tnNo; dmsg << "nXion: " << nXion; - #endif +#endif - for (auto& eq : com_mod.eq) { + for (auto &eq : com_mod.eq) { if (eq.phys != EquationType::phys_CEP) { continue; } if (com_mod.dmnId.size() != 0) { Vector sA(tnNo); - Array sF(nXion,tnNo); + Array sF(nXion, tnNo); for (int a = 0; a < tnNo; a++) { if (!all_fun::is_domain(com_mod, eq, a, EquationType::phys_CEP)) { @@ -53,13 +52,14 @@ void cep_init(Simulation* simulation) for (int iDmn = 0; iDmn < eq.nDmn; iDmn++) { auto cPhys = eq.dmn[iDmn].phys; int dID = eq.dmn[iDmn].Id; - if ((cPhys != EquationType::phys_CEP) || (dID >= 0 && !utils::btest(com_mod.dmnId(a),dID))) { + if ((cPhys != EquationType::phys_CEP) || + (dID >= 0 && !utils::btest(com_mod.dmnId(a), dID))) { continue; } int nX = eq.dmn[iDmn].cep.nX; int nG = eq.dmn[iDmn].cep.nG; - Vector Xl(nX); + Vector Xl(nX); Vector Xgl(nG); eq.dmn[iDmn].cep.ionic_model->init(Xl, Xgl); @@ -67,11 +67,11 @@ void cep_init(Simulation* simulation) sA(a) = sA(a) + 1.0; for (int i = 0; i < nX; i++) { - sF(i,a) = sF(i,a) + Xl(i); + sF(i, a) = sF(i, a) + Xl(i); } for (int i = 0; i < nG; i++) { - sF(i+nX,a) = sF(i+nX,a) + Xgl(i); + sF(i + nX, a) = sF(i + nX, a) + Xgl(i); } } } @@ -82,28 +82,28 @@ void cep_init(Simulation* simulation) for (int a = 0; a < tnNo; a++) { if (!utils::is_zero(sA(a))) { for (int i = 0; i < cep_mod.Xion.nrows(); i++) { - cep_mod.Xion(i,a) = sF(i,a) / sA(a); + cep_mod.Xion(i, a) = sF(i, a) / sA(a); } } } } else { - for (int a = 0; a < tnNo; a++) { + for (int a = 0; a < tnNo; a++) { if (!all_fun::is_domain(com_mod, eq, a, EquationType::phys_CEP)) { continue; } int nX = eq.dmn[0].cep.nX; int nG = eq.dmn[0].cep.nG; - Vector Xl(nX); + Vector Xl(nX); Vector Xgl(nG); eq.dmn[0].cep.ionic_model->init(Xl, Xgl); for (int i = 0; i < nX; i++) { - cep_mod.Xion(i,a) = Xl(i); + cep_mod.Xion(i, a) = Xl(i); } for (int i = 0; i < nG; i++) { - cep_mod.Xion(i+nX,a) = Xgl(i); + cep_mod.Xion(i + nX, a) = Xgl(i); } } } @@ -115,131 +115,109 @@ void cep_init(Simulation* simulation) //----------- // State variable integration. // -void cep_integ(Simulation* simulation, const int iEq, const int iDof, SolutionStates& solutions) -{ - auto& Yo = solutions.old.get_velocity(); +void cep_integ(Simulation *simulation, const int iEq, const int iDof, + SolutionStates &solutions, const Vector &I4f) { + auto &Yo = solutions.old.get_velocity(); static bool IPASS = true; using namespace consts; - auto& com_mod = simulation->com_mod; + auto &com_mod = simulation->com_mod; - #define n_debug_cep_integ - #ifdef debug_cep_integ +#define n_debug_cep_integ +#ifdef debug_cep_integ DebugMsg dmsg(__func__, com_mod.cm.idcm()); dmsg.banner(); - #endif +#endif - auto& cm = com_mod.cm; + auto &cm = com_mod.cm; int tnNo = com_mod.tnNo; double dt = com_mod.dt; double time = com_mod.time; - auto& cep_mod = simulation->cep_mod; - auto& cem = cep_mod.cem; - auto& eq = com_mod.eq[iEq]; + auto &cep_mod = simulation->cep_mod; + auto &cem = cep_mod.cem; + auto &eq = com_mod.eq[iEq]; - auto& Xion = cep_mod.Xion; + auto &Xion = cep_mod.Xion; int nXion = cep_mod.nXion; - Vector I4f(tnNo); - - #ifdef debug_cep_integ +#ifdef debug_cep_integ dmsg << "cem.cpld: " << cem.cpld; dmsg << "time: " << time; - #endif - - // Electromechanics: get fiber stretch for stretch activated currents - // - if (cem.cpld) { - for (int iM = 0; iM < com_mod.nMsh; iM++) { - auto& msh = com_mod.msh[iM]; - - if (msh.nFn != 0) { - Vector sA(msh.nNo); - post::fib_stretch(com_mod, iEq, msh, solutions.current.get_displacement(), sA); - for (int a = 0; a < msh.nNo; a++) { - int Ac = msh.gN(a); - I4f(Ac) = sA(a); - } - } - } - } +#endif // Ignore first pass as Xion is already initialized if (IPASS) { IPASS = false; - // Copy action potential after diffusion as first state variable + // Copy action potential after diffusion as first state variable } else { for (int Ac = 0; Ac < tnNo; Ac++) { - Xion(0,Ac) = Yo(iDof,Ac); + Xion(0, Ac) = Yo(iDof, Ac); } } // Integrate electric potential based on cellular activation model // if (com_mod.dmnId.size() != 0) { - Vector sA(tnNo); - Array sF(nXion,tnNo); + Vector sA(tnNo); + Array sF(nXion, tnNo); Vector sY(tnNo); + cep_mod.calcium = 0.0; + for (int Ac = 0; Ac < tnNo; Ac++) { if (!all_fun::is_domain(com_mod, eq, Ac, Equation_CEP)) { continue; } for (int iDmn = 0; iDmn < eq.nDmn; iDmn++) { - auto& dmn = eq.dmn[iDmn]; + auto &dmn = eq.dmn[iDmn]; auto cPhys = dmn.phys; int dID = dmn.Id; - if (cPhys != Equation_CEP || (dID >= 0 && !utils::btest(com_mod.dmnId(Ac),dID))) { + if (cPhys != Equation_CEP || + (dID >= 0 && !utils::btest(com_mod.dmnId(Ac), dID))) { continue; - } + } int nX = dmn.cep.nX; int nG = dmn.cep.nG; - #ifdef debug_cep_integ - dmsg << "nX: " << nX ; - dmsg << "nG: " << nG ; - #endif +#ifdef debug_cep_integ + dmsg << "nX: " << nX; + dmsg << "nG: " << nG; +#endif - auto Xl = Xion.rows(0,nX-1,Ac); + auto Xl = Xion.rows(0, nX - 1, Ac); // [NOTE] nG can be 0. Vector Xgl; if (nG != 0) { Xgl.resize(nG); for (int i = 0; i < nG; i++) { - Xgl(i) = Xion(i+nX,Ac); + Xgl(i) = Xion(i + nX, Ac); } } - double yl = 0.0; - if (cem.cpld) { - yl = cem.Ya(Ac); - } - - cep_integ_l(cep_mod, dmn.cep, Xl, Xgl, time - dt, yl, I4f(Ac), dt, com_mod.x.col(Ac)); + cep_integ_l(cep_mod, dmn.cep, Xl, Xgl, time - dt, I4f(Ac), dt, + com_mod.x.col(Ac)); + cep_mod.calcium[Ac] += Xl[dmn.cep.ionic_model->get_calcium_index()]; sA(Ac) = sA(Ac) + 1.0; for (int i = 0; i < nX; i++) { - sF(i,Ac) += Xl(i); + sF(i, Ac) += Xl(i); } for (int i = 0; i < nG; i++) { - sF(nX+i,Ac) += Xgl(i); - } - - if (cem.cpld) { - sY(Ac) = sY(Ac) + yl; + sF(nX + i, Ac) += Xgl(i); } } } all_fun::commu(com_mod, sA); all_fun::commu(com_mod, sF); + all_fun::commu(com_mod, cep_mod.calcium); if (cem.cpld) { all_fun::commu(com_mod, sY); @@ -248,9 +226,7 @@ void cep_integ(Simulation* simulation, const int iEq, const int iDof, SolutionSt for (int Ac = 0; Ac < tnNo; Ac++) { if (!utils::is_zero(sA(Ac))) { Xion.set_col(Ac, sF.col(Ac) / sA(Ac)); - if (cem.cpld) { - cem.Ya(Ac) = sY(Ac) / sA(Ac); - } + cep_mod.calcium[Ac] = cep_mod.calcium[Ac] / sA(Ac); } } @@ -262,32 +238,25 @@ void cep_integ(Simulation* simulation, const int iEq, const int iDof, SolutionSt int nX = eq.dmn[0].cep.nX; int nG = eq.dmn[0].cep.nG; - auto Xl = Xion.rows(0,nX-1,Ac); - auto Xgl = Xion.rows(nX,nX+nG-1,Ac); + auto Xl = Xion.rows(0, nX - 1, Ac); + auto Xgl = Xion.rows(nX, nX + nG - 1, Ac); - double yl = 0.0; - if (cem.cpld) { - yl = cem.Ya(Ac); - } - - cep_integ_l(cep_mod, eq.dmn[0].cep, Xl, Xgl, time - dt, yl, I4f(Ac), dt, com_mod.x.col(Ac)); + cep_integ_l(cep_mod, eq.dmn[0].cep, Xl, Xgl, time - dt, I4f(Ac), dt, + com_mod.x.col(Ac)); + cep_mod.calcium[Ac] = Xl[eq.dmn[0].cep.ionic_model->get_calcium_index()]; for (int i = 0; i < nX; i++) { - Xion(i,Ac) = Xl(i); + Xion(i, Ac) = Xl(i); } for (int i = 0; i < nG; i++) { - Xion(nX+i,Ac) = Xgl(i); - } - - if (cem.cpld) { - cem.Ya(Ac) = yl; + Xion(nX + i, Ac) = Xgl(i); } } } for (int Ac = 0; Ac < tnNo; Ac++) { - Yo(iDof,Ac) = Xion(0,Ac); + Yo(iDof, Ac) = Xion(0, Ac); } } @@ -299,16 +268,15 @@ void cep_integ(Simulation* simulation, const int iEq, const int iDof, SolutionSt // mechanics. The equations are integrated at domain nodes. // void cep_integ_l(CepMod &cep_mod, cepModelType &cep, Vector &X, - Vector &Xg, const double t1, double &yl, - const double I4f, const double dt, - const Vector& x) { + Vector &Xg, const double t1, const double I4f, + const double dt, const Vector &x) { using namespace consts; - #define n_debug_cep_integ_l - #ifdef debug_cep_integ_l +#define n_debug_cep_integ_l +#ifdef debug_cep_integ_l DebugMsg dmsg(__func__, cep_mod.cm.idcm()); dmsg.banner(); - #endif +#endif // Feedback coefficient for stretch-activated-currents const double Ksac = I4f > 1.0 ? cep.Ksac * (sqrt(I4f) - 1.0) : 0.0; @@ -316,12 +284,12 @@ void cep_integ_l(CepMod &cep_mod, cepModelType &cep, Vector &X, // Total time steps const unsigned nt = static_cast(dt / cep.dt); - #ifdef debug_cep_integ_l +#ifdef debug_cep_integ_l dmsg << "nt: " << nt; dmsg << "Ksac: " << Ksac; dmsg << "cep.cepType: " << cep.cepType; dmsg << "cep.odes.tIntTyp: " << cep.odes.tIntType; - #endif +#endif svmp::check_not_null( cep.ionic_model, "ionic model was not constructed."); @@ -333,8 +301,10 @@ void cep_integ_l(CepMod &cep_mod, cepModelType &cep, Vector &X, cep.ionic_model->integ(cep.odes, cep.imyo, t, cep.dt, Istim, Ksac, X, Xg); } - if (isnan(X(0)) || isnan(yl)) { - throw std::runtime_error("[cep_integ_l] A NaN has been computed during time integration of electrophysiology variables."); + if (isnan(X(0))) { + throw std::runtime_error( + "[cep_integ_l] A NaN has been computed during time integration of " + "electrophysiology variables."); } } -} +} // namespace cep_ion diff --git a/Code/Source/solver/cep_ion.h b/Code/Source/solver/cep_ion.h index 2d93e1847..754dec77f 100644 --- a/Code/Source/solver/cep_ion.h +++ b/Code/Source/solver/cep_ion.h @@ -1,13 +1,13 @@ -// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the University of California, and others. -// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the +// University of California, and others. SPDX-License-Identifier: BSD-3-Clause -#ifndef CEP_ION_H -#define CEP_ION_H +#ifndef CEP_ION_H +#define CEP_ION_H #include "Array.h" -#include "SolutionStates.h" #include "ComMod.h" #include "Simulation.h" +#include "SolutionStates.h" #include "all_fun.h" #include "consts.h" @@ -16,15 +16,14 @@ namespace cep_ion { -void cep_init(Simulation* simulation); +void cep_init(Simulation *simulation); -void cep_integ(Simulation* simulation, const int iEq, const int iDof, SolutionStates& solutions); +void cep_integ(Simulation *simulation, const int iEq, const int iDof, + SolutionStates &solutions, const Vector &I4f); void cep_integ_l(CepMod &cep_mod, cepModelType &cep, Vector &X, - Vector &Xg, const double t1, double &yl, - const double I4f, const double dt, - const Vector& x); -}; + Vector &Xg, const double t1, const double I4f, + const double dt, const Vector &x); +}; // namespace cep_ion #endif - diff --git a/Code/Source/solver/consts.h b/Code/Source/solver/consts.h index 7c52f5825..05983c4b0 100644 --- a/Code/Source/solver/consts.h +++ b/Code/Source/solver/consts.h @@ -314,30 +314,29 @@ enum class MeshGeneratorType /// Map for string to MeshGeneratorType. extern const std::map mesh_generator_name_to_type; -enum class OutputNameType -{ - outGrp_NA = 500, +enum class OutputNameType { + outGrp_NA = 500, outGrp_A = 501, - outGrp_Y = 502, - outGrp_D = 503, - outGrp_I = 504, - outGrp_WSS = 505, - outGrp_trac = 506, - outGrp_vort = 507, + outGrp_Y = 502, + outGrp_D = 503, + outGrp_I = 504, + outGrp_WSS = 505, + outGrp_trac = 506, + outGrp_vort = 507, outGrp_vortex = 508, - outGrp_stInv = 509, - outGrp_eFlx = 510, + outGrp_stInv = 509, + outGrp_eFlx = 510, outGrp_hFlx = 511, - outGrp_absV = 512, - outGrp_fN = 513, + outGrp_absV = 512, + outGrp_fN = 513, outGrp_fA = 514, - outGrp_stress = 515, - outGrp_cauchy = 516, + outGrp_stress = 515, + outGrp_cauchy = 516, outGrp_mises = 517, - outGrp_J = 518, - outGrp_F = 519, + outGrp_J = 518, + outGrp_F = 519, outGrp_strain = 520, - outGrp_divV = 521, + outGrp_divV = 521, outGrp_Visc = 522, outGrp_fS = 523, outGrp_C = 524, @@ -345,37 +344,43 @@ enum class OutputNameType outGrp_ionicState = 526, outGrp_fibStretch = 527, outGrp_fibStretchRate = 528, + outGrp_activeTensionFibers = 529, + outGrp_activeTensionSheets = 530, + outGrp_activeTensionNormal = 531, out_velocity = 599, - out_pressure = 598, - out_temperature = 597, + out_pressure = 598, + out_temperature = 597, out_voltage = 596, - out_acceleration = 595, - out_displacement = 594, - out_integ =593, - out_WSS = 592, - out_traction = 591, + out_acceleration = 595, + out_displacement = 594, + out_integ = 593, + out_WSS = 592, + out_traction = 591, out_vorticity = 590, - out_vortex = 589, - out_strainInv = 588, + out_vortex = 589, + out_strainInv = 588, out_energyFlux = 587, - out_heatFlux = 586, - out_absVelocity = 585, + out_heatFlux = 586, + out_absVelocity = 585, out_fibDir = 584, - out_fibAlign = 583, - out_stress = 582, + out_fibAlign = 583, + out_stress = 582, out_cauchy = 581, - out_mises = 580, - out_jacobian = 579, + out_mises = 580, + out_jacobian = 579, out_defGrad = 578, - out_strain = 577, - out_divergence = 576, + out_strain = 577, + out_divergence = 576, out_viscosity = 575, out_fibStrn = 574, out_CGstrain = 573, out_CGInv1 = 572, out_fibStretch = 571, - out_fibStretchRate = 570 + out_fibStretchRate = 570, + out_activeTensionFibers = 569, + out_activeTensionSheets = 568, + out_activeTensionNormal = 567 }; /// @brief Simulation output file types. diff --git a/Code/Source/solver/distribute.cpp b/Code/Source/solver/distribute.cpp index 51ec5782b..00986613e 100644 --- a/Code/Source/solver/distribute.cpp +++ b/Code/Source/solver/distribute.cpp @@ -1530,8 +1530,8 @@ void dist_eq(ComMod& com_mod, const CmMod& cm_mod, const cmType& cm, const std:: // All ranks but the master need to allocate the ionic model instance. if (!cm.mas(cm_mod)) { - cep.ionic_model = IonicModelFactory::create_model( - cep_model_type_to_name.at(cep.cepType)); + cep.ionic_model = + IonicModelFactory::create(cep_model_type_to_name.at(cep.cepType)); } cm.bcast(cm_mod, &cep.nX); @@ -1568,7 +1568,21 @@ void dist_eq(ComMod& com_mod, const CmMod& cm_mod, const cmType& cm, const std:: } cep.ionic_model->distribute_parameters(cm_mod, cm); - } + } + + if (supports_active_stress(lEq.dmn[iDmn].phys)) { + cm.bcast(cm_mod, dmn.active_stress_model_name); + + if (dmn.active_stress_model_name != "") { + // All ranks but the master need to allocate the active stress instance. + if (!cm.mas(cm_mod)) { + dmn.active_stress = + ActiveStressFactory::create(dmn.active_stress_model_name); + } + + dmn.active_stress->distribute_parameters(cm_mod, cm); + } + } if ((dmn.phys == EquationType::phys_struct) || (dmn.phys == EquationType::phys_ustruct)) { dist_mat_consts(com_mod, cm_mod, cm, dmn.stM); @@ -1586,10 +1600,9 @@ void dist_eq(ComMod& com_mod, const CmMod& cm_mod, const cmType& cm, const std:: // cm.bcast(cm_mod, &cep_mod.cem.cpld); - if (cep_mod.cem.cpld); { - cm.bcast(cm_mod, &cep_mod.cem.aStress); + if (cep_mod.cem.cpld) { cm.bcast(cm_mod, &cep_mod.cem.aStrain); - } + } if (com_mod.ibFlag) { if (cm.slv(cm_mod)) { @@ -1707,21 +1720,6 @@ void dist_mat_consts(const ComMod& com_mod, const CmMod& cm_mod, const cmType& c cm.bcast(cm_mod, &lStM.b2); cm.bcast(cm_mod, &lStM.mu0); - // Distribute fiber stress - cm.bcast(cm_mod, &lStM.Tf.fType); - - if (utils::btest(lStM.Tf.fType, static_cast(BoundaryConditionType::bType_std))) { - cm.bcast(cm_mod, &lStM.Tf.g); - - } else if (utils::btest(lStM.Tf.fType, static_cast(BoundaryConditionType::bType_ustd))) { - lStM.Tf.gt.distribute(cm_mod, cm); - } - - // Broadcast directional stress distribution parameters - cm.bcast(cm_mod, &lStM.Tf.eta_f); - cm.bcast(cm_mod, &lStM.Tf.eta_s); - cm.bcast(cm_mod, &lStM.Tf.eta_n); - // Distribute CANN parameter table if (lStM.isoType == ConstitutiveModelType::stArtificialNeuralNet) { cm.bcast(cm_mod, &lStM.paramTable.num_rows); diff --git a/Code/Source/solver/factory.h b/Code/Source/solver/factory.h new file mode 100644 index 000000000..c41d0beb2 --- /dev/null +++ b/Code/Source/solver/factory.h @@ -0,0 +1,122 @@ +// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the +// University of California, and others. SPDX-License-Identifier: BSD-3-Clause + +#ifndef FACTORY_H +#define FACTORY_H + +#include "FE/Common/FEException.h" + +#include +#include + +/** + * @brief Abstract self-registering factory. + * + * This class gives a way to create a dynamic register of classes derived from a + * certain base class, and then instantiate them by name. To be compatible with + * this factory, the derived classes must be default constructibleß. + * + * It combines the + * [factory](https://en.wikipedia.org/wiki/Abstract_factory_pattern) and + * [singleton](https://en.wikipedia.org/wiki/Singleton_pattern) patterns. There + * should always exist only one instance of this class, which cannot be accessed + * directly but only manipulated through the static methods of this class. + * + * To register a class into the factory, you can call the register_child + * static method, passing a class derived from BaseType as template argument and + * a label for it as argument. A shortcut for this is to use the macro + * REGISTER_IN_FACTORY. + */ +template class Factory { +public: + /** + * @brief Register a derived class in the factory. + * + * @param[in] name Label for the class to be registered. An instance of that + * class can then be created by passing this same label to the create + * method. + */ + template + static bool register_child(const std::string &name) { + auto &factory_instance = get_instance(); + + if (factory_instance.children.find(name) != + factory_instance.children.end()) { + svmp::raise( + "A model with name '" + name + + "' was already registered in the ionic model factory."); + } + + factory_instance.children[name] = []() -> std::unique_ptr { + return std::make_unique(); + }; + + return true; + } + + /** + * @brief Instantiate a derived classs by name. + */ + static std::unique_ptr create(const std::string &name) { + const auto &factory_instance = get_instance(); + + auto iter = factory_instance.children.find(name); + if (iter == factory_instance.children.end()) { + svmp::raise( + "No class with name '" + name + "' was registered in the factory."); + } + + return iter->second(); + } + + /** + * @brief Iterate through the registered classes. + * + * For every registered class derived from BaseType, creates a dummy instance + * of it, and then calls the provided function on that instance. All the dummy + * instances are destroyed after the function call. + */ + static void + visit(const std::function &f) { + const auto &factory_instance = get_instance(); + + for (auto &[name, builder] : factory_instance.children) { + std::unique_ptr dummy = builder(); + f(name, *dummy); + } + } + +protected: + /** + * @brief Default constructor. + */ + Factory() = default; + + /** + * @brief Access the singleton instance. + */ + static Factory &get_instance() { + static Factory instance; + return instance; + } + + /** + * @brief Registered derived classes. + * + * Each derived class is represented by a function that takes no argument and + * returns a unique_ptr constructing an instance of that class. + * This requires classes derived from BaseType to be default constructible. + */ + std::map()>> children; +}; + +/** + * @brief Macro to register a class in the factory. + */ +#define REGISTER_IN_FACTORY(BaseType, DerivedType, name) \ + namespace FactoryInternals { \ + static inline volatile const bool registered_##BaseType##_##DerivedType = \ + Factory::register_child(name); \ + } + +#endif \ No newline at end of file diff --git a/Code/Source/solver/fsi.cpp b/Code/Source/solver/fsi.cpp index 1e13820e0..4b7d32c13 100644 --- a/Code/Source/solver/fsi.cpp +++ b/Code/Source/solver/fsi.cpp @@ -69,7 +69,7 @@ void construct_fsi(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const So Array3 lK(dof*dof,eNoN,eNoN), lKd(dof*nsd,eNoN,eNoN); Array xl(nsd,eNoN), al(tDof,eNoN), yl(tDof,eNoN), dl(tDof,eNoN), bfl(nsd,eNoN), fN(nsd,nFn), pS0l(nsymd,eNoN), lR(dof,eNoN); - Vector pSl(nsymd), ya_l(eNoN); + Vector pSl(nsymd), ya_l_f(eNoN), ya_l_s(eNoN), ya_l_n(eNoN); std::array fs_1; fs::get_thood_fs(com_mod, fs_1, lM, vmsStab, 1); @@ -98,7 +98,9 @@ void construct_fsi(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const So // Create local copies fN = 0.0; pS0l = 0.0; - ya_l = 0.0; + ya_l_f = 0.0; + ya_l_s = 0.0; + ya_l_n = 0.0; for (int a = 0; a < eNoN; a++) { int Ac = lM.IEN(a,e); @@ -126,8 +128,10 @@ void construct_fsi(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const So pS0l.set_col(a, pS0.col(Ac)); } - if (cem.cpld) { - ya_l(a) = cem.Ya(Ac); + if (eq.dmn[cDmn].active_stress != nullptr) { + ya_l_f(a) = cep_mod.cem.Ya_f[Ac]; + ya_l_s(a) = cep_mod.cem.Ya_s[Ac]; + ya_l_n(a) = cep_mod.cem.Ya_n[Ac]; } } @@ -214,7 +218,9 @@ void construct_fsi(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const So case Equation_struct: { auto N0 = fs_1[0].N.col(g); - struct_ns::struct_3d(com_mod, cep_mod, fs_1[0].eNoN, nFn, w, N0, Nwx, al, yl, dl, bfl, fN, pS0l, pSl, ya_l, lR, lK); + struct_ns::struct_3d(com_mod, cep_mod, fs_1[0].eNoN, nFn, w, N0, + Nwx, al, yl, dl, bfl, fN, pS0l, pSl, ya_l_f, + ya_l_s, ya_l_n, lR, lK); } break; case Equation_lElas: throw std::runtime_error("[construct_fsi] LELAS3D not implemented"); @@ -224,8 +230,11 @@ void construct_fsi(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const So case Equation_ustruct: auto N0 = fs_1[0].N.col(g); auto N1 = fs_1[1].N.col(g); - ustruct::ustruct_3d_m(com_mod, cep_mod, vmsStab, fs_1[0].eNoN, fs_1[1].eNoN, nFn, w, Jac, N0, N1, Nwx, al, yl, dl, bfl, fN, ya_l, lR, lK, lKd); - break; + ustruct::ustruct_3d_m(com_mod, cep_mod, vmsStab, fs_1[0].eNoN, + fs_1[1].eNoN, nFn, w, Jac, N0, N1, Nwx, al, + yl, dl, bfl, fN, ya_l_f, ya_l_s, ya_l_n, lR, + lK, lKd); + break; } } else if (nsd == 2) { @@ -245,7 +254,9 @@ void construct_fsi(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const So case Equation_struct: { auto N0 = fs_1[0].N.col(g); - struct_ns::struct_2d(com_mod, cep_mod, fs_1[0].eNoN, nFn, w, N0, Nwx, al, yl, dl, bfl, fN, pS0l, pSl, ya_l, lR, lK); + struct_ns::struct_2d(com_mod, cep_mod, fs_1[0].eNoN, nFn, w, N0, + Nwx, al, yl, dl, bfl, fN, pS0l, pSl, ya_l_f, + ya_l_s, ya_l_n, lR, lK); } break; case Equation_ustruct: diff --git a/Code/Source/solver/initialize.cpp b/Code/Source/solver/initialize.cpp index 07b924c66..7baf4f330 100644 --- a/Code/Source/solver/initialize.cpp +++ b/Code/Source/solver/initialize.cpp @@ -126,7 +126,9 @@ void init_from_bin(Simulation* simulation, const std::string& fName, std::array< } else if (cepEq) { bin_file.read((char*)Ad.data(), Ad.msize()); bin_file.read((char*)Xion.data(), Xion.msize()); - bin_file.read((char*)cem.Ya.data(), cem.Ya.msize()); + bin_file.read((char*)cem.Ya_f.data(), cem.Ya_f.msize()); + bin_file.read((char*)cem.Ya_s.data(), cem.Ya_s.msize()); + bin_file.read((char*)cem.Ya_n.data(), cem.Ya_n.msize()); } else if (risFlag) { bin_file.read((char*)Ad.data(), Ad.msize()); @@ -147,7 +149,9 @@ void init_from_bin(Simulation* simulation, const std::string& fName, std::array< } else if (cepEq) { bin_file.read((char*)Xion.data(), Xion.msize()); - bin_file.read((char*)cem.Ya.data(), cem.Ya.msize()); + bin_file.read((char*)cem.Ya_f.data(), cem.Ya_f.msize()); + bin_file.read((char*)cem.Ya_s.data(), cem.Ya_s.msize()); + bin_file.read((char*)cem.Ya_n.data(), cem.Ya_n.msize()); } else if (risFlag) { init_ris_data(com_mod, bin_file); @@ -679,10 +683,26 @@ void initialize(Simulation* simulation, Vector& timeP) if (cep_mod.cepEq) { cep_mod.Xion.resize(cep_mod.nXion,tnNo); cep_ion::cep_init(simulation); + } + + // Electromechanics. + // @todo[michelebucelli] There's probably a better solution than initializing + // these vectors all the time. E.g. we could put the calcium evaluation + // behind a getter, that returns zero if the vector has not been + // initialized. + { + cep_mod.calcium.resize(tnNo); + cep_mod.cem.Ya_f.resize(tnNo); + cep_mod.cem.Ya_s.resize(tnNo); + cep_mod.cem.Ya_n.resize(tnNo); + } - // Electro-Mechanics - if (cep_mod.cem.cpld) { - cep_mod.cem.Ya.resize(tnNo); + // Setup the initial conditions for the active stress models. + for (auto &eq : com_mod.eq) { + for (auto &dmn : eq.dmn) { + if (dmn.active_stress != nullptr) { + dmn.active_stress->init(tnNo); + } } } diff --git a/Code/Source/solver/ionic_aliev_panfilov.h b/Code/Source/solver/ionic_aliev_panfilov.h index 768448c00..9f0c956ee 100644 --- a/Code/Source/solver/ionic_aliev_panfilov.h +++ b/Code/Source/solver/ionic_aliev_panfilov.h @@ -11,8 +11,7 @@ /** * @brief Aliev-Panfilov ionic model. * - * **Reference**: Aliev, Panfilov. A simple two-variable model of cardiac - * excitation. Chaos, Solitons and Fractals (1996). + * **Reference**: [Aliev, Panfilov (1996)](https://doi.org/10.1016/0960-0779(95)00089-5) */ class AlievPanfilov : public IonicModel { public: diff --git a/Code/Source/solver/ionic_bueno_orovio.h b/Code/Source/solver/ionic_bueno_orovio.h index 0ec037be0..fc8868f4f 100644 --- a/Code/Source/solver/ionic_bueno_orovio.h +++ b/Code/Source/solver/ionic_bueno_orovio.h @@ -12,9 +12,7 @@ /** * @brief Bueno-Orovio ionic model. * - * **Reference**: Bueno-Orovio, Cherry, Fenton. Minimal model for human - * ventricular action potentials in tissue. Journal of Theoretical Biology - * (2008) + * **Reference**: [Bueno-Orovio, Cherry, Fenton (2008)](https://doi.org/10.1016/j.jtbi.2008.03.029) */ class BuenoOrovio : public IonicModel { public: diff --git a/Code/Source/solver/ionic_fitzhugh_nagumo.h b/Code/Source/solver/ionic_fitzhugh_nagumo.h index dc660d0bd..572aa2ad4 100644 --- a/Code/Source/solver/ionic_fitzhugh_nagumo.h +++ b/Code/Source/solver/ionic_fitzhugh_nagumo.h @@ -13,10 +13,8 @@ * @brief FitzHugh-Nagumo ionic model. * * **References**: - * - FitzHugh, Impulses and physiological states in theoretical models of nerve - * membrane. Biophysical Journal (1961) - * - Nagumo, Arimoto, Yoshizawa. An active pulse transmission line simulating - * nerve axon. Proceedings of the IRE (1962). + * - [FitzHugh (1961)](https://doi.org/10.1016/S0006-3495(61)86902-6) + * - [Nagumo, Arimoto, Yoshizawa (1962)](https://doi.org/10.1109/JRPROC.1962.288235) */ class FitzHughNagumo : public IonicModel { public: diff --git a/Code/Source/solver/ionic_model.cpp b/Code/Source/solver/ionic_model.cpp index bc33d0dd6..6bcfdd0bd 100644 --- a/Code/Source/solver/ionic_model.cpp +++ b/Code/Source/solver/ionic_model.cpp @@ -77,7 +77,7 @@ void IonicModel::integ(const odeType &ode_solver_params, const int zone_id, default: svmp::raise( "Unknown time integration type: " + - std::to_string(static_cast(ode_solver_params.tIntType))); + std::to_string(static_cast(ode_solver_params.tIntType))); } } @@ -254,27 +254,3 @@ std::vector IonicModel::get_registered_outputs() const { return result; } - -std::unique_ptr -IonicModelFactory::create_model(const std::string &name) { - const auto &factory_instance = get_instance(); - - auto iter = factory_instance.children.find(name); - if (iter == factory_instance.children.end()) { - svmp::raise( - "No model with name '" + name + - "' was registered in the ionic model factory."); - } - - return iter->second(); -} - -void IonicModelFactory::visit( - const std::function &f) { - const auto &factory_instance = get_instance(); - - for (auto &[name, builder] : factory_instance.children) { - std::unique_ptr dummy = builder(); - f(name, *dummy); - } -} diff --git a/Code/Source/solver/ionic_model.h b/Code/Source/solver/ionic_model.h index 22c1332fb..7ffe09535 100644 --- a/Code/Source/solver/ionic_model.h +++ b/Code/Source/solver/ionic_model.h @@ -16,6 +16,8 @@ #include "CmMod.h" +#include "factory.h" + // Forward declarations. class outputType; @@ -83,11 +85,9 @@ class odeType { * specified by classes derived from this. * * **References**: - * - Colli Franzone, Pavarino, Scacchi. Mathematical Cardiac Electrophysiology. - * Springer, 2014. - * - Goktepe, Kuhl. Computational Modeling of Cardiac Electrophysiology: a - * Novel Finite Element Approach. International Journal for Numerical Methods - * in Biomedical Engineering, 2009. + * - [Colli Franzone, Pavarino, Scacchi + * (2014)](https://doi.org/10.1007/978-3-319-04801-7) + * - [Goktepe, Kuhl (2009)](https://doi.org/10.1007/s00466-009-0434-z) * * ### Numerical methods * @@ -447,93 +447,16 @@ class IonicModel { }; /** - * @brief Self-registering factory for ionic models. - * - * This class gives a way to register ionic models when they are defined, and - * then instantiate concrete ionic models, derived from IonicModel, by name. To - * be compatible with this factory, classes derived from IonicModel must be - * default constructible. + * @brief Alias for the ionic model factory. * - * It combines the - * [factory](https://en.wikipedia.org/wiki/Abstract_factory_pattern) and - * [singleton](https://en.wikipedia.org/wiki/Singleton_pattern) patterns. There - * should always exist only one instance of this class, which cannot be accessed - * directly but only manipulated through the static methods of this class. - * - * To register a new ionic model into the factory, you can call the - * register_model static method, passing a class derived from Ionic as template - * argument and a label for the model as argument. A shortcut for this is to - * use the macro REGISTER_IONIC_MODEL. + * See the documentation for @ref Factory for more details on how this works. */ -class IonicModelFactory { -public: - /** - * @brief Register a child model. - */ - template static bool register_model(const std::string &name) { - auto &factory_instance = get_instance(); - - if (factory_instance.children.find(name) != - factory_instance.children.end()) { - svmp::raise( - "A model with name '" + name + - "' was already registered in the ionic model factory."); - } - - factory_instance.children[name] = []() { - return std::make_unique(); - }; - - return true; - } - - /** - * @brief Instantiate a model from its name. - */ - static std::unique_ptr create_model(const std::string &name); - - /** - * @brief Iterate through registered ionic models. - * - * For every registered ionic model, creates a dummy instance of it, and then - * calls the provided function on that model. All the dummy model instances - * are destroyed after the function call. - */ - static void - visit(const std::function &f); - -protected: - /** - * @brief Default constructor. - */ - IonicModelFactory() = default; - - /** - * @brief Access the singleton instance. - */ - static IonicModelFactory &get_instance() { - static IonicModelFactory instance; - return instance; - } - - /** - * @brief Registered ionic models. - * - * Each ionic model is represented by a function that takes no argument and - * returns a unique_ptr constructing an instance of that model. - * This requires classes derived from IonicModel to be default - * constructible. - */ - std::map()>> children; -}; +using IonicModelFactory = Factory; /** * @brief Macro to register a ionic model in the factory. */ #define REGISTER_IONIC_MODEL(name, type) \ - namespace IonicModelFactoryInternals { \ - static inline volatile const bool ionic_model_factory_registered_##type = \ - IonicModelFactory::register_model(name); \ - } + REGISTER_IN_FACTORY(IonicModel, type, name) #endif \ No newline at end of file diff --git a/Code/Source/solver/ionic_ttp.h b/Code/Source/solver/ionic_ttp.h index f23531d4c..1a90408b2 100644 --- a/Code/Source/solver/ionic_ttp.h +++ b/Code/Source/solver/ionic_ttp.h @@ -18,12 +18,8 @@ * reference below. * * **References**: - * 1. Ten Tusscher, Noble, Noble, Panfilov. A model for human ventricular - * tissue. American Journal of Physiology - Heart and Circulatory Physiology - * (2004). - * 2. Ten Tusscher, Panfilov. Alternans and spiral breakup in a human - * ventricular tissue model. American Journal of Physiology - Heart and - * Circulatory Physiology (2006). + * 1. [Ten Tusscher, Noble, Noble, Panfilov (2004)](https://doi.org/10.1152/ajpheart.00794.2003) + * 2. [Ten Tusscher, Panfilov (2006)](https://doi.org/10.1152/ajpheart.00109.2006) * * Model parameters are from reference 2 above. Default parameters are for * epicardium state (source: https://models.cellml.org/e/80d) diff --git a/Code/Source/solver/mat_models.cpp b/Code/Source/solver/mat_models.cpp index 66831dda6..cda281a68 100644 --- a/Code/Source/solver/mat_models.cpp +++ b/Code/Source/solver/mat_models.cpp @@ -203,23 +203,6 @@ void voigt_to_cc(const int nsd, const Array& Dm, Tensor4& CC) -/// @brief Compute additional fiber-reinforcement stress. -/// -/// Reproduces Fortran 'GETFIBSTRESS' subroutine. -// -void compute_fib_stress(const ComMod& com_mod, const CepMod& cep_mod, const fibStrsType& Tfl, double& g) -{ - using namespace consts; - - g = 0.0; - - if (utils::btest(Tfl.fType, iBC_std)) { - g = Tfl.g; - } else if (utils::btest(Tfl.fType, iBC_ustd)) { - g = Tfl.gt.value(com_mod.time)[0]; - } -} - /** * @brief Perform the necessary tensor operations to calculate S_iso (isochoric @@ -304,29 +287,12 @@ Eigen::Matrix compute_sheet_normal(const Eigen::Matrix -void compute_pk2cc(const ComMod& com_mod, const CepMod& cep_mod, const dmnType& lDmn, const Matrix& F, const int nfd, - const Eigen::Matrix fl, const double ya, Matrix& S, Matrix<3*(nsd-1)>& Dm, double& Ja) -{ +template +void compute_pk2cc(const ComMod &com_mod, const CepMod &cep_mod, + const dmnType &lDmn, const Matrix &F, const int nfd, + const Eigen::Matrix fl, + const double ya_f, const double ya_s, const double ya_n, + Matrix &S, Matrix<3 * (nsd - 1)> &Dm, double &Ja) { using namespace consts; using namespace mat_fun; using namespace utils; @@ -353,22 +319,19 @@ void compute_pk2cc(const ComMod& com_mod, const CepMod& cep_mod, const dmnType& double nd = static_cast(nsd); double Kp = stM.Kpen; - // Fiber-reinforced stress - compute total active stress - double Ta = 0.0; - compute_fib_stress(com_mod, cep_mod, stM.Tf, Ta); - - // Distribute total active stress among fiber directions - double Tfa = stM.Tf.eta_f * Ta; // Fiber direction - double Tsa = stM.Tf.eta_s * Ta; // Sheet direction - double Tna = stM.Tf.eta_n * Ta; // Sheet-normal direction + // Active stress from active stress models, already distributed among the + // fiber, sheet and sheet-normal directions by the active stress model. + double Tfa = ya_f; // Fiber direction + double Tsa = ya_s; // Sheet direction + double Tna = ya_n; // Sheet-normal direction // Validate directional distribution is supported for this constitutive model // Only Guccione, HO, and HO-ma models support sheet and sheet-normal stress contributions - bool supports_directional_distribution = (stM.isoType == ConstitutiveModelType::stIso_Gucci || + bool supports_directional_distribution = (stM.isoType == ConstitutiveModelType::stIso_Gucci || stM.isoType == ConstitutiveModelType::stIso_HO || stM.isoType == ConstitutiveModelType::stIso_HO_ma); - - if (!supports_directional_distribution && (stM.Tf.eta_s > 0.0 || stM.Tf.eta_n > 0.0)) { + + if (!supports_directional_distribution && (ya_s > 0.0 || ya_n > 0.0)) { throw std::runtime_error("Directional distribution of active stress (eta_s > 0 or eta_n > 0) " "is only supported for Guccione, Holzapfel-Ogden (HO), and Holzapfel-Ogden Modified Anisotropy (HO-ma) models. " "Current model does not support sheet or sheet-normal stress contributions. " @@ -395,11 +358,6 @@ void compute_pk2cc(const ComMod& com_mod, const CepMod& cep_mod, const dmnType& Hss = Matrix::Zero(); } - // Electromechanics coupling - active stress - if (cep_mod.cem.aStress) { - Tfa = Tfa + ya; - } - // Electromechanics coupling - active strain Matrix Fe = F; Matrix Fa = Matrix::Identity(); @@ -865,7 +823,7 @@ void compute_pk2cc(const ComMod& com_mod, const CepMod& cep_mod, const dmnType& * */ void compute_pk2cc(const ComMod& com_mod, const CepMod& cep_mod, const dmnType& lDmn, const Array& F, const int nfd, - const Array& fl, const double ya, Array& S, Array& Dm, double& Ja) + const Array& fl, const double ya_f, const double ya_s, const double ya_n, Array& S, Array& Dm, double& Ja) { // Number of spatial dimensions int nsd = com_mod.nsd; @@ -886,7 +844,7 @@ void compute_pk2cc(const ComMod& com_mod, const CepMod& cep_mod, const dmnType& Eigen::Matrix3d Dm_2D = Eigen::Matrix3d::Zero(); // Call templated function - compute_pk2cc<2>(com_mod, cep_mod, lDmn, F_2D, nfd, fl_2D, ya, S_2D, Dm_2D, Ja); + compute_pk2cc<2>(com_mod, cep_mod, lDmn, F_2D, nfd, fl_2D, ya_f, ya_s, ya_n, S_2D, Dm_2D, Ja); // Copy results back mat_fun::convert_to_array(S_2D, S); @@ -910,7 +868,7 @@ void compute_pk2cc(const ComMod& com_mod, const CepMod& cep_mod, const dmnType& Dm_3D.setZero(); // Call templated function - compute_pk2cc<3>(com_mod, cep_mod, lDmn, F_3D, nfd, fl_3D, ya, S_3D, Dm_3D, Ja); + compute_pk2cc<3>(com_mod, cep_mod, lDmn, F_3D, nfd, fl_3D, ya_f, ya_s, ya_n, S_3D, Dm_3D, Ja); // Copy results back mat_fun::convert_to_array(S_3D, S); diff --git a/Code/Source/solver/mat_models.h b/Code/Source/solver/mat_models.h index 3060f68d5..6a959e132 100644 --- a/Code/Source/solver/mat_models.h +++ b/Code/Source/solver/mat_models.h @@ -24,10 +24,33 @@ void cc_to_voigt(const int nsd, const Tensor4& CC, Array& Dm); void voigt_to_cc(const int nsd, const Array& Dm, Tensor4& CC); -void compute_fib_stress(const ComMod& com_mod, const CepMod& cep_mod, const fibStrsType& Tfl, double& g); - -void compute_pk2cc(const ComMod& com_mod, const CepMod& cep_mod, const dmnType& lDmn, const Array& F, const int nfd, - const Array& fl, const double ya, Array& S, Array& Dm, double& Ja); +/** + * @brief Compute 2nd Piola-Kirchhoff stress and material stiffness tensors + * including both dilational and isochoric components. + * + * Reproduces the Fortran 'GETPK2CC' subroutine. + * + * @param[in] com_mod Object containing global common variables. + * @param[in] cep_mod Object containing electrophysiology-specific common + * variables. + * @param[in] lDmn Domain object. + * @param[in] F Deformation gradient tensor. + * @param[in] nfd Number of fiber directions. + * @param[in] fl Fiber directions. + * @param[in] ya_f Active tension along the fiber direction. + * @param[in] ya_s Active tension along the sheet direction. + * @param[in] ya_n Active tension along the sheet-normal direction. + * @param[out] S 2nd Piola-Kirchhoff stress tensor (modified in place). + * @param[out] Dm Material stiffness tensor (modified in place). + * @param[out] Ja Jacobian for active strain + * + * @return None, but modifies S, Dm, and Ja in place. + */ +void compute_pk2cc(const ComMod &com_mod, const CepMod &cep_mod, + const dmnType &lDmn, const Array &F, const int nfd, + const Array &fl, const double ya_f, + const double ya_s, const double ya_n, Array &S, + Array &Dm, double &Ja); void compute_pk2cc_shlc(const ComMod& com_mod, const dmnType& lDmn, const int nfd, const Array& fNa0, const Array& gg_0, const Array& gg_x, double& g33, Vector& Sml, Array& Dml); diff --git a/Code/Source/solver/output.cpp b/Code/Source/solver/output.cpp index e518c613b..8e872982c 100644 --- a/Code/Source/solver/output.cpp +++ b/Code/Source/solver/output.cpp @@ -286,7 +286,9 @@ void write_restart(Simulation* simulation, std::array& timeP, const So } else if (cepEq) { restart_file.write((char*)Ad.data(), Ad.msize()); restart_file.write((char*)Xion.data(), Xion.msize()); - restart_file.write((char*)cem.Ya.data(), cem.Ya.msize()); + restart_file.write((char*)cem.Ya_f.data(), cem.Ya_f.msize()); + restart_file.write((char*)cem.Ya_s.data(), cem.Ya_s.msize()); + restart_file.write((char*)cem.Ya_n.data(), cem.Ya_n.msize()); } else if (risFlag) { restart_file.write((char*)Ad.data(), Ad.msize()); @@ -309,7 +311,9 @@ void write_restart(Simulation* simulation, std::array& timeP, const So } else if (cepEq) { restart_file.write((char*)Xion.data(), Xion.msize()); - restart_file.write((char*)cem.Ya.data(), cem.Ya.msize()); + restart_file.write((char*)cem.Ya_f.data(), cem.Ya_f.msize()); + restart_file.write((char*)cem.Ya_s.data(), cem.Ya_s.msize()); + restart_file.write((char*)cem.Ya_n.data(), cem.Ya_n.msize()); } else if (risFlag) { write_ris_data(com_mod, restart_file); diff --git a/Code/Source/solver/post.cpp b/Code/Source/solver/post.cpp index b53e85745..fc3990173 100644 --- a/Code/Source/solver/post.cpp +++ b/Code/Source/solver/post.cpp @@ -1712,8 +1712,6 @@ void tpost(Simulation* simulation, const mshType& lM, const int m, Array Array Nx(nsd,fs.eNoN); Vector N(fs.eNoN); - double ya = 0.0; - int insd = nsd; if (lM.lFib) { insd = 1; @@ -1865,6 +1863,21 @@ void tpost(Simulation* simulation, const mshType& lM, const int m, Array Array sigma(nsd,nsd); Array S(nsd,nsd); + // Interpolate the active stress from active stress models to the + // current Gauss point so that the active contribution is included in + // the reported stress, consistently with the residual assembly. + double ya_g_f = 0.0; + double ya_g_s = 0.0; + double ya_g_n = 0.0; + if (eq.dmn[cDmn].active_stress != nullptr) { + for (int a = 0; a < fs.eNoN; a++) { + int Ac = lM.IEN(a,e); + ya_g_f = ya_g_f + N(a)*cep_mod.cem.Ya_f[Ac]; + ya_g_s = ya_g_s + N(a)*cep_mod.cem.Ya_s[Ac]; + ya_g_n = ya_g_n + N(a)*cep_mod.cem.Ya_n[Ac]; + } + } + if (cPhys == EquationType::phys_lElas) { if (nsd == 3) { double detF = lambda*(ed(0) + ed(1) + ed(2)); @@ -1896,8 +1909,9 @@ void tpost(Simulation* simulation, const mshType& lM, const int m, Array Array Dm(nsymd,nsymd); double Ja; - - mat_models::compute_pk2cc(com_mod, cep_mod, eq.dmn[cDmn], F, nFn, fN, ya, S, Dm, Ja); + + mat_models::compute_pk2cc(com_mod, cep_mod, eq.dmn[cDmn], F, nFn, + fN, ya_g_f, ya_g_s, ya_g_n, S, Dm, Ja); // TODO: Add viscous stress @@ -1915,7 +1929,9 @@ void tpost(Simulation* simulation, const mshType& lM, const int m, Array } else if (cPhys == EquationType::phys_struct) { Array Dm(nsymd,nsymd); double Ja; - mat_models::compute_pk2cc(com_mod, cep_mod, eq.dmn[cDmn], F, nFn, fN, ya, S, Dm, Ja); + + mat_models::compute_pk2cc(com_mod, cep_mod, eq.dmn[cDmn], F, nFn, + fN, ya_g_f, ya_g_s, ya_g_n, S, Dm, Ja); // TODO: Add viscous stress diff --git a/Code/Source/solver/read_files.cpp b/Code/Source/solver/read_files.cpp index 63d9ba918..befd3b9e4 100644 --- a/Code/Source/solver/read_files.cpp +++ b/Code/Source/solver/read_files.cpp @@ -7,6 +7,7 @@ #include "Core/Exception.h" #include "FE/Common/FEException.h" +#include "active_stress.h" #include "all_fun.h" #include "consts.h" #include "ionic_model.h" @@ -1253,7 +1254,7 @@ void read_cep_domain(Simulation* simulation, EquationParameters* eq_params, Doma { const std::string model_name = cep_model_type_to_name.at(model_type); - lDmn.cep.ionic_model = IonicModelFactory::create_model(model_name); + lDmn.cep.ionic_model = IonicModelFactory::create(model_name); lDmn.cep.ionic_model->read_parameters( *domain_params->ionic_models.at(model_name)); @@ -1410,6 +1411,19 @@ void read_cep_equation(CepMod* cep_mod, Simulation* simulation, EquationParamete } } +/** + * @brief Read parameters related to active stress. + */ +void read_active_stress(dmnType &lDmn, DomainParameters *domain_params) { + if (domain_params->active_stress.defined()) { + const std::string name = domain_params->active_stress.get_model_name(); + + lDmn.active_stress_model_name = name; + lDmn.active_stress = ActiveStressFactory::create(name); + lDmn.active_stress->read_parameters(domain_params->active_stress); + } +} + //------------- // read_domain //------------- @@ -1576,17 +1590,26 @@ void read_domain(Simulation* simulation, EquationParameters* eq_params, eqType& read_cep_domain(simulation, eq_params, domain_params, lEq.dmn[iDmn]); } + // Read active stress parameters + if (supports_active_stress(lEq.dmn[iDmn].phys)) { + read_active_stress(lEq.dmn[iDmn], domain_params); + } + // Read material/constitutive model parameters for nonlinear // elastodynamics simulations (both solids and shells) // - if ( (lEq.dmn[iDmn].phys == EquationType::phys_shell) || - (lEq.dmn[iDmn].phys == EquationType::phys_struct) || - (lEq.dmn[iDmn].phys == EquationType::phys_ustruct)) { - read_mat_model(simulation, eq_params, domain_params, lEq.dmn[iDmn]); - if (utils::is_zero(lEq.dmn[iDmn].stM.Kpen) && lEq.dmn[iDmn].phys == EquationType::phys_struct) { - //err = "Incompressible struct is not allowed. Use "// "penalty method or ustruct" - throw std::runtime_error("An incompressible material model is not allowed for 'struct' physics; use penalty method or ustruct."); - } + if ((lEq.dmn[iDmn].phys == EquationType::phys_shell) || + (lEq.dmn[iDmn].phys == EquationType::phys_struct) || + (lEq.dmn[iDmn].phys == EquationType::phys_ustruct)) { + read_mat_model(simulation, eq_params, domain_params, lEq.dmn[iDmn]); + if (utils::is_zero(lEq.dmn[iDmn].stM.Kpen) && + lEq.dmn[iDmn].phys == EquationType::phys_struct) { + // err = "Incompressible struct is not allowed. Use "// "penalty + // method or ustruct" + throw std::runtime_error( + "An incompressible material model is not allowed for 'struct' " + "physics; use penalty method or ustruct."); + } } // Set parameters for a fluid viscosity model. @@ -2038,10 +2061,6 @@ void read_files(Simulation* simulation, const std::string& file_name) throw std::runtime_error("Both electrophysiology and struct have to be solved for electro-mechanics"); } - if (cep_mod.cem.aStress && cep_mod.cem.aStrain) { - throw std::runtime_error("Cannot set both active strain and active stress coupling"); - } - if (cep_mod.cem.aStrain) { if (com_mod.nsd != 3) { throw std::runtime_error("Active strain coupling is allowed only for 3D bodies"); @@ -2051,9 +2070,16 @@ void read_files(Simulation* simulation, const std::string& file_name) auto& eq = com_mod.eq[iEq]; for (int i = 0; i < eq.nDmn; i++) { auto& dmn = eq.dmn[i]; + if ((dmn.phys != EquationType::phys_ustruct) && (dmn.phys != EquationType::phys_struct)) { continue; } + + if (dmn.active_stress != nullptr) { + svmp::raise( + "Active strain and active stress cannot be used together."); + } + if ((dmn.stM.isoType != ConstitutiveModelType::stIso_HO)) { throw std::runtime_error("Active strain is allowed with Holzapfel-Ogden passive constitutive model only"); } @@ -2257,39 +2283,6 @@ void read_mat_model(Simulation* simulation, EquationParameters* eq_params, Domai throw std::runtime_error("[read_mat_model] Constitutive model type '" + cmodel_str + "' is not implemented."); } - // Set fiber reinforcement stress. - if (domain_params->fiber_reinforcement_stress.defined()) { - auto& fiber_params = domain_params->fiber_reinforcement_stress; - auto fiber_stress = fiber_params.type.value(); - std::transform(fiber_stress.begin(), fiber_stress.end(), fiber_stress.begin(), ::tolower); - - if (fiber_stress == "steady") { - lDmn.stM.Tf.fType = utils::ibset(lDmn.stM.Tf.fType, static_cast(BoundaryConditionType::bType_std)); - lDmn.stM.Tf.g = fiber_params.value.value(); - - } else if (fiber_stress == "unsteady") { - lDmn.stM.Tf.fType = - utils::ibset(lDmn.stM.Tf.fType, - static_cast(BoundaryConditionType::bType_ustd)); - - lDmn.stM.Tf.gt = FourierInterpolation::from_time_series_file( - fiber_params.temporal_values_file_path.value(), - /* n_dimensions = */ 1, fiber_params.ramp_function.value()); - } - - // Read directional stress distribution parameters - if (fiber_params.directional_distribution.defined()) { - // Validate: ensures exactly 3 parameters specified (no empty blocks), sums to 1.0, non-negative - fiber_params.directional_distribution.validate(); - - // Read the validated values (validate() ensures all three are defined) - lDmn.stM.Tf.eta_f = fiber_params.directional_distribution.fiber_direction.value(); - lDmn.stM.Tf.eta_s = fiber_params.directional_distribution.sheet_direction.value(); - lDmn.stM.Tf.eta_n = fiber_params.directional_distribution.sheet_normal_direction.value(); - } - // Otherwise (no block at all), defaults (eta_f=1.0, eta_s=0.0, eta_n=0.0) from ComMod.h are used - } - // Check for shell model // // ST91 is the default and the only dilational penalty model for diff --git a/Code/Source/solver/set_equation_props.h b/Code/Source/solver/set_equation_props.h index b797c4a02..1cd236b7f 100644 --- a/Code/Source/solver/set_equation_props.h +++ b/Code/Source/solver/set_equation_props.h @@ -556,14 +556,24 @@ SetEquationPropertiesMapType set_equation_props = { outPuts = {OutputNameType::out_displacement, OutputNameType::out_stress, OutputNameType::out_cauchy, OutputNameType::out_strain}; //simulation->com_mod.pstEq = true; } else { - nDOP = {14,2,0,0}; - outPuts = { - OutputNameType::out_displacement, OutputNameType::out_mises, OutputNameType::out_stress, - OutputNameType::out_cauchy, OutputNameType::out_strain, OutputNameType::out_jacobian, - OutputNameType::out_defGrad, OutputNameType::out_integ, OutputNameType::out_fibDir, - OutputNameType::out_fibAlign, OutputNameType::out_velocity, OutputNameType::out_acceleration, - OutputNameType::out_fibStretch, OutputNameType::out_fibStretchRate - }; + nDOP = {17, 2, 0, 0}; + outPuts = {OutputNameType::out_displacement, + OutputNameType::out_mises, + OutputNameType::out_stress, + OutputNameType::out_cauchy, + OutputNameType::out_strain, + OutputNameType::out_jacobian, + OutputNameType::out_defGrad, + OutputNameType::out_integ, + OutputNameType::out_fibDir, + OutputNameType::out_fibAlign, + OutputNameType::out_velocity, + OutputNameType::out_acceleration, + OutputNameType::out_fibStretch, + OutputNameType::out_fibStretchRate, + OutputNameType::out_activeTensionFibers, + OutputNameType::out_activeTensionSheets, + OutputNameType::out_activeTensionNormal}; } // Set solver parameters. @@ -597,25 +607,26 @@ SetEquationPropertiesMapType set_equation_props = { read_domain(simulation, eq_params, lEq, propL); - nDOP = {16, 2, 0, 0}; - outPuts = { - OutputNameType::out_displacement, - OutputNameType::out_mises, - OutputNameType::out_stress, - OutputNameType::out_cauchy, - OutputNameType::out_strain, - OutputNameType::out_jacobian, - OutputNameType::out_defGrad, - OutputNameType::out_integ, - OutputNameType::out_fibDir, - OutputNameType::out_fibAlign, - OutputNameType::out_velocity, - OutputNameType::out_pressure, - OutputNameType::out_acceleration, - OutputNameType::out_divergence, - OutputNameType::out_fibStretch, - OutputNameType::out_fibStretchRate - }; + nDOP = {19, 2, 0, 0}; + outPuts = {OutputNameType::out_displacement, + OutputNameType::out_mises, + OutputNameType::out_stress, + OutputNameType::out_cauchy, + OutputNameType::out_strain, + OutputNameType::out_jacobian, + OutputNameType::out_defGrad, + OutputNameType::out_integ, + OutputNameType::out_fibDir, + OutputNameType::out_fibAlign, + OutputNameType::out_velocity, + OutputNameType::out_pressure, + OutputNameType::out_acceleration, + OutputNameType::out_divergence, + OutputNameType::out_fibStretch, + OutputNameType::out_fibStretchRate, + OutputNameType::out_activeTensionFibers, + OutputNameType::out_activeTensionSheets, + OutputNameType::out_activeTensionNormal}; // Set solver parameters. read_ls(simulation, eq_params, SolverType::lSolver_GMRES, lEq); diff --git a/Code/Source/solver/set_output_props.h b/Code/Source/solver/set_output_props.h index 59660ce6f..35cfbd8c4 100644 --- a/Code/Source/solver/set_output_props.h +++ b/Code/Source/solver/set_output_props.h @@ -38,8 +38,11 @@ std::map output_props_map = {OutputNameType::out_fibAlign, std::make_tuple(OutputNameType::outGrp_fA, 0, 1, "Fiber_alignment") }, {OutputNameType::out_fibDir, std::make_tuple(OutputNameType::outGrp_fN, 0, nsd, "Fiber_direction") }, {OutputNameType::out_fibStrn, std::make_tuple(OutputNameType::outGrp_fS, 0, 1, "Fiber_shortening") }, - {OutputNameType::out_fibStretch, std::make_tuple(OutputNameType::outGrp_fibStretch, 0, 1, "Fiber_stretch") }, - {OutputNameType::out_fibStretchRate, std::make_tuple(OutputNameType::outGrp_fibStretchRate, 0, 1, "Fiber_stretch_rate") }, + {OutputNameType::out_fibStretch, std::make_tuple(OutputNameType::outGrp_fibStretch, 0, 1, "Fiber_stretch") }, + {OutputNameType::out_fibStretchRate, std::make_tuple(OutputNameType::outGrp_fibStretchRate, 0, 1, "Fiber_stretch_rate") }, + {OutputNameType::out_activeTensionFibers, std::make_tuple(OutputNameType::outGrp_activeTensionFibers, 0, 1, "Active_tension_fibers") }, + {OutputNameType::out_activeTensionSheets, std::make_tuple(OutputNameType::outGrp_activeTensionSheets, 0, 1, "Active_tension_sheets") }, + {OutputNameType::out_activeTensionNormal, std::make_tuple(OutputNameType::outGrp_activeTensionNormal, 0, 1, "Active_tension_normal") }, {OutputNameType::out_heatFlux, std::make_tuple(OutputNameType::outGrp_hFlx, 0, nsd, "Heat_flux") }, {OutputNameType::out_integ, std::make_tuple(OutputNameType::outGrp_I, 0, 1, nsd == 2 ? "Area" : "Volume") }, diff --git a/Code/Source/solver/sv_struct.cpp b/Code/Source/solver/sv_struct.cpp index ddc2f7652..9ab5feca6 100644 --- a/Code/Source/solver/sv_struct.cpp +++ b/Code/Source/solver/sv_struct.cpp @@ -224,7 +224,7 @@ void construct_dsolid(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const // STRUCT: dof = nsd Vector ptr(eNoN); - Vector pSl(nsymd), ya_l(eNoN), N(eNoN); + Vector pSl(nsymd), ya_l_f(eNoN), ya_l_s(eNoN), ya_l_n(eNoN), N(eNoN); Array xl(nsd,eNoN), al(tDof,eNoN), yl(tDof,eNoN), dl(tDof,eNoN), bfl(nsd,eNoN), fN(nsd,nFn), pS0l(nsymd,eNoN), Nx(nsd,eNoN), lR(dof,eNoN); Array3 lK(dof*dof,eNoN,eNoN); @@ -247,7 +247,9 @@ void construct_dsolid(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const // Create local copies fN = 0.0; pS0l = 0.0; - ya_l = 0.0; + ya_l_f = 0.0; + ya_l_s = 0.0; + ya_l_n = 0.0; for (int a = 0; a < eNoN; a++) { int Ac = lM.IEN(a,e); @@ -276,8 +278,10 @@ void construct_dsolid(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const pS0l.set_col(a, pS0.col(Ac)); } - if (cem.cpld) { - ya_l(a) = cem.Ya(Ac); + if (eq.dmn[cDmn].active_stress != nullptr) { + ya_l_f(a) = cep_mod.cem.Ya_f[Ac]; + ya_l_s(a) = cep_mod.cem.Ya_s[Ac]; + ya_l_n(a) = cep_mod.cem.Ya_n[Ac]; } } @@ -302,7 +306,8 @@ void construct_dsolid(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const pSl = 0.0; if (nsd == 3) { - struct_3d(com_mod, cep_mod, eNoN, nFn, w, N, Nx, al, yl, dl, bfl, fN, pS0l, pSl, ya_l, lR, lK); + struct_3d(com_mod, cep_mod, eNoN, nFn, w, N, Nx, al, yl, dl, bfl, fN, + pS0l, pSl, ya_l_f, ya_l_s, ya_l_n, lR, lK); #if 0 if (e == 0 && g == 0) { @@ -315,7 +320,8 @@ void construct_dsolid(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const #endif } else if (nsd == 2) { - struct_2d(com_mod, cep_mod, eNoN, nFn, w, N, Nx, al, yl, dl, bfl, fN, pS0l, pSl, ya_l, lR, lK); + struct_2d(com_mod, cep_mod, eNoN, nFn, w, N, Nx, al, yl, dl, bfl, fN, + pS0l, pSl, ya_l_f, ya_l_s, ya_l_n, lR, lK); } // Prestress @@ -336,11 +342,14 @@ void construct_dsolid(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const /// @brief Reproduces Fortran 'STRUCT2D' subroutine. // -void struct_2d(ComMod& com_mod, CepMod& cep_mod, const int eNoN, const int nFn, const double w, - const Vector& N, const Array& Nx, const Array& al, const Array& yl, - const Array& dl, const Array& bfl, const Array& fN, const Array& pS0l, - Vector& pSl, const Vector& ya_l, Array& lR, Array3& lK) -{ +void struct_2d(ComMod &com_mod, CepMod &cep_mod, const int eNoN, const int nFn, + const double w, const Vector &N, const Array &Nx, + const Array &al, const Array &yl, + const Array &dl, const Array &bfl, + const Array &fN, const Array &pS0l, + Vector &pSl, const Vector &ya_l_f, + const Vector &ya_l_s, const Vector &ya_l_n, + Array &lR, Array3 &lK) { using namespace consts; using namespace mat_fun; @@ -387,7 +396,10 @@ void struct_2d(ComMod& com_mod, CepMod& cep_mod, const int eNoN, const int nFn, F(0,0) = 1.0; F(1,1) = 1.0; S0 = 0.0; - double ya_g = 0.0; + + double ya_g_f = 0.0; + double ya_g_s = 0.0; + double ya_g_n = 0.0; for (int a = 0; a < eNoN; a++) { ud(0) = ud(0) + N(a)*(rho*(al(i,a)-bfl(0,a)) + dmp*yl(i,a)); @@ -407,20 +419,25 @@ void struct_2d(ComMod& com_mod, CepMod& cep_mod, const int eNoN, const int nFn, S0(1,1) = S0(1,1) + N(a)*pS0l(1,a); S0(0,1) = S0(0,1) + N(a)*pS0l(2,a); - ya_g = ya_g + N(a)*ya_l(a); + ya_g_f = ya_g_f + N(a) * ya_l_f(a); + ya_g_s = ya_g_s + N(a) * ya_l_s(a); + ya_g_n = ya_g_n + N(a) * ya_l_n(a); } #ifdef debug_struct_2d dmsg << "ud: " << ud(0) << " " << ud(1); dmsg << "F: " << F(0,0); - dmsg << "ya_g: " << ya_g; - #endif + dmsg << "ya_g_f: " << ya_g_f; + dmsg << "ya_g_s: " << ya_g_s; + dmsg << "ya_g_n: " << ya_g_n; +#endif S0(1,0) = S0(0,1); // 2nd Piola-Kirchhoff stress (S) and material stiffness tensor in Voight notation (Dm) Array S(2,2), Dm(3,3); double Ja; - mat_models::compute_pk2cc(com_mod, cep_mod, dmn, F, nFn, fN, ya_g, S, Dm, Ja); + mat_models::compute_pk2cc(com_mod, cep_mod, dmn, F, nFn, fN, ya_g_f, ya_g_s, + ya_g_n, S, Dm, Ja); // Viscous 2nd Piola-Kirchhoff stress and tangent contributions Array Svis(2,2); @@ -518,15 +535,17 @@ void struct_2d(ComMod& com_mod, CepMod& cep_mod, const int eNoN, const int nFn, lK(dof+1,a,b) = lK(dof+1,a,b) + w*( T1 + afu*(BmDBm + Kvis_u(3,a,b)) + afv*Kvis_v(3,a,b) ); } } - } /// @brief Reproduces Fortran 'STRUCT3D' subroutine. -void struct_3d(ComMod& com_mod, CepMod& cep_mod, const int eNoN, const int nFn, const double w, - const Vector& N, const Array& Nx, const Array& al, const Array& yl, - const Array& dl, const Array& bfl, const Array& fN, const Array& pS0l, - Vector& pSl, const Vector& ya_l, Array& lR, Array3& lK) -{ +void struct_3d(ComMod &com_mod, CepMod &cep_mod, const int eNoN, const int nFn, + const double w, const Vector &N, const Array &Nx, + const Array &al, const Array &yl, + const Array &dl, const Array &bfl, + const Array &fN, const Array &pS0l, + Vector &pSl, const Vector &ya_l_f, + const Vector &ya_l_s, const Vector &ya_l_n, + Array &lR, Array3 &lK) { using namespace consts; using namespace mat_fun; @@ -585,7 +604,10 @@ void struct_3d(ComMod& com_mod, CepMod& cep_mod, const int eNoN, const int nFn, F(1,1) = 1.0; F(2,2) = 1.0; S0 = 0.0; - double ya_g = 0.0; + + double ya_g_f = 0.0; + double ya_g_s = 0.0; + double ya_g_n = 0.0; for (int a = 0; a < eNoN; a++) { ud(0) = ud(0) + N(a)*(rho*(al(i,a)-bfl(0,a)) + dmp*yl(i,a)); @@ -619,7 +641,9 @@ void struct_3d(ComMod& com_mod, CepMod& cep_mod, const int eNoN, const int nFn, S0(1,2) = S0(1,2) + N(a)*pS0l(4,a); S0(2,0) = S0(2,0) + N(a)*pS0l(5,a); - ya_g = ya_g + N(a)*ya_l(a); + ya_g_f = ya_g_f + N(a) * ya_l_f(a); + ya_g_s = ya_g_s + N(a) * ya_l_s(a); + ya_g_n = ya_g_n + N(a) * ya_l_n(a); } S0(1,0) = S0(0,1); @@ -631,7 +655,8 @@ void struct_3d(ComMod& com_mod, CepMod& cep_mod, const int eNoN, const int nFn, // Array S(3,3), Dm(6,6); double Ja; - mat_models::compute_pk2cc(com_mod, cep_mod, dmn, F, nFn, fN, ya_g, S, Dm, Ja); + mat_models::compute_pk2cc(com_mod, cep_mod, dmn, F, nFn, fN, ya_g_f, ya_g_s, + ya_g_n, S, Dm, Ja); // Viscous 2nd Piola-Kirchhoff stress and tangent contributions Array Svis(3,3); @@ -799,6 +824,5 @@ void struct_3d(ComMod& com_mod, CepMod& cep_mod, const int eNoN, const int nFn, } } } - }; diff --git a/Code/Source/solver/sv_struct.h b/Code/Source/solver/sv_struct.h index 7f1eabc94..a0018305c 100644 --- a/Code/Source/solver/sv_struct.h +++ b/Code/Source/solver/sv_struct.h @@ -19,16 +19,23 @@ void b_struct_3d(const ComMod& com_mod, const int eNoN, const double w, const Ve void construct_dsolid(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const SolutionStates& solutions); -void struct_2d(ComMod& com_mod, CepMod& cep_mod, const int eNoN, const int nFn, const double w, - const Vector& N, const Array& Nx, const Array& al, const Array& yl, - const Array& dl, const Array& bfl, const Array& fN, const Array& pS0l, - Vector& pSl, const Vector& ya_l, Array& lR, Array3& lK); - -void struct_3d(ComMod& com_mod, CepMod& cep_mod, const int eNoN, const int nFn, const double w, - const Vector& N, const Array& Nx, const Array& al, const Array& yl, - const Array& dl, const Array& bfl, const Array& fN, const Array& pS0l, - Vector& pSl, const Vector& ya_l, Array& lR, Array3& lK); - +void struct_2d(ComMod &com_mod, CepMod &cep_mod, const int eNoN, const int nFn, + const double w, const Vector &N, const Array &Nx, + const Array &al, const Array &yl, + const Array &dl, const Array &bfl, + const Array &fN, const Array &pS0l, + Vector &pSl, const Vector &ya_l_f, + const Vector &ya_l_s, const Vector &ya_l_n, + Array &lR, Array3 &lK); + +void struct_3d(ComMod &com_mod, CepMod &cep_mod, const int eNoN, const int nFn, + const double w, const Vector &N, const Array &Nx, + const Array &al, const Array &yl, + const Array &dl, const Array &bfl, + const Array &fN, const Array &pS0l, + Vector &pSl, const Vector &ya_l_f, + const Vector &ya_l_s, const Vector &ya_l_n, + Array &lR, Array3 &lK); }; #endif diff --git a/Code/Source/solver/ustruct.cpp b/Code/Source/solver/ustruct.cpp index 09777be1f..69be045c7 100644 --- a/Code/Source/solver/ustruct.cpp +++ b/Code/Source/solver/ustruct.cpp @@ -249,7 +249,7 @@ void construct_usolid(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const // USTRUCT: dof = nsd+1 Vector ptr(eNoN); - Vector pSl(nsymd), ya_l(eNoN), N(eNoN); + Vector pSl(nsymd), ya_l_f(eNoN), ya_l_s(eNoN), ya_l_n(eNoN), N(eNoN); Array xl(nsd,eNoN), al(tDof,eNoN), yl(tDof,eNoN), dl(tDof,eNoN), bfl(nsd,eNoN), fN(nsd,nFn), pS0l(nsymd,eNoN), Nx(nsd,eNoN), lR(dof,eNoN); Array3 lK(dof*dof,eNoN,eNoN), lKd(dof*nsd,eNoN,eNoN); @@ -264,7 +264,9 @@ void construct_usolid(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const // Create local copies fN = 0.0; - ya_l = 0.0; + ya_l_f = 0.0; + ya_l_s = 0.0; + ya_l_n = 0.0; for (int a = 0; a < eNoN; a++) { int Ac = lM.IEN(a,e); @@ -289,8 +291,10 @@ void construct_usolid(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const } } - if (cem.cpld) { - ya_l(a) = cem.Ya(Ac); + if (eq.dmn[cDmn].active_stress != nullptr) { + ya_l_f(a) = cep_mod.cem.Ya_f[Ac]; + ya_l_s(a) = cep_mod.cem.Ya_s[Ac]; + ya_l_n(a) = cep_mod.cem.Ya_n[Ac]; } } @@ -336,12 +340,16 @@ void construct_usolid(ComMod& com_mod, CepMod& cep_mod, const mshType& lM, const if (nsd == 3) { auto N0 = fs[0].N.col(g); auto N1 = fs[1].N.col(g); - ustruct_3d_m(com_mod, cep_mod, vmsStab, fs[0].eNoN, fs[1].eNoN, nFn, w, Jac, N0, N1, Nwx, al, yl, dl, bfl, fN, ya_l, lR, lK, lKd); + ustruct_3d_m(com_mod, cep_mod, vmsStab, fs[0].eNoN, fs[1].eNoN, nFn, w, + Jac, N0, N1, Nwx, al, yl, dl, bfl, fN, ya_l_f, ya_l_s, + ya_l_n, lR, lK, lKd); } else if (nsd == 2) { auto N0 = fs[0].N.col(g); auto N1 = fs[1].N.col(g); - ustruct_2d_m(com_mod, cep_mod, vmsStab, fs[0].eNoN, fs[1].eNoN, nFn, w, Jac, N0, N1, Nwx, al, yl, dl, bfl, fN, ya_l, lR, lK, lKd); + ustruct_2d_m(com_mod, cep_mod, vmsStab, fs[0].eNoN, fs[1].eNoN, nFn, w, + Jac, N0, N1, Nwx, al, yl, dl, bfl, fN, ya_l_f, ya_l_s, + ya_l_n, lR, lK, lKd); } } // for g = 0 to fs[0].nG @@ -864,12 +872,15 @@ void ustruct_3d_c(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const in /// @brief Replicates Fortran USTRUCT2D_M. // -void ustruct_2d_m(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const int eNoNw, const int eNoNq, - const int nFn, const double w, const double Je, const Vector& Nw, const Vector& Nq, - const Array& Nwx, const Array& al, const Array& yl, const Array& dl, - const Array& bfl, const Array& fN, const Vector& ya_l, Array& lR, - Array3& lK, Array3& lKd) -{ +void ustruct_2d_m(ComMod &com_mod, CepMod &cep_mod, const bool vmsFlag, + const int eNoNw, const int eNoNq, const int nFn, + const double w, const double Je, const Vector &Nw, + const Vector &Nq, const Array &Nwx, + const Array &al, const Array &yl, + const Array &dl, const Array &bfl, + const Array &fN, const Vector &ya_l_f, + const Vector &ya_l_s, const Vector &ya_l_n, + Array &lR, Array3 &lK, Array3 &lKd) { using namespace consts; using namespace mat_fun; @@ -916,7 +927,11 @@ void ustruct_2d_m(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const in Vector vd{-fb[0], -fb[1]}; Vector v(2); Array vx(2,2), F(2,2); - double ya_g = 0.0; + + double ya_g_f = 0.0; + double ya_g_s = 0.0; + double ya_g_n = 0.0; + F(0,0) = 1.0; F(1,1) = 1.0; @@ -937,7 +952,9 @@ void ustruct_2d_m(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const in F(1,0) = F(1,0) + Nwx(0,a)*dl(j,a); F(1,1) = F(1,1) + Nwx(1,a)*dl(j,a); - ya_g = ya_g + Nw(a)*ya_l(a); + ya_g_f = ya_g_f + Nw(a) * ya_l_f(a); + ya_g_s = ya_g_s + Nw(a) * ya_l_s(a); + ya_g_n = ya_g_n + Nw(a) * ya_l_n(a); } double Jac = mat_fun::mat_det(F, 2); @@ -957,9 +974,10 @@ void ustruct_2d_m(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const in // isochoric elasticity tensor in Voigt notation (Dm) Array Siso(2,2), Dm(3,3); double Ja = 0; - mat_models::compute_pk2cc(com_mod, cep_mod, eq.dmn[cDmn], F, nFn, fN, ya_g, Siso, Dm, Ja); + mat_models::compute_pk2cc(com_mod, cep_mod, eq.dmn[cDmn], F, nFn, fN, ya_g_f, + ya_g_s, ya_g_n, Siso, Dm, Ja); - // Viscous 2nd Piola-Kirchhoff stress and tangent contributions + // Viscous 2nd Piola-Kirchhoff stress and tangent contributions Array Svis(2,2); Array3 Kvis_u(4, eNoNw, eNoNw); Array3 Kvis_v(4, eNoNw, eNoNw); @@ -1144,12 +1162,15 @@ void ustruct_2d_m(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const in /// @brief Reproduces Fortran USTRUCT3D_M. // -void ustruct_3d_m(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const int eNoNw, const int eNoNq, - const int nFn, const double w, const double Je, const Vector& Nw, const Vector& Nq, - const Array& Nwx, const Array& al, const Array& yl, const Array& dl, - const Array& bfl, const Array& fN, const Vector& ya_l, Array& lR, - Array3& lK, Array3& lKd) -{ +void ustruct_3d_m(ComMod &com_mod, CepMod &cep_mod, const bool vmsFlag, + const int eNoNw, const int eNoNq, const int nFn, + const double w, const double Je, const Vector &Nw, + const Vector &Nq, const Array &Nwx, + const Array &al, const Array &yl, + const Array &dl, const Array &bfl, + const Array &fN, const Vector &ya_l_f, + const Vector &ya_l_s, const Vector &ya_l_n, + Array &lR, Array3 &lK, Array3 &lKd) { using namespace consts; using namespace mat_fun; @@ -1199,7 +1220,11 @@ void ustruct_3d_m(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const in Vector vd{-fb[0], -fb[1], -fb[2]}; Vector v(3); Array vx(3,3), F(3,3); - double ya_g = 0.0; + + double ya_g_f = 0.0; + double ya_g_s = 0.0; + double ya_g_n = 0.0; + F(0,0) = 1.0; F(1,1) = 1.0; F(2,2) = 1.0; @@ -1237,7 +1262,9 @@ void ustruct_3d_m(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const in F(2,1) = F(2,1) + Nwx(1,a)*dl(k,a); F(2,2) = F(2,2) + Nwx(2,a)*dl(k,a); - ya_g = ya_g + Nw(a)*ya_l(a); + ya_g_f = ya_g_f + Nw(a) * ya_l_f(a); + ya_g_s = ya_g_s + Nw(a) * ya_l_s(a); + ya_g_n = ya_g_n + Nw(a) * ya_l_n(a); } double Jac = mat_fun::mat_det(F, 3); @@ -1258,7 +1285,8 @@ void ustruct_3d_m(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const in // Array Siso(3,3), Dm(6,6); double Ja = 0; - mat_models::compute_pk2cc(com_mod, cep_mod, eq.dmn[cDmn], F, nFn, fN, ya_g, Siso, Dm, Ja); + mat_models::compute_pk2cc(com_mod, cep_mod, eq.dmn[cDmn], F, nFn, fN, ya_g_f, + ya_g_s, ya_g_n, Siso, Dm, Ja); // Viscous 2nd Piola-Kirchhoff stress and tangent contributions Array Svis(3,3); @@ -1560,7 +1588,6 @@ void ustruct_3d_m(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const in lK(11,a,b) = lK(11,a,b) + w*Jac*T1; } } - } /// @brief Replicates 'SUBROUTINE USTRUCT_DOASSEM(d, eqN, lKd, lK, lR)' diff --git a/Code/Source/solver/ustruct.h b/Code/Source/solver/ustruct.h index 3020acacc..953d4d035 100644 --- a/Code/Source/solver/ustruct.h +++ b/Code/Source/solver/ustruct.h @@ -29,11 +29,15 @@ void ustruct_2d_c(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const in const Array& dl, const Array& bfl, Array& lR, Array3& lK, Array3& lKd); -void ustruct_2d_m(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const int eNoNw, const int eNoNq, - const int nFn, const double w, const double Je, const Vector& Nw, const Vector& Nq, - const Array& Nwx, const Array& al, const Array& yl, const Array& dl, - const Array& bfl, const Array& fN, const Vector& ya_l, Array& lR, - Array3& lK, Array3& lKd); +void ustruct_2d_m(ComMod &com_mod, CepMod &cep_mod, const bool vmsFlag, + const int eNoNw, const int eNoNq, const int nFn, + const double w, const double Je, const Vector &Nw, + const Vector &Nq, const Array &Nwx, + const Array &al, const Array &yl, + const Array &dl, const Array &bfl, + const Array &fN, const Vector &ya_l_f, + const Vector &ya_l_s, const Vector &ya_l_n, + Array &lR, Array3 &lK, Array3 &lKd); void ustruct_3d_c(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const int eNoNw, const int eNoNq, const double w, const double Je, const Vector& Nw, const Vector& Nq, @@ -41,11 +45,15 @@ void ustruct_3d_c(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const in const Array& dl, const Array& bfl, Array& lR, Array3& lK, Array3& lKd); -void ustruct_3d_m(ComMod& com_mod, CepMod& cep_mod, const bool vmsFlag, const int eNoNw, const int eNoNq, - const int nFn, const double w, const double Je, const Vector& Nw, const Vector& Nq, - const Array& Nwx, const Array& al, const Array& yl, const Array& dl, - const Array& bfl, const Array& fN, const Vector& ya_l, Array& lR, - Array3& lK, Array3& lKd); +void ustruct_3d_m(ComMod &com_mod, CepMod &cep_mod, const bool vmsFlag, + const int eNoNw, const int eNoNq, const int nFn, + const double w, const double Je, const Vector &Nw, + const Vector &Nq, const Array &Nwx, + const Array &al, const Array &yl, + const Array &dl, const Array &bfl, + const Array &fN, const Vector &ya_l_f, + const Vector &ya_l_s, const Vector &ya_l_n, + Array &lR, Array3 &lK, Array3 &lKd); void ustruct_do_assem(ComMod& com_mod, const int d, const Vector& eqN, const Array3& lKd, const Array3& lK, const Array& lR); diff --git a/Code/Source/solver/vtk_xml.cpp b/Code/Source/solver/vtk_xml.cpp index 4468cf95d..c9c94f476 100644 --- a/Code/Source/solver/vtk_xml.cpp +++ b/Code/Source/solver/vtk_xml.cpp @@ -1331,6 +1331,27 @@ void write_vtus(Simulation* simulation, const SolutionStates& solutions, const b } } break; + case OutputNameType::outGrp_activeTensionFibers: { + for (int a = 0; a < msh.nNo; a++) { + int Ac = msh.gN(a); + d[iM].x(is, a) = simulation->cep_mod.cem.Ya_f[Ac]; + } + } break; + + case OutputNameType::outGrp_activeTensionSheets: { + for (int a = 0; a < msh.nNo; a++) { + int Ac = msh.gN(a); + d[iM].x(is, a) = simulation->cep_mod.cem.Ya_s[Ac]; + } + } break; + + case OutputNameType::outGrp_activeTensionNormal: { + for (int a = 0; a < msh.nNo; a++) { + int Ac = msh.gN(a); + d[iM].x(is, a) = simulation->cep_mod.cem.Ya_n[Ac]; + } + } break; + default: throw std::runtime_error("Undefined output"); break; diff --git a/Documentation/Doxyfile b/Documentation/Doxyfile index fba8c016a..d90c27367 100644 --- a/Documentation/Doxyfile +++ b/Documentation/Doxyfile @@ -190,11 +190,12 @@ ENUM_VALUES_PER_LINE = 1 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 +FORMULA_MACROFILE = ./Documentation/macros.tex USE_MATHJAX = YES MATHJAX_VERSION = MathJax_3 MATHJAX_FORMAT = chtml MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@3 -MATHJAX_EXTENSIONS = ams +MATHJAX_EXTENSIONS = ams physics MATHJAX_CODEFILE = SEARCHENGINE = YES SERVER_BASED_SEARCH = NO diff --git a/Documentation/macros.tex b/Documentation/macros.tex new file mode 100644 index 000000000..18601e50d --- /dev/null +++ b/Documentation/macros.tex @@ -0,0 +1,8 @@ +\newcommand{\fiberdirection}{\mathbf{f}} +\newcommand{\sheetdirection}{\mathbf{s}} +\newcommand{\sheetnormaldirection}{\mathbf{n}} +\newcommand{\calcium}{[\text{Ca}^{2+}]} +\newcommand{\Tact}{T_\text{act}} +\newcommand{\fiberstretch}{\lambda} +\newcommand{\fiberstretchrate}{\dot{\lambda}} +\newcommand{\astressstate}{\mathbf{s}} \ No newline at end of file diff --git a/tests/cases/electromechanics/slab/README.md b/tests/cases/electromechanics/slab/README.md new file mode 100755 index 000000000..c37f66d62 --- /dev/null +++ b/tests/cases/electromechanics/slab/README.md @@ -0,0 +1,71 @@ + +# **Problem Description** + +Simulate cardiac electromechanics on a slab of myocardial tissue. This test +couples cardiac electrophysiology (`CEP`) to solid mechanics (`struct`), +reproducing the geometry and stimulation setting of the Niederer electrophysiology +benchmark [1] with the addition of active contraction and finite-strain +mechanics. + +## Electrophysiology + +The propagation of the transmembrane potential is modeled with the +ten-Tusscher-Panfilov (`TTP`) cell activation model [2, 3], using epicardial +parameters (included from `../../cep/ttp_parameters/ttp_epicardium_parameters.xml`) +and anisotropic conductivity aligned with the fiber direction. The domain is split +into two `Domain`s: an unstimulated region (`domain 1`) and a stimulated region +(`domain 2`) where an external `Istim` stimulus initiates depolarization. + +``` + + -35.714 + 0.0 + 2.0 + 10000.0 + +``` + +## Mechanics + +The tissue is modeled as a nearly incompressible Holzapfel-Ogden material with +modified anisotropy (`HolzapfelOgden-ModifiedAnisotropy`) [4]. Active contraction +is driven by the calcium concentration computed by the electrophysiology model, +through the Nash-Panfilov active-stress model [5] with a directional distribution +along the fiber, sheet, and sheet-normal directions. + +``` + + NashPanfilov + + 0.7 + 0.2 + 0.1 + + ... + +``` + +The slab is fixed with a zero-displacement Dirichlet boundary condition on the +`X1` face, and contracts as the depolarization wave propagates through the tissue. + +## References + +[1] S. A. Niederer, E. Kerfoot, A. P. Benson, et al. Verification of cardiac tissue +electrophysiology simulators using an N-version benchmark. Philosophical Transactions +of the Royal Society A, 369(1954):4331–4351, 2011. + +[2] K. H. W. J. ten Tusscher, D. Noble, P. J. Noble, and A. V. Panfilov. A model for +human ventricular tissue. American Journal of Physiology-Heart and Circulatory +Physiology, 286(4):H1573–H1589, apr 2004. + +[3] K. H. W. J. ten Tusscher and A. V. Panfilov. Alternans and spiral breakup in a +human ventricular tissue model. American Journal of Physiology-Heart and Circulatory +Physiology, 291(3):H1088–H1100, sep 2006. + +[4] G. A. Holzapfel and R. W. Ogden. Constitutive modelling of passive myocardium: a +structurally based framework for material characterization. Philosophical Transactions +of the Royal Society A, 367(1902):3445–3475, 2009. + +[5] M. P. Nash and A. V. Panfilov. Electromechanical model of excitable tissue to +study reentrant cardiac arrhythmias. Progress in Biophysics and Molecular Biology, +85(2-3):501–522, 2004. \ No newline at end of file diff --git a/tests/cases/electromechanics/slab/mesh/X0.vtp b/tests/cases/electromechanics/slab/mesh/X0.vtp new file mode 100644 index 000000000..eaebcc0eb --- /dev/null +++ b/tests/cases/electromechanics/slab/mesh/X0.vtp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c0889ad4a8a4659996309b5bba825c1ac5c3f03d2faca2e84e48f1e035ca240 +size 5219 diff --git a/tests/cases/electromechanics/slab/mesh/X1.vtp b/tests/cases/electromechanics/slab/mesh/X1.vtp new file mode 100644 index 000000000..07ad82435 --- /dev/null +++ b/tests/cases/electromechanics/slab/mesh/X1.vtp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8248936f54f9537dcad8e2344beb804485d0f5cbfb960f0a8b025310e1af99ae +size 5128 diff --git a/tests/cases/electromechanics/slab/mesh/volume.vtu b/tests/cases/electromechanics/slab/mesh/volume.vtu new file mode 100644 index 000000000..dd12aca89 --- /dev/null +++ b/tests/cases/electromechanics/slab/mesh/volume.vtu @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d9c504f6d9dc221739d107e19414233cdda640d73647eba03902a1605274149 +size 421967 diff --git a/tests/cases/electromechanics/slab/result_001.vtu b/tests/cases/electromechanics/slab/result_001.vtu new file mode 100644 index 000000000..218eb3976 --- /dev/null +++ b/tests/cases/electromechanics/slab/result_001.vtu @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21118d35f6feef67ca5def7ec4b963216841cfd434d7471fedc3de8707ff8627 +size 1447264 diff --git a/tests/cases/electromechanics/slab/solver.xml b/tests/cases/electromechanics/slab/solver.xml new file mode 100644 index 000000000..528cd0739 --- /dev/null +++ b/tests/cases/electromechanics/slab/solver.xml @@ -0,0 +1,187 @@ + + + + false + 3 + 1 + 1.0 + 0.50 + STOP_SIM + + true + result + 1 + 0 + + 1000 + 0 + + 1 + 1 + 0 + + + + ./mesh/volume.vtu + + + ./mesh/X0.vtp + + + + ./mesh/X1.vtp + + + ./mesh/volume.vtu + + (1, 0, 0) + (0, 1, 0) + (0, 1, 0) + + + + true + + 1 + 1 + 1e-12 + + + TTP + + 0.012571 + 0.082715 + 0.0 + 0.0 + + ../../cep/ttp_parameters/ttp_epicardium_parameters.xml + + + 14.838 + 3.98E-5 + 0.153 + + + RK4 + + + + TTP + + 0.012571 + 0.082715 + 0.0 + 0.0 + + ../../cep/ttp_parameters/ttp_epicardium_parameters.xml + + + 14.838 + 3.98E-5 + 0.153 + + + + -35.714 + 0.0 + 2.0 + 10000.0 + + + RK4 + + + + true + true + + + + + fsils + + 100 + 1e-12 + 50 + + + + + 1 + 6 + 1e-12 + + 1e-3 + + + 59.0e-6 + 8.023 + 18472.0e-6 + 16.026 + 2481.0e-6 + 11.12 + 216.0e-6 + 11.436 + 100.0 + + + ST91 + 1.0 + + + 1.0 + + + + NashPanfilov + + + 0.7 + 0.2 + 0.1 + + + + FE + + 0.1 + 1.0 + 4.0e3 + 1e2 + 1.25e-4 + 8e-4 + + + + + true + true + true + true + true + true + true + true + + true + true + true + + + + + fsils + + 1e-12 + 1e-14 + 1000 + + + + Dir + 0.0 + + + + + diff --git a/tests/cases/struct/LV_HolzapfelOgden_active/solver.xml b/tests/cases/struct/LV_HolzapfelOgden_active/solver.xml index 2b71e8ac6..91c3cc3fc 100644 --- a/tests/cases/struct/LV_HolzapfelOgden_active/solver.xml +++ b/tests/cases/struct/LV_HolzapfelOgden_active/solver.xml @@ -76,10 +76,14 @@ ST91 1e6 - - LV_stress.dat - false - + + UniformUnsteady + + + false + LV_stress.dat + + true diff --git a/tests/cases/struct/directionally_distributed_active_stress/README.md b/tests/cases/struct/directionally_distributed_active_stress/README.md index 7bd1cd282..f2c342d2c 100644 --- a/tests/cases/struct/directionally_distributed_active_stress/README.md +++ b/tests/cases/struct/directionally_distributed_active_stress/README.md @@ -24,14 +24,20 @@ allows the total active stress to be distributed among the three orthogonal fibe The fractions are specified in the solver.xml file as: ```xml - - stress.dat + + UniformUnsteady + 0.7 0.2 0.1 - + + + false + stress.dat + + ``` These fractions must sum to 1.0. diff --git a/tests/cases/struct/directionally_distributed_active_stress/solver.xml b/tests/cases/struct/directionally_distributed_active_stress/solver.xml index 5411ad0fa..ff2765222 100644 --- a/tests/cases/struct/directionally_distributed_active_stress/solver.xml +++ b/tests/cases/struct/directionally_distributed_active_stress/solver.xml @@ -86,15 +86,20 @@ 1e7 - - stress.dat - false + + UniformUnsteady + 0.7 0.2 0.1 - + + + false + stress.dat + + true diff --git a/tests/cases/struct/tensile_adventitia_Guccione_active/solver.xml b/tests/cases/struct/tensile_adventitia_Guccione_active/solver.xml index d70d088ce..a46f517c0 100644 --- a/tests/cases/struct/tensile_adventitia_Guccione_active/solver.xml +++ b/tests/cases/struct/tensile_adventitia_Guccione_active/solver.xml @@ -81,10 +81,14 @@ ST91 - - stress.dat - false - + + UniformUnsteady + + + false + stress.dat + + true diff --git a/tests/cases/ustruct/LV_Guccione_active/solver.xml b/tests/cases/ustruct/LV_Guccione_active/solver.xml index 32cbf658c..b472ced03 100644 --- a/tests/cases/ustruct/LV_Guccione_active/solver.xml +++ b/tests/cases/ustruct/LV_Guccione_active/solver.xml @@ -57,10 +57,14 @@ - - fib_stress.dat - true - + + UniformUnsteady + + + true + fib_stress.dat + + 1e-3 1e-3 diff --git a/tests/cases/ustruct/LV_HolzapfelOgden_active/solver.xml b/tests/cases/ustruct/LV_HolzapfelOgden_active/solver.xml index 24c553250..3a0defe27 100644 --- a/tests/cases/ustruct/LV_HolzapfelOgden_active/solver.xml +++ b/tests/cases/ustruct/LV_HolzapfelOgden_active/solver.xml @@ -79,10 +79,14 @@ 1e-5 1e-5 - - LV_stress.dat - false - + + UniformUnsteady + + + false + LV_stress.dat + + true diff --git a/tests/conftest.py b/tests/conftest.py index 38f058381..a0e8ce9be 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -96,6 +96,9 @@ def _detect_oversubscribe_flag(): "WSS": 1.0e-8, "Fiber_stretch": 1.0e-10, "Fiber_stretch_rate": 1.0e-10, + "Active_tension_fibers": 1.0e-10, + "Active_tension_sheets": 1.0e-10, + "Active_tension_normal": 1.0e-10, } # Number of processors to test diff --git a/tests/test_electromechanics.py b/tests/test_electromechanics.py new file mode 100644 index 000000000..5eaa0cdad --- /dev/null +++ b/tests/test_electromechanics.py @@ -0,0 +1,28 @@ +from .conftest import run_with_reference +import os +import subprocess + +# Common folder for all tests in this file +base_folder = "electromechanics" + +# Fields to test +fields = [ + "Membrane_potential", + "Calcium", + "Cauchy_stress", + "Def_grad", + "Displacement", + "Jacobian", + "Stress", + "Strain", + "Velocity", + "VonMises_stress", + "Active_tension_fibers", + "Active_tension_sheets", + "Active_tension_normal", +] + + +def test_slab(n_proc): + test_folder = "slab" + run_with_reference(base_folder, test_folder, fields, n_proc, t_max=1) diff --git a/tests/unitTests/material_model_tests/test_material_common.h b/tests/unitTests/material_model_tests/test_material_common.h index dc41639f1..42f74498e 100644 --- a/tests/unitTests/material_model_tests/test_material_common.h +++ b/tests/unitTests/material_model_tests/test_material_common.h @@ -300,7 +300,9 @@ class TestMaterialModel : public TestBase { public: int nFn; Array fN; - double ya_g; + double ya_g_f; + double ya_g_s; + double ya_g_n; bool ustruct; TestMaterialModel(const consts::ConstitutiveModelType matType, const consts::ConstitutiveModelType penType) { @@ -315,7 +317,9 @@ class TestMaterialModel : public TestBase { // Initialize fibers and other material parameters nFn = 2; // Number of fiber directions fN = Array(nsd, nFn); // Fiber directions array (initialized to zeros) - ya_g = 0.0; // ? + ya_g_f = 0.0; // Active tension along fibers. + ya_g_s = 0.0; // Active tension along sheets. + ya_g_n = 0.0; // Active tension along sheet normals. // Flag to use struct or ustruct material models // If struct, calls compute_pk2cc() and uses strain energy composed of isochoric and volumetric parts @@ -354,8 +358,8 @@ class TestMaterialModel : public TestBase { } // Call compute_pk2cc to compute S and Dm - mat_models::compute_pk2cc(com_mod, cep_mod, dmn, F, nFn, fN, ya_g, S, Dm, J); - + mat_models::compute_pk2cc(com_mod, cep_mod, dmn, F, nFn, fN, ya_g_f, + ya_g_s, ya_g_n, S, Dm, J); } /**